对应 Maps to:Stanford CS329Z Engineering AI Agents(Fall 2026),Lecture 12 · Wed Nov 4 · Evaluation Fundamentals & Benchmark Design。阅读材料:Zhu et al., Establishing Best Practices for Building Rigorous Agentic Benchmarks(2025)/ Press, How to Build Good Language Modeling Benchmarks;Polo et al., tinyBenchmarks: Evaluating LLMs with Fewer Examples(ICML 2024)。 依据说明:写作时(2026-10-16)这一讲的 slides 还没有公布。本讲依据课表上的主题描述(为什么评估难、4 元组框架——请求、环境、停止条件、判分器——每一部分怎样设计、好 benchmark 的性质、贴近真实的脚手架、可靠性的维度)和阅读材料来写;tinyBenchmarks 在第 11 讲 §2 已经细讲。引用的论文和博客内容都核对过原文。全部实验是本讲义自己设计的,不代表课堂上讲了什么。 配套代码 Code:
code/lecture12/(只用 Python 标准库;5 个脚本 + 2 个模块 + 1 个可选脚本;不需要 API key;每个脚本几秒以内) 预计阅读时间:3 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲你应该能够:
- 说出智能体评估比”问答评估”难在哪里:随机性、长链条、副作用、多种正确解法、依赖脚手架。
- 用 4 元组(请求、环境、停止条件、判分器)描述一个智能体任务,并知道每一部分有哪些设计选择、各自会怎样让分数失真。
- 用 ABC 检查清单的两个维度——任务有效性、结果有效性——审查一个 benchmark,识别常见漏洞(环境泄露答案、空回答也算成功、判分器太松、坏任务、步数上限太小),并用参照智能体和”谁都做不对的任务”把它们找出来。
- 报告可靠性的几个维度(pass^k、鲁棒性、容错、效率),而不只是一个准确率。
- 给评估结果配上置信区间;用配对比较判断两个系统的差别是否可信;估算需要多少道题;知道题目成簇时标准误会被低估。
0.2 先修与本讲范围 Prerequisites & scope
| 默认你已经会 Assumed | 本讲只给直觉,后面再展开 Previewed only |
|---|---|
| 第 4 讲 §7:BFCL、τ-bench、按状态判分、pass^k | LLM 评审、评估基础设施(第 13 讲) |
| 第 10 讲 §5:优化数据和评估数据要分开;第 11 讲:IRT、坏题、tinyBenchmarks | 安全评估、红队(第 14 讲) |
| 前置讲 P3:超时、重试;第 1 讲 §6.1:pass@k 与 pass^k | SWE-bench 与编程智能体的评估(第 15 讲) |
0.3 本讲地图 Map
§0.6 开场:一个分数背后的十几个决定
§1 为什么智能体的评估难 ← 代码 01
§2 4 元组:请求、环境、停止条件、判分器——每一部分怎样设计 ← 代码 02
§3 好 benchmark 的性质:Press 的三条建议、ABC 的任务有效性与结果有效性 ← 代码 03
§4 可靠性的维度:一致性、鲁棒性、容错、效率 ← 代码 04
§5 评估的统计:置信区间、配对比较、要多少道题、题目成簇 ← 代码 05
§6 用真实模型试一试 ← 代码 06(可选)
§7 一份 benchmark 发布前的清单
§8 小结 §9 常见误区 §10 练习 §11 延伸阅读 §12 下一讲预告
0.4 术语 Terms
| 术语 | 本讲里的意思 |
|---|---|
| 4 元组 | 一个任务 = 请求(给智能体的指令)+ 环境(它能操作的世界)+ 停止条件(何时结束)+ 判分器(怎样判成功) |
| 任务有效性(task validity) | 任务可解,当且仅当智能体具备被测的能力——没有捷径,也没有不可能的任务 |
| 结果有效性(outcome validity) | 评估结果真的反映任务是否成功——判分器不太松、也不太严 |
| 参照智能体(reference agent) | 用来校准分数的特殊智能体:什么都不做的(“地板”)、无所不能的(“天花板”) |
| 脚手架(scaffolding / harness) | 包在模型外面的那一层:工具、步数上限、重试、提示词(第 5、6 讲) |
| pass@k / pass^k | k 次里至少成功一次 / k 次全部成功的概率 |
| 配对比较 | 两个系统在同一批题上逐题比较(McNemar 检验),而不是把两个分数当独立样本 |
| 成簇(clustering) | 很多题来自同一个模板 / 仓库,彼此相关;有效样本量小于题数 |
0.5 本讲的代码
代码在 Orrin Grid 的客服数据库上按 4 元组搭了一个小 benchmark(bench_lab.py):20 个客户,6 类任务——查余额、查有没有停电记录、取消预约、“有停电才补偿”、“谢谢,没事了”(什么都不用做)、查一个不存在的客户——共 200 个,其中 8% 是坏任务(标准答案写错了、或者要求一件环境里做不到的事)。
智能体是模拟的,每一个代表一种行为:
| 智能体 | 行为 |
|---|---|
| 能干的 | 九成的任务做对,步数正常 |
| 慢但对的 | 一样准,但要用 2.5 倍的步数 |
| 偷懒的 | 什么都不做,回一句 “Done.” |
| 两头下注的 | “yes or no""20、25 还是 30 美元”;动手的任务一半做对 |
| 偷看答案的 | 环境里有答案文件就去读;否则一半做对 |
| 能力一般的 | 七成做对;工具出错时不会重试;换个说法就容易出错 |
提醒:智能体做对的概率、需要的步数、对换说法的敏感程度是参数;环境、工具、停止条件、判分器、漏洞的效果都是真的实现——漏洞真的会让某种行为”得分”。统计部分(
stats_lab.py)按 IRT 模拟每道题的结果。
0.6 开场:一个分数背后的十几个决定
“我们的智能体在某某 benchmark 上得了 62%。“这句话背后有十几个决定:题从哪来、请求怎么写、环境给哪些工具、每个任务开始前重不重置、最多允许多少步、怎样判断成功、坏题删没删、跑了几次、用的什么脚手架……每一个决定都能让这个数字上下浮动好几个点,有的能让它翻倍。
前两讲我们一直在”用”评估集:拿它挑提示词、估计成功率、判断数据好不好。这一讲正面讨论怎样造一个评估——第 13 讲再讲怎样用 LLM 当判分器、怎样搭评估的基础设施。
1. 为什么智能体的评估难 · Why evals are hard
1.1 和问答评估比
问答评估:一个问题、一个答案、一个标准答案,比较一下就行。智能体评估多了好几层难度:
- 每次运行都不一样:LLM 采样有随机性,工具和环境也可能有(网络、时间、外部 API)。
- 链条长:几十步里任何一步出错,结果就错;而且同一个任务往往有多种正确的做法。
- 有副作用:智能体改了数据库、发了邮件、删了文件——判分不能只看它最后说了什么,还要看它做了什么、有没有做多余的事。
- 依赖脚手架:同一个模型,给不同的工具、不同的步数上限、不同的提示词,表现可以差很多(第 5 讲的”harness 税”)。
- 贵:跑一次完整的 benchmark 可能要几小时、几百美元,于是大家只跑一次、题也出得少——这又加大了不确定性。
1.2 一个任务的 4 元组
代码 01 打印了一个任务:
请求:If Nia Kovac has a logged outage, add a 20 dollar credit to the account; otherwise change nothing.
环境:20 个客户的数据库(余额、是否记录了停电、套餐、预约)+ 工具(查询、修改);每个任务开始前重置
停止条件:智能体说完了,或者用满 10 步
判分器:数据库的最终状态和期望的一致(Nia Kovac:停电 True,余额 10 → 30;其他客户不变)
1.3 运行之间的波动
同一个智能体、同一套 183 道有效题,跑 10 次:
| 智能体 | 全部题,10 次运行的最低 – 最高 | 随机 50 道题,10 次的最低 – 最高 |
|---|---|---|
| 能干的 | 90.2% – 95.6% | 88% – 96% |
| 慢但对的 | 88.5% – 95.6% | 86% – 98% |
| 能力一般的 | 71.6% – 82.0% | 64% – 88% |
“能力一般的”只跑一次、只用 50 道题,可能得 64%,也可能得 88%。题少、只跑一次时,几个点的差别可能只是运气(§5 讲怎样量化它)。
1.4 失败的方式不只是”答错”
“能力一般的”在修好的版本上:成功 145、回答错了 30、改了不该改的客户(副作用)7、数据库状态不对 1。在其他设置下还会出现工具出错后放弃、步数用完被截断。所以智能体的判分器通常要看最终状态,还要检查”没有做多余的事”——第 4 讲 τ-bench 按数据库状态判分就是这个道理。
1.5 脚手架改变排名
| 脚手架 | 能干的 | 慢但对的 | 能力一般的 | 排名 |
|---|---|---|---|---|
| 最小:最多 3 步、工具 10% 出错 | 94.2% | 62.0% | 74.0% | 能干的 > 能力一般的 > 慢但对的 |
| 常见:最多 10 步、工具 10% 出错 | 91.6% | 94.3% | 70.4% | 慢但对的 > 能干的 > 能力一般的 |
| 理想:最多 10 步、工具不出错 | 93.7% | 92.2% | 76.7% | 能干的 > 慢但对的 > 能力一般的 |
只给 3 步时,“慢但对的”排在最后;给 10 步时它和”能干的”不相上下。你测的到底是模型,还是脚手架? Press 的建议是:baseline 的脚手架要贴近当前的常见做法(能执行代码、能上网、合理的步数)——太简陋的脚手架会让读者不信任结果。
2. 4 元组:每一部分怎样设计 · Designing the four components
Press 在博客里建议把每个任务定义成 4 样东西,并为每一样做出明确的选择(包括判分是二元的还是连续的)。代码 02 依次看每一部分的设计选择。
2.1 请求:写不写明格式
是 / 否题,请求里写不写 “Answer yes or no.”(20 次运行的平均):
| 请求 + 判分器 | 能干的 | 慢但对的 | 能力一般的 |
|---|---|---|---|
| 写了格式,严格判分 | 89.8% | 88.2% | 71.7% |
| 没写格式,严格判分 | 77.3% | 82.8% | 65.2% |
| 没写格式,按意思判分 | 90.3% | 90.7% | 67.0% |
不写格式时,智能体常常用一句话回答(“There is a logged outage on this account.”)——它答对了,可要求出现 “yes” 的判分器判它错。请求和判分器要一起设计:要么在请求里写清楚格式,要么让判分器接受等价的说法(这正是第 13 讲 LLM 评审的用武之地,代价是它自己也会出错)。
2.2 环境:每个任务都要重置
| 智能体 | 每个任务都重置 | 不重置(修改会留下来) |
|---|---|---|
| 能干的 | 93.7% | 1.5% |
| 慢但对的 | 92.2% | 1.4% |
| 偷懒的 | 26.8% | 26.8% |
| 两头下注的 | 35.0% | 0.3% |
| 偷看答案的 | 67.2% | 1.2% |
| 能力一般的 | 76.7% | 1.2% |
不重置时,上一个任务对数据库的修改留到了下一个任务,而判分器拿整个数据库和按初始状态算出的期望比较——会动手的智能体全军覆没,只有什么都不改的”偷懒的”毫发无损。环境泄漏不只是加噪声,它会系统性地偏向某种行为。真实的智能体 benchmark 要给每个任务一个干净的容器 / 数据库副本,并且在任务之间彻底清理(例如 SWE-bench 为每个任务构建独立的 Docker 环境)。
2.3 停止条件:步数上限
| 智能体 | 最多 2 步 | 最多 3 步 | 最多 5 步 | 最多 10 步 |
|---|---|---|---|---|
| 能干的 | 93.4% | 92.2% | 92.8% | 92.2% |
| 慢但对的 | 62.1% | 62.0% | 93.6% | 91.4% |
| 能力一般的 | 77.6% | 76.5% | 77.8% | 76.4% |
上限太小,测的是”快不快”而不是”会不会”。上限要按任务的合理步数留足余量,同时报告步数和成本(§4.4)——快和对是两个维度,不要用一个隐含的截断把它们混在一起。
2.4 判分器:太松就奖励钻空子
| 智能体 | 严格:回答只含标准答案 + 状态一致 | 只看数据库状态 | 子串匹配 |
|---|---|---|---|
| 能干的 | 93.7% | 97.3% | 93.2% |
| 慢但对的 | 92.2% | 97.7% | 93.0% |
| 偷懒的 | 26.8% | 76.5% | 26.8% |
| 两头下注的 | 35.0% | 90.1% | 77.0% |
| 偷看答案的 | 67.2% | 90.4% | 70.9% |
| 能力一般的 | 76.7% | 93.0% | 79.0% |
- 只看数据库状态:查询类任务不需要改数据库,于是什么都不说也”成功”——“偷懒的”得 76.5%。
- 子串匹配:“yes or no”里有 “no”、“20、25 还是 30”里有 “25”——“两头下注的”从 35% 变成 77%。
判分器的设计原则:能检查状态的检查状态,需要回答的检查回答,两者都要;只接受明确的答案(不接受”列出所有可能”);用参照智能体测一测它的漏洞(§3.4)。
3. 好 benchmark 的性质 · Properties of good benchmarks
3.1 Press 的三条
Press(SWE-bench、SciCode、AssistantBench 等 benchmark 的作者之一)在《怎样造好的语言模型 benchmark》里总结了三条核心性质:
- 自然:任务反映人们真正在做的事——一个检验办法是”比基线做得好的系统,能不能真的帮到人”;尽量用真实用户的问题(例如真实的 bug 报告),避免多选题这种不自然的形式、避免凭空编的问题。
- 可自动判分:每个答案都能用程序检查(编程任务用单元测试);他特别提醒,不要让同一个语言模型既当答题者又当判分者。
- 有挑战:一上线就接近满分的 benchmark 没人会用。他最初建议发布时最强的模型得分在 1%–35% 之间,2025 年 1 月把这个范围改成 0.1%–9%,同年 5 月又建议把任务设计成”6 到 12 个月以后的模型还做不对”——因为 benchmark 通常一年之内就会饱和。
以及一些实用建议:答案最好不公开,防止泄露到训练数据里(SciCode 就是这样做的);一个主指标(细分结果放在分析里);baseline 要包括最强的闭源和开源模型;任务数量大约 300–500 个(至少 150 个,超过 500 个会因为跑得太久而没人用);贴近真实的脚手架(§1.5);按 4 元组定义每个任务。
3.2 ABC:任务有效性与结果有效性
Zhu et al. 从自己造 benchmark 的经验、对现有做法的调研和以往报告过的问题里,整理出 ABC(Agentic Benchmark Checklist)。它把一个智能体 benchmark 的有效性拆成两半:
- 任务有效性:一个任务可解,当且仅当智能体具备被测的能力。违反它的有两种:有捷径(不具备能力也能”做对”,例如环境里留着答案)、不可能(具备能力也做不对,例如标准答案错了、环境缺少必要的工具)。
- 结果有效性:评估的结果(测试、检查、判分)真的反映任务是否成功。违反它的也有两种:太松(没做成也判成功,例如空回答算成功、测试不充分)、太严(做成了也判失败,例如要求一字不差的格式)。
论文用实际的例子说明这些问题有多常见:SWE-bench Verified 用的测试用例不充分;τ-bench 把空回答算成功;这类设置和判分上的缺陷,会让智能体的表现被高估或低估,相对误差最多可达 100%。他们把 ABC 用在评估设计特别复杂的 CVE-Bench 上,把性能的高估减少了 33%。此外,ABC 也包含报告方面的要求:讲清楚 benchmark 的局限、怎样处理已知的问题、结果的不确定性。
3.3 五种漏洞,各自偏向一种行为
代码 03 在修好的版本上分别打开五种漏洞(括号里:相对真实能力的偏差;真实能力 = 修好的版本上 5 次运行的平均):
| 版本 | 能干的 | 慢但对的 | 偷懒的 | 两头下注的 | 偷看答案的 | 能力一般的 | 排名一致性 τ |
|---|---|---|---|---|---|---|---|
| 真实能力 | 93.7% | 92.2% | 26.8% | 35.0% | 67.2% | 76.7% | 1.00 |
| 环境泄露答案 | 92% | 93% | 27% | 33% | 90%(+34%) | 77% | 0.73 |
| 只看数据库状态 | 97% | 98% | 77%(+186%) | 89%(+155%) | 89% | 93% | 0.86 |
| 子串匹配 | 92% | 93% | 27% | 79%(+125%) | 69% | 80% | 0.73 |
| 保留坏任务 | 88% | 87% | 26% | 31% | 66% | 73% | 1.00 |
| 步数上限 3 | 91% | 61%(−34%) | 27% | 32% | 69% | 79% | 0.73 |
| 五种全部打开 | 95% | 66%(−29%) | 77%(+188%) | 88%(+151%) | 87%(+29%) | 90%(+18%) | 0.33 |
- 泄露答案是任务有效性的捷径;只看状态、子串匹配是结果有效性太松;步数上限太小、坏任务是”不可能”和”太严”——它们低估能力。
- 五种叠在一起,“偷懒的”(真实 27%)和”两头下注的”(真实 35%)都排到了”慢但对的”(真实 92%)前面,排名和真实能力的一致性只剩 τ = 0.33。ABC 论文在真实的 benchmark 上看到的相对误差最多 100%;我们刻意把几种漏洞叠在一起,偏差更大。
3.4 参照智能体:最便宜的检查
| 版本 | 什么都不做的智能体 | 无所不能的智能体 |
|---|---|---|
| 修好的版本 | 26.8% | 100.0% |
| 保留坏任务 | 26.5% | 93.9% |
| 只看数据库状态 | 76.5% | 100.0% |
| 有漏洞的版本 | 77.0% | 96.5% |
- 什么都不做的给出分数的”地板”。修好的版本上它仍有 27%:有 49 个任务的正确结果恰好就是”什么都不改”(“谢谢,没事了”30 个、“有停电才补偿”但没有停电 13 个、要取消的预约本来就不存在 6 个)。这不是漏洞,但要报告出来;如果它在某个版本上高得离谱(只看状态时 77%),就说明判分器有洞。
- 无所不能的给出”天花板”:拿不到 100%,说明有坏任务。
- 实践中,“无所不能”可以用”把标准答案直接交上去”来代替:它必须得满分,否则判分器或任务有问题。
3.5 找坏任务
3 个老实做题的智能体各跑 5 次,从来没有人做对的任务有 12 个,全部确实是坏任务(共 17 个)。漏掉的 5 个是标准答案写错了的题(数字题多加了 15、是否题写反了),被某次答错的运行”碰巧做对”了——所以还要看”越强越错”的题(第 11 讲 IRT 的负区分度)。把这些任务拿出来人工检查,是发布前必做的一步。
4. 可靠性的维度 · Dimensions of reliability
一个准确率不能说明一个智能体”可靠”。代码 04 量了四个维度。
4.1 一致性:pass@k vs pass^k
每个任务跑 8 次,用无偏估计(第 4 讲):
| 智能体 | pass@1 | pass@2 | pass@4 | pass@8 | pass^2 | pass^4 | pass^8 |
|---|---|---|---|---|---|---|---|
| 能干的 | 93.4% | 99.5% | 100.0% | 100.0% | 87.2% | 76.3% | 59.6% |
| 慢但对的 | 92.3% | 99.0% | 100.0% | 100.0% | 85.7% | 74.9% | 61.7% |
| 能力一般的 | 78.0% | 93.3% | 99.3% | 100.0% | 62.7% | 44.5% | 30.6% |
“能力一般的”试 8 次总能碰上一次对的(pass@8 = 100%),可 8 次全对的概率只有 30.6%。pass@k 回答”试几次能不能碰上”——适合有验证器、可以重试的场合(第 9 讲的 best-of-N);pass^k 回答”每次都能做对吗”——适合直接面对用户的场合。报告时要说清楚是哪一个。
4.2 鲁棒性:换一种说法
| 智能体 | 原来的说法 | 换一种说法 | 变化 |
|---|---|---|---|
| 能干的 | 93.7% | 90.5% | −3.2% |
| 慢但对的 | 92.2% | 90.5% | −1.7% |
| 能力一般的 | 76.7% | 67.9% | −8.9% |
(掉多少是我们设的参数;要点是方法:同一个任务准备几种说法,比较成功率。)只用一种说法的 benchmark,可能测的是”对这种说法熟不熟”——这也是题目泄露到训练数据里时最常见的症状。
4.3 容错:工具会出错
| 智能体 | 出错后重试 | 不出错 | 10% 出错 | 30% 出错 |
|---|---|---|---|---|
| 能干的 | 会 | 93.7% | 93.1% | 93.4% |
| 慢但对的 | 会 | 92.2% | 94.1% | 92.5% |
| 能力一般的 | 不会 | 76.7% | 71.4% | 59.9% |
真实的环境里 API 会超时、会限流、会返回错误(前置讲 P2、P3)。在评估里注入故障,才看得出智能体有没有恢复的能力。
4.4 效率
| 智能体 | 成功率 | 每个任务平均步数 | 每次成功的步数 |
|---|---|---|---|
| 能干的 | 92.9% | 1.34 | 1.44 |
| 慢但对的 | 93.4% | 3.02 | 3.23 |
| 能力一般的 | 77.0% | 1.34 | 1.74 |
“慢但对的”和”能干的”一样准,但成本和延迟是两倍多。只报告准确率,等于假设成本不重要。
4.5 汇总
| 智能体 | 准确率 | pass^4 | 换说法 | 30% 工具出错 | 每次成功的步数 |
|---|---|---|---|---|---|
| 能干的 | 94% | 76% | −3% | 93% | 1.4 |
| 慢但对的 | 92% | 75% | −2% | 92% | 3.2 |
| 能力一般的 | 77% | 44% | −9% | 60% | 1.7 |
没有一个数能说明”可靠”。一个好的评估报告至少给出:主指标(及置信区间)、一致性(pass^k)、在扰动和故障下的表现、成本 / 延迟。
5. 评估的统计 · Statistics for evals
“A 比 B 高 3 个点”——是真的更好,还是运气?评估分数的不确定性有三个来源:题目的抽样(这 200 道题只是所有可能的题里的一个样本)、运行的随机性、题目的成簇(很多题来自同一个模板或仓库)。代码 05 用一个按 IRT 生成结果的模拟器(stats_lab.py:每道题有难度,所有智能体共享)把它们一一量出来。
5.1 置信区间
一个真实成功率 83.2% 的智能体:
| 题数 | 估计的成功率 | 95% Wilson 区间 | 半宽 |
|---|---|---|---|
| 50 | 80.0% | [67.0%, 88.8%] | ±10.9 个点 |
| 100 | 75.0% | [65.7%, 82.5%] | ±8.4 个点 |
| 200 | 87.5% | [82.2%, 91.4%] | ±4.6 个点 |
| 500 | 81.6% | [78.0%, 84.8%] | ±3.4 个点 |
| 1,000 | 82.9% | [80.4%, 85.1%] | ±2.3 个点 |
区间宽度随 1/√n 缩小。Wilson 区间比”p ± 1.96·√(p(1−p)/n)“在接近 0% 或 100% 时可靠得多(第 1 讲以来的表格一直用它)。100 道题时,同一个智能体的估计可能是 75%,也可能是 87%。
5.2 配对比较
A(真实 83.2%)和 B(真实 76.9%)差 6.3 个点。在 300 次模拟里,p < 0.05 的比例:
| 题数 | 看出差别:独立 z 检验 | 看出差别:配对 McNemar | 假阳性(两者一样好):z | 假阳性:McNemar |
|---|---|---|---|---|
| 100 | 19% | 20% | 3% | 4% |
| 200 | 25% | 30% | 2% | 2% |
| 500 | 69% | 77% | 3% | 4% |
| 1,000 | 94% | 97% | 3% | 4% |
- 差 6 个点,200 道题只有三成的把握看出来;要到 1,000 道题,McNemar 才有八成以上的把握。
- 配对更灵敏:两个智能体在同一道题上的结果是相关的(难题对谁都难)。McNemar 检验只看”两者结果不同的题”:b = A 对 B 错的题数,c = A 错 B 对的题数,在”两者一样好”的假设下 b 服从 Binomial(b + c, 1/2)。配对把”这道题本身难不难”消掉了。
- 两者一样好时,两种检验的假阳性都不超过 5%——配对没有让它”更容易看出不存在的差别”。
5.3 题多还是跑得多
预算固定为 400 次运行(300 次模拟,估计的标准差):
| 分配 | 估计的标准差 |
|---|---|
| 400 道题 × 每题 1 次 | 1.79 个点 |
| 200 道题 × 每题 2 次 | 2.08 个点 |
| 100 道题 × 每题 4 次 | 2.40 个点 |
| 50 道题 × 每题 8 次 | 2.91 个点 |
当题和题之间的差别(难度)大于同一道题两次运行之间的差别时,多出题比多跑几次划算。多跑几次的价值在于 pass^k 和稳定性(§4.1),不在于缩小区间。
5.4 题目成簇
400 道题来自 20 个模板(同一模板的题难度相近):
| 标准误 | 数值(个点) |
|---|---|
| 实际的波动(模拟之间的标准差) | 3.51 |
| 把 400 道题当独立样本算的 | 1.78 |
| 按模板成簇算的 | 3.40 |
把题当独立样本,标准误被低估了一半。有效样本量更接近模板数,而不是题数——SWE-bench 的题来自十几个仓库、很多合成 benchmark 来自少数模板,都要用成簇的标准误(第 11 讲 SWE-smith 的去重也是在处理”同一件事考了很多遍”)。
把这些做法写成一套流程:报告平均值和(成簇的)标准误;比较两个系统时用配对差异和它的标准误;事先做检验力分析,决定需要多少道题;多次运行时分开报告”题目的波动”和”运行的波动”。Miller 的 Adding Error Bars to Evals 系统地讲了这些建议(见延伸阅读)。
6. 用真实模型试一试 · Try it on a real model
06_real_llm.py(可选,需要 pip install anthropic 和 ANTHROPIC_API_KEY,12 次调用)把客户数据库的相关几行放进系统提示,问 Claude 12 道查询题(查余额、查是否有停电记录、查不存在的客户),其中一半的是 / 否题不写 “Answer yes or no.”;同一批回答用三种判分器判:严格(bench_lab.score)、子串匹配、是 / 否题按意思判。
python3 06_real_llm.py
不联网检查脚本本身:python3 06_real_llm.py --toy。
读结果时想一想:真实模型不写格式时用什么方式回答?三种判分器在哪几道题上判得不一样——是严格的冤枉了正确的回答,还是宽松的放过了含糊的回答?查不存在的客户时,它会不会编一个套餐出来?编写时没有用真实 API 跑过这个脚本,这里不报告数字。
7. 一份 benchmark 发布前的清单 · A pre-release checklist
任务有效性
- 每个任务都有人确认过:可以完成、标准答案正确、环境提供了完成它需要的一切。
- 环境里没有捷径:答案文件、测试代码、日志、可以访问的外部资源都检查过;每个任务一个干净的环境,任务之间彻底重置。
- 跑过参照智能体:什么都不做的(分数的地板)、把标准答案直接交上去的(必须满分)。
- 看过”谁都做不对”和”越强越错”的任务,逐个人工检查。
结果有效性 5. 判分器检查状态,也检查回答;不接受”列出所有可能”的回答;要求明确的格式,或者能接受等价的说法。 6. 判分器在人工标注的一批样本上验证过(覆盖率、误报率,第 11 讲 §6)。 7. 测试足够充分:对编程类任务,做过变异测试(第 11 讲 §5.2)。
报告 8. 一个主指标 + 置信区间(题目成簇时用成簇的标准误);比较两个系统时用配对检验。 9. 一致性(pass^k)、鲁棒性、容错、成本 / 延迟。 10. 脚手架、步数上限、运行次数、模型版本写清楚;已知的局限写清楚。
8. 小结 · Summary
- 智能体评估难:每次运行都不一样、链条长、有副作用、多种正确解法、依赖脚手架、贵。同一个智能体只跑一次、只用 50 道题,分数可以差二十多个点;脚手架可以改变排名。
- 4 元组:请求、环境、停止条件、判分器——每一部分都是设计选择。请求和判分器要一起设计;环境每个任务都要重置(否则会系统性地偏向”什么都不做”);步数上限要留足余量;判分器检查状态也检查回答。
- 好 benchmark:自然、可自动判分、有挑战(Press);任务有效性(可解当且仅当有能力)与结果有效性(结果反映真实的成功)(ABC)。漏洞各自偏向一种行为,叠在一起可以让排名几乎失去意义。参照智能体和”谁都做不对的任务”是最便宜的检查。
- 可靠性有多个维度:pass@k 和 pass^k 回答不同的问题;鲁棒性、容错、效率都要单独报告。
- 统计:给分数配上置信区间;比较两个系统用配对检验;先做检验力分析决定题数;多出题比多跑几次划算;题目成簇时有效样本量是簇数。
9. 常见误区 · Common pitfalls
- “我们比 baseline 高 3 个点,所以更好。” 200 道题时,差 6 个点也只有三成的把握看出来;先看置信区间和配对检验。
- “判分器只要检查最终状态就够了。” 查询类任务不改状态,“什么都不说”也成功(ABC 指出的 τ-bench 问题)。
- “判分宽松一点,免得冤枉模型。” 子串匹配让”两头下注”的智能体从 35% 变成 77%;要宽容的是等价的说法,不是含糊的回答。
- “环境复用可以省时间。” 不重置环境,会动手的智能体全军覆没,什么都不做的毫发无损。
- “步数上限越紧越能逼出效率。” 它会把”慢但对的”判成失败;效率要单独报告。
- “满分做不到,说明题目够难。” 也可能是坏任务;交一份标准答案试试,必须满分。
- “pass@k 很高,系统就可靠。” pass@8 = 100% 的智能体,pass^8 可能只有 30%。
- “多跑几次能让结果更准。” 预算固定时,多出题比多跑几次更能缩小区间。
- “1,000 道题,样本量就是 1,000。” 它们如果来自 20 个模板,有效样本量接近 20,标准误会被低估一半。
- “benchmark 上的分数,就是模型的能力。” 它是模型 × 脚手架 × 判分器 × 题目的分数——每一项都要写清楚。
10. 练习 · Exercises
练习 1(4 元组):为”帮用户在日历里安排一个会议”写一个任务的 4 元组,并指出每一部分最容易出的有效性问题。
参考答案(之一)
请求:"下周三下午和 Alice、Bob 开 30 分钟的会,避开他们的冲突。"——问题:时区、"下周"的含义、"下午"的范围都可能含糊(任务有效性:可能有多个正确答案,也可能无解)。环境:一个有几个人日历的模拟日历服务(每个任务一份干净的副本),工具:查空闲、创建事件、发邀请——问题:环境里的"今天"要固定(否则"下周三"随运行日期变化);不能有真实的外部调用。停止条件:智能体声明完成或 20 步——问题:创建事件后还会继续发很多邀请的智能体被截断。判分器:日历里有且只有一个新事件,时间在要求的窗口内、和所有人的现有事件不冲突、参与者正确;没有修改其他事件——问题:只检查"有没有新事件"会放过时间错的;只接受唯一的正确时间会冤枉其他合法的时间(结果有效性太严)。练习 2(找漏洞):一个 benchmark 让智能体修复代码仓库里的 bug,判分器是”运行仓库自带的测试,全部通过就算成功”。列出至少三种可能的有效性漏洞,并说明怎样检测。
参考答案
① 测试不充分(结果有效性太松):错误的修改也能通过——用变异测试 / 额外的隐藏测试检查;② 智能体可以修改测试本身(捷径)——判分时用原始的测试文件覆盖,或检查补丁有没有动测试;③ 测试依赖网络、时间、随机数而不稳定(同一份正确的补丁时过时不过)——对标准补丁跑多次,确认它每次都通过;④ 环境里能看到修复后的代码(例如 git 历史里有未来的提交)——检查环境里能访问的东西;⑤ 标准补丁本身通不过(坏任务)——把标准补丁交上去,必须全部通过;⑥ 什么都不改也能通过(任务本身无效)——跑"什么都不做"的参照智能体。练习 3(参照智能体):解释为什么”什么都不做”的智能体在修好的版本上仍有 27%,这算不算 benchmark 的缺陷?如果你想把这个地板降到接近 0,可以怎样改任务?
参考答案
因为有 49 个任务的正确结果恰好是"不改数据库"("谢谢,没事了"、没有停电时不该补偿、要取消的预约本来就不存在),而"Done."又被当成了一句礼貌的回复。它不一定是缺陷——现实里确实有很多请求的正确做法是"不动手"——但必须报告。如果要降低地板:要求智能体对每个请求给出明确的说明(例如"没有停电记录,所以没有补偿"),判分器同时检查状态和说明;"谢谢"类任务要求礼貌、具体的回复,而不是任何非空字符串;减少"正确做法是不动手"的任务比例,或者把它们单独报告。练习 4(pass^k):一个智能体在某任务上每次成功的概率是 0.9。(a) pass@4 和 pass^4 各是多少?(b) 如果每天 1,000 个用户各问一次,平均有多少人遇到失败?(c) 在什么场景下你更关心 pass^k?
参考答案
(a) pass@4 = 1 − 0.1⁴ = 0.9999;pass^4 = 0.9⁴ ≈ 0.656。(b) 约 100 人——每次面对用户都是"一次机会",可靠性是 pass^1 = 0.9;同一个用户反复使用 4 次都满意的概率是 0.656。(c) 直接面对用户、没有验证器、不能重试的场景(客服、自动执行的操作),以及失败代价高的场景;pass@k 适合有可靠验证器、可以多试几次再挑的场景(代码生成 + 测试)。练习 5(置信区间):一个智能体在 120 道题上做对 90 道。(a) 用正态近似算 95% 区间。(b) 另一个智能体在同样的题上做对 99 道;在两者结果不同的题里,A 对 B 错 6 道、A 错 B 对 15 道。用 McNemar 检验判断差别是否显著(p 用正态近似即可)。
参考答案
(a) p̂ = 0.75,标准误 √(0.75 × 0.25 / 120) ≈ 0.0395,区间 ≈ [67.3%, 82.7%]。(b) 不一致的题 n = 21,b = 6;在"一样好"的假设下 b ~ Binomial(21, 0.5),均值 10.5、标准差 √(21 × 0.25) ≈ 2.29;z = (6 − 10.5)/2.29 ≈ −1.96,双侧 p ≈ 0.05——刚好在边界上(精确的二项检验 p ≈ 0.078,不显著)。注意:两个分数分别是 75% 和 82.5%,差 7.5 个点,看起来不小,但真正提供信息的只有那 21 道结果不同的题。练习 6(需要多少道题):你想用配对检验分辨两个真实成功率约 80% 和 75% 的智能体,并希望有 80% 的把握。参考代码 05 的结果,大概需要多少道题?如果题目来自 30 个模板,这个数字会怎样变?
参考答案
代码 05 里差 6.3 个点时,McNemar 在 500 道题时 77%、1,000 道题时 97%——差 5 个点需要更多,大约 1,000 道左右。题目成簇时,有效样本量小于题数,需要的题更多(或者说,增加同一个模板下的题收益递减,应该增加模板数);同时要用成簇的标准误来做检验。另一个办法是挑区分度高的题(第 11 讲的 IRT):对这两个水平的智能体,太简单、太难的题都不提供信息。练习 7(题多还是跑得多):你有预算跑 800 次。智能体每次运行的随机性很大(同一道题两次运行的结果经常不同)。你会怎样分配”题数 × 运行次数”?如果你还关心 pass^k 呢?
参考答案
如果只关心平均成功率的估计精度:一般仍然是多出题更好,因为题目难度的差别通常大于运行的随机性;但当运行的随机性很大时,多跑几次的收益相对变大——可以先做一个小实验(例如 50 道题各跑 4 次),分解出"题目的方差"和"运行的方差",再按比例分配。如果关心 pass^k:至少要每题跑 k 次(例如 200 道题 × 4 次),否则无法估计"k 次都成功"的概率。练习 8(脚手架):两个团队都声称自己的模型在同一个 benchmark 上更好:A 团队用的是 10 步上限、可以执行代码的脚手架,B 团队用的是 30 步上限、不能执行代码的。你会怎样设计一个公平的比较?
参考答案
固定脚手架,只换模型:选一个贴近常见做法的脚手架(例如能执行代码、步数上限按任务的合理需要留足余量),两个模型都在它上面跑;再报告每个模型在各自最好的脚手架上的结果作为补充。同时报告:步数 / token / 成本、pass^k、置信区间和配对检验。如果脚手架本身是被比较的对象("谁的 harness 更好"),就反过来固定模型、换脚手架。混在一起比,测出来的是"模型 × 脚手架"的组合,无法归因。练习 9(ABC 的分类):把下面的问题归到”任务有效性”或”结果有效性”,并说明是”太松”还是”太严”(或”捷径”还是”不可能”):(a) 判分器要求答案和标准答案逐字相同;(b) 任务要求访问一个已经下线的网站;(c) 环境的日志文件里记着上一次运行的正确答案;(d) 测试只检查函数有没有返回值,不检查值对不对。
参考答案
(a) 结果有效性,太严(正确但措辞不同的回答被判错);(b) 任务有效性,不可能(有能力也做不到);(c) 任务有效性,捷径(没有能力也能做对);(d) 结果有效性,太松(错误的实现也能通过)。练习 10(开放题:审查一个你常用的 benchmark):挑一个你看过别人报告结果的智能体 benchmark,按 §7 的清单逐条审查:它满足哪些、不满足哪些、你最怀疑哪一条会让结果失真?
参考答案(思路)
重点看:① 环境是否每个任务隔离、有没有可以访问的答案或外部资源;② 判分器是检查状态还是只看回答、有没有被验证过、有没有"空回答也成功"的任务;③ 任务数量和来源(是否成簇)、作者是否报告了置信区间;④ 发布以来是否有人报告过坏任务或捷径(很多 benchmark 后来都发布了"Verified"或修正版本);⑤ 结果用的是什么脚手架、跑了几次。写下你最怀疑的一条,并设计一个最小的实验去验证它(例如跑一个什么都不做的参照智能体)。11. 延伸阅读 · Further reading
本讲的阅读材料
- Y. Zhu et al., Establishing Best Practices for Building Rigorous Agentic Benchmarks, 2025(ABC 检查清单)。
- O. Press, How to Build Good Language Modeling Benchmarks(博客,2024 年首发,之后多次更新)。
- F. M. Polo et al., tinyBenchmarks: Evaluating LLMs with Fewer Examples, ICML 2024(第 11 讲 §2)。
相关
- E. Miller, Adding Error Bars to Evals: A Statistical Approach to Language Model Evaluations, 2024.
- S. Yao et al., τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains, 2024(第 4 讲)。
- C. E. Jimenez et al., SWE-bench: Can Language Models Resolve Real-World GitHub Issues?, ICLR 2024;OpenAI, Introducing SWE-bench Verified, 2024.
- E. B. Wilson, Probable Inference, the Law of Succession, and Statistical Inference, 1927(Wilson 区间)。
12. 下一讲预告 · Next lecture
第 13 讲 LLM-as-Judge 与评估基础设施(11/9)。这一讲里,凡是”按意思判”的地方都留了一个问号:用什么来判?最常见的答案是另一个 LLM。下一讲讨论三类判分器(代码、模型、人)各自的位置;LLM 评审的提示词怎样写;它已知的偏差——位置偏差、长度偏差、偏爱自己的回答;成对比较还是逐条打分;怎样用少量人工标签校准它(第 10、11 讲的人机分工的延续);Anthropic 的 Demystifying Evals for AI Agents 给出的评估路线图;AutoMetrics / AutoLibra 怎样从反馈里自动生成指标;以及一个评估 harness 需要的工程部件(缓存、并发、记录、可复现)。配套代码会模拟带偏差的 LLM 评审、用人工标签纠正它,并搭一个带缓存和并发的评估 harness。