先画依赖图,再写评测:你的指标会互相污染
三个指标同时掉,往往只是一个根因被记了三次。评测体系的顺序错了:先把执行依赖图画出来,再规定每个指标在什么条件下可评,才轮到 Judge 和门禁。
把工具权限和自主决策交给 Agent 之后,第一个绕不开的问题就是怎么判定 Agent 有没有走偏。绝大多数 Agent 评测体系不是没东西:测试集有,Judge 有,看板有,回归也有,还有一批人每周过 badcase。
这套体系坏在一个更靠前的地方:指标是先堆起来的,归因是后补的。加指标的时候每层都有当时的理由,等要看为什么掉分时,没人记得这张表当初为什么这么排。
顺序应该是反的:先把被测 Agent 的执行依赖图画出来,再按这张图规定每一个指标在什么条件下可评。结论就这一句:上游没跑通的时候,下游指标记「失败」,等于把一个根因重复记成了好几个失败。这类污染从看板上完全看不出来,红色反而越堆越密。
三个指标一起报警,根因只有一个#
常见的搭法是先列一张指标表:幻觉率、检索精确率、召回率、回答忠实度、工具调用准确率、任务完成率。跑一遍,看板出来,某个数字掉了,再去 badcase 里手工翻原因。这六个指标沿着执行链排成单向依赖。路由决定走哪条分支,分支决定检索是否执行,检索的输出决定回答忠实度能不能算,工具调用的入参决定工具调用准确率能不能算。
任何一个模块掉线,排在它后面的检索、引用和忠实度三项分数同时失去意义。阿里全球化商品中心的智能答疑 Agent 把这条链拆得很清楚,四段是感知、规划、记忆、工具,链上任一处断掉,后面三项一起失效。
期望路径:查询意图 → 知识检索 → 引用绑定 → 生成
实际路径:查询意图 → 技能分支 → 直接生成
检索模块 → 未执行
引用模块 → 无输入
忠实度 → 无比较对象
看板:三项同时记失败
根因:一处plaintext我在自己的用例集里见过两条这种形态,都是把一处失败记成多处失败。两条形态不同,扩散结构却一致。一条是知识根本没有检索到,但回答写得很自信。看板上这是两个指标同时掉,翻下去只有一个原因,可修的对象只有检索的召回。
两条链的形态不同,扩散结构却一致:一个根因沿依赖链向下,扩散成多个「失败」。多轮会话里丢失上一轮的上下文对象,是其中一条;会话再往下问「那这个呢」,Agent 答不上来,看板却同时记成多轮完成率失败、意图识别失败、检索失败。看板上的红色越密,真正该改的那一处越容易被淹掉。
「不可评」是一个正当结果#
给每个指标定义可评条件,是后面所有判定成立的前提:指标的上游依赖是什么,依赖没满足时记什么。可评条件没定义出来,加多少指标都是在放大噪声。依赖不满足时记 error 并跳过统计,failed 只留给真做错的模块。error 和 failed 在看板上都显示成红色,指向的行动却不同:error 说明这次运行没真正检验这个模块,failed 说明这个模块做错了。
| 上游状态 | 下游指标 | 记什么 | 指向的行动 |
|---|---|---|---|
| 依赖已执行 | 全部 | 正常判定 | 按分数走 RCA |
| 依赖未执行(上游失败) | 依赖其输出的所有指标 | error,跳过统计 | 修上游 |
| 依赖未执行(上游是正确拒答) | 同上 | error,跳过统计 | 无需行动 |
| 依赖执行但返回为空 | 该模块自身 | 按用例语义判定 | 可能是真失败 |
「不可评」最实际的价值是它逼你把执行链写下来,写不出来说明还没搞清楚这个 Agent 怎么工作。上面这张表的第二行是依赖未执行且上游失败,第三行是依赖未执行且上游正确拒答。两行的下游都记 error 并跳过统计,差别只在行动上:第二行要去修上游,第三行本来就该跳过。没有这张表,做评测的人遇到正确拒答这类 case,会反复排查一个根本不存在的问题。
一个评测范围只能有一个主指标#
定义完可评性,接着要收敛判定口径;判定口径不收敛,看板上的通过率没人敢拿去做决策。我的做法是每个评测范围只设一个主指标,这个指标单独决定用例通过与否。
其余指标全部降级为诊断维度,只负责解释为什么,成败由主指标单独决定。端到端范围用任务完成率,工具范围用调用准确率,多轮范围用多轮完成率。多个指标一起做 AND 时,一个格式问题就能否定一个内容完全正确的用例。
一个评测范围只能有一个主指标,这个位置留给最接近用户结果的那个指标,最容易测的排不进来。多指标一起做 OR,会让几乎一切都能通过;一起做 AND,看板上的通过率又莫名偏低,翻出来的 badcase 全是措辞和排版,真正的内容问题反倒没人翻。
小米零售的 AI 问数项目把这个分离讲得很直白。企微领域一次评测放进 18 个指标、122 条用例,中间层返回的结构化查询构造准确率是 93.2%,这一层判的是查询构造得对不对。
另一版另建 44 条专项用例复测,专门盯同一根因,不再沿用原测试集。两个数字都真实,指向的却是两件事:一个回答查询有没有构造对,一个回答用户实际拿到什么。结构化查询构造准确率仍然是 93.2%,最终答案准确率是 100%(44/44)。17 条失败 case 里还有一组更能说明主指标该怎么选。
按根因聚类新增专项用例,修的是一整类问题,看板变绿的同时系统也在变好。17 个失败里 8 个来自同一个根因。时间字段和偏移规则分了两套口径,占比接近一半。按原题逐条修 Prompt,能把这 8 条修到通过,看板立刻变绿,却只覆盖这 8 条。
单轮平均分是多轮场景里最常见的陷阱。小米零售问数在「达成率」上补齐指标边界、表达决策表、用户归属和过滤约束之后,指标选择准确率从约 65% 提到约 98%,差的只是这几条口径知识。一个退款会话里,Agent 每一轮都解释得很自然,语气、结构、同理心都没问题,全程却没调用订单查询工具就承诺了退款。按单轮平均分这个会话能拿高分,按端到端完成率算失败。
四层一起看,才分得出说得好和做成了。对话型 Agent 我拆成四层,每层只回答一个不同的问题,单轮平均分只回答了其中一层。腾讯 SRE 的错误码 Agent 更狠:必需查询有没有执行、参数对不对、工具失败有没有显式降级、证据支不支持结论、置信度匹不匹配,每一项单独判一遍。
| 层 | 看什么 | 典型陷阱 |
|---|---|---|
| Turn | 单轮回答质量、工具选择 | 单轮好看但方向错了 |
| Session | 多轮目标是否连贯达成 | 中途丢上下文对象 |
| Trace | 工具调用链、入参、返回 | 宣称调用过但实际没有 |
| Outcome | 用户问题是否真的解决 | 回答正确但业务状态没变 |
只看动作类型,分不出「有证据地答对」和「碰巧答对」。假阳性里有一类要提前排掉:多轮评测常固定等一小段时间,持久化链路本身一有延迟,评测就会把延迟当成「Agent 忘了」。这种假失忆属于观测问题,单列在能力分之外。
忠实于拿到的材料,不等于事实正确#
评测能证明的只有一件事:回答有没有忠实于手里拿到的材料。忠实于拿到的材料,证明不了材料本身是对的;两件事一旦混在一起,检索侧的问题都会算到生成侧头上,优化方向从一开始就偏了。
语法对、执行通、忠实于拿到的表,三项全对,只有业务口径错了。小米零售问数踩过这样一个坑:Text-to-SQL 语法全对,直接汇总支付金额,漏掉取消、退款、退货的过滤,结果比标准口径高出近 10%。
高德本地生活的经营分析 Agent 也栽在同一类问题上。一次行业接入里,用总量除以对象数量得到的平均值,与直接查询结果相差约 10%;追下去发现分子分母来自两套数据源,统计范围没对齐。
口径错在数据源,改措辞改不掉这个错。高德那条线的解法绕开了改 Prompt:先统一来源,再核验总量等于对象数乘平均值这个恒等式是否闭合。更隐蔽的一种错是数字来源正确、对象范围错了,把全部纳入统计的对象算成尚未开始使用的对象。这种错光核对数字查不出来,这笔账没算错,只是答错了问题。
有一道判据要写进评测体系:金额、口径、状态、时间窗这些能拿权威系统对账的字段,都要留一条独立于模型输出的对账路径。没有这条路径,评测给的信心是假的。忠实度和事实正确是两笔账,评测不能只算前一笔。文档把真正的报错原因写成另一套说法,Agent 忠实复述出来仍然错。
主指标定下来之后,指标表必须按 Agent 类型分开建。同一张表覆盖所有类型,等于给每个类型都留下一半测不到的地方。我按类型分开建表,六种的主指标是这样:前三种有客观口径,后三种各有各的说法。这六条按能不能指回一个可改对象排,不是拍脑袋定的。
- 知识问答类,主指标是答案正确率,配套看引用命中率和检索轮次;
- 任务执行类,主指标是端到端任务完成率,过程看工具调用链走没走完;
- 推理决策类,主指标是结论正确率,过程看关键步骤有没有被跳过;
- 创意生成类没有客观主指标,走人工评审加可用性采纳率;
- 多轮引导类按多轮完成率判,过程看澄清轮次和重复询问率;
- 多 Agent 协作类仍按任务完成率判,成本侧重在子任务之间重复打包的上下文。
指标表必须按类型分开建,同一个「幻觉率」就是活例子:放在知识问答上是核心指标,放在创意生成上几乎没意义。方案、架构这类无法用对错判的输出,值得单独建表,阿里的架构师 Agent 在需求覆盖这一维能到 95% 以上。这个数字拿有证据支撑的需求条目除以原始需求条目,衡量的是覆盖,跟上线成功率、免人工比例是三回事。我按六个维度验收——需求覆盖、系统影响、证据支撑、风险识别、验证方式、不确定性声明。
Judge 只判 Trace 里能查出的事实#
再往下是判定层,这一层最容易坏,因为是唯一有权给分的地方。规则层先做粗筛,把明显通过、明显失败、存疑三类分开。只把存疑的部分交给模型精判,高风险和低置信的走人工。这么分是为了让模型只做模型擅长的那部分判断,粗筛已经把能确定的判完了。
精判阶段有一条硬要求:Judge 读 Trace 里的事实,不读最终回复。Judge 判断的是这一次工具到底有没有被调用、入参是什么、返回是什么。从回答文本里推断调用过没有,那是在猜;Trace 里的调用记录是结构化的,没有解释空间。
模型完全有能力写出一段读起来像是查过订单的回答,任何基于文本语义的判定都会在这类用例上出错。这个区别比看上去重要。Judge 自身也要被当成被测对象来治理:备一份金标校准集,边界样本配 few-shot,多模型对抗防同源偏好。
经验基线和人工的一致率在 85% 上下,这个数字当门槛太松,当基线才合适。这个基线用来观察 Judge 有没有漂移。
腾讯 SRE 那条线上的对照更完整。同一批 50 条 case,v3 到 v4 与人工的一致率,从 66%(33/50)提到 88%(44/50)。一致率 66% 和硬冲突率 20% 这两个数字,分母都是这 50 条 case,分子分别是一次判定和一次硬冲突,33/50 变成 44/50,硬冲突从 10/50 降到 0。
一致率能被这样量化,Judge 才算一个可治理的组件,从此可查、可改、可追。可治理的前提是把这条口径写进报告,让人知道分子的来源。
Real 和 Mock 各自隔离了什么#
评测执行有两种模式,Real 和 Mock 两种模式隔离的东西不一样,都不能省。只跑一种,等于默认另一类问题不存在。
Real 模式接真实工具返回,评的是端到端能不能跑通,代价是外部波动混了进来:同一个用例今天通过明天失败,可能跟 Agent 一点关系都没有。Mock 模式固定工具返回,把外部变量钉死,评的是策略在固定输入下能不能工作。Agent 的决策路径完整保留下来,你能干净地看到「给定这些输入,Agent 会不会做正确的判断」。
功能性回归跑 Mock,发布门禁跑 Real,两条链路共用一份用例,结果才可比。只跑 Real,策略问题会被环境噪声盖住;只跑 Mock,证明不了真实环境里能跑得通。
数据集的搭建顺序跟这个逻辑一致,先做 50 到 200 条高质量 golden case,覆盖核心路径和高风险边界。跑稳之后再扩四路——分类采样集、长尾集、对抗集、线上回流集。按模块给一组起步规模:
| 用例类型 | 建议规模 |
|---|---|
| 基础技能 | ≥50 条 |
| 知识问答 | ≥100 条 |
| 多轮对话 | ≥10 组 |
| 上下文衰减 | ≥10 组 |
| 其余专项 | ≥20 条 |
回归要跑得完也跑得稳,超时、重试次数和并发度都要先定死。这组配置压的是两件事:超时压住跑不完的长任务,重试和并发把外部抖动挡在 Agent 能力之外。
数据集维护上有一件事值得单独记。高德那条线上返修时做过一次证据瘦身:只保留影响半径内的证据,取舍按错误字段和引用关系来定。评测集同样适用:判断变了才重跑,只改版式就保留原判断。全量重跑既贵,又会破坏本来就正确的部分。
评测集要收线上样本,但只靠随机抽样不够:线上绝大多数是正常样本,随机抽一千条,高风险边界一条都覆盖不到,而决定事故率的恰恰是那些边界。成本指标必须和质量指标在同一次运行里采集,这事常被往后推。分两次跑出来的两个数字,最后还是要在汇报时对齐,等于白付一次时间成本。
我每次评测固定记三样:调用次数、token 用量、端到端延迟。这组记录和质量分数落在同一条记录上。
一次改动同时抬高通过率和平均工具轮次,说明这次改动多半把成本挪了地方。两套数字分两次跑、存在两张表里的话,没有人会做这个对比。工具轮次对体感的影响比分数大得多。
同一次操作,一条路径是 3 次工具调用、约 2 秒落地,另一条是 15 次调用、约 15 秒收敛。通过率上两者可能打平,用户感受到的却是两个产品。
把人工耗时折算进同一个口径,成本表才真正有意义——这一步把「好不好」翻译成「值不值」,是评测报告里最该有的一段。线上还要补一组单独的数。小米零售问数上线后服务 1000 多名店长,渗透率 88.8%,口径是活跃店长里用过问数的比例;同期沉淀约 1.6 万条真人对话,满意度 4.32 分(满分 5 分)。
这些数字不属于离线评测,但这一组线上数字是离线指标有没有选对的唯一校验。离线评测通过而线上关键指标恶化是真实存在的。我在门禁里加了一条:离线提升但线上核心指标下滑,就触发回滚或降级,不再继续观察。这条要写进发布流程,交给流程盯着,不指望人记得看。
收尾是把分数变成改动,分数到不了改动,前面整篇讲的东西都不落地。badcase 归因是一串固定动作:汇总证据,用「问题现象 × 功能模块」的映射表缩圈,按模块标出通过、软通过、失败,定主责和次责,收尾做结构化落盘。
落盘不能省,落盘是下一次复盘的起点,也是回归集的新增输入。输出的优化建议按可执行程度分四档:只报告、开工单、给配置建议、直接给 PR 候选。
分级的好处是拿到报告的人一眼知道这条要不要现在动,省掉这一步报告就会变成一堆没人认领的描述。发布门禁上我只认连续成功率。至少一次成功率在方差大的系统里几乎是必然事件,拿它当门禁等于没有门禁。
配套三样东西:置信区间、显著性判断、最小可感知变化阈值。缺了这三样,两个版本之间的小差异能让人争论一整天,而那可能只是噪声。指标本身也分三档:最上一档用于上线门禁,中间一档用于版本比较和工程优化,最低一档用于体验改善和长期观察。最上一档的数量要克制,超过五条以后看板就只提供讨论素材,不再指导行动。
评测体系本身的搭建成本也要算进去。阿里把 Skill 做成可评测之后,新增一个用例只要改一份声明式配置,公共部分与单用例部分分开写,单用例只声明输入、断言和判定方式。
脚手架太贵的评测,跑两次就没人愿意再跑;这套顺序走完之后,看板上的每个红点都能落到一处具体的修改对象。看板的价值就在这里——把争论收敛成几个具体的修改对象。判据和指标都定住之后,新的问题会自己长出来:让系统自己出题、自己判分,评测集就会反过来污染自己。到这一步,评测集从标尺变成了被测对象。