文章目录
用户问:
最近一周已结算的退货金额,按货号和尺码看一下
“已结算”对应哪个状态?”退货金额”用明细金额、实退金额,还是结算金额?”货号”落到 product_code、product_no,还是另一个商品主数据字段?”尺码”能不能单独分组,还是必须和 SKU 维度一起使用?门店、品牌、组织权限由谁注入?
这些不是 SQL 语法问题,是业务知识问题。模型会写 SQL,不等于它懂你的业务。
Text2SQL 的公开资料已经很多了——schema linking、RAG 召回表结构、prompt 工程、SQL 校验、准确率评测,方案不缺。但企业内部真正做自然语言查数时,拦住你的往往不是模型能力,而是业务知识没有被工程化。
所以这篇文章不讲怎么生成 SQL。我想讲的是一个更底层的问题:DSL 是对的,但 DSL 从哪里来?
问题不在 Text2SQL,而在知识生产
上一篇文章里,我写过一个判断:企业内部数据查询不能让模型自由写 SQL。比较稳的做法,是让模型先生成结构化查询规格,再由系统通过受控数据面、元数据和 SQL builder 编译 SQL。
这个判断现在看还是成立的。
但它往前再走一步,就会遇到 DSL 的问题。
系统要理解“货号”“尺码”“应收”“已结算”“退货金额”“可用库存”,就必须有一层业务知识,把用户语言和数据库语言接起来。你可以把它叫 DSL,也可以叫业务语义层、指标目录、查询知识库,名字不重要,核心是这层东西必须稳定、可审核、可被系统消费。
问题是,这层知识很难靠人手写出来。
真实系统里,表很多,字段名不统一,字段注释经常过期,老系统也未必有完整外键。很多指标口径不在文档里,而是藏在历史报表 SQL 里。很多默认过滤条件不在需求说明里,而是写在老代码里。很多业务词,不同部门叫法还不一样。
所以企业 Text2SQL 做不好,往往不是模型不够聪明,而是业务知识没有被工程化。
你继续调 prompt,只能缓解一部分问题。真正要补的是知识生产机制。
DSL 是好方向,但人工提炼太慢
DSL 的价值很明确。
它能把业务语言和数据库语言固定成契约。用户说“货号”,系统知道优先映射到哪个字段;用户说“已结算”,系统知道需要哪些状态条件;用户说“退货金额”,系统知道该用哪个金额字段、怎么聚合、哪些场景不能直接回答。
一个很简单的 DSL 片段可能长这样:
term: 货号
aliases:
- 商品编码
- 款号
maps_to:
- table: order_detail
column: product_code
sql_guidance: 用户说货号时,优先映射到 product_code。
review_status: reviewed
格式并不复杂。
真正麻烦的是,这条规则怎么来。
如果全靠开发和业务专家一条条写,起步会很慢。专家知道很多口径,但他说出来的通常是自己最熟的那部分。系统一多、表一多、字段一多,很快就写不动。更麻烦的是,业务还会变。今天整理完的 DSL,如果没有持续更新机制,过几个月又会变成另一份过期文档。
所以 DSL 的难点不在设计格式,而在提炼。
更准确地说,DSL 不是配置文件,它是业务知识建模。靠纯人工维护,迟早会卡在规模和更新频率上。
业务日志里藏着 DSL 的原材料
过去我们看历史 SQL,常常把它当成遗留系统的副产品。
Mapper 里一堆旧 SQL,BI 平台里一堆查询记录,运营后台里一堆导出日志,客服群和需求单里一堆口径确认。平时它们很散,也不太干净。
但换到 Text2SQL 视角,这些东西反而很值钱。
因为它们记录了企业真实的数据使用习惯。
历史 SQL 里能看到哪些表经常一起 join,哪些字段经常作为过滤条件,哪些状态值总是默认排除,哪些金额字段会被聚合,哪些字段会出现在分组和排序里。
业务日志里能看到用户真正怎么问问题。报表导出记录里能看到业务最终关心哪些列。需求单和故障单里能看到哪些口径最容易争议。
这些东西不能直接变成最终规则,但可以变成 DSL 草稿的证据。
比较合理的流程应该是这样:
业务日志 / 历史 SQL
-> 提取字段、过滤、join、聚合、别名
-> 归并同类查询和相似表达
-> 生成术语、指标、关系、模板草稿
-> 带着证据交给人审核
-> 发布成可检索的 DSL / 知识文件
这里最重要的变化,是人不用再从空白文件开始写 DSL。
机器先从历史证据里提候选项,人再判断哪些是规则,哪些只是偶然写法,哪些需要补充边界,哪些应该废弃。
历史 SQL 不是脏东西,它是企业数据使用习惯的行为日志。
知识不能停在文档态,要进入工程闭环
DSL 草稿从业务日志里提出来之后,还有一个问题要解决:这些知识以什么形态存在?
普通文档的问题是粒度太粗。模型检索到一篇长文档,也未必知道哪一句是规则,哪一句是经验,哪一句只是历史背景。更麻烦的是,文档里通常没有证据、没有置信度、没有审核状态。草稿、猜测和正式口径混在一起,最后系统会把不确定的东西当规则用。
业务知识要能被系统稳定消费,至少要满足五个条件:
- 可索引:术语、指标、表、字段、join、模板都能独立召回
- 可组合:一次查询可以组合多个知识片段
- 可审查:每条知识都有证据、置信度和审核状态
- 可演进:线上错一次,就补一条知识,而不是只改 prompt
- 可治理:能区分 draft、reviewed、deprecated
一条知识如果没有证据,就很难审核;没有状态,就很难进入生产;没有边界,就很容易被模型过度使用;没有索引,就只能躺在文档里等人想起来。
这也是为什么我现在更倾向于把 DSL 看成知识资产,而不是配置文件。
AI 的位置:批量起草,人来定稿
说到这里,AI 的位置就清楚了。
AI 不应该直接替业务拍板,也不应该绕过受控链路去执行 SQL。它最适合做的,是把大量业务证据压缩成可审核草稿。
它可以从历史 SQL 中提取候选术语,可以根据字段注释和查询上下文归并同义词,可以把复杂 SQL 解释成业务口径草稿,可以总结高频过滤条件,也可以根据 join 证据起草关系说明。
更重要的是,它可以帮人发现问题。
比如同一个“结算金额”,在不同 SQL 里用了两个字段;同一个“货号”,在不同场景里映射不一致;某个指标总是带一个状态过滤,但 DSL 里没有写;某条关系只有一次证据,不应该直接升级成强规则。
这些事让人肉眼一条条看,很累。让 AI 先整理一版,人再审核,效率会高很多。
但边界也要守住。
财务、库存、结算类指标的最终口径,必须由人确认。低置信度的字段映射,不能直接进生产。AI 生成的术语、关系、指标说明,都应该带证据、问题和审核状态。
一句话:
AI 负责把材料整理到可判断的程度,人负责判断。
以前做 DSL,常见方式是专家访谈加人工录入。质量可能不错,但起步慢,覆盖低,更新难。引入 AI 以后,可以先从业务日志和历史 SQL 里批量生成候选 DSL,再让人审核高频、高价值、高风险的部分。
人的工作从“从零写知识”,变成“审核和校准知识”。
这才是 AI 在这件事上的真正提效点。
真实产出:一个零售订单系统的知识库长什么样
前面讲的都是思路。下面直接贴一个我们从零售订单系统的业务日志和历史 SQL 里提炼出来的知识库目录结构,544 张表、50 个业务术语、369 个查询模板、11 组语义关系——全部由 AI 批量生成草稿,人工审核定稿。
order_glossary_full_preview/
├── index.md # 知识库入口索引
├── glossary/ # 业务术语(50 条)
│ ├── index.md # 按维度分类的术语目录
│ ├── product_code.md # "货号":同义词、字段映射、置信度、证据
│ ├── amount.md # "销售金额":定义、聚合方式、证据
│ ├── order_status.md # "订单状态":值枚举、过滤规则
│ └── ...
├── tables/ # 表概念文件(544 张)
│ ├── index.md
│ ├── order.md # 表结构 + 字段注释 + 索引信息
│ ├── order_dtl.md
│ └── ...
├── ddl/ # 原始 DDL(从 information_schema 提取)
│ ├── order.sql
│ └── ...
├── relationships/ # 语义关系(11 组高频 join)
│ ├── index.md # 按证据次数排序
│ ├── order__order_no__order_dtl__order_no.md
│ └── ...
└── references/ # 查询参考
├── joins.md # 观测到的 join 图谱(含证据次数)
└── query_templates/ # 归一化查询模板(369 个)
├── index.md
└── tpl_xxx.md
每一层解决不同的问题:
| 目录 | 解决什么 | 从哪来 | 给谁用 |
|---|---|---|---|
glossary/ |
用户说"货号",系统知道映射到哪个字段 | AI 从历史 SQL 字段频次 + 注释提取,人审核 | Query Planner 做术语映射 |
tables/ |
模型知道表结构、字段类型和注释 | 从 information_schema 自动导出 | Schema linking / RAG 召回 |
ddl/ |
保留原始 DDL 作为证据底座 | 自动导出 | 人工核对、版本追溯 |
relationships/ |
模型知道哪些表能 join、怎么 join、什么场景不该 join | AI 从历史 SQL join 证据生成,含业务解释和反模式 | SQL builder 选择 join 策略 |
references/query_templates/ |
模型知道常见查询长什么样 | 从历史 SQL 归一化、去参数、聚类 | Few-shot 示例召回 |
拿 glossary/product_code.md 举例,一条术语知识长这样:
type: Business Glossary
title: 商品编号
category: 商品维度
aliases: [货号, 商品外码]
related_tables: [order_dtl]
related_columns: [order_dtl.product_code]
review_status: draft
正文包含定义、字段映射表(含置信度)、Text2SQL 指导、歧义提醒和证据来源。用户说"货号",系统通过 aliases 命中这条术语,读到映射的是 order_dtl.product_code,并且知道不要和 product_no(商品内码)混淆。
再看 relationships/ 里的一条:order.order_no = order_dtl.order_no,证据次数 133 次。AI 生成的不只是 join 条件,还包括业务含义(主从关系)、适用场景(明细查询、多维度报表)、不适用场景(纯订单头统计不该 join 明细,否则数据膨胀),以及 Text2SQL 指导(过滤条件下推、分库感知、去重策略)。
这整套东西不是人从零写出来的。是 AI 从 Mapper 历史 SQL + 字段注释 + information_schema 批量提取候选,人审核高频高价值的部分。50 个术语、11 组关系里,高频的先审,低频的标 draft 留着后续确认。
知识库一旦建起来,Text2SQL 的链路就不再是"模型自由发挥写 SQL",而是"模型基于结构化知识做受控推理"。每次线上出错,不是调 prompt,而是补一条知识、改一个置信度、标一个 deprecated——知识在工程闭环里持续生长。
分水岭不在模型,在知识能不能长出来
Text2SQL 做到企业内部场景,最后拼的不是谁的 prompt 更长,而是谁能把业务经验持续沉淀成机器可消费的知识。
DSL 是一个好方向,但靠人手写走不远。业务日志、历史 SQL、字段注释、报表逻辑,本来就记录了大量真实口径。把这些东西提炼出来,按可索引、可审查、可演进的方式组织起来,再让 AI 批量生成草稿、人来审核定稿,Text2SQL 才会从一次次调模型,变成一套能持续生长的知识工程。
真正的分水岭,不是模型第一次能不能写出 SQL。
而是系统每错一次,能不能多沉淀一条以后用得上的知识。