SQL 生成这件事,看起来只是"把自然语言翻译成 SQL",但真正落到生产环境里,你会发现它同时被两股力量拉扯:一边是业务方希望模型能懂各种奇奇怪怪的口径、方言函数、历史遗留表结构,也就是定制能力;另一边是线上系统要求它每次生成的语句都能跑、不报错、不慢查询,也就是推理稳定性。这两者往往互相打架——定制得越深,模型越容易"自由发挥";约束得越死,遇到新场景又直接摆烂。最近不少人在讨论 SQL 生成到底该选哪家大模型,以及背后的算力该怎么配,我把自己这段时间的实测和踩坑整理一下,尽量说人话。
1. SQL 生成任务的真实难点拆解
1.1 为什么"会写 SQL"和"能稳定生成 SQL"是两回事
很多人评估模型时习惯拿几个经典问题去问,比如"查一下每个部门薪资最高的员工",模型答得挺漂亮,就认为可以上线了。但生产环境的 SQL 生成完全是另一回事。真实场景里,你面对的是几百张表、字段命名毫无规范、同一个业务概念在不同表里有三四种叫法,还有大量视图、存储过程、分区表。模型要在这种环境里生成一条能跑的语句,靠的不是"会 SQL 语法",而是对上下文的精确理解和对边界的严格自律。
我做过一个对比测试:同一个模型,在只给表名的情况下生成 SQL,和在给出完整 DDL、字段注释、样例数据、业务口径说明的情况下生成 SQL,准确率差距能到 40 个百分点以上。这说明什么?说明 SQL 生成的质量,很大程度上不取决于模型"聪不聪明",而取决于你喂给它的上下文够不够、准不准。而上下文一多,token 消耗就上去了,推理成本和时间也跟着涨,这就直接牵扯到算力问题。
另一个容易被忽略的点是方言差异。MySQL、PostgreSQL、SQL Server、Oracle、DB2 在函数名、日期处理、字符串拼接、分页语法上都不一样。模型如果训练数据里这几种方言混在一起,它很容易生成"四不像"——比如用了 MySQL 的LIMIT却配了 SQL Server 的TOP。这种错误在语法检查阶段可能过,但一执行就挂。所以定制能力的核心之一,其实是把方言约束住。
1.2 定制能力和推理稳定性为什么会冲突
定制能力的本质,是让模型学会你私有的那套规则:特定的表命名习惯、特定的业务口径、特定的函数封装。常见做法有两种,一种是通过提示词工程把规则塞进上下文,另一种是通过微调让模型把这些规则"内化"。前者灵活但吃 token,后者稳定但更新慢。
推理稳定性的本质,是让模型的输出可预测、可校验、可回滚。你希望同样的输入永远得到语义一致的输出,希望生成的 SQL 能通过静态检查,希望执行计划不会突然变成全表扫描。要做到这些,光靠模型本身不够,还得有外层的校验、重试、缓存机制。
冲突就出在这里:你为了定制能力往上下文里塞的东西越多,模型的注意力就越容易被稀释,输出就越不稳定;你为了稳定性把输出约束得越死,模型面对新场景的泛化能力就越差。所以选型时不能只看"哪个模型 SQL 写得好",而要看哪个模型在"给定丰富上下文"和"给定严格约束"这两个条件下,表现都足够稳。
1.3 一个被低估的变量:算力供给的稳定性
大家讨论模型选型时,很少把算力单独拎出来说。但实际做 Agent 类应用,算力供给的稳定性直接决定了你的推理稳定性。原因很简单:SQL 生成往往不是一次调用就完事,而是一个多轮流程——先做意图理解,再检索相关表结构,再生成候选 SQL,再做语法校验,失败了还要重试。这一套下来,单次用户请求可能触发 3 到 8 次模型调用。
如果算力供给不稳定,出现排队、超时、限流,你的重试逻辑就会被打乱,用户体验直接崩。更麻烦的是,有些场景下你需要并发处理大量 SQL 生成请求,比如批量数据查询、报表自动生成,这时候算力的弹性就非常关键。所以我在选型时会把"算力支撑是否坚实"作为一个独立维度来评估,而不是只看模型跑分。
2. 定制能力的三条实现路径与各自代价
2.1 提示词工程:最快上手,但天花板明显
提示词工程是绝大多数团队的第一步。做法是把表结构、字段说明、业务规则、few-shot 示例全部拼进 prompt,让模型照着生成。优点是改起来快,今天发现一个口径问题,改改提示词明天就生效,不用重新训练。
但它的代价也很直接。第一是token 成本。一个中等规模的库,把相关表的 DDL 和注释塞进去,轻松就是几千 token,如果再加 few-shot 示例,上万 token 很常见。每次调用都吃这么多 token,成本和延迟都上去了。第二是上下文长度限制。再大的上下文窗口也有上限,表一多就塞不下,只能做检索,而检索本身又会引入新的不确定性。第三是规则冲突。当提示词里的规则越来越多,模型很容易顾此失彼,遵守了 A 规则就忘了 B 规则。
我的经验是,提示词工程适合规则相对稳定、表数量可控的场景,比如几十张表以内的业务域。一旦超过这个规模,就得考虑别的路径。
2.2 微调:把规则内化,但要算清账
微调的思路是拿一批高质量的"问题-SQL"对,让模型学会你的私有规则。好处是推理时不用塞那么多上下文,token 省了,延迟也低了,而且规则内化之后一致性更好。
但微调有几个坑必须提前想清楚。第一是数据质量。你喂进去的 SQL 如果有错,模型学到的就是错的,而且这种错误很难通过提示词纠正回来。第二是更新成本。业务口径变了,你得重新准备数据、重新训练、重新验证,周期比改提示词长得多。第三是灾难性遗忘。微调过头,模型可能在你的私有场景上表现很好,但通用 SQL 能力反而退化了。
还有一个现实问题:微调本身是要吃算力的。全量微调对显存和卡数的要求很高,LoRA 这类参数高效微调虽然门槛低一些,但也不是随便一台机器就能跑。所以微调这条路,适合有稳定数据来源、有持续迭代预算的团队。
2.3 检索增强:折中方案,但检索质量决定一切
检索增强(RAG)是介于两者之间的方案。核心思路是:不把所有表结构都塞进上下文,而是根据用户问题先检索出最相关的几张表和字段,再拼进 prompt。这样既保留了定制能力(通过检索到的私有 schema),又控制了 token 消耗。
这个方案的效果,几乎完全取决于检索质量。检索错了表,后面全错。我踩过的坑是:早期用纯向量检索,结果经常把语义相似但业务无关的表召回进来,比如"订单"和"订单日志"向量距离很近,但用错了就是灾难。后来改成"向量检索 + 关键词过滤 + 业务域标签"的混合策略,召回准确率才上来。
另外,检索增强对算力的要求也不低,因为检索本身要跑 embedding 模型,如果并发高,embedding 的算力消耗不能忽略。所以这条路也不是"免费"的。
| 路径 | 定制能力 | 推理稳定性 | token 成本 | 迭代速度 | 算力需求 |
|---|---|---|---|---|---|
| 提示词工程 | 中 | 中 | 高 | 快 | 中 |
| 微调 | 高 | 高 | 低 | 慢 | 高 |
| 检索增强 | 中高 | 中高 | 中 | 中 | 中高 |
3. 推理稳定性到底靠什么兜底
3.1 语法校验只是第一道关
很多人以为生成的 SQL 能通过语法解析就算稳了,其实差得远。语法正确但语义错误的 SQL 比比皆是,比如 join 条件写错导致笛卡尔积、聚合函数用错层级、where 和 having 混用。所以校验至少要分三层:语法层(能不能解析)、语义层(表名字段名是否存在、类型是否匹配)、性能层(执行计划是否合理、有没有全表扫描风险)。
语法层可以用现成的 SQL parser,比如sqlparse、sqlglot这类库,成本很低。语义层需要接元数据服务,把生成的 SQL 里的表名、字段名拿去和真实 schema 比对。性能层最麻烦,通常要拿EXPLAIN去跑,但生产环境不可能每条都跑,所以一般只对高风险语句做抽样检查。
3.2 重试机制的设计比模型本身更重要
SQL 生成失败是常态,关键是怎么重试。我见过两种极端:一种是不重试,失败就报错,用户体验差;另一种是无脑重试,同一个 prompt 反复问,结果模型每次给的都差不多,白白烧算力。
比较合理的做法是带反馈的重试。第一次生成失败后,把失败原因(比如"字段 xxx 不存在")作为反馈拼进下一轮 prompt,让模型有针对性地修正。实测下来,带反馈的重试通常 2 到 3 轮就能收敛,比无脑重试效率高得多。但这也意味着单次请求的模型调用次数增加了,对算力的弹性要求更高。
还有一个技巧是缓存。相同或相似的问题,如果之前生成过正确的 SQL,直接复用,不用再调模型。缓存命中率在报表类场景里能到 30% 以上,省下来的算力相当可观。
3.3 输出格式约束:让模型"按模板说话"
让模型自由输出 SQL,稳定性天然就差。更好的做法是约束输出格式,比如要求模型输出 JSON,里面包含sql、explanation、confidence三个字段。这样解析起来稳定,还能根据 confidence 决定要不要人工介入。
更进一步,可以用结构化生成的方式,让模型只填充模板里的槽位,而不是自由发挥。比如预定义好SELECT ... FROM ... WHERE ...的骨架,模型只负责填表名、字段、条件。这样生成的 SQL 结构可控,出错概率大幅降低。代价是灵活性下降,复杂查询可能表达不了,所以适合固定模式的查询场景。
4. 算力配置:被忽视的选型维度
4.1 推理算力和训练算力是两笔账
很多人一说算力就想到训练,其实对 SQL 生成这类应用,推理算力才是长期开销。训练是一次性的(或者周期性的),推理是每天都在跑的。一个日活几千的 Agent 应用,每天可能产生几万次模型调用,这个量级的推理算力需求,远比训练一次模型要大。
推理算力的需求,主要看三个变量:调用量、单次 token 数、模型规模。调用量好估算,单次 token 数取决于你的上下文策略(提示词工程吃 token 多,微调吃得少),模型规模则直接决定单次推理的算力消耗。这三个变量乘起来,就是你需要的推理算力盘子。
4.2 精度选择对算力的影响
推理时用 FP16 还是 INT8,对算力需求影响很大。简单说,INT8 量化能把显存占用和算力需求降到 FP16 的一半左右,但会带来一定的精度损失。对 SQL 生成这种任务,精度损失主要体现在"边界情况"上,比如复杂嵌套查询、罕见函数调用。我的经验是:通用查询用 INT8 没问题,复杂查询建议走 FP16,可以做分级路由,简单请求走量化模型,复杂请求走全精度模型。
这里要提醒一句,量化不是免费的午餐。有些模型量化后会出现"胡言乱语",生成的 SQL 语法都对但逻辑离谱。所以量化后一定要做充分的回归测试,别直接上线。
4.3 弹性算力对 Agent 场景的意义
Agent 类应用的算力需求是波动的。白天业务高峰,SQL 生成请求集中;晚上批量报表任务跑起来,又是一波高峰。如果算力是固定的,要么高峰期不够用,要么低谷期浪费。
所以我在选型时会特别关注算力供给是否弹性。弹性算力的价值在于:高峰期能快速扩容,保证推理不排队;低谷期能缩容,控制成本。对 SQL 生成这种"调用量波动大、单次延迟敏感"的场景,弹性算力几乎是刚需。这也是为什么我在评估模型服务时,会把背后的算力支撑能力作为一个独立考量——模型再好,算力供给跟不上,线上照样崩。
5. 我的选型实测与踩坑记录
5.1 测试集怎么设计才有参考价值
我见过太多"用几个 demo 问题测模型"的选型方式,结论基本不可信。真正有参考价值的测试集,应该覆盖这几类:简单单表查询(占实际请求的大头)、多表 join(考验 schema 理解)、聚合与分组(考验语义理解)、嵌套子查询(考验复杂逻辑)、方言特有问题(考验定制能力)、边界情况(空值、除零、类型转换)。
每类准备 20 到 30 条,总共一两百条,人工标注正确答案,然后跑准确率。更重要的是,要记录失败案例的失败原因,是语法错、语义错还是性能差。这个分布比一个笼统的准确率数字有用得多。
5.2 我踩过的几个坑
第一个坑是过度依赖模型自带的 schema 理解。早期我偷懒,只给表名不给字段,结果模型经常猜字段名,猜错的概率很高。后来老老实实把 DDL 和注释都喂进去,准确率立刻上了一个台阶。教训是:上下文能给的尽量给,别让模型猜。
第二个坑是忽略方言约束。有一次测试集里混了 MySQL 和 SQL Server 的题,模型生成的 SQL 方言混乱,我一开始以为是模型能力问题,后来发现是提示词里没明确指定方言。加上"请使用 MySQL 语法"这句约束后,问题基本消失。教训是:方言必须显式约束,不能指望模型自己判断。
第三个坑是重试逻辑写得太粗暴。最初的重试是把原 prompt 再发一遍,结果模型给的答案几乎一样,白白浪费算力。改成带错误反馈的重试后,收敛速度快了很多。教训是:重试要带信息,不能空转。
第四个坑是没做算力容量规划。上线初期调用量小,没感觉;后来业务量上来,高峰期开始出现超时。回头一算,才发现推理算力根本没留够余量。教训是:算力规划要按峰值算,不能按均值算。
5.3 关于"选哪家"的一点个人看法
回到标题的问题,SQL 生成推荐哪家大模型。我的看法是:没有绝对的最优解,只有匹配你场景的解。如果你的场景表结构稳定、规则清晰,提示词工程加一个通用能力强的模型就够了;如果你的场景规则复杂、更新频繁,可能需要在微调和检索增强上多投入;如果你对推理稳定性要求极高,那算力供给的弹性就必须重点考察。
我自己的做法是分级路由:简单查询走轻量模型,复杂查询走能力强的模型,同时用缓存和带反馈的重试兜底。这样既控制了成本,又保证了稳定性。至于算力,我会优先选择供给弹性好、能按需扩容的方案,因为 SQL 生成这类 Agent 应用的负载波动太明显了,固定算力很难兼顾成本和体验。
最后分享一个小心得:别指望一次选型定终身。模型迭代很快,今天的最优解半年后可能就不是了。所以架构上要留好切换模型的余地,把模型调用抽象成一层接口,换模型时只改配置不改业务代码。这样无论后面哪家模型更强,你都能快速跟上,而不用推倒重来。