为什么“会写 SQL”还不够
数据驱动决策的前提,是业务同事能快速问出问题,并理解答案背后的口径。
但在真实企业里,这件事往往比想象中难。
一位运营同事可能会这样问:
我想看最近 7 天放款金额。日报里有一列,财务口径里又是另一列,渠道复盘时还会出现第三种算法。我只是想知道业务跑得怎么样,为什么要先上一堂 SQL 课?
这不是某个业务人员“不专业”,而是企业数据天然存在的问题:同一句业务词,可能映射到不同环节、不同时间轴、不同表、不同状态过滤。
以“放款金额”为例,在消费信贷链路里至少可能有四种合法含义:
如果这些规则只存在于某个人脑子里、某张 BI 报表的列名里、某段复制过很多次的 SQL 里,再强的模型也只能猜。
所以,我们没有把目标定义成“让大模型直接连数仓、自由选表、自由写 SQL”。我们真正想建设的是一套企业问数能力:
业务问题
-> 识别领域与指标
-> 读取语义层与知识库
-> 澄清歧义
-> 只读执行 SQL
-> 输出结论、口径、来源与置信信息
-> 发现错误后回流为知识和评测换句话说,企业问数 Agent 的核心不是 SQL 生成,而是口径治理、数据治理。
外部经验给我们的启发
我们在设计这套能力时,重点参考了两个方向的公开实践。
Anthropic 在 How Anthropic enables self-service data analytics with Claude 中提到,自助分析 Agent 的挑战不只是模型会不会写查询,而是概念映射、知识新鲜度、检索路径和信任机制。
OpenAI 在 Inside OpenAI’s in-house data agent 中介绍了内部数据 Agent 的多层上下文策略:表使用信息、人工注释、代码增强、机构知识、记忆层、运行时探查,以及持续 eval。
这些经验给了我们三个判断:
不能让 Agent 直接在全量表里猜。
企业里有大量近重复表、历史字段和口径遗留。没有治理层,模型越自信越危险。上下文比 prompt 更重要。
好的提示词只能减少偶发错误,稳定能力必须来自结构化知识、语义层、工具边界和评测闭环。问数系统必须可运营。
业务口径会变,指标会新增,报表会迁移。Agent 如果不能从纠错里学习,很快就会变成另一套过时文档。
因此,我们选择吸收大厂方法论,但不照搬规模。大型科技公司的难点可能是“海量表发现正确数据集”,而垂直业务场景的难点往往是“有限表上同一个词有多个口径”。后者更需要把业务概念拆深、讲透、评测住。
我们抽象出的三类失败模式
企业问数 Agent 最常见的失败,不是 SQL 报错,而是“看起来合理但业务口径错了”。
如果没有治理,业务同事很难发现这些问题。因为错误答案通常也会有 SQL、有表格、有解释,看起来同样专业。
所以我们给系统定了一个约束:
Agent 可以生成 SQL,但必须先说明它采用的业务口径;口径不清时,必须先澄清或暴露默认假设。
架构原则:事实、流程、执行三层分离
我们把企业问数能力拆成三层。
这个分层非常关键。
如果事实写在流程里,几周后就会出现“文档一套、Agent 一套”的漂移。
如果流程写在知识库里,业务同事又很难维护。
如果执行层顺便定义口径,SQL 工具就会越权成为业务规则来源。
因此,最稳的方式是:
业务事实只维护一份
流程只负责如何使用事实
执行工具只负责核对和只读查询这也解释了为什么我们把“知识”做成独立资产,而不是塞进某个插件、某个 prompt 或某段代码里。
一次问数应该如何发生
以“最近 7 天签约金额和放款成功到账金额分别是多少”为例,理想流程不是直接写 SQL,而是:
识别业务领域:这是信贷动支 / 放款链路问题。
识别指标:签约金额、放款成功金额。
命中语义层:优先使用已经钦定的指标模板。
读取歧义规则:确认“签约”和“到账”不是同一个环节。
生成 SQL:使用受控表、受控字段、受控状态过滤。
执行前自审:时间字段、状态字段、粒度、JOIN、币种、范围是否正确。
只读执行:通过受控数据工具执行查询。
输出答案:结论先行,附口径、SQL、来源、owner 或置信说明。
伪 SQL 可能长这样:
-- 签约金额:按合同签署时间归属
SELECT DATE(contract_signed_at) AS stat_date,
SUM(amount) AS signed_amount
FROM loan_contract_flow
WHERE contract_signed_at >= CURRENT_DATE - INTERVAL '7 days'
AND contract_signed_at IS NOT NULL
AND is_deleted = false
GROUP BY 1;
-- 放款成功金额:按实际到账时间归属
SELECT DATE(disbursed_at) AS stat_date,
SUM(amount) AS success_amount
FROM loan_contract_flow
WHERE disbursed_at >= CURRENT_DATE - INTERVAL '7 days'
AND disbursed_at IS NOT NULL
AND disbursement_status = 'SUCCESS'
AND is_deleted = false
GROUP BY 1;注意这里没有把两列强行放在同一个时间轴里解释。签约看签约时间,到账看到账时间。按天对比时,两列不同是预期现象,不一定是 SQL 写错。
如果用户继续问“为什么两个值不一样”,Agent 应该解释业务动作:
签约金额表示合同已经签署。
放款成功金额表示资金已经实际到账。
二者之间可能存在处理中、失败、撤销等状态。
所以到账金额通常小于或等于签约金额。一个好的问数 Agent,必须能把 SQL 翻译回业务动作。
上下文分层:让模型少猜一点
我们把 Agent 可用的上下文分成四层。
第一层:钦定数据源
高频指标必须有“默认数据源”,并列出不推荐使用的近重复表。
原因很简单:企业数仓里经常存在多张相似表。它们可能来自不同历史阶段、不同报表、不同加工链路。人知道哪张“现在该用”,模型不知道。
所以对每个高频概念,要明确:
推荐表。
适用场景。
不适用场景。
已弃用或仅用于历史对账的表。
第二层:语义层
语义层存放高频指标的机器可读定义,例如:
指标名。
统计对象。
时间字段。
状态过滤。
默认维度。
推荐 SQL 模板。
应使用的数据工具。
命中语义层时,Agent 不应重新发明 SQL,而应优先使用钦定模板。
这和很多团队的直觉相反:我们不是让模型“更自由”,而是让模型在高频问题上“更少发挥”。
第三层:业务知识库
业务知识库维护人能看懂、能审阅、能持续更新的内容,包括:
指标卡片。
表说明。
字段枚举。
歧义规则。
典型问法。
纠错记录。
golden cases。
其中“歧义规则”非常重要。它不只解释定义,还规定默认路由。
例如:
这类规则如果只写在 prompt 里,很难被业务同事维护;如果写成知识页,就可以被评审、被链接、被评测。
第四层:流程技能
事实层告诉 Agent “什么是对的”。流程层告诉 Agent “该怎么做”。
流程技能包括:
如何识别领域。
先读语义层还是先读 Wiki。
什么时候必须反问。
什么时候可以直接出数。
什么时候必须先跑查询计划检查。
回答里必须包含哪些来源信息。
我们发现,知识和流程最好分开维护。事实经常由业务 owner 更新,流程则更像工程规范,需要由平台侧维护。
信任机制:让业务同事不用审 SQL,也能审口径
大多数业务用户不会逐行审 SQL,也不应该被要求这样做。
但业务用户必须能判断答案是否可信。
因此,每条高置信答案都应该带一段 provenance 信息:
来源:语义层 / 指标页 / 实时 schema 探查
口径:签约金额,按合同签署时间统计
执行:只读查询工具
时间范围:最近 7 天
数据新鲜度:最新数据时间 ...
Owner:指标负责人 / 数据负责人这段信息不能让错误答案自动变正确,但能让使用者判断:
这个答案能不能转发给团队?
这个数字是否适合进入日报?
如果有疑问应该找谁确认?
这是知识库口径,还是模型临时推断?
我们把它称为信任基础设施。
对抗式自审:让 Agent 在执行前先挑自己的错
执行 SQL 前,Agent 应该像评审员一样检查自己的答案。
常见红线包括:
时间字段是否选对。
状态过滤是否选对。
金额、人数、申请数是否混淆。
日报口径和 cohort 口径是否混用。
成功状态和提交状态是否混用。
是否存在无界时间范围。
是否用了
SELECT *。是否引入 1:N JOIN 导致金额膨胀。
是否选错数据工具或数据域。
这一步看起来像“多想一步”,但在企业问数里非常值钱。
因为最昂贵的错误不是 SQL 语法错,而是业务口径错。
纠错回流:不要只修复一次对话
Agent 第一次答错并不可怕。真正可怕的是同一个错误下周还会再发生。
所以我们把纠错定义为一条闭环:
业务指出错误
-> 记录原问法
-> 记录错误点
-> 记录正确口径
-> 补充证据:SQL、截图、owner 说明
-> 更新指标页 / 歧义页 / 表说明
-> 追加 eval case
-> 跑质量门禁
-> 合入知识库业务同事不需要手写 Markdown。最小材料包只需要:
原问法。
哪里错了。
正确口径是什么。
证据在哪里。
剩下的结构化写作、目录定位、链接补齐、eval case 生成、门禁检查,可以由 Agent 辅助完成。
这背后的原则是:
每一次纠错,都不应该只修复当前对话,而应该修复下一次所有相似对话。
评测:判口径,不只判数字
问数 Agent 的评测不能只看“数字是否等于某个固定值”。
线上数据会每天变化,数字本身不是稳定断言。更稳定的评测对象是口径:
是否用了正确时间字段。
是否用了正确状态过滤。
是否用了正确主表。
是否先聚合再 JOIN。
是否在模糊问题里先澄清。
是否暴露默认口径。
是否拒绝高风险查询。
因此,我们把评测分成三层。
这类评测不需要一开始很大。几十条高频 case,就足以拦住一批最容易出事故的问题。
我们学到的五个教训
教训一:先治理概念,再生成 SQL
“放款金额”这种词如果不先消歧,SQL 写得再漂亮也是错的。
企业问数的 ROI 不一定来自更复杂的 SQL 生成,而来自更早地把业务概念映射做对。
教训二:历史 SQL 是原材料,不是真理
很多团队想把历史查询日志直接塞给 Agent 做 RAG。
问题是,历史 SQL 往往缺少上下文:它当时回答什么问题?适用于什么范围?有哪些隐含过滤?为什么后来不用了?
更稳的做法是先做 SQL 口径审查:
这条 SQL 适合回答什么?
不适合回答什么?
应该沉淀成指标页、表页,还是歧义规则?教训三:指导目标,不要写死路径
问法千变万化,过度处方会把 Agent 推向错误路径。
流程技能应该规定目标和红线,例如“先查语义层”“模糊词必须澄清”“大查询先解释计划”,但不要把每个问题都写成固定脚本。
教训四:工具面要克制
给 Agent 太多重叠工具,会增加选择错误。
我们更倾向于:
工具边界清晰。
默认只读。
大查询先收窄。
每个数据域有明确路由。
工具只执行事实核对,不定义业务口径。
教训五:知识库和流程层必须分家
单一事实源不是口号。
如果纠错只改 prompt,不改知识库,几周后就会出现两份口径。
如果业务事实写进流程代码,业务 owner 就很难参与维护。
更可持续的方式是:
知识库管“是什么”
流程层管“怎么做”
执行层管“查事实”与 OpenAI、Anthropic 实践的异同
我们的结论是:不必一开始追求“大而全”的企业数据 Agent。
如果业务场景足够垂直,先把高频指标、钦定数据源、歧义规则和几十条 eval 做扎实,就能获得很大收益。
如果从零开始,建议先做这五件事
1. 选 10 个最高频问题
不要一开始覆盖所有表。
先找业务每天都问、反复对数、最容易争议的 10 个问题。
2. 为每个问题写清口径卡片
至少写清:
指标解释。
统计对象。
时间字段。
状态过滤。
默认表。
不适用场景。
owner。
3. 建一个薄语义层
把最高频、最稳定的指标做成机器可读模板。
不要让模型每次都重新理解。
4. 补歧义规则
把“完成率”“放款金额”“有效用户”“今日数据”这类模糊词单独维护。
模糊词不治理,Agent 就会把业务习惯误当成唯一事实。
5. 建最小评测集
几十条 case 就够开始。
重点不是覆盖率好看,而是覆盖那些“答错会造成业务误判”的问题。
结语:问数 Agent 的护城河不是模型,而是知识运营
模型会越来越强,SQL 生成会越来越容易。
但企业问数的长期护城河,不是让模型多写几行 SQL,而是:
谁能把业务口径讲清楚。
谁能把纠错沉淀下来。
谁能持续维护知识新鲜度。
谁能用评测防止能力退化。
谁能让业务同事参与知识共建。
大模型不是数据治理的替代品。
更准确地说,它是把数据治理带到业务日常的一种新界面。