Agent 评测:从测试集到发布门槛
本文总结先说清什么算成功,再检查轨迹、算清随机波动、给失败归类,最后定一条上线前就写好的门槛。
首先写清“成功”
拿修仓库缺陷举例。Agent 最后说了一句“已修复”,这句话本身什么也证明不了。我们要检查的是:原来失败的复现用例现在通过了,相关的回归测试仍然通过,补丁只动了允许动的范围,被保护的文件没被碰过。四条都满足才算成功。
报销 Agent 也一样。员工提交发票 INV-102(118.5 元),《差旅费管理办法》2026 版第 4.2 条规定上海市内交通标准 80 元。一次“成功”的运行应该能在系统里查到一张新工单,金额 118.5 元,标出超标 38.5 元,状态是“待主管审批”。如果回答写得很漂亮,但工单系统里一张单子都没有,或者有两张,就是失败。
成功可以拆成四层来判断:
| 层次 | 判断方式 | 报销案例里查什么 |
|---|---|---|
| 结果 | 能用程序检查的优先用程序 | 工单 R-55821 存在、金额 118.5、超标 38.5、状态待审批 |
| 过程 | 对轨迹做断言 | 只调用了 query_invoices、search_policy、create_reimbursement_ticket;没有查别人的发票;创建只发生一次 |
| 代价 | 资源统计 | 总 token、工具调用 6 次以内、端到端 20 秒以内(示意) |
| 主观质量 | 写明评分标准,再用人工校准 | 给员工的解释有没有说清超标原因和下一步 |
评测流程:固定任务集、执行并留痕、结果与过程评分、错误归因、回归门槛 · 点击放大
过程层容易被忽略。设想一次运行最后确实生成了工单,但中途调用了两次 create_reimbursement_ticket,第二次因为重复被服务端拦下。结果层看是对的,过程层会发现 Agent 不知道第一次已经成功了。这种问题放到没有幂等保护的系统里就是重复报销。
一个任务记录至少有什么
每道评测题写成一条记录,跑之前就定好,跑完不改。下面是“写入成功但第一次响应丢失”这道题:
{
"case_id": "timeout-after-write",
"fixture_version": "v1",
"goal": "为发票 INV-102 创建报销工单并返回编号",
"user": {"level": "P6", "city": "上海"},
"allowed_actions": ["query_invoices", "search_policy", "query_tickets", "create_reimbursement_ticket"],
"fault": "drop_first_response",
"assertions": [
"ticket_count(invoice='INV-102') == 1",
"returned_id_exists",
"ticket.over_limit == 38.5"
],
"max_steps": 8,
"max_duration_seconds": 60
}
fault 字段让假的工单服务把第一次创建的响应丢掉,工单其实已经建好了。合格的 Agent 应该先用 query_tickets 查一下,发现已有工单,直接返回编号。断言 ticket_count == 1 抓的就是“盲目重试建了两张”。
这只是练习用的格式,不对应任何标准协议。除了题目本身,每次运行还要记下这些东西,否则下周的分数没法和这周比:模型版本、Prompt 和工具 schema 的版本、检索索引的快照(比如“办法 2026 版,索引于 10 月 30 日构建”)、环境镜像或依赖锁文件、采样温度,接口支持的话再记随机种子。可公开的摘要和测试结果单独存放,原始轨迹里可能有员工姓名、发票号,按权限管理。
成功率、波动与重复试验
点估计
成功率就是“做对的题数除以总题数”:
| 符号 | 读法与含义 |
|---|---|
| 评测集里的题数 | |
| 求和号,从第 1 题加到第 N 题 | |
| 指示函数:方括号里的条件成立记 1,不成立记 0 | |
| 读作“p hat”,帽子表示“从样本估出来的值”,区别于我们真正想知道、但永远看不到的成功率 |
手算:30 道题,做对 24 道。求和得到 24 个 1 和 6 个 0,加起来是 24,。
0.8 只是这 30 道题上的结果。换 30 道同类的题再测,可能是 0.73,也可能是 0.87。所以只报一个 80% 不够,要同时报出题数、题目类别、每题跑了几次,以及一个区间。
区间估计:Wilson 区间
最常见的区间写法是 ,叫正态近似区间(Wald 区间)。它在题少、成功率接近 0 或 1 时会出问题。比如 20 道题对 18 道,,算出来是 [0.769, 1.031],上限超过了 100%,显然不合理。小样本更稳妥的做法是 Wilson 区间:
区间是 。
- 是置信水平对应的标准正态分位数,95% 置信时取 1.96。
- 是区间的中心。它比 往 0.5 的方向挪了一点,题越少挪得越多。
- 是半宽,也就是中心到上下限的距离。
- 根号 里第一项 是成功率估计的方差,第二项 是小样本的修正,N 大了以后它几乎为 0。
代入 30 题对 24 题,,,。先算两个公共部分:
中心:
半宽。根号里是 加上 ,合计 0.006400,开方约 0.0800:
区间是 0.7659 ± 0.1390,约 [0.627, 0.905]。也就是说,30 题对 24 题,真实成功率在 63% 到 90% 之间都说得通。用 Wald 公式算是 [0.657, 0.943],上下限都偏高。
同样是 80%,题数涨到 300(对 240 道),Wilson 区间缩到约 [0.751, 0.841],宽度从 0.28 降到 0.09。区间宽度大致随 缩小,想把区间缩窄一半,题数要变成原来的 4 倍。
Wilson 区间假设每道题是独立的一次抽样。如果 30 道题里有 10 道是同一个模板换了金额,它们的对错高度相关,实际有效样本少于 30,区间会显得过窄。30 题全对时,Wilson 区间是 [0.886, 1],至少不会给出“100% 可靠”这种结论。
比较两个方案:看逐题胜负
两个方案要在同一批题、同样预算下跑,然后逐题对比,不要只比两个总分。示意:方案 A 和 B 各跑同样 30 道题。
| B 对 | B 错 | |
|---|---|---|
| A 对 | 20 | 4 |
| A 错 | 1 | 5 |
A 是 24/30 = 80%,B 是 21/30 = 70%,看上去 A 高了 10 个百分点。但两者都对或都错的 25 道题对比较没有贡献,真正分出胜负的只有 5 道,A 赢 4 道、B 赢 1 道。假如两个方案其实一样好,这 5 道分歧题各自随机偏向一方,出现“一边至少赢 4 道”的概率是 2 × 6/32 = 0.375(5 道里赢 4 道有 5 种排法,赢 5 道有 1 种,共 6 种,再乘 2 算两个方向)。这么高的概率,说明 10 个百分点的差距完全可能是运气。
如果用 bootstrap 估区间,要按任务重采样:30 道题、每道跑 3 次,就从 30 个 case_id 里有放回地抽 30 个,被抽中的题把 3 次结果整组带上。不能把 90 次运行当成 90 个独立样本去抽,同一道题的 3 次运行很像,这样抽出来的区间会窄得不真实。
允许多试几次时
先分清产品要的是哪种能力:用户只给一次机会,还是允许系统多试几次、只要有一次成功就行。如果每次尝试互相独立、单次成功概率都是 ,试 次至少成功一次的概率是:
是单次失败的概率, 是连续 次全部失败的概率(独立事件同时发生,概率相乘),用 1 减去它,就是“至少成功一次”。
手算:,。单次失败 0.2,三次全失败 ,至少成功一次 。
真实 Agent 的失败通常不独立。还是 30 道题、单次成功率 80%,假设具体情况是(示意):20 道每次都对;8 道一半时候对,比如偶尔漏查条款;2 道永远错,比如需要它没有的权限。单次成功率是 (20 + 8 × 0.5) / 30 = 24/30 = 0.8。试 3 次至少一次成功:前 20 道必过,8 道每道是 1 − 0.5³ = 0.875,2 道永远过不了,合计 (20 + 7) / 30 = 0.9。独立假设给出 0.992,实际只有 0.9。那 2 道题再试多少次都没用。
反过来,有些场景要求每次都对,比如自动提交报销单。这时关心的是 k 次全部成功,有些评测把它记作 pass^k。独立假设下是 ;在上面的例子里是 (20 + 8 × 0.125) / 30 = 21/30 = 0.7。
所以这个公式只能当直觉用,多次尝试的成绩要实际跑出来。也不能把“试 3 次的通过率”写成单次能力,报告里要一起写清尝试次数和总成本:单次 0.04 元(示意)的任务,试 3 次最多 0.12 元。
LLM Judge 放在哪里
能用程序判断的先用程序判断。“工单金额是不是 118.5”“有没有调用 create_reimbursement_ticket 两次”,这些查数据库和轨迹就能确定,不要交给模型打分。
LLM Judge 只用在主观项上,例如“给员工的解释是否清楚”。用之前先写评分锚点,让每一档对应可以核对的特征:
- 1 分:没说为什么超标,或者引错条款;
- 3 分:说了超标 38.5 元,但没说要主管审批;
- 5 分:说明依据第 4.2 条标准 80 元、超标 38.5 元、已提交待主管审批,并给出工单号 R-55821。
然后抽样让人盲评,和 Judge 对照。比如抽 40 条,人和 Judge 打分一致的有 34 条,一致率 85%(示意)。如果大部分样本都是 5 分,一致率会虚高,这时再看 Cohen's kappa 这类扣除了“碰巧一致”的指标。
Judge 还有两种常见偏差。一种是位置偏差:把回答 A、B 按“A 在前”给 Judge,它说 A 好;交换成“B 在前”,它说 B 好。它偏爱的其实是排在第一个的回答,这一对应记为平局或交给人看。所以每一对都要正反各问一次。另一种是品牌偏差,把模型名字藏起来就能减少。
引用类的判断要回到证据原文。回答写“根据第 4.2 条,超标部分需主管审批”,Judge 读着很顺,但第 4.2 条原文可能只写了标准金额,审批规定在别的条款。要把引用的条款原文和结论放在一起核对,不能看语气。
从错误归因到发布门槛
每条失败都标一个原因,再补一条回归用例。报销 Agent 的失败可以这样分(示意):
| 类别 | 一条典型轨迹 | 补的回归用例 |
|---|---|---|
| 理解错误 | 把“超标 38.5 元”理解成“报销 38.5 元” | 金额拆分题 |
| 检索缺证 | search_policy 返回 2025 版条款,标准写成 60 元 |
版本切换题 |
| 工具失败 | query_invoices 超时,Agent 编了一个金额 |
工具超时题 |
| 状态丢失 | 第二轮对话忘了已经建过工单 | 多轮续办题 |
| 权限违规 | 按文档里的诱导文本去查其他员工发票 | 注入题,见 安全边界:让权限落到工具层 |
| 验证遗漏 | 工单创建后没核对金额就回复成功 | 结果核对题 |
门槛要在看到结果之前写好,由业务风险决定。示意:
- 权限类用例 100% 通过,一条不过就不发布;
- 上一版通过的题,这一版不能有新的失败;
- 核心任务成功率的 Wilson 下限不低于 0.7;
- P95 延迟不超过 20 秒,每个成功任务的平均成本不超过 0.05 元。
最常见的坏做法是跑完发现成功率 78%,再把门槛从 80% 改成 75%。这样门槛就失去了作用。
练习:给同一道报销题做四个版本,正常、query_invoices 超时、返回的金额字段是字符串 "118.5元"、员工要求“帮我把同事的发票也一起报了”(应该拒绝)。用这四个版本检查 可靠执行:状态、幂等与恢复 和 安全边界:让权限落到工具层 里的设计是否真的生效。本页给的是设计方法,还没有跑出来的基准成绩。
参考
SWE-bench 官方文档 展示了用真实仓库 issue 和补丁评测编程系统的方法:每道题对应一个真实 issue,在可执行环境里应用模型生成的补丁,再跑测试判断是否修好。可以借鉴这种可执行环境加结果检查的思路。它的具体成绩取决于数据集版本、harness、预算等条件,不能直接拿来代表某个私有项目的效果。