做 Text-to-SQL Agent 这件事,我最近刚好把一个内部数据问答项目从"能跑通 Demo"推到"敢给运营同事用",整个过程下来最深的感触就是:把自然语言转成 SQL 只是最简单的 5%,剩下 95% 的精力全花在标题里说的"另外三件事"上。
很多团队第一次做 Text-to-SQL Agent 的时候,都会兴奋地拿几个问题去测模型,比如"帮我查一下上个月华东区的销售额",看到模型输出了像模像样的 JOIN 和 GROUP BY,就觉得大功告成了。等你把这条 SQL 真的扔到生产库上一跑,问题就全冒出来了:它查了 3 分钟没返回、它把 2000 万行的表全表扫了、它 join 了一张你跟本没授权的表、它甚至在你开玩笑说"把离职员工的工资调低"的时候给你生成了一条 UPDATE。那一刻你会明白,生成 SQL 是模型的本事,管住 SQL 才是你自己的本事。
这篇文章就围绕"三件事"展开:一是权限与查询安全,二是 SQL 正确性校验与纠错,三是查询结果交付与可观测性。每件事我都会结合实操代码、参数计算和排坑经验来讲,最后给出一套可以直接抄走的完整实现。适合正在做数据中台、BI 助手、AI Agent 开发的工程师,也适合那些刚接触 Text-to-SQL、想知道"Demo 和生产之间到底隔了多少个坑"的朋友。
1. 先搞懂:Text-to-SQL Agent 到底在解决什么问题
1.1 传统 Text-to-SQL 的教科书范式
传统的 Text-to-SQL 任务,学术上的定义很简单:输入一段自然语言问句,输出一条对应的 SQL 查询语句。评判标准也只有一个——这条 SQL 是否能正确回答问题。这个范式里,模型是唯一的参与者,它拿到问题和数据库 Schema,生成 SQL,任务结束。你甚至不需要真的去执行这条 SQL,只需要拿到结果去跟标准答案做对比。
这种模式在评测集上可以做得很好看,因为评测集里的 Schema 都是人工整理好的,字段命名清晰,表关系明确,问题也都是精心构造的。可一旦到了真实项目里,你会发现几个残酷的事实:真实的元数据通常非常脏,字段注释缺失、命名混乱、同义词泛滥;业务方的问题往往是模糊的,包含大量隐含假设;更关键的是,SQL 跑在真实数据库上,一旦出错产生的后果是实打实的。
所以我一直觉得,Text-to-SQL 从学术任务变成工程落地,本质上是把"一条 SQL"的问题变成了"一个完整的服务"的问题。生成 SQL 只是服务里的一环,而且是很靠前的一环。
1.2 为什么必须引入 Agent 而不是单次生成
既然单次生成的 SQL 经常出错,一个自然的想法就是:让模型自己能检查、能改、能调。这就绕不开 Agent 的概念。Agent 的内核不是"会调用工具",而是"有目标、能规划、能根据环境反馈调整行为"。放在 Text-to-SQL 的场景里,Agent 意味着:
- 生成 SQL 后,先把它拿去解析验证,发现语法错误就让模型重写;
- 把 EXPLAIN 结果喂给模型,告诉它"这条 SQL 走了全表扫描,代价很大",让它换一种写法;
- 查询结果返回后,让模型判断结果是否合理,比如你问"华东区占比",结果是 NULL,模型会意识到可能筛选条件有问题;
- 用户追问"那华南呢",Agent 能记住上一轮的上下文,而不是重新理解整个对话。
这一系列"生成—反馈—修正—再执行"的循环,就是 Agent 和单次调用最本质的区别。标题里说的"除了生成 SQL,还得管三件事",本质上是给这个循环加了工程约束:安全约束、质量约束、交付约束。没有这些约束,Agent 就是一个拿了数据库钥匙的熊孩子,聪明但危险。
1.3 拆解标题:三件事究竟是哪三件
先说结论,我这边的经验是,落地一个 Text-to-SQL Agent,除了生成 SQL,你至少还得管好下面三件事:
| 排序 | 关键项 | 一句话解释 | 放任不管的后果 |
|---|---|---|---|
| 第一件 | 权限与查询安全 | 让 Agent 只有"读"的权限,且只能读该读的数据 | 数据泄露、删库、绕过行级权限 |
| 第二件 | 正确性校验与纠错 | 生成只是开始,验证、纠错、重跑才是常态 | 给业务方一堆跑错/跑挂的 SQL |
| 第三件 | 结果交付与可观测性 | 控制返回规模、管理上下文、追踪慢查询 | 数据库被拖垮、用户骂娘、出了事故无法回溯 |
我见过不少团队,DEMO 演示的时候一片叫好,上生产一周就下线了,原因几乎都出在这三件事上,而不是出在 SQL 生成率上。模型生成 SQL 的能力这几年进步确实很快,尤其是大模型出现之后,复杂 JOIN 和多表关联已经不算什么难题。难的是怎么让这条 SQL 安全、正确、高效地跑在你的生产环境里,并且让人能放心地把 Agent 的输出交给业务方使用。接下来的几个章节,我会逐件拆解。