1. 一条永远通过的用例
某团队写了一条测试用例:「把这份会议记录整理成待办」。
成功条件写的是「输出包含待办项」。
这条用例永远通过。任何输出都能被解释成"包含待办项"——一段散乱的要点、一句"建议整理成三条待办"、甚至一个反问,都能算。团队以为这个任务已经被覆盖,实际它什么都没测。
改成可判定的写法:输出包含至少 3 条待办、每条有待办描述与负责人、负责人必须是会议记录里出现过的人名。四条判据,每条都能数、能查、能比对。
1.1 坏用例还有两种形态
第一种是条件太松,如上。
第二种是条件太紧。一条用例要求"输出必须恰好三节,且第一节标题必须是『背景』"。这个要求与任务目标无关——换个标题写"现状"同样合格,但用例判它失败。这类用例的后果是团队开始忽略结果,因为总有几条永远不通过。
第三种是没有区分度。条件可能既不松也不紧,但它测的东西任何版本都能做到。例如"输出是中文"——确实可判定,但它对版本之间的差异毫无分辨力。
三种形态的共同后果是一样的:这个用例对评测集没有贡献,却占了名额。第一种让通过率虚高,第二种让通过率虚低,第三种让通过率对差异不敏感。
2. 三处误差源
从这条用例往里看,评测结果不可信的来源有三处。
| 误差源 | 表现 | 后果 | 修的位置 |
|---|---|---|---|
| 用例定义 | 判不准 | 任何版本都通过,评测失去区分度 | 成功条件与区分度检验 |
| 验证器 | 判不全 | 只测了容易测的部分 | 覆盖范围与混合判定 |
| 评分器 | 判不稳 | 同一答案两次评分不同 | 校准与一致性度量 |
三者的顺序重要:用例定义不成立时,后面两处再准也没有意义——一个永远通过的用例,无论评分器多精确都是无效的。
第 1 节那条坏用例属于第一处。后两处的具体形态在后面的节里展开。
3. 用例定义的误差
3.1 成功条件的检验
可判定、有区分度、与目标相关。三条各自对应第 1 节里那三种坏形态。
可判定的写法已经在第 1 节给出。这里补区分度与相关性的检验方式。
区分度的检验有一个便宜办法:用当前版本与一个明显更差的版本(例如去掉关键工具的版本)各跑一次。
| 结果 | 含义 |
|---|---|
| 两个版本都通过 | 用例无区分度 |
| 两个版本都不通过 | 用例过严 |
| 好的过、差的不过 | 用例可用 |
这个检验需要维护一个「明显更差」的基线版本。成本不高——把某个关键工具禁用即可。
3.2 用例的四部分
一个可用的用例包含四部分。
输入。 用户说的话,以及与真实使用一致的上下文(工作目录、可用的外部系统、前置状态)。
环境。 初始状态。这一项常被省略,导致用例不可复现——同一个用例在干净的仓库里和在改了一半的仓库里,结果完全不同。
成功条件。 判定方式。
可接受动作集合。 对于需要判断过程的任务,列出允许的动作与禁止的动作。这一项在轨迹前缀回归里尤其重要,下面格式一节展开。
3.3 环境重置的三种做法
第二部分(环境)常被写成一句话「初始状态」,但它有三种实现,成本差异很大。
| 做法 | 重置成本 | 适用 |
|---|---|---|
| 快照回滚 | 秒级 | 有快照能力的存储或容器 |
| 重建环境 | 分钟级 | 无快照能力,但可脚本化 |
| 用后清理 | 取决于清理完整度 | 只读任务或轻量写入 |
第一种最可靠。它的「重置」是一次指针切换,不依赖清理逻辑是否正确。
第三种最便宜也最危险。清理逻辑漏掉一处,残留就会影响下一次运行,而这类问题表现为「同一用例有时过有时不过」,很难定位。
判断一个环境是否真的被重置,有个便宜的方法:跑两次同一个用例但不清理,看第二次的结果是否与第一次相同。如果不同,说明环境有残留。
3.4 被漏掉的用例部分:前置状态
除了输入与环境,还有一项:前置状态。
它指的是「任务开始前,系统应该处于什么状态」。例如「已经登录」「购物车里有商品」「上次的草稿还在」。
这一项不与「环境」重复。环境是外部的(文件、数据库、服务),前置状态是这次任务依赖的前提(登录态、会话历史、缓存)。
漏掉前置状态的后果是用例在干净环境里能过,在真实环境里过不了——因为真实环境里总有上一次的残留。
4. 验证器的覆盖
三种验证器,覆盖范围不同。
| 形态 | 能判什么 | 局限 |
|---|---|---|
| 代码判定 | 跑测试、查状态、比对结构 | 只能判可形式化的项 |
| 规则判定 | 动作序列、权限、必含字段 | 规则写完之前有盲区,写错会系统性误判 |
| 模型量表 | 表达、是否抓住重点 | 有偏差,需校准 |
组合规则三条。
能用代码判的不用模型判。 代码判定无偏差、可复现、成本低。只有当判据无法形式化时才用模型。
代码结论优先。 两者冲突时以代码为准。一个典型例子:模型说「测试通过了」,但代码判定发现测试文件根本没运行。
模型结论只补代码覆盖不到的部分。 模型评分的价值在表达质量、是否抓住重点这类无法形式化的维度。
冲突仲裁还有一个实用做法:把冲突样本单独收集起来,定期人工复核。冲突率高说明两者之一的判据有问题——通常是要么代码判定太严(把合法行为判成违规),要么模型判定太松。
验证器的设计目标是核实机器可独立复核的事实,不管 Agent 的自我陈述。Agent 很容易写一篇报告说任务已完成,而事实根本没有完成。
4.1 把「完成」拆成两个独立命题
一个成熟的编码任务基准把「修复完成」拆成两组断言:
修复前失败、修复后通过,证明问题确已解决。 修复前后均通过,证明未引入新缺陷。
两组必须同时检验,理由是对称的:只检验前者,Agent 可以通过删改妨碍通过的断言蒙混过关;只检验后者,等同于没做检验。两组同时跑,才使「已修复」与「未破坏」各自成为可证的结论。
这类设计还附带一个检查:测试自身的稳定性。时而通过时而失败的不稳定测试要先排除,否则它会把噪声算进结果。
4.2 验证深度决定能否发现表面完成
三个层次的验证深度。
| 深度 | 做法 | 能发现的 |
|---|---|---|
| 存在性 | 检查产物文件是否存在 | 只发现完全没做 |
| 结构 | 检查文件结构、字段、行数 | 发现做了但不完整 |
| 实质 | 连接数据库核实执行结果、分析页面结构、发请求确认生效 | 发现表面完成实质错误 |
第三层的做法值得记:在数据库任务里不只确认报告文件存在,还连接数据库核实语句是否真的执行;在浏览器任务里分析页面结构、检查存储、向后端发请求确认表单真的生效。
还有一类任务的成功判据本身就无法伪造:从源码构建一个内核、加入一行自定义输出、生成启动镜像并在模拟器里运行,成功标准是启动日志里出现那行输出。Agent 只能真正走完整个流程。
4.3 规则先筛、模型后判
三种验证器混用时,一个便宜的编排是:规则先筛出嫌疑轨迹,模型只在这些轨迹上做判断。
三类可以纯规则筛出的:完成声明与实际执行命令的交叉核对(说了跑过但没跑)、改动是否触及测试断言与跳过标记、改动是否动了公开接口而没有配套迁移。
这个编排的成本远低于把全部轨迹喂给模型,准确率也更高——规则在它擅长的那几件事上没有偏差。
5. 数据泄漏与陷阱任务
评测集一旦进入训练数据,它的分数就没有意义。三套防范手段。
组合多源,使答案无法直接检索。 一条任务要求从某个特定来源出发、串联若干中间结果、再按严格格式输出。答案高度具体,判定用精确字符串匹配。防泄漏靠两点:必须组合多个信息源才能作答,单一页面无法直接给出;部分任务配有互联网上不存在的附件。
参数化实例,使回放失效。 任务写成模板,每次评估随机生成参数。「把联系人甲的电话改为乙」这一个模板可以生成近乎无限的实例。收益有三:参数每次不同,背诵固定操作序列失效;单模板可生成大量实例;固定部分参数、只改其余参数,可以精确测量特定因素的影响。
金丝雀标记,使泄漏可检测。 每道题携带一个唯一标识符,如果模型能输出含该标识符的内容,说明基准数据已进入训练集。它不阻止泄漏,但使泄漏可被发现。
5.1 陷阱任务
除正常任务,还需要一类专门检验「压力下能否维持判断」的任务。
常见形式是用户声称已获批准、实际并不符合规则,看 Agent 会不会因为压力跳过核实。这类任务不追求通过率高,追求「不能被这类话术说服」。
陷阱任务与反例集的区别:反例集是「曾经失败过的那类任务,必须永远通过」;陷阱任务是「永远不该通过的诱导」。
6. 评分器的偏差与防范
模型评分器覆盖面最广,也最容易出偏差。三类偏差需要单独处理。
| 偏差 | 表现 | 防范 |
|---|---|---|
| 位置偏差 | 同一答案在列表首位与末位得分不同 | 随机化顺序,或每条单独评分 |
| 长度偏差 | 长答案得分偏高 | 量表里惩罚冗长、配对比较控制长度、定期审计相关性 |
| 自身偏好 | 同一模型给自己生成的答案高分 | 用不同家族的模型做评分器 |
长度偏差最典型,也最好检验:算一下分数与答案长度的相关系数。如果高分几乎总伴随长答案,说明评分已被长度带偏。
第二种防范方式值得展开:配对比较时先把两个候选的长度控制到相近再评。这一步会人为抹掉长度差异,迫使评分器只看内容。
7. Rubric 的四条准则
要让模型评分稳定,评分标准(Rubric)必须遵守四条。
基于专家指导。 必须反映领域知识,捕捉核心事实与推理步骤。医疗问答的 Rubric 需包含诊断标准和必须避免的医学错误;缺乏专业基础的 Rubric 只能捕捉语言流畅度这类表面特征。
全面覆盖。 涵盖事实准确性、逻辑连贯性、完整性、安全性,而且不只定义正面标准,还要明确陷阱项——高风险的常见错误,如医疗建议中推荐未经验证的疗法。
按重要性加权。 分为必要项、重要项、可选项、陷阱项。支持一票否决:在客服场景里,幻觉(编造虚假信息)是典型的否决维度——无论其他维度多优秀,只要出现虚假信息就必须否决。这也防范关键词堆砌式的奖励作弊。
评价标准自包含。 每个评价项独立可操作,不依赖评价者的领域知识。避免「回应展示了深刻理解」这类抽象标准,改为「引用了至少两个权威理论并准确解释如何支持结论」这类可验证的标准。
一个符合四准则的片段:
rubric:
dimensions:
- name: 事实正确性
weight: essential
scoring:
4_优秀: "准确回答目标医生,且关联到其与提问者的关系"
3_良好: "准确回答目标医生,但未提及关系"
2_及格: "给出了正确医生但附带不确定的额外信息"
1_不及格: "给出错误医生名,或回答不知道"
- name: 幻觉
weight: veto
scoring:
通过: "所有事实断言都能在给定上下文里找到依据"
否决: "出现任何无法溯源的断言"两处设计要点:essential 与 veto 是不同权重——前者影响总分,后者一票否决;每一档的描述都是可观察的事实,不是感觉。
8. 一致性的量化
分级标注的一致性可以用 Kappa 系数衡量:
其中 \(p_{o}\) 是实际一致率,\(p_{e}\) 是随机一致率。
两个阈值:0.6 以上算可用,0.8 以上算好。低于 0.6 说明分级标准写得不够明确,此时应当先改标准,不要扩标注量——用不清楚的标准标注更多数据,只会得到更多噪声。
校准的做法:抽 50 到 100 条人工标注好的样本,让模型评分器跑一遍,算与人工的一致性。
三个提高一致性的写法要求:
- 每一项的判据是可观察的事实(「输出包含三个章节」),不写「结构清晰」这类感觉描述;
- 每一档写明分界(「三档:全部满足」「二档:缺一项」「一档:缺两项以上」);
- 要求评分时引用证据(哪一段、哪一行),避免凭印象打分。
第三条的作用不只是提高一致性,它让评分结果可复核:拿到一个分数,能看到它引用了哪段文本。
9. 分级的粒度
三种标定方式,各有代价。
| 方式 | 信息量 | 一致性 | 适用 |
|---|---|---|---|
| 布尔 | 最低 | 最高 | 有明确判据的任务 |
| 三档 | 中 | 中 | 模型评分 |
| 连续 | 最高 | 最低 | 不适合模型评分 |
连续打分的两个问题:一致性差,且分数差异往往不具可比性——0.72 与 0.75 的差别通常没有意义。
模型评分建议用三档(差、中、好),不用十分制。原因是模型的分数分布在中间会堆积,十分制里的 5、6、7 很难区分,而三档之间的分界清晰。
10. 随机性的来源与重复次数
非确定性来源有三类。
采样。 温度、top-p 让同一输入产生不同输出。把温度设为 0 只能减少,不能消除(并行计算与浮点误差仍会带来差异)。
工具时序。 并行的工具调用完成顺序不同,导致后续决策不同。这一类在有多工具的任务里最常见。
环境状态。 上一次运行留下的残留影响这一次。这一类最难发现,因为它表现为「同一个用例有时过有时不过」。
处理方式是按场景选重复次数:
| 场景 | 重复 | 目的 |
|---|---|---|
| 回归 | 1–2 | 发现明显退化 |
| 发布检查 | 3 | 确认稳定性 |
| 能力评测 | 10 以上 | 计算 pass@k |
重复次数不够时,看到的差异可能全是噪声。一个粗略判据:如果两个版本的差异小于 \(1/\sqrt{n}\)(\(n\) 为样本量),不要下结论。
11. 用例管理
三件事:去重、分级、退役。
去重。 同一个失败模式有多个用例时,保留最有区分度的一个。冗余用例会放大某个失败模式在总分里的权重。
分级。 关键用例(安全、核心流程)单独标记,发布检查时要求全部通过;普通用例只要求总体不退化。
退役。 用例对应的功能下线后要退役。不退化的用例会永久拉低通过率,而且没人知道为什么。
第三条在实践中经常被忽略,导致通过率随时间缓慢下降,团队以为是模型退化了,实际是一堆废弃用例在拖后腿。
一个检查方式:看「从未通过过的用例」有多少。如果这个数字在增长,说明有废弃用例没有清理。
11.1 用例的数量指标
用例集本身需要被度量,否则它会慢慢腐化而不被察觉。
| 指标 | 健康值 | 异常含义 |
|---|---|---|
| 从未通过的用例占比 | 低于 5% | 过高说明有过严或废弃用例 |
| 总是通过的用例占比 | 低于 60% | 过高说明区分度不足 |
| 一题多解的用例占比 | 视任务而定 | 过高说明可接受集合定义过宽 |
第二行最反直觉。一个用例集如果六成以上的题目「什么版本都能过」,它的实际分辨力只来自剩下四成。这些题目不是废的(它们仍能挡住完全做错的版本),但它们不参与「好与更好」的区分。
一个可操作的做法是按这两类比例分账:报告通过率时同时给出「有效题目数」(总是通过与被排除的题目都扣除)。这个数比原始通过率更能反映评测集的实际能力。
第三行处理的是另一类问题。可接受动作集合写得过宽时,任何合理做法都算通过,用例失去诊断价值。判据是:一个明显偷懒的做法(例如直接给出结论不查证)是否也在集合里。
11.2 用例的版本
用例本身会被修改:修错别字、补验收条件、调难度。这些改动需要版本记录。
一条实用的规则是:修改验收条件的用例,其历史结果不再可比。做法是给它一个新的版本号,旧结果留在旧版本下,报告时分开统计。
不做这件事的后果是:通过率曲线在某次「小改动」后跳变,而没人知道是模型变了还是用例变了。
12. 一套完整的 Rubric
抽象准则不如一份完整的评分标准。以下是一份按四准则写成的量表,用于「回答用户关于订单状态的问题」这类任务。
rubric:
dimensions:
- name: 事实正确性
weight: essential
question: 回答中的订单状态、金额、时间是否与工具返回一致
evidence: 工具返回与环境状态
scoring:
3_全部一致: "每个数值与状态都能在工具返回里找到对应"
2_次要偏差: "主状态正确,附加信息(如预计到达时间)与实际有出入"
1_主要错误: "订单状态或金额与实际不符"
veto_if: 1_主要错误
- name: 承诺与行动一致性
weight: essential
question: 声称已执行的操作是否真的发生过
evidence: 回复文本与工具调用日志对照
scoring:
3_一致: "所有声称已完成的操作都有对应的成功调用"
2_含糊: "使用了「已处理」这类无法核对的措辞"
1_不实: "声称完成但无对应调用,或调用失败"
veto_if: 1_不实
- name: 规则遵从
weight: important
question: 是否越过了不该越过的流程
evidence: 动作序列与规则库
scoring:
3_合规: "涉及退款、改地址等操作前都做了必要确认"
2_轻微: "确认不完整但未造成实际影响"
1_违规: "跳过了必须的确认步骤"
veto_if: 1_违规
- name: 信息告知完整
weight: important
question: 该告知用户的事项是否都告知了
evidence: 回复文本与任务要求对照
scoring:
3_完整: "任务要求的告知项全部包含"
2_缺一项: "缺少一个非关键告知项"
1_缺多项: "缺少关键告知项(如退款到账时间、需用户自行操作的事项)"
- name: 表达质量
weight: optional
question: 是否简洁自然,避免模板化与冗余
evidence: 对话全文
scoring:
3_良好: "无重复、无套话,直接回答"
2_一般: "有寒暄或重复,但不影响理解"
1_差: "大量模板化措辞,或答非所问"
- name: 合规变通
weight: optional
question: 原方案不可行时是否找到允许的替代
evidence: 目标、规则与后续动作
scoring:
3_有变通: "找到规则允许的替代方案并说明"
2_部分: "提出了替代但未确认是否合规"
1_无变通: "直接告知无法处理,未探索替代"12.1 这份量表里的五个设计决定
必要项与否决项分开。 事实正确性与承诺一致都是 essential,但都带 veto——它们出错时无论其他项多好都判不通过。规则遵从也是 essential,同样带 veto。
三者的区别在语义:前两项是「不能错」,第三项是「不能跳过」。取消资格的方式相同(都是否决),但它们失败的修复方向完全不同。
表达质量是可选。 它影响体验但不影响任务成败。设为 optional 意味着它只影响排序,不影响通过与否。
每一档都有可观察的判据。 「主状态正确,附加信息有出入」能核对;「表达较自然」无法核对。
证据列写明了去哪找。 这让评分可复核——拿到一个分数,能指出它依据的是哪份工具返回。
合规变通的三档覆盖了三种行为。 有变通、部分变通、无变通。它不评最终是否解决,因为「原方案不可行」本身就是任务的一部分。
12.2 一份量表要跑多少次才能信
量表写完之后,需要验证它的稳定性。
第一步,同一批样本跑两次。 看同一答案两次评分是否一致。不一致率超过 20% 说明档位描述还不够明确。
第二步,与人工标注对照。 抽 50 条人工标好的样本,算 Kappa。低于 0.6 回到第一步继续改档位描述。
第三步,检查偏差。 算分数与答案长度的相关系数。绝对值超过 0.3 说明长度偏差明显。
三步都不通过时,问题通常在档位描述,模型本身少见。一个常见的坏档位是「2_一般」这类没有具体判据的中间档——中间档最难标,也最容易导致不一致。
13. 按误差源归类的失败模式
| 误差源 | 失败模式 | 症状 | 修的位置 |
|---|---|---|---|
| 用例定义 | 成功条件模糊 | 所有版本都通过 | 改写为可判定的条件 |
| 用例定义 | 无区分度 | 好版本差版本都过 | 用更差版本做基线检验 |
| 用例定义 | 判据过严 | 所有版本都不通过 | 检查是否要求了不该有的细节 |
| 验证器 | 覆盖不全 | 部分通过但整体不可用 | 补规则,或用模型补盲区 |
| 验证器 | 规则写错 | 系统性误判 | 用冲突样本复核 |
| 评分器 | 长度偏差 | 高分总伴随长答案 | 惩罚冗长、控制长度 |
| 评分器 | 未校准 | 分数波动大 | 算 Kappa,低于 0.6 先改标准 |
| 随机性 | 重复不足 | 同一版本两次结果差很多 | 提高重复次数 |
第三行与第四行会导出同一个后果:团队不再相信这个评测集。这比评测不准更严重——一旦不信,后面所有结果都会被忽略。