SQL 执行报错后,怎么让大模型自动纠错?
简化版
把原 SQL、数据库返回的具体报错、表结构上下文(最好再加上用户的原始问题)一起交给模型,让它返回修正后的 SQL 和修改理由,然后重新走一遍安全校验再执行。有三个关键点:报错要取最内层的数据库异常(Unknown column 'u.user_id' in 'on clause'),而不是框架包装后的 bad SQL grammar;纠错要有次数上限,通常一次;纠错之后,旧 SQL 的执行结果、图表、解读要全部作废,避免新 SQL 配旧结论。
详细版
纠错流程:
执行报错
→ 沿异常链取到最底层的数据库报错
→ 组装纠错 Prompt:原问题 + 原 SQL + 报错 + 表结构
→ 模型返回 { sql, fixReason }
→ 保存修正 SQL 和理由(保留原 SQL 与原报错)
→ 旧的执行结果、图表、解读失效
→ 修正 SQL 重新走安全校验 → 执行
├─ 成功:展示结果
└─ 失败:记录新报错,不再自动重试
适合交给模型纠错的错误:字段名或表名写错、别名引用错误、group by 不完整、函数方言不兼容、类型不匹配。不适合纠错的:权限不足、超时、连接失败(这些不是 SQL 写法的问题),以及「没报错但口径错了」的语义问题(没有报错信息可供纠正)。
完整版教学
一、为什么模型能改好自己写错的 SQL
Text-to-SQL 最常见的错误并不复杂:字段名差一个字母、把外键当成了对方表的主键、group by 漏了一列。这类错误的共同点是数据库会给出精确的报错,比如:
Unknown column 'u.user_id' in 'on clause'
Expression #2 of SELECT list is not in GROUP BY clause
报错信息本身就指明了「哪里错、错成什么样」。把它和表结构一起交给模型,相当于给了它一次带答案提示的重试机会。这种「执行 → 拿到真实反馈 → 修正」的闭环,比一开始就要求模型一次写对要现实得多,也是很多 Agent 自愈设计的基本思路。
二、报错信息一定要取最内层
Java 的数据访问框架会把数据库异常一层层包装。直接取最外层的消息,拿到的往往是:
外层:bad SQL grammar [select ... ]; nested exception is ...
最内层:Unknown column 'u.user_id' in 'on clause'
「bad SQL grammar」只说明 SQL 有语法问题,模型拿到它只能瞎猜;最内层才告诉它是 u.user_id 这一列不存在。所以保存错误时要沿着 getCause() 一路找到最底层的异常:
// 沿异常链找到最底层的数据库异常,它才包含具体的出错位置
Throwable cause = e;
while (cause.getCause() != null && cause.getCause() != cause) {
cause = cause.getCause();
}
String errorMessage = cause.getMessage();
易错点:纠错效果差,第一个要检查的就是交给模型的报错是不是最内层的那一条。
三、纠错 Prompt 要给哪些东西
| 输入 | 作用 | 缺了会怎样 |
|---|---|---|
| 原 SQL | 在它的基础上改 | 模型重写一条全新的 SQL,可能引入新错误 |
| 数据库报错 | 指出具体错在哪 | 模型只能猜 |
| 表结构上下文 | 知道正确的字段名和关联关系 | 改完还是错 |
| 用户原始问题 | 保证修正后仍然回答同一个问题 | 为了能跑通,悄悄改掉了查询意图 |
最后一项容易被忽略。模型纠错时的目标是「让 SQL 能执行」,如果不知道原问题,它可能用删掉条件、换一张表的方式让 SQL 跑通,结果回答的已经不是用户问的问题了。输出同样要求结构化:sql 加上 fixReason,修改理由展示给用户,方便判断改得对不对。
四、次数上限:为什么通常只纠一次
纠错可以无限循环吗?不应该。原因有三:
成本 每次纠错都是一次模型调用,加一次数据库执行
收益 一次纠错能解决大部分「写错字段名」这类简单错误;
一次没改好,多半是理解层面的问题,再改几次也难
风险 反复纠错可能在「错误 A」和「错误 B」之间来回震荡
所以常见的设计是自动纠错最多一次,还失败就把最终报错展示给用户,由人来决定是改问法还是重新生成。需要人工触发时,也可以让用户再点一次纠错,但每一次都要保存当次的报错和修正,便于回看。
五、纠错后要清理什么
修正后的 SQL 是一条新 SQL,和它相关的旧产物都要作废:安全校验状态、查询结果、执行耗时、错误信息、结果摘要、图表配置、数据解读、推荐追问。不清理就会出现页面上显示的是新 SQL,下面的图表和解读却来自旧 SQL 的情况,用户完全看不出来。同时要保留纠错前的原件:原 SQL 和原报错存到单独的字段,修正 SQL 和修改理由另存,这样事后能说清楚「最初写成什么样、因为什么报错、改成了什么」。
六、哪些错误不该交给模型纠
| 错误类型 | 例子 | 该怎么处理 |
|---|---|---|
| 写法错误 | 字段不存在、group by 不完整、函数不兼容 | 交给模型纠错 |
| 安全校验不通过 | 查了白名单外的表、包含危险关键字 | 不纠错,写法本身不合规,重试多半还是同样的问题 |
| 权限 / 超时 / 连接 | 无权限、执行超时、数据库连接断开 | 不纠错,这不是 SQL 写法问题,应提示用户或运维 |
| 语义错误 | SQL 能跑,但口径错了 | 没有报错可供纠正,要靠元数据约束和结果核对 |
把「不该纠」的情况排除掉,纠错这一步才不会浪费调用,也不会掩盖真正的问题。
七、纠错与 Agent 的关系
在多步分析的 Agent 里,某一步 SQL 执行失败时,最自然的处理就是自动触发一次纠错,而不是直接把整个步骤判失败:
第 1 次:生成 SQL → 校验 → 执行 → 报错
第 2 次:纠错 → 校验 → 执行
成功 → 继续图表、解读
失败 → 这一步标为执行失败,记录最终报错
这就是 Agent 的「观察 → 修正」能力的一个具体落地,也是为什么纠错要设计成可以被其他流程复用的独立能力。
八、常见误区与追问
- 误区:把框架抛出的异常消息直接交给模型就行。 外层往往只有 bad SQL grammar 这种笼统信息,要沿异常链取最内层的数据库报错。
- 误区:纠错可以一直重试,直到成功为止。 一次没改好多半是理解问题,反复重试成本高还可能来回震荡,通常只自动纠一次。
- 误区:纠错只需要原 SQL 和报错。 还要给表结构,最好给原问题,否则模型可能为了能跑通而改掉查询意图。
- 误区:纠错之后旧的图表和解读还能继续用。 修正后的 SQL 结果可能完全不同,旧的执行结果、图表、解读都要作废。
- 误区:所有执行失败都应该纠错。 权限、超时、连接问题和安全校验失败都不是写法问题,交给模型纠错只会浪费调用。
- 追问:纠错后的 SQL 还要再做安全校验吗? 必须要,修正后的 SQL 是模型新生成的内容,和第一次一样不可信。
- 追问:怎么评估纠错功能的效果? 统计纠错触发率和纠错后成功率,触发率高说明上下文或元数据有问题,应该从源头改。
九、加强记忆
SQL 执行报错是模型能拿到的最精确的反馈,纠错就是把原 SQL、最内层数据库报错、表结构和原问题交给模型,返回修正 SQL 和理由,再重新校验执行。报错一定取异常链的最底层,笼统的 bad SQL grammar 没用;原问题要带上,防止模型为了跑通改掉意图。纠错设上限,通常只自动一次,失败就交给人;纠错前留原件,纠错后旧的结果、图表、解读全部作废。安全校验失败、权限、超时、连接问题和语义错误都不该交给模型纠。在 Agent 里,纠错是单步失败后的自愈手段,所以要做成独立可复用的能力。
项目实战落地
项目里怎么做的
《AI Agent数据分析平台》把纠错做成了分析记录上的一个「纠错」按钮:
- 执行失败时保存最内层报错:执行 SQL 的地方捕获异常后,沿
getCause()找到最底层的数据库异常,比如Unknown column 'u.user_id' in 'on clause',写进analysis_query.error_message,状态记为「执行失败」; - 纠错:用户点「纠错」后,后端要求这条记录同时有
generated_sql和error_message,按「SQL 分析」用途选模型,用SQL纠错模板把{originSql}、{errorMessage}、{datasetSchema}填进去,要求模型只返回包含sql和fixReason的 JSON; - 保存与失效:修正结果写进
corrected_sql、correction_reason、raw_correction_response,同时把generated_sql更新为修正后的 SQL,旧的安全状态、查询结果、执行耗时、错误信息、结果摘要、图表、解读和推荐追问全部清空,状态记为「已纠错」; - 自动重新执行:前端拿到纠错结果后,自动调用一次执行接口,重新走安全校验和查询。
在 Agent 多步执行里,这套纠错能力被直接复用:某一步第一次校验或执行失败,后台自动纠错一次再重试,仍然失败才把这一步标为「执行失败」。
为什么这样取舍
- 手动触发纠错:用户能先看到失败原因,再决定要不要让模型改,纠错过程在页面上完整可见。
- Agent 里自动纠错只重试一次:后台执行没有人盯着,重试必须有上限,避免错误 SQL 造成无限循环。
面试官还会追问
- 纠错后的 SQL 再执行一次还是失败,页面会怎样?用户还能再纠错吗?
- 后端为什么要求
generated_sql和error_message同时存在才允许纠错?
学完《AI Agent数据分析平台》,上面这些追问你都会迎刃而解。