让分析 agent 先过考试:拆开 Data Culture 在 dbt 上的评测框架

目录
分析 agent 接上数仓以后,第一个该回答的问题是“它答对了多少”。Data Culture 第一次给客户上线时答不上来:团队把 agent 接到 Snowflake 的 MCP 服务器上给测试用户用,连一个大概的准确率都说不出。1之后他们定下一条规矩:没法量的,不上线。这个做法借自 Chip Huyen 在《AI Engineering》里讲的“评测驱动开发”。1
dbt Labs 10 月 26 日的博客记录了 Data Culture 分析工程总监 Opeyemi Fabiyi 在 dbt Summit 2026 上的分享;团队把评测管道开源成了 agent-trust-for-dbt 仓库。12这篇把仓库拆成五层,一层一层讲。读完应该能照着搭一个自己的版本。
同一个模型,准确率从 0 走到 94.7%
电商客户的项目里,模型从头到尾没换,考卷和判分程序也没换,每次只多加一层上下文:1
直接查原始数据时一道都答不对;换成 dbt 建模好的表,到 50%;再依次加上列描述、业务规则和默认过滤条件、已验证查询和命名指标,最后到 94.7%。医疗客户也从 0% 起步,加了同类的业务规则和已验证查询之后,过了 Data Culture 内部 90% 的上线线;原文没给中间的数。1
这组数有两种读法。第一,列描述只加了 2.6 个点,业务规则加了 26.3 个点。agent 缺的主要是这家公司内部怎么算,列名说明帮得有限。第二,52.6%、78.9%、94.7% 恰好等于 10/19、15/19、18/19,考卷很可能只有 19 道题左右,一道题约 5.3 个百分点。这是我从百分比倒推的,原文没给题数;50% 用 19 除不出来,那一级的题数可能不同。题这么少的时候,两个版本差一两道题,就不该当成真的变好或变坏。
一张图看全:五层,从数据到成绩单
仓库 README 里有一张结构图,我照着它重画成五层:2
- 数据底座。 用公开的 Olist 巴西电商数据,跑在 DuckDB 上。dbt 项目分 staging、intermediate、marts 三层,只有打了
agent标签的 marts 对 agent 开放。 - 契约层。 一个叫 AGENTS 的 schema,用四张表告诉 agent 能查什么、怎么算。
- agent 层。 Claude 或其他大模型通过 DuckDB 的 MCP 服务器读契约、写 SQL,交回答案、SQL 和查询结果。
- 评测层。 40 道金标准问题逐一跑过 agent,先做三关确定性比对,都没过再请 LLM 裁判,最后出成绩单。
- 综合方法。 只有文档:什么时候自动判、什么时候交给人,怎么拿专家过去的分析做对照,怎么把纠错写回去,以及这套办法抓不到什么。
仓库自己写明,这是一份教学用的参考实现,不是生产评测系统,客户数据也不在里面。2下面重点展开第 2 层和第 4 层,它们最值得照抄。
agent 只看一份契约:AGENTS schema 里的四张表
契约由 dbt 宏 build_agents_schema 生成,一共四张表:2
agents.root:业务口径。仓库里放的是一份olist_business_context.md,写明“收入”默认指 GMV(已完成订单的商品金额,不含运费),哪些订单状态算完成,时间要先从 UTC 转成圣保罗时间再按天、按月截断,数人头要用customer_unique_id,以及哪些问题超出范围(成本、利润、网站流量、预测)。agents.dbt_model:只登记打了agent标签的 marts,staging 和 intermediate 不在里面。agents.dbt_column:这些表每一列的类型和说明。agents.dbt_dependency:表之间的血缘。
agent 启动时先检查 agents.root 里有没有业务口径,没有就报错,不启动。提示词规定读的顺序:先读 root,再看 dbt_model 挑粒度合适的表,再读要用的列,最后只查 marts。它只能用自己真正跑过的查询作答,问题超出范围就拒答并说明原因。2
只靠提示词不够,代码里还有一道闸。每条 SQL 执行前都要过校验:只允许一条 SELECT;检查查询的逻辑计划,引用的表必须登记在 agents.dbt_model 里,read_csv 这类直接读文件的函数会被拦下。2这里有个小矛盾:提示词说不许查 agents schema,校验器却放行它,因为 agent 要读契约。文字比代码严,照搬时最好把两边统一。
业务规则放在哪里也有讲究。地区划分、部门归类、去重这些规则,在 intermediate 模型里算一次,agent 看到的是已经算好的列。2这和图 1 里 78.9% 那一级对得上:规则写进模型和口径文档,agent 就不用每次自己推。
考卷怎么出:两个分析师会写出同一条 SQL 的题
金标准问题的标准是:两个分析师读到这道题,会写出基本相同的 SQL,得到相同的结果。每道题的期望答案先由业务专家确认过才能用。1仓库的指南把它拆成几条:3
- 用业务人员的说法,不把列名写进题里;
- 一道题只考一件事,并且来自真实需求;
- 期望 SQL 要实际跑过,请业务专家或数据负责人确认;
- 日期写死,不用“上个月”这种会变的说法;
- 期望 SQL 只返回验证答案需要的列,多一列就会造成误判。
仓库的 40 道题按类型分七类。下面每类举一道仓库里的原题,括号里是难度:2
- 聚合,9 道:“2018 年第一季度的 GMV 是多少?”(简单)
- 比率,10 道:“2018 年 3 月的客户满意度(CSAT)是多少?”(简单)
- 多指标,7 道:“2017 年第四季度,州内订单和跨州订单的平均配送时间各是多少?”(中等)
- 时间序列,6 道:“2017 年每个月新增了多少客户?”(中等)
- 过滤,3 道:“2017 年有多少订单被取消?”(简单)
- 排名,2 道:“2017 年 GMV 最高的 10 个商品品类是哪些?”(简单)
- 越界,3 道:“能预测一下 2019 年第一季度的 GMV 吗?”(简单)。这类题的期望 SQL 留空:数据里没有预测,agent 应该拒答。
难度分三档:简单 14 道,一张表、一次聚合、一个明确的过滤条件;中等 17 道,涉及多张表、日期逻辑、地区分组或一条业务规则;困难 9 道,要用窗口函数、跨期对比、帕累托分析,或同时用上几条业务规则。23
指南建议从 20 到 40 道起步,质量优先。用户问到没覆盖的就加题,口径或数据模型变了就改期望 SQL 并重新确认,过时的题直接退掉。3
判分从严到宽,三关都没过才请裁判
Data Culture 最早的评测用精确匹配,人工翻结果时发现,一批“错题”其实是对的:数字对,只是列名不同;三个品类都对,多了一行合法的“未分类”;数字对,多带了一列辅助数据。1于是他们改为比较查询结果,不再比较 SQL 文本。
仓库的 eval_pipeline.py 先处理拒答:越界题拒答算对;越界题却给了答案、能答的题却拒答,都算错。2剩下的题依次过三关:
- 严格比对。 列名和行都排好序,值转成文本算 MD5,一致就过。
- 不看列名。 行数相同时去掉列名,只比每一行的值。
- 允许误差。 数字差在 0.01 加 0.1% 以内算相等,文字忽略大小写和首尾空格。
三关都没过、两边都有结果时,才交给 LLM 裁判。裁判看两份结果(各最多 20 行),判断“对提问的人来说,这两份结果给出的是不是同一个答案”,打 0 到 1 分,0.8 分及以上算过;默认裁判模型是 claude-sonnet-4-6。2按原文的说法,确定性比对能处理约 80% 的题,快、免费、可以重放;剩下约 20% 交给裁判。1
- 原文提到有一级“容忍多出来的列”,代码里多一列或多一行都会直接判错,只能靠裁判救回来。
- README 说裁判给出 PASS 或 FAIL,外加一句理由;代码里只有一个分数,没有理由。
- 原文给的开源链接写成
dbt-agent-trust,实际仓库名是agent-trust-for-dbt。
我按代码讲,因为跑出来的成绩由代码决定。
成绩单要能告诉你下一层该补什么
每道题记一行:问题、类别、难度、期望 SQL、agent 写的 SQL、有没有作答、过没过、在哪一关过的、裁判分、token、成本和耗时。汇总时分别算总通过率、可答题的通过率、越界题的处理,以及按关卡、难度、类别拆开的通过数。2
成绩单的用处是决定下一步补哪一层。比率题集中挂掉,多半是口径没写清;时间序列题挂掉,先查时区和每周从哪天算起;靠裁判才过的题多,说明期望 SQL 返回的列可能不够干净。补完一层,用同一套题重跑;一次只改一样,涨跌才能归到那一层头上。图 1 的五个台阶,就是这样一级一级量出来的。1
和 Anthropic 的内部做法对照:方向一致,细节不同
Anthropic 的数据团队 6 月发过一篇文章,讲内部怎么用 Claude 做自助分析:不加 skills 时,准确率在他们的评测上没超过 21%;加上之后整体超过 95%,部分领域接近 99%。4两家的做法放在一起看:
| Data Culture(dbt Summit 分享) | Anthropic 内部分析 agent | |
|---|---|---|
| 起点和终点 | 原始数据 0%,加满上下文 94.7%(电商客户) | 不加 skills 不超过 21%,加上后整体超过 95% |
| 加了什么 | dbt 建模表、列描述、业务规则、已验证查询和命名指标 | 规范的数据集、语义层、业务上下文、skills |
| 考卷 | 两个分析师会写出同一条 SQL 的题,专家确认答案 | 问答对:由仪表板生成再人工确认,加上用户纠正过的问题 |
| 怎么判分 | 比查询结果:三关确定性比对,最后 LLM 裁判 | 答案锚定在快照日期或稳定的表上,否则改查 SQL 本身 |
| 历史查询 | 已验证查询和命名指标一起带来 15.8 个点 | 直接检索几千个历史 SQL 文件,变化不到 1 个点 |
| 上线线 | 内部 90% | 起初每个领域约 90% |
| 维护 | 口径变了改期望 SQL,过时的题退掉 | 不维护时,一个月从约 95% 掉到约 65% |
两边一致的地方有三点:准确率主要是上下文问题;同一套题,一次只改一处;越界和拒答要单独测。
分歧在历史查询上。Data Culture 的最后一级“已验证查询和命名指标”带来 15.8 个点。Anthropic 发现,让 agent 直接 grep 几千个历史 SQL 文件,准确率变化不到 1 个点,他们建议把查询库提炼成文档和 skills。4合起来看,经人确认、数量有限的查询有用,未经整理的一大堆历史查询几乎没用。Anthropic 还试过让大模型自动生成指标定义,在评测上反而拖分,他们的结论是让 Claude 起草文档,定义由人来定。4这和 Data Culture 让专家确认每道题的答案是同一个思路。
另一个提醒来自维护。Anthropic 上线时离线准确率约 95%,一个月后掉到约 65%,原因是表和口径变了,文档没跟上。4Data Culture 的 94.7% 只是某一天的快照,考卷和契约如果不随数据模型一起维护,这个数也会掉。
公开榜单的分数同样不能直接搬过来。Spider 2.0 用 632 道企业级任务测 text-to-SQL,当时最好的 agent 只做对 21.3%,同样的配置在老的 Spider 1.0 上是 91.2%。5换到自己的仓库,要有自己的考卷。
照着搭:哪些直接借,哪些要改
可以直接借的:
- AGENTS 契约表加只读 SQL 校验。 agent 能查什么由 dbt 标签决定,口径写在一份由人维护的文档里。
- 按结果比对的三关,加上拒答题。 确定性的部分越多,评测越便宜,也越能重放。
- 按关卡和类别拆开的成绩单。 用它决定下一层补什么。
要按自己的情况改的:
- 数字容差。 0.01 加 0.1% 对金额可能太松,对比率可能太严,按指标分别定。
- 多出来的列和行。 如果业务能接受 agent 多给一列辅助数据,把这条规则写进比对代码,不要全交给裁判。
- 裁判只给分、不给理由。 审计场景要把理由、两份原始结果和裁判模型的版本一起存下来,否则事后没法复核。这和我在裁判模型那篇里提的要求一样:结论要能重放。
- 题量和上线线。 19 道题时一道就是 5 个多点。“过 90%”最好同时写明最少题数,以及每一类至少几道。
先写考卷,再接 agent。考卷写好以后,换模型、加上下文、改口径,每一步的涨跌都能量出来。
参考
- How Data Culture measured its way from zero trust to a shippable AI analytics agent, dbt Labs Blog
- agent-trust-for-dbt, GitHub
- Golden question guide, GitHub
- How Anthropic enables self-service data analytics with Claude, Claude Blog
- Spider 2.0: Evaluating Language Models on Real-World Enterprise Text-to-SQL Workflows, arXiv
评论
匿名评论审核后显示,用 GitHub、X、Facebook 登录后直接显示。邮箱不公开,只用于回复通知。
评论暂时加载不出来,稍后再试。