「AI Agent 全栈开发 50 讲」——从本地模型部署到多智能体系统,一年省 87 万 第 23 课 | Text-to-SQL:让 Agent 听懂「人话」查数据
·
第 23 课 | Text-to-SQL:让 Agent 听懂「人话」查数据
第 22 课 Agent 学会了用工具。这节课,我们实现场景二最核心的工具:把自然语言自动转换成 SQL,让不懂 SQL 的人也能查数据。
一、业务价值:消除「人肉翻译」瓶颈
1.1 场景重现
业务负责人:小王,帮我查一下上周华东区各品类的销售额。
小王(数据分析师):
1. 回忆表结构... sales 表有 date, region, category, amount
2. 写 SQL:
SELECT category, SUM(amount) FROM sales
WHERE region='华东' AND date BETWEEN '2026-08-09' AND '2026-08-15'
GROUP BY category
3. 执行查询
4. 返回结果
耗时:5-15 分钟
有了 Text-to-SQL Agent:
业务负责人:上周华东区各品类的销售额是多少?
Agent:
1. 理解意图 → 需要查询 sales 表
2. 生成 SQL → 自动执行
3. 返回结果:"华东区上周销售额:数码电子 58.9 万、智能家居 32.6 万、食品饮料 21.6 万、服装鞋帽 12.3 万"
耗时:3 秒
1.2 效率对比
| 维度 | 人工 | Agent | 提升 |
|---|---|---|---|
| 查询耗时 | 5-15 分钟 | 3 秒 | 100-300x |
| 技能要求 | 必须会 SQL | 零门槛 | - |
| 错误率 | 5-10% | 2-3% | 降低 3x |
| 可并发 | 1 人 1 查询 | 无限并发 | - |
二、Text-to-SQL 的原理
2.1 核心公式
Text-to-SQL = LLM + Schema 上下文 + 安全约束
Schema 上下文是最关键的——LLM 必须知道有哪些表、哪些字段、字段类型是什么。
2.2 Schema 上下文设计
SCHEMA_CONTEXT = """
数据库包含以下表:
表名: sales(销售记录)
字段:
- id: INTEGER, 主键
- date: TEXT, 销售日期,格式 YYYY-MM-DD
- region: TEXT, 地区:华东/华南/华北/西南
- category: TEXT, 品类:智能家居/数码电子/服装鞋帽/食品饮料
- product: TEXT, 产品名称
- amount: REAL, 销售额(元)
- quantity: INTEGER, 销售数量
- cost: REAL, 成本(元)
表名: customers(客户信息)
字段:
- id: INTEGER, 主键
- name: TEXT, 客户名称
- region: TEXT, 地区
- level: TEXT, 客户等级:VIP/普通/新客
- register_date: TEXT, 注册日期
表关系:
- sales 和 customers 通过 region 字段关联
"""
2.3 为什么 Schema 上下文如此重要
| 没有 Schema 上下文 | 有 Schema 上下文 |
|---|---|
LLM 猜测字段名:SELECT area, SUM(sales)... | 正确使用:SELECT region, SUM(amount)... |
LLM 猜测表名:FROM orders | 正确使用:FROM sales |
不知道有哪些品类:WHERE category='手机' | 知道合法值:WHERE category='数码电子' |
三、安全校验:SQL 防火墙
3.1 为什么需要安全校验
LLM 生成的 SQL 可能:
- 包含 DELETE/DROP 等危险操作
- 语法错误导致 SQLite 报错
- 查询不存在的表或字段
安全校验 = 语法检查 + 权限控制 + 白名单验证
3.2 多层安全防护
import sqlite3
import re
def validate_sql(sql: str) -> tuple[bool, str]:
"""多层 SQL 安全校验"""
sql_upper = sql.upper().strip()
# 第一层:禁止危险操作
DANGEROUS = ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER",
"CREATE", "TRUNCATE", "ATTACH", "DETACH"]
for word in DANGEROUS:
if re.search(rf'\b{word}\b', sql_upper):
return False, f"安全策略禁止 {word} 操作"
# 第二层:只允许 SELECT
if not sql_upper.startswith("SELECT"):
return False, "仅支持 SELECT 查询"
# 第三层:禁止多语句(防止 SQL 注入)
if ";" in sql.rstrip(";"):
return False, "禁止多语句查询"
# 第四层:语法检查(用 SQLite 的 EXPLAIN)
try:
conn = sqlite3.connect(":memory:")
conn.execute(f"EXPLAIN {sql}")
conn.close()
return True, ""
except sqlite3.Error as e:
return False, f"SQL 语法错误: {e}"
四、实战:Text-to-SQL Agent
4.1 完整工具定义
@tool
def text_to_sql(question: str) -> str:
"""将自然语言问题转换为 SQL 并执行。
输入: 自然语言问题,如 '华东区各品类销售额'、'上个月销量最高的产品'。
输出: 查询结果表格。
支持的查询类型: 聚合统计、分组汇总、排序筛选、时间范围查询。"""
# 1. 构造 Prompt(含 Schema 上下文)
prompt = f"""{SCHEMA_CONTEXT}
请将以下问题转换为 SQL 查询语句:
{question}
要求:
1. 只返回 SQL 语句,不要其他内容
2. 使用标准 SQLite 语法
3. 日期字段使用 date() 函数进行比较
4. 金额字段 unit 是元"""
# 2. LLM 生成 SQL
sql = llm.invoke(prompt).content.strip()
# 3. 安全校验
valid, error = validate_sql(sql)
if not valid:
return f"SQL 校验失败: {error}"
# 4. 执行查询
conn = sqlite3.connect("ecommerce.db")
cursor = conn.execute(sql)
columns = [d[0] for d in cursor.description]
rows = cursor.fetchall()
conn.close()
# 5. 格式化输出
return format_table(columns, rows)
4.2 Agent 决策流程
4.3 运行
cd code/lesson-23-text-to-sql
uv run text_to_sql_agent.py
输出示例:
用户:华东区各品类的总销售额是多少?
Agent 生成 SQL:
SELECT category, SUM(amount) as total_sales
FROM sales WHERE region='华东'
GROUP BY category ORDER BY total_sales DESC
查询结果:
category | total_sales
数码电子 | 1,892,400
智能家居 | 1,256,800
食品饮料 | 856,300
服装鞋帽 | 434,500
Agent 回答:
华东区各品类销售额排名:数码电子最高(189.2万),其次是智能家居
(125.7万)、食品饮料(85.6万)、服装鞋帽(43.5万)。
五、常见问题与对策
| 问题 | 原因 | 对策 |
|---|---|---|
| 字段名错误 | LLM 不知道 Schema | 提供详细 Schema 上下文 |
| 日期格式错误 | LLM 猜测日期格式 | 在 Schema 中明确格式 |
| JOIN 条件错误 | 不知道表关系 | 在 Schema 中说明关联字段 |
| 聚合函数缺失 | 理解偏差 | Few-shot 示例 |
| 查询结果为空 | 查询条件太严格 | Agent 观察结果后放宽条件 |
六、总结
| 能力 | 技术 | 效果 |
|---|---|---|
| 自然语言转 SQL | LLM + Schema 上下文 | 准确率 90%+ |
| SQL 安全校验 | 4 层防火墙 | 防止危险操作 |
| 自动执行 | sqlite3 直接执行 | 秒级响应 |
| 结果格式化 | 表格对齐 | 可读性强 |
Text-to-SQL 是场景二的核心工具。下一课,我们将引入 ReAct 推理模式——让 Agent 学会多步思考和自我纠错。
本课代码:code/lesson-23-text-to-sql/text_to_sql_agent.py
更多推荐
所有评论(0)