这篇是银行问答系统的两条查询路径的第二篇。上一篇先说明为什么制度文档和业务数据不能共用一条检索链路,这里只追踪数据查询这条路:模型生成 SQL 以后,系统怎样判断它能不能执行,执行失败后怎样处理,最后又怎样避免把结果解释错。
Text2SQL 指的是把自然语言问题转换成 SQL。它解决的是“用户想查什么”到“数据库需要执行什么”之间的转换,但 SQL 能生成出来,并不代表结果就可信。
Text2SQL 的演示通常很顺利:给模型一份表结构,再问“哪个行业的敞口最大”,它能生成一条像模像样的 SQL。
真正接入业务库之后,问题才刚开始。
一条 SQL 即使语法正确,也可能查询了不该访问的表、返回过多数据,或者在结果解读时被模型补上了数据库里根本不存在的趋势和数字。生成 SQL 只是中间步骤,不能直接等同于“可以执行”。
关键字黑名单为什么不够
最直观的做法是列一份危险词清单:出现 DELETE、UPDATE 或 DROP 就拒绝执行。
这类规则很快会遇到两个问题。第一,注释和字符串里也可能出现这些词,正常查询会被误伤。第二,写操作可以被包装在更复杂的语句里,单纯检查文本很难看出真正的意图。
更可靠的方式是先把 SQL 解析成抽象语法树。系统使用 SQLGlot 读取语句结构,再检查几个条件:
- 只能有一条语句;
- 顶层只能是 SELECT;
- 不能包含写入、删除或修改操作;
- 涉及的表必须在允许列表中;
- 子查询不能绕开表权限;
- 没有 LIMIT 时补上上限,过大的结果集直接限制。
这一步检查的是结构,不是关键词。
代码校验之后,还要看执行环境
即使 AST 校验没有问题,也不应该让应用使用高权限账号连接数据库。
执行侧使用只读权限,同时限制可访问表、最大返回行数和执行时间。这样即使上层校验出现遗漏,也不会让一条模型生成的语句改写数据或无限占用资源。
多层限制看似重复,实际承担的是不同责任:应用层负责尽早拒绝明显风险,数据库权限负责兜底,资源限制负责避免一次查询拖垮服务。
失败后为什么只重试两次
字段名猜错、分组条件不完整、时间口径不一致,都是 Text2SQL 的常见失败方式。数据库返回错误后,系统把错误原因整理成模型可理解的反馈,再让它修正一次。
重试次数并不是越多越好。超过两次仍然失败,通常说明问题不在 SQL 写法,而在上游没有召回足够的表结构或业务口径。继续重试只会增加调用成本,也会掩盖真正需要修复的环节。
最难发现的错误在结果解释阶段
SQL 报错至少会暴露问题。更危险的情况是查询正常返回,但模型在解读时写出了结果表里没有的数字,或者把“一个行业占比最高”扩展成“整体风险正在上升”。
因此,结果解释也要受到约束:关键数字来自查询结果,无法验证的推断不应写成事实;遇到模糊结果时,宁可展示表格和口径,也不要补一段听起来合理的结论。
一条可靠的 Text2SQL 链路,不是让模型一次写对 SQL,而是让系统知道什么时候该执行、什么时候该停下,以及怎样把失败变成下一轮改进的线索。