← 回到知识图谱
实践持续完善

Agent 评测:从测试集到发布门槛

本文总结先说清什么算成功,再检查轨迹、算清随机波动、给失败归类,最后定一条上线前就写好的门槛。

创建 2026-10-31更新 2026-11-02阅读约 14 分钟
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 日构建”)、环境镜像或依赖锁文件、采样温度,接口支持的话再记随机种子。可公开的摘要和测试结果单独存放,原始轨迹里可能有员工姓名、发票号,按权限管理。

成功率、波动与重复试验

点估计

成功率就是“做对的题数除以总题数”:

p^=1N∑i=1N1[case i succeeds]\hat p=\frac{1}{N}\sum_{i=1}^{N}\mathbf{1}[\text{case }i\text{ succeeds}]
符号 读法与含义
NN 评测集里的题数
∑i=1N\sum_{i=1}^{N} 求和号,从第 1 题加到第 N 题
1[⋅]\mathbf{1}[\cdot] 指示函数:方括号里的条件成立记 1,不成立记 0
p^\hat p 读作“p hat”,帽子表示“从样本估出来的值”,区别于我们真正想知道、但永远看不到的成功率 pp

手算:30 道题,做对 24 道。求和得到 24 个 1 和 6 个 0,加起来是 24,p^=24/30=0.8\hat p = 24/30 = 0.8。

0.8 只是这 30 道题上的结果。换 30 道同类的题再测,可能是 0.73,也可能是 0.87。所以只报一个 80% 不够,要同时报出题数、题目类别、每题跑了几次,以及一个区间。

区间估计:Wilson 区间

最常见的区间写法是 p^±1.96p^(1−p^)/N\hat p \pm 1.96\sqrt{\hat p(1-\hat p)/N},叫正态近似区间(Wald 区间)。它在题少、成功率接近 0 或 1 时会出问题。比如 20 道题对 18 道,p^=0.9\hat p=0.9,算出来是 [0.769, 1.031],上限超过了 100%,显然不合理。小样本更稳妥的做法是 Wilson 区间:

p~=p^+z22N1+z2N,h=z1+z2Np^(1−p^)N+z24N2\tilde p=\frac{\hat p+\dfrac{z^2}{2N}}{1+\dfrac{z^2}{N}},\qquad h=\frac{z}{1+\dfrac{z^2}{N}}\sqrt{\frac{\hat p(1-\hat p)}{N}+\frac{z^2}{4N^2}}

区间是 [p~−h, p~+h][\tilde p-h,\ \tilde p+h]。

  • zz 是置信水平对应的标准正态分位数,95% 置信时取 1.96。
  • p~\tilde p 是区间的中心。它比 p^\hat p 往 0.5 的方向挪了一点,题越少挪得越多。
  • hh 是半宽,也就是中心到上下限的距离。
  • 根号 ⋅\sqrt{\cdot} 里第一项 p^(1−p^)/N\hat p(1-\hat p)/N 是成功率估计的方差,第二项 z2/(4N2)z^2/(4N^2) 是小样本的修正,N 大了以后它几乎为 0。

代入 30 题对 24 题,p^=0.8\hat p=0.8,N=30N=30,z2=1.962=3.8416z^2=1.96^2=3.8416。先算两个公共部分:

z2N=3.841630≈0.1281,z22N=3.841660≈0.0640\frac{z^2}{N}=\frac{3.8416}{30}\approx 0.1281,\qquad \frac{z^2}{2N}=\frac{3.8416}{60}\approx 0.0640

中心:

p~=0.8+0.06401+0.1281=0.86401.1281≈0.7659\tilde p=\frac{0.8+0.0640}{1+0.1281}=\frac{0.8640}{1.1281}\approx 0.7659

半宽。根号里是 0.8×0.2/30≈0.0053330.8\times0.2/30\approx0.005333 加上 3.8416/3600≈0.0010673.8416/3600\approx0.001067,合计 0.006400,开方约 0.0800:

h=1.961.1281×0.0800≈1.7375×0.0800≈0.1390h=\frac{1.96}{1.1281}\times 0.0800\approx 1.7375\times 0.0800\approx 0.1390

区间是 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。区间宽度大致随 1/N1/\sqrt{N} 缩小,想把区间缩窄一半,题数要变成原来的 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 次运行很像,这样抽出来的区间会窄得不真实。

允许多试几次时

先分清产品要的是哪种能力:用户只给一次机会,还是允许系统多试几次、只要有一次成功就行。如果每次尝试互相独立、单次成功概率都是 pp,试 kk 次至少成功一次的概率是:

P(at least one success)=1−(1−p)kP(\text{at least one success})=1-(1-p)^k

1−p1-p 是单次失败的概率,(1−p)k(1-p)^k 是连续 kk 次全部失败的概率(独立事件同时发生,概率相乘),用 1 减去它,就是“至少成功一次”。

手算:p=0.8p=0.8,k=3k=3。单次失败 0.2,三次全失败 0.23=0.0080.2^3=0.008,至少成功一次 1−0.008=0.9921-0.008=0.992。

真实 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。独立假设下是 pk=0.83=0.512p^k=0.8^3=0.512;在上面的例子里是 (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、预算等条件,不能直接拿来代表某个私有项目的效果。

这是一篇可以继续生长的笔记。沿着关系继续阅读 ↗