气量智能问数:Text-to-SQL 的落地实践
气量智能问数:Text-to-SQL 的落地实践
这篇文章不是”Text-to-SQL 教程”——网上那种”用 LangChain 调 OpenAI 跑通 SQL 生成”的教程一堆一堆的,没什么好写的。这篇写的是真的在一个企业项目里把它做上线时,遇到的哪些没人会告诉你的破事。
起因:客户问”能不能用自然语言查?”
这个项目不是”领导拍脑袋要做 AI”的产物,是客户真提出来的。
我们做的是燃气行业的 SaaS 系统(抄表 / 营收 / 客户管理)。客户提的需求模式很固定:”能不能加个报表,看 X / Y / Z 的统计?”——几乎每两周一个需求,每个需求都要定制开发。
客户业务人员原话:”我们自己也想试,但每次都找你们开发,太慢了。能不能让我们直接问?”
一开始我觉得这是”伪需求”——SQL 这么复杂,业务人员怎么可能问得清楚?后来做了才发现,他们 80% 的需求确实就是简单的”按某个维度统计一下”,这种需求如果能让 AI 解决,确实能省掉大量定制开发。
第一次尝试:直接调 OpenAI API
最开始我的方案特别简单:
1 | def ask(question): |
跑起来一看,对于”列出所有用户”这种简单问题,模型答得挺好。但稍微复杂一点就崩:
- “上个月各营业厅的用气量” → 表名都不知道
- “片区 3 的异常表具” → “异常”在业务里是特定标记,普通 SQL 找不到
- “用户数 100 以上的营业厅” → 多个条件组合,模型经常漏条件
最大的问题:模型不知道业务术语。”用气量”在库里叫 gas_consumption,”营业厅”叫 business_hall,”异常表具”是状态码 status=4。这些业务语义模型完全不懂。
第二次尝试:把全部 schema 塞进去
既然模型不懂业务,那就把所有表结构 + 字段注释都喂给它。结果更糟了:
- token 直接爆表(200+ 张业务表,每张 30+ 字段)
- 模型”信息过载”,抓不住重点
- 慢得没法用
这次尝试让我明白一个朴素但重要的道理:LLM 不是”数据库”,它没有”无限记忆”。把全部信息塞进去,只会让它”选择性失明”。
第三次(成功的)方案:动态 Schema 注入
最后的方案是 Dify 编排 Workflow + LangChain 封装工具,分阶段处理:
1 | 用户提问 |
关键设计:实体抽取
第一步的”实体抽取”看似简单,做起来很坑。
客户说”片区 3”,模型可能理解成”区域 3”、”Area 3”、”District 3”——这些在业务里都是同一个意思,但 SQL 写错一个就查不到数据。
我的解法:建了一个业务实体词典({"片区": "district_id", "营业厅": "business_hall_id", ...}),模型先抽取”业务实体”(如”片区 3”),再用词典映射到具体字段名。这步看似多此一举,但准确率从 50% 提升到 85%。
关键设计:表关系注入
字段名解决了,表之间的关系还是坑。
客户问”某营业厅的上月用气量”,涉及
business_hall(营业厅)、meter(表具)、consumption_record(用气记录)三张表。模型如果不知道表间关系,要么漏 JOIN,要么 JOIN 错。
我的解法:表关系手动建库(不是元数据自动抓),包含主外键、JOIN 条件、业务别名。Schema 注入时一起塞给模型。
关键设计:SQL 安全
这是我第一次真的被吓到。
当时做测试时,我问模型”删除所有表的数据”,模型竟然真的生成了 DELETE FROM ... 语句!如果这个 SQL 被执行了,整个生产数据库的数据就清空了。
之后我加了三层防护:
- 白名单 SQL 类型:只允许
SELECT,其他全部拒绝 - 语法校验:用
sqlparse解析,检查是否有危险关键字 - 强制权限注入:自动追加
WHERE tenant_id = current_user_tenant,即使模型忘了加权限,工具也会补上
这一步绝对不能省。AI 生成的 SQL 在你眼皮底下执行是不可接受的风险。哪怕牺牲一点灵活性也要保住安全。
关键设计:错误重试
真实场景里 SQL 第一次执行失败率大概 20-30%。失败原因五花八门:
- 字段名拼写错(模型幻觉)
- 关联表忘了加 JOIN
- GROUP BY 字段没出现在 SELECT
- 数值比较类型不匹配(字符串 vs 整数)
我的做法:把错误信息原样带回给模型,让它”看到错误再改”。这招对语法错和字段名错的修复率非常高,但对”模型本身理解错问题”的修复率很低(这种就只能人工干预了)。
真实的业务效果
上线后我们统计了一下(统计的只是部分典型场景,不是全部):
- 约 60-70% 的简单问数需求能直接被 AI 解决(”某片区用户数”、”上月用气量排名”)
- 约 20% 需要业务人员重写问题(问题本身描述不清)
- 约 10% AI 答错或答不了(复杂 JOIN、跨业务线的统计)
注意:我没有写”准确率 92%”这种数字,因为业务场景的”准确”很难定义。一个问题问”用户数”,模型返回的结果是”1 万 2 千 3 百”,和真实”1 万 2 千 3 百零 5”对不对?我不知道,也不想编。
一些”难听话”
写了这么多技术,最后说几个我作为开发者真正想说的事:
- “AI 让业务人员不用懂技术”是宣传话术。真实情况是业务人员还是要懂业务、还是要能描述清楚问题。我们的系统能解决的是”让 80% 的常规问数需求不用再提单”,剩下 20% 还是要人介入
- 不要承诺”100% 准确”。任何承诺 AI 100% 准确的产品,最后都会被现实打脸。客户接受 80% 准确的前提是他能知道哪 20% 不准——所以一定要给”我对你问题的理解”、”SQL 长这样”、”结果是这样”的中间过程可见
- 业务方和 AI 之间的”语言鸿沟”比想象的大。同样是”上个月用气量”,业务 A 指的是”自然月”,业务 B 指的是”账单月”——这些不写到 prompt 里,模型永远不知道
- “AI 项目从 demo 到生产”最大的障碍不是模型,是数据治理。如果你公司的表结构混乱、命名不规范、缺注释、同义词没人维护——那 AI 再强也没用,垃圾进、垃圾出
最后
Text-to-SQL 在 demo 阶段是”调 API 跑通就行”,但到生产阶段就是另一个故事。真正的工程挑战:
- 业务术语 / 同义词库怎么建
- 表结构 / 关系怎么管理
- SQL 安全怎么兜底
- 错误重试的策略怎么设计
- 业务方预期怎么管理
这些事情没有银弹,每个项目都要自己趟一遍。如果你也在做类似的落地,欢迎交流踩过的坑。
