第 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 决策流程

通过

失败

用户提问
『华东区各品类销售额?』

Agent 决策

调用 text_to_sql

LLM 生成 SQL
含 Schema 上下文

安全校验
语法检查 + 权限控制

执行 SQL
SQLite

返回错误 → 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 观察结果后放宽条件

六、总结

能力技术效果
自然语言转 SQLLLM + Schema 上下文准确率 90%+
SQL 安全校验4 层防火墙防止危险操作
自动执行sqlite3 直接执行秒级响应
结果格式化表格对齐可读性强

Text-to-SQL 是场景二的核心工具。下一课,我们将引入 ReAct 推理模式——让 Agent 学会多步思考和自我纠错。


本课代码:code/lesson-23-text-to-sql/text_to_sql_agent.py

Logo

聚焦RDK与具身智能,汇集机器人开发教程、开源案例和技术交流

更多推荐