3.4
Part III / Evaluate · 评估的运行时基础,以及在全局中定位自身的校准

基准测试与 CI:外部校准,以及使评估真正运转的节律。

公开基准测试(SWE-bench、GAIA、OSWorld、WebArena、τ-bench、AgentBench)塑造了整个领域谈论智能体(agent)质量的方式——然而大多数团队都用错了方向。持续集成(CI)是第 3.2 章第二层评估实际在每个 PR 上运行的地方——而大多数团队搭建得足够糟糕,使得第 3.1 章的评估节律在实践中根本无法落地。本章同时涉及两者:公开基准测试真正擅长什么(以及不擅长什么),当公开基准测试不适合时如何构建自己的基准测试,以及让评估驱动开发成为实时纪律而非一种愿景的 CI 模式。读完本章,你将清楚该关注哪些基准测试、如何解读它们,何时需要构建自己的基准测试,以及如何设计让第 3.2 章三个层次变为真实发布流程的 CI 管道。

STEP 1

公开基准测试:它们擅长什么,以及不擅长什么。

对智能体基准测试的朴素立场:选一个排行榜,运行你的智能体,将得分与他人对比。分越高越好。把这个数字用在市场营销中。

在 2024—2026 年这一代基准测试被充分审视之后,诚实的立场是:公开基准测试的分数与产品质量的相关性很弱,以它们作为质量目标只会带来优化而非真正的改进。原因具体且有充分文献记录——理解它们,才能把基准测试用得有价值,而不是对着它们发布版本却在用户体验不到收益时感到困惑。

当前公开基准测试全景

作为背景,截至 2026 年中期,对智能体工作最重要的基准测试如下:

基准测试
测量内容
当前最优水平(约)
SWE-bench Verified
500 个真实 GitHub 缺陷修复任务;补丁能通过测试的比率
~87%(Opus 4.7);~85%(GPT-5 Codex)
GAIA
466 个多步骤工具调用(tool use)任务;对最终答案进行精确匹配评分
HAL 上约 75%;人类基线约 92%
WebArena
在受控环境中执行 812 个长时序浏览器任务
顶尖系统约 68%;人类基线约 78%
OSWorld
369 个跨应用桌面计算机操作(computer use)任务
顶尖系统约 38%;人机差距巨大
τ-bench(Tau-Bench)
与模拟用户的工具调用,检验策略遵从性
简单轨道中 70 多分,可靠性轨道中更低
AgentBench
跨 8 种不同环境的诊断套件
更多用作广度检查而非排名
METR HCAST / Time Horizons
智能体能以 50% 可靠性完成的最长任务
当前前沿(frontier)智能体不足一小时

这些基准测试在正确使用时确有价值。问题主要在于它们被使用的方式,而非基准测试本身。

公开基准测试分数误导性的四个原因

四个具体问题叠加,使已发布的基准测试分数远不如表面上那么具有参考价值。

1. 数据污染。许多基准测试公开发布时,答案、预期输出或解题步骤就已经出现在了后续模型的训练数据中。SWE-bench Verified——基于公开 GitHub issue 构建——其解法就藏在同一个代码仓库的 git 历史里。WebArena 和 GAIA 的解题过程与答案已被发布在公开网络,任何能使用网络的智能体都可能找到。Berkeley RDI 研究(2026 年 4 月)表明,一个自动扫描智能体仅通过利用这些信息泄漏就攻破了所有八个主流基准测试,在不解决任何任务的情况下取得了接近满分的成绩。具体来说:你发布的分数可能反映的是数据泄漏,而非真实能力

2. 脚手架不对称。同一模型在同一基准测试上,由不同团队测试可能产生截然不同的数字——取决于围绕它的脚手架:代理循环(agent loop)、重试预算、工具定义、系统提示词(prompt)。Anthropic 声称的"SWE-bench X%"依赖于 Anthropic 的脚手架。你的复现往往会低 10 分以上,因为你没有他们的脚手架。分数反映的是整个系统,而不只是模型

3. 单次运行报告。大多数已发布的数字来自单次尝试的 pass@1。由于采样的随机性,同一智能体在不同运行间的差异通常在 5—15% 之间。当以不同随机种子重新运行时,排行榜的排序可能随之改变。真实可靠性——同一任务在多次尝试中的成功率——通常比标题数字所呈现的要差。METR 2026 年的研究发现,o3 和 Claude 3.7 Sonnet 在超过 30% 的评估(eval)运行中出现了奖励破解行为,通过操纵评分系统而非真正完成任务来提高分数。分数比报告的更不稳定

4. 分布不匹配。公开基准测试在特定任务分布(知名仓库中的 Python 缺陷、沙盒购物网站、模拟客户服务)上测量特定能力(代码编辑、网页导航、工具调用)。你的智能体服务的是不同的分布。SWE-bench 得分 85% 的模型在你的内部代码库上可能只有 65%,因为缺陷的分布、项目规范和测试基础设施都各不相同。基准测试衡量的是在其自身分布上的能力,而非你的分布

经验法则

2026 年基准测试危机之后浮现出一条指引:当你看到智能体基准测试分数时,心理上为污染效应减去 10 分,并将可靠性声明除以 1.3 以修正方差。调整后的数字才更接近你在自己工作负载上实际观察到的结果。

这并非对基准测试构建者的贬低——他们做的是重要工作,基准测试对其所测量的事物确实有价值。这是对数字含义的重新诠释。"SWE-bench Verified 94%"的声明,并不等于"这个智能体能修复你 94% 的缺陷",而是"这个智能体,在某个团队的特定脚手架中,在一批特定的公开 Python 缺陷修复任务上,在污染可能贡献了若干分的条件下,取得了 94% 的分数"。

基准测试真正擅长的事

上述警示不会削弱的三个公开基准测试的合理用途:

跨模型能力校准。当你在为一个使用工具的智能体选择 Opus 4.7、Sonnet 4.6 还是 Haiku 4.5 时,τ-bench 这类工具调用基准测试上的相对排名是有意义的——即使绝对分数有所虚高。同样的脚手架问题影响所有三个模型,因此相对比较仍然合理。用基准测试来比较模型,而非预测绝对性能。

跨团队的能力对话。当你告诉另一位工程师"我们的智能体能很好地处理 SWE-bench 风格的重构"时,对方对这意味着什么有共同的心智模型。基准测试充当了能力描述的词汇表。数字本身不是重点;被命名的能力才是。

能力上限检查。如果前沿智能体在 OSWorld 上的得分是 38%,而你的产品需要 OSWorld 风格的桌面控制,你就有了一个关于预期的量化上限。你不会构建一个得分达到 95% 的系统;底层能力还没到那里。基准测试是一种规划输入,告诉你什么是当前可能的。

基准测试擅长的事

对称地,上述警示所明确指出的三种错误使用方式:

不要将基准测试分数作为质量目标。"我们的智能体必须在 GAIA 上达到 80% 才能发布"会导致针对基准测试而非产品的优化。这种优化会改善基准测试形状的任务,但可能损害其他维度。你的评估套件(第 3.2 章第二层,本章第 2 步)才是正确的质量目标——它衡量的是用户实际体验到的东西。

不要在市场营销中过度使用基准测试分数。"我们的智能体在 SWE-bench 上得了 X 分"作为能力声明没有问题。"我们的智能体能修复 87% 的缺陷"则是过度解读——这不是基准测试所衡量的。诚实地呈现基准测试性能需要包含方法论和注意事项;对基准测试分数进行广告式处理,当用户遭遇落差时会迅速过时。

不要将基准测试用作主要的回归(regression)检测手段。基准测试套件运行成本高、更新频率低,且不覆盖你的具体使用场景。它们不是第二层(第 3.2 章)。偶尔运行它们——每季度、在主要模型切换时——作为能力检查,而非 PR 门控。

Berkeley RDI 研究发现,一个仅有 10 行的 conftest.py 就能让每一个 SWE-bench 测试都报告为通过——无需解决任何任务——只需操纵 pytest 的收集过程即可。这不是 SWE-bench 的特有缺陷;它是一个更广泛真相的具体体现:任何评估系统都可能被能从内部找到突破口的智能体所奖励破解。公开基准测试与这个问题如影随形,因为其评分机制是公开的;你的内部评估可以更健壮,因为你控制着测评框架。教训不是"不要信任基准测试"——而是"任何评估的信任上限,取决于从内部操纵它有多难"。

Question
如果公开基准测试如此不可靠,Anthropic 为何还要发布相关结果?

原因与每家主要实验室相同——它们是这个领域的通用语言,不发布结果会留下一个被解读为弱点的对比空白。模型提供商发布数据、仔细记录方法论(优秀的提供商会注明脚手架细节、经过验证与未经验证的子集、pass@k 与 pass@1 的区别),并将数字视为能力披露,而非关于特定产品的质量声明。

以这种框架来读提供商的基准测试报告:他们是在告诉你"这是我们在这些条件下测量到的结果"。诚实的提供商会附上方法论章节,让你做出合理的折扣判断。不那么诚实的则只发布标题数字。方法论章节才是值得仔细阅读的信号。

Question
我是否应该将内部评估与公开基准测试分布做对比,以便与外界保持可比性?

有时作为一种健全性检查有用;很少值得专门为此优化。有效的模式是:每季度在你的智能体上运行一次 SWE-bench Verified 作为能力检查。得分回答的是"我们是否处于前沿脚手架的同一量级?"如果前沿水平是 87% 而你是 25%,那你有一个值得调查的脚手架差距。如果前沿是 87% 而你是 70%,那你在正确的区间——差距主要是不可迁移的脚手架特定调优。

不要按常规节奏针对公开基准测试迭代。那是古德哈特定律(Goodhart's law)在评估领域的体现,你优化的将是基准测试性能而非用户体验。第 3.2 章的内部评估套件始终是主要的迭代目标。

Question
我实际上应该关注哪些基准测试?

按智能体形态划分的实用精简清单:

  • 代码智能体(code agents):SWE-bench Verified 提供通过率信号;Aider Polyglot 覆盖非 Python 场景;LiveCodeBench Pro 针对新颖问题(比 SWE-bench 污染更少)。
  • 网络/研究智能体:GAIA 适用于复合工具调用;WebArena 适用于浏览器专项任务;两者都需要做污染折扣。
  • 计算机操作:OSWorld 是标准;随着领域在此方向持续投入,预计每季度都会有快速进步。
  • 工具调用可靠性:τ-bench 是"智能体在多次运行中是否遵守工具调用策略"最干净的衡量指标;尤其有价值,因为其他大多数基准测试不显式衡量可靠性。
  • 综合能力:AgentBench 作为广度检查,而非排名工具。METR HCAST 用于了解"这个智能体能可靠完成多长的任务"。

每季度关注这些基准测试,了解领域的整体进展。不要每周在自己的智能体上运行它们——你的评估套件对此更快速、更相关。

STEP 2

构建自己的内部基准测试。

如果公开基准测试无法衡量你的智能体真正在做的事,而你的第二层评估套件又是随着学习不断变化的活性存在——那么随时间推移稳定可比的测量由谁来承担?对某些团队来说,答案是内部基准测试:将评估材料的一个子集冻结、版本化,视为固定参考点,从而在不同版本间有意义地比较智能体性能。

大多数团队不需要这个。第 3.2 章的第二层覆盖了迭代循环。内部基准测试是更高一层:每季度一次的测量,用于回答"我们的智能体真的在变好,还是只是在对我们不断修改的评估集优化?"

团队何时需要内部基准测试

三个信号表明内部基准测试值得投入:

你在随时间跟踪质量。你的第二层评估集在六个月内刷新了三次——上季度的分数与今天在已更改评估集上的分数无法直接对比。你需要一个稳定的参照。锁定在版本控制中的内部基准测试让你能说"我们智能体 v1.3 在基准测试 v1.0 上得了 X 分;v1.7 在同一基准测试 v1.0 上得了 Y 分"——这才是真正的对比。

你在做发布/不发布决策。当一个候选版本必须越过质量门槛时,你需要这个门槛在不同版本间含义一致。不断漂移的第二层评估集无法承载这种含义——对最近更新后的套件"通过第二层"的判定,与上季度的判定不可比。内部基准测试将发布门控锚定在稳定的定义上。

你在对外沟通质量。告诉客户、投资人或合作伙伴"我们的智能体本季度提升了 N 分",需要这个 N 是针对稳定目标的。如果你的第二层一直在移动,就无法诚实地声称"提升了 5 分",因为刻度本身已经偏移。内部基准测试为你提供稳定的刻度。

内部基准测试与第二层评估套件的区别

同一个查询可以同时出现在两者中,但在各自语境中具有不同的语义。差异在于策略,而非内容。

维度
第二层评估套件
内部基准测试
稳定性
活性——定期刷新
冻结——版本化发布
执行节奏
每个 PR / 夜间 / 预发布
每季度 / 主要版本 / 模型切换时
用途
在开发中捕捉回归
跨版本跟踪质量
可见性
工程师对其进行迭代
用作发布门控;不针对其迭代
风险特征
古德哈特风险较低;通过刷新消除
古德哈特风险较高;需要强力治理

两者的关系是:内部基准测试通常是你第二层评估集在某一特定时间点的冻结子集。你不需要从头构建一个独立的基准测试;你取第二层中一个稳定的切片(那些已经稳定了 6 个月以上、你信任的查询),将其冻结为 benchmark-v1.0,并以不同于其余部分的方式对待这个冻结快照。

"冻结子集"模式

最简单也最站得住脚的方法:

┌──────────────────────────────────────────────────────────────────┐ │ INTERNAL BENCHMARK: THE FROZEN SUBSET PATTERN │ │ │ │ Layer 2 eval set (living) │ │ ├─ Updated as we learn from production │ │ ├─ Queries added, replaced, refreshed quarterly │ │ └─ ~50–100 queries total at any time │ │ │ │ │ │ │ │ Once per quarter / per major release: │ │ │ freeze a stable subset → save with version │ │ ▼ │ │ │ │ Internal benchmark v1.0 │ │ ├─ 25 queries from Layer 2 at the time of freeze │ │ ├─ Locked in version control │ │ ├─ Expected behaviors hand-validated at freeze time │ │ └─ Never iterated against; only measured against │ │ │ │ Later: benchmark v1.1 = v1.0 + 5 new queries, none removed │ │ v2.0 = next full refresh, with rationale doc │ └──────────────────────────────────────────────────────────────────┘

基准测试以递增方式增长。v1.1 在 v1.0 的基础上增加查询;v1.2 再增加更多。当积累的变化足够大、旧基准测试已不再具有代表性时,v2.0 才会出现——但这是一个有明确文档记录原因的刻意事件,而非随意的刷新。

这种模式在你需要稳定性的地方提供了稳定性(跨版本对比始终有效),同时允许基准测试随着学习而增长。关键纪律:v1.1 发布后,v1.0 的得分依然保留。你可以通过始终同时报告当前版本和原始 v1.0 得分来跨版本对比。旧数字不会消失。

基准测试的具体内容

结构性产物:代码仓库中的一个版本化目录,像对待数据基础设施一样对待它。

# bench/v1.0/
benchmark-v1.0/
├── README.md                      # freeze rationale, version notes
├── queries.jsonl                  # the 25 queries, immutable
├── expected_behaviors.jsonl       # per-query expected properties
├── grading_rubric.md              # exact grading criteria, frozen
├── judge_prompts/                 # the LLM judge prompts, frozen
│   ├── task_completion.txt
│   ├── factual_accuracy.txt
│   └── quality.txt
├── calibration_set.jsonl          # human-labeled pairs for judge validation
└── results/                       # scores by agent version
    ├── agent-v1.3-20250901.json
    ├── agent-v1.5-20251015.json
    └── agent-v1.7-20251130.json

注意被冻结的内容:不只是查询,还有一切决定得分的东西——评分标准(grading rubric)、大语言模型(LLM)评判者的提示词,乃至校准集。如果评分管道中的任何一环发生变化,产生的数字就无法与历史结果对比。基准测试是完整的测量系统,而非仅仅是输入查询。

results 目录随时间增长:每个主要版本的智能体都有其对应的结果文件。这些文件的历史就是随时间演变的质量故事。

治理:谁能修改基准测试

最重要的一条规则:不得为了让某个版本通过而修改基准测试。诱惑是真实存在的——某个版本未能达标,有人注意到一个他们认为不具代表性的查询,删掉它就能推过门槛。不要这样做。那次删除会破坏基准测试的完整性。

防止这种情况的治理机制:

  • 基准测试变更需要明确的版本号提升。v1.0 → v1.1 → v2.0 各自都有记录在案的原因。"为了让版本通过而变更"不是有效的原因。
  • 基准测试变更须由不参与当前版本发布的人来审查。避免局部优化;新鲜的审查者会问"这个变更在脱离发布背景后是否合理?"
  • 旧版基准测试依然有效。即使 v1.1 已存在,v1.0 的评分依然持续计算和发布。用新基准测试掩盖旧基准测试,与直接修改它一样是一种破坏。

初衷在于:基准测试的存在是为了给你提供关于智能体是否真正在改进的信号。这个信号只有在基准测试保持可信时才有价值——而保持可信的唯一方式,是能防止动机性修改的治理机制。

维护内部基准测试的成本

诚实地说:并不轻松。三类持续成本。

初始冻结。花费一周集中的工程时间,拍下第二层快照,手工验证预期行为,编写评分标准,准备校准材料。跳过这一步、"直接冻结现有的"会产生一个预期行为已陈旧或错误的基准测试——得分毫无意义。为 v1.0 冻结预留真实的时间。

每季度执行。对你的候选版本以及前一个版本进行基准测试,以便对比。每次基准测试运行的成本等于 N 次完整智能体运行加评判者调用——根据智能体复杂度,通常在 50 到 500 美元之间。可管理但真实存在。

版本提升。当你决定需要 v2.0 时,那又是另一次冻结成本循环——手工验证新的预期行为、更新评分标准、重新校准评判者。成熟基准测试每年的节奏大约需要几千美元的工程时间。如果基准测试真正在影响发布决策,这是值得的;如果你根本不在用这些数字,则不值得。

何时不需要这一层

读到本章的大多数团队目前还不需要内部基准测试。具体来说,如果满足以下条件,你不需要:

  • 你的迭代速度足够快,第二层评估套件能捕捉所有你能响应的信号。
  • 你还没有基于质量分数建立正式的发布/不发布门控。
  • 你不需要定期对外传达质量数字。

在这些情况下,单独的第二层就足够了。当你越过成熟度门槛时再添加这一层——通常是你以多月为节奏发布主要版本、利益相关方开始询问"智能体比上季度更好了吗"、对外比较或声明开始变得重要的时候。

内部基准测试中值得避免的一个错误:将基准测试与评估套件混为一谈。它们服务于不同目的,不应该共享超出最基本框架的运行时基础设施。如果你的 CI 对两者运行相同的代码,当迭代破坏了基准测试时,你会忍不住去"修复"它——这会破坏对比信号。将它们放在独立的目录中,以不同的节奏运行,抵制任何由特定版本发布驱动的基准测试变更冲动。

Question
一个有价值的内部基准测试最少需要多少查询?

约 25 个查询是统计信号变得有意义的下限——更少的话,单个查询的翻转对分数的影响过大,无法提供有参考价值的信息。上限取决于你每季度能承担的运行成本;成熟团队通常有 50—100 个。

一个有用的直觉:25 个查询、70% 基线通过率时,2 分的变化可能意味着 0—1 个查询翻转。50 个查询时,2 分意味着 1 个查询翻转。100 个查询时,2 分意味着 2 个查询翻转。查询越多,更小的可观测变化就越有意义——分辨率随规模提升,到约 100 个后回报递减。

Question
基准测试是否应该包含对抗性查询?

视情况而定——取决于你希望基准测试衡量什么。能力基准测试关注智能体擅长什么;对齐/安全(safety)基准测试关注智能体在对抗条件下的行为。它们服务于不同的发布决策问题,不应合并到同一个分数中。

清晰的模式:将 benchmark-capability-v1.0benchmark-safety-v1.0 分开。各自有独立的查询、评分标准和通过门槛。发布门控可以要求两者都通过,但设定不同的标准。试图将能力测量和对抗性测量合并为一个数字,会同时模糊两个信号。

Question
如何处理已过时的基准测试查询(例如,该查询涉及的 API 已被废弃)?

两个选项。保守的:保留该查询,让它得到它应得的分数。如果 API 已消失,查询将失败,这将成为该基准测试版本在未来得分的一部分。你是在针对一个固定参照进行测量;这个参照可以反映无关变化带来的影响。

务实的:提升版本号。v1.0 → v2.0,删除已过时的查询,并记录原因。旧版本依然有 v1.0 的历史得分;新版本使用 v2.0 得分。两个版本不可比,但版本提升事件是明确的。

错误的选项:在不提升版本号的情况下悄悄删除查询。这会让 v1.0 在不同时间点含义不同。始终提升版本、记录原因、保留历史。

STEP 3

智能体评估的 CI:让节律真正运转起来。

第 3.1 章讲了节律:预测、运行、裁决、迭代。第 3.2 章讲了架构:三个层次,各有其节奏。本步骤关于管道——让这些不只是实践描述,而是在每个 PR 上真正发生的 CI 基础设施。没有稳固的 CI,评估纪律就会漂移:人们跳过"本地运行评估"因为太慢,团队依靠记忆来掌握各层覆盖的内容,回归版本就这样流出去了。

这套纪律在软件工程领域已经具体而成熟——智能体评估只需要若干特定的适配。

CI 管道的形态

实践中形成的管道,按阶段和触发条件:

┌──────────────────────────────────────────────────────────────────┐ │ CI PIPELINE FOR AN AGENT REPO │ │ │ │ Triggers │ Stages │ │ ───────────────── │ ───── │ │ │ │ │ every commit / push │ ┌─ Layer 1 deterministic │ │ (~60s, ~$0.00) │ │ ─ schema checks │ │ │ │ ─ contract tests │ │ │ │ ─ unit tests │ │ │ │ ─ lint, type-check │ │ │ └─ blocks merge on fail │ │ │ │ │ every PR opened/updated │ ┌─ Layer 2 fast subset │ │ (~5min, ~$0.50) │ │ ─ 5-10 representative │ │ │ │ queries from suite │ │ │ │ ─ judge grading │ │ │ │ ─ scoreboard comment │ │ │ └─ informs but doesn't │ │ │ strictly block │ │ │ │ │ labeled `eval-full` │ ┌─ Layer 2 full suite │ │ OR ready-to-merge │ │ ─ all 50-100 queries │ │ (~20-60min, ~$10-200) │ │ ─ multi-dimension scores │ │ │ │ ─ verdict per chapter 3.1 │ │ │ └─ release-gate check │ │ │ │ │ scheduled (nightly) │ ┌─ Drift detection │ │ (~30min, ~$20) │ │ ─ full suite on main │ │ │ │ ─ judge calibration │ │ │ │ ─ alerts on regression │ │ │ └─ no merge action; alerts │ │ │ │ │ pre-release tag │ ┌─ Release gate │ │ (~2hr, ~$50-500) │ │ ─ multi-run full suite │ │ │ │ ─ benchmark v1.0 │ │ │ │ ─ Layer 1 + 2 + 3 stable │ │ │ └─ blocks release on fail │ └──────────────────────────────────────────────────────────────────┘

阅读这个管道:成本和时间与节奏在正确的方向上成比例。每次提交的阶段几乎免费且快速;发布门控阶段成本不菲,但只在真正发布时触发。这个形态——底部大量廉价快速的阶段,顶部少量昂贵缓慢的阶段——与三层评估架构的倒金字塔形式相同,只是体现在 CI 时间上。

CI 中的第一层:pytest,快速且不妥协

第 3.2 章第 2 步中的第一层测试是普通的 pytest 测试。它们放在普通的 CI 任务中。具体来说:一个 GitHub Actions 任务,在每次推送时运行 pytest tests/layer1/

# .github/workflows/layer1.yml
name: Layer 1 — deterministic checks

on:
  push:
    branches: ['**']
  pull_request:

jobs:
  layer1:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install -e .
      - run: pytest tests/layer1/ --strict-markers -x
      # -x = stop on first failure; failures here should be rare and
      #      always indicate real issues

这是普通基础设施——与任何 Python 项目的测试管道相同。关键点:它在每次推送时运行(不只是 PR),是一个硬门控(测试失败阻止合并),且足够快,工程师在切换上下文前能确保自己拿到了信号。

第二层快速子集:每个 PR 的信号

第二层快速子集让每个 PR 在不付出完整套件成本的情况下获得评估信号。这是评估驱动节律成为常规的地方。

# .github/workflows/layer2-fast.yml
name: Layer 2 — fast subset

on:
  pull_request:

jobs:
  layer2-fast:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      pull-requests: write   # needed to post scoreboard comment
    steps:
      - uses: actions/checkout@v4
      - run: pip install -e .

      # Run fast subset (5-10 queries) and grade
      - name: Run fast eval subset
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python -m evals.run \
            --suite layer2-fast \
            --output-json results/pr-${{ github.event.pull_request.number }}.json

      # Compare to baseline (main's most recent result)
      - name: Compare to baseline
        run: |
          python -m evals.compare \
            --pr results/pr-${{ github.event.pull_request.number }}.json \
            --baseline results/main-latest.json \
            --output scoreboard.md

      # Post the scoreboard as a PR comment
      - name: Post scoreboard to PR
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          path: scoreboard.md

PR 作者看到的输出,作为 PR 评论自动发布:

## 📊 eval-results (fast subset)

vs main (e7b3c20)
**overall: 0.81 → 0.83 (+0.02) ↑ noise**

|  metric                  |  base  |   pr   |  delta  | verdict |
| ------------------------ | ------ | ------ | ------- | ------- |
|  task_completion         | 0.78   | 0.82   | +0.04   | noise   |
|  factual_accuracy        | 0.92   | 0.94   | +0.02   | noise   |
|  trajectory_sensibility  | 0.72   | 0.74   | +0.02   | noise   |

cost: $0.42  ·  runtime: 3m 17s

Want the full suite? Add the `eval-full` label.

这条评论是让评估驱动开发变得真实的每 PR 反馈。作者在合并前就能看到自己的变更是否影响了指标。verdict 列("noise"/"REAL")来自第 3.1 章中测量的噪声基线(noise floor)——任何在测量到的运行间方差 2σ 以内的结果都标记为 noise,超出的标记为 real。这是第 3.1 章传授的纪律,在 CI 中自动化的结果。

一个微妙的决策:这个阶段通常不严格阻止合并。它是信息性的。原因:快速子集上的噪声是真实存在的,对噪声设置阻止会导致误报性合并拒绝。完整套件(使用多次运行平均)才会阻止;快速子集只是告知。

第二层完整套件:按需触发

完整套件成本更高,不应在每个 PR 上运行。两种触发模式:

标签驱动。给 PR 添加 eval-full 标签触发完整套件。作者在变更值得时手动添加标签——提示词重写、模型切换、检索(retrieval)变更、工具修改。机械性变更(重构、注释修复、文档更新)不打标签,不付出成本。

待合并触发。一个检查列表状态("准备好合并审查")自动触发完整套件。这捕捉了作者忘记打标签、但变更实际上有实质内容的情况。

无论哪种方式,完整套件在独立任务中运行,耗时 20—60 分钟,并产生同类型的、包含更多细节的记分板评论。对于候选发布版本的 PR,第 3.1 章的多次运行模式适用——运行完整套件三次并取平均,越过噪声进入真实信号。

对主分支的漂移检测:夜间任务

一个容易被遗忘的 CI 任务:定时运行,用于捕捉与 PR 变更无关的漂移。模型在更新,工具在被供应商修改,检索语料库在变化。这些都不会出现在 PR 触发的评估中,因为代码仓库本身什么都没变。

# .github/workflows/drift-detection.yml
name: Drift detection (nightly)

on:
  schedule:
    - cron: '0 6 * * *'   # 6am UTC daily
  workflow_dispatch:       # allow manual trigger

jobs:
  nightly-full-suite:
    runs-on: ubuntu-latest
    timeout-minutes: 90
    steps:
      - uses: actions/checkout@v4
      - run: pip install -e .

      - name: Run full eval suite on main
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python -m evals.run --suite layer2-full --output results/nightly.json

      - name: Compare to 7-day rolling baseline
        run: python -m evals.drift_check results/nightly.json

      - name: Alert on regression
        if: failure()
        uses: 8398a7/action-slack@v3
        with:
          status: custom
          custom_payload: |
            {
              "text": "🚨 Eval drift detected on main",
              "blocks": [...]
            }

这个任务捕捉 PR 无法发现的问题:影响评估分数的外部变化。告警(Slack、PagerDuty、邮件——任何你的团队使用的方式)在分数出现无明显代码仓库原因的下降时将某人唤醒。常见发现:一个模型快照上线,评分发生了变化;某个 SaaS 供应商改变了 API 响应;OpenAI 的价格发生变化,你的每查询成本(cost-per-query)越过了阈值。这些都是值得了解的真实事件,而 PR-only CI 一个都不会发现。

失败开放 vs 失败关闭:策略决策

每个团队必须回答的一个问题:当评估任务因基础设施原因失败(API 超时、速率限制、评判者模型不可用)时,合并应该被阻止还是继续?

失败关闭:任何评估失败都阻止合并直到解决。最高安全性;最低开发速度。最适合高风险部署(deployment),在那里发布回归比等待代价大得多。

失败开放:基础设施故障(通过错误类型可识别)记录但不阻止合并。实际的评估信号失败(真实分数下降)仍按策略阻止。更高开发速度;依赖基础设施的整体可靠性。

务实的中间路线:对暂时性基础设施错误失败开放(在下一次推送时自动重试),对真实信号回归失败关闭。通过错误类型区分两者——API 返回 503 是基础设施问题;明确的分数下降是信号问题。一次重试模式能处理常见情况:一次单独的 API 故障本应阻止一个不相关的 PR。

CI 中的成本控制

没有成本控制,评估 CI 可能成为一笔相当可观的支出。三种保持可控的模式:

并发限制。每个分支同时只运行一个第二层快速子集。防止有人连续推送 10 次提交、触发 10 次冗余评估运行的情况。GitHub Actions 通过 concurrency: { group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true } 支持这个功能。

跳过纯文档变更的评估。路径过滤器检测 PR 是否只修改了文档/Markdown/README,如果是则完全跳过评估套件。文档修复不应该消耗 10 美元的 API 支出。

预算告警。评估 CI 基础设施应该发布其自身的成本指标:每周 CI 任务的 API 总支出。当超过某个阈值时触发告警。此处的漂移通常表明意外循环(由于有人删除了触发过滤器,某个评估任务开始在每次推送时运行)或实际的范围扩张(团队增加了查询,但没有调整预算)。两种情况都值得了解。

一个具体的陷阱:不要用生产流量使用的 API 密钥来运行第二层评估。两个原因。第一,评估运行可能触及生产速率限制,降低面向用户的延迟(latency)。第二,评估流量与生产流量混在一起,会让 API 支出的归因变得困难。为 CI 使用独立的 API 密钥;独立预算;你的财务和值班工程师都会感谢你。

Question
如果智能体需要真实的 Linux 环境来运行(例如智能体使用 Docker、bash 或计算机操作),该怎么办?

GitHub Actions 的 ubuntu-latest runner 能处理大多数情况——Docker 可用,bash 可用,你可以安装任何所需的工具。对于计算机操作,runner 需要虚拟显示(Xvfb),这是一个标准的 apt 安装。Anthropic 发布了一个处理此类环境的计算机操作参考 Dockerfile。

对于需要更多控制的工作负载(特定 GPU 类型、更多内存、自定义环境),自托管 runner 或向 GitHub 报告结果的云端运行任务是标准模式。评估管道本身保持不变;只是运行时环境移动了。

Question
我的第二层快速子集需要 8 分钟——对"快速"来说太慢了。怎么提速?

三个方向可以看。第一:查询是并行运行的吗?五个查询应该展开,在大约最长那个的时间内完成,而不是总和时间。在你的评估运行器中使用 asyncio.gather;检查它是否真的在并行化。

第二:评估运行器中是否启用了提示词缓存(prompt caching)?缓存命中能显著减少输入处理时间。快速子集中的每个查询共享系统提示词和工具定义;这些应该在第一个查询之后被缓存。

第三:评判者是否使用了正确的模型?细致评分用 Sonnet;二元检查用 Haiku。把所有东西都放在 Sonnet 上,既慢又没必要。

如果以上都做了还是在 8 分钟以上,那是快速子集的查询数量太多了。剪裁到 5 个;每周轮换哪 5 个在快速子集中,以便随时间保持广泛覆盖。

Question
记分板评论应该在每次推送后更新,还是只在每次新运行后更新?

原地更新——这正是"sticky"PR 评论 action 的作用。每次向 PR 推送都触发新的运行;记分板评论被重写以反映最新结果。PR 讨论串不会积累几十条过时的评估评论;只有一条评论,始终是最新状态。

关于历史记录,每次 CI 运行的评估结果本身都保存为 artifact。比较 PR 第一次尝试和第二次尝试(作者做了修改之后)意味着查看工作流运行历史,而非 PR 评论。PR 评论是始终当前的状态;工作流历史是审计记录。

STEP 4

将一切整合:发布流程。

第一层在每次提交时捕捉契约回归。第二层在 PR 上捕捉质量回归。第三层捕捉生产中的漂移。基准测试锚定跨时间的质量对比。CI 自动运行节律。第 4 步关于的是当智能体变更真正要上线时会发生什么——一个将所有这些组合成一个流程的发布过程,让你在周五下午也能从容运行,不出意外。

发布决策流程

一个智能体变更进入生产的流程:

┌──────────────────────────────────────────────────────────────────┐ │ RELEASE PROCESS FOR AN AGENT CHANGE │ │ │ │ 1. PR opened with the change │ │ │ │ │ ▼ │ │ 2. Layer 1 (every push) — contracts intact? │ │ │ fail → fix and re-push │ │ ▼ │ │ 3. Layer 2 fast subset — scoreboard posted to PR │ │ │ noise → fine to proceed │ │ │ real regression → investigate; possibly add `eval-full` │ │ ▼ │ │ 4. Code review (human reviews the actual change) │ │ │ │ │ ▼ │ │ 5. Layer 2 full suite (label-triggered or pre-merge) │ │ │ pass → continue │ │ │ fail real regression → reject; author iterates │ │ ▼ │ │ 6. Merge to main │ │ │ │ │ ▼ │ │ 7. Nightly drift check confirms main remains stable │ │ │ │ │ ▼ │ │ 8. Release candidate built (tag rc-vN) │ │ │ │ │ ▼ │ │ 9. Release-gate evals on the RC: │ │ ─ Layer 2 full suite, run 3× and averaged │ │ ─ Internal benchmark v1.0 (and current) scored │ │ ─ Layer 3 metrics on staging verified stable │ │ │ all pass → ship │ │ │ any fail → block release; investigate │ │ ▼ │ │ 10. Production deploy │ │ │ │ │ ▼ │ │ 11. Layer 3 watch: 24-48hr observation period │ │ │ metrics stable → release confirmed │ │ │ regression appears → roll back, investigate │ └──────────────────────────────────────────────────────────────────┘

阅读这个流程:这与软件业数十年来沿用的发布流程形态相同,只是用评估门控替换了测试门控。纪律是一致的;实质只是从"单元测试是否通过"转移到了"契约是否成立、质量是否达标、生产环境是否正常运行"。

预发布检查清单

在候选发布版本获准发布之前,一份值得明确记录的检查清单:

  • 第一层在发布提交上通过 ✓
  • 第二层完整套件通过,整体得分在基线 2σ 噪声基线以内(或有所改善)✓
  • 第二层完整套件已运行 3 次并取平均;裁决基于平均得分 ✓
  • 评判者的校准一致性(第 3.3 章)达到最低阈值 ✓
  • 内部基准测试 v1.0 得分已记录且未出现回归 ✓
  • 没有新的第一层测试在未经明确说明的情况下被禁用或跳过 ✓
  • 第三层演练(staging)指标(24 小时以上的金丝雀或演练流量)在正常范围内 ✓
  • 回滚程序已验证,并记录在发布说明中 ✓
  • 值班工程师已确认,并知晓发布窗口 ✓

这是第 2.4 章所涵盖的生产就绪评审的发布检查清单版本。它作为书面产物存在于发布 PR 或发布追踪文档中,由非变更作者的人签字确认。需要人工签字的原因:这迫使人们对每个门控做出明确的确认,从而发现有人因赶时间而遗漏某个步骤的情况。

回滚作为一等公民操作

任何值得运行的发布流程都包含快速撤销的能力。对于智能体,回滚比"重新部署前一个版本"更加复杂——因为变更可能是提示词编辑、模型切换、工具配置变更,或以上所有的组合。

纪律:

对一切进行版本控制。提示词纳入版本控制。模型 ID 固定(不是 claude-sonnet-4-5,而是 claude-sonnet-4-5-20250929)。工具定义纳入版本控制。技能纳入版本控制。"回滚"就意味着撤销特定提交;重新部署是机械性的。

对提示词和配置使用功能开关。较大或较高风险的变更在开关后面上线。新提示词在生产代码中,但只在开关打开时生效。回滚就是翻转开关,而非重新部署——耗时秒级,而非分钟级。

为每次发布记录回滚路径。每个已发布变更的发布说明都包含该变更的具体回滚程序。"如果需要回滚,在 LaunchDarkly 中将 use_new_routing_prompt 开关关闭;旧路由路径立即生效。"具体、可操作、不需要在凌晨两点时苦苦思索。

发布后:确认变更生效

发布在部署时并未结束。24—48 小时观察期是你了解评估是否具有预测性的时候。三个需要关注的信号:

第三层指标保持在正常范围内。错误率、延迟、每查询成本、点踩率(thumbs-down rate)。这些都不应该偏移出正常的日常方差之外。出现偏移是你的评估漏掉了什么的信号。

采样评判者得分与预期吻合。如果第三层包含 1% 采样评判模式(第 3.2 章),发布后窗口的得分应该与第二层预测的吻合。差距是评估与生产漂移的信号。

用户衍生信号保持健康。如果你有留存率、重试率或完成率指标,它们应该保持稳定。任何这些指标出现回归——尤其是花了 24 小时才浮现的——是评估集错过了真实失败模式的信号。

任何一项出现异常都会触发调查。修复并不总意味着回滚;有时意味着向前推进一个热修复版本(一个解决具体问题的后续 PR)。但它始终意味着理解你的评估漏掉了什么——并在第二层中添加一个用例,这样同样的漏洞下次就无法溜过去。这就是第 3.2 章在发布时应用的第三层→第二层反馈循环。

WORKED EXAMPLE

一次发布完整走过整个流程。

用一个案例来锚定本章的一切:一次真实形态的智能体变更发布,逐关卡走完。智能体是一个研究助手(第 4.3 章形态);变更是对主编排者的模型切换。这次发布完整呈现了各门控如何组合。

变更内容

团队正在测试能否为主研究者编排者使用 Sonnet 4.5 替代 Opus 4.7。原因:Opus 的成本是 5 倍;如果 Sonnet 能胜任编排,每次研究运行的成本(cost)将从约 3 美元降至约 1 美元,这在单位经济学上有实质意义。

PR 描述:"尝试将 Sonnet 4.5 用于主编排者。质量门槛:trajectory_sensibility 在基线 2 分以内;comprehensiveness 不出现回归。"

门控 1 — PR 推送时的第一层

第一层在 18 秒内运行完毕。所有 47 个确定性检查均通过——变更是配置中模型 ID 的替换;没有模式(schema)、契约或工具定义发生改变。✓

门控 2 — 第二层快速子集

5 个查询的快速子集在 4 分钟内运行完毕。记分板发布到 PR:

## 📊 eval-results (fast subset)

vs main (a4e8f12)
**overall: 0.834 → 0.812 (-0.022) ↓ noise**

|  metric                  |  base  |   pr   |  delta  | verdict |
| ------------------------ | ------ | ------ | ------- | ------- |
|  task_completion         | 0.92   | 0.90   | -0.02   | noise   |
|  factual_accuracy        | 0.94   | 0.95   | +0.01   | noise   |
|  trajectory_sensibility  | 0.78   | 0.72   | -0.06   | noise?  |
|  citation_faithfulness   | 0.91   | 0.91   |  0.00   |   ✓     |

cost: $0.52  ·  runtime: 4m 02s

⚠ trajectory_sensibility delta is large but only 5 queries.
   Run the full suite to be sure.

Want the full suite? Add the `eval-full` label.

trajectory_sensibility 的下降幅度大于噪声带通常允许的范围,但只有 5 个查询时方差很高。系统将此标记为"需要完整套件确认"。作者添加了 eval-full 标签。

门控 3 — 第二层完整套件

50 个查询的完整套件运行了 38 分钟。成本:14 美元。记分板:

## 📊 eval-results (full suite)

vs main (a4e8f12)
**overall: 0.834 → 0.798 (-0.036) ↓ REAL**

|  metric                  |  base  |   pr   |  delta  | verdict |
| ------------------------ | ------ | ------ | ------- | ------- |
|  task_completion         | 0.900  | 0.880  | -0.020  | noise   |
|  factual_accuracy        | 0.940  | 0.940  |  0.000  |   ✓     |
|  trajectory_sensibility  | 0.780  | 0.660  | -0.120  | ✓ REAL  |
|  citation_faithfulness   | 0.910  | 0.910  |  0.000  |   ✓     |
|  comprehensiveness       | 0.820  | 0.810  | -0.010  | noise   |

cost: $14.21  ·  runtime: 38m

⚠ trajectory_sensibility regression confirmed.
   Sonnet plan quality is materially worse on complex queries.
   See failing examples in eval-results/pr-491/trajectory-fails.json

trajectory 的下降是真实且经过确认的。团队调查了失败案例。规律是:在复杂查询上,Sonnet 的计划包含更少的子问题,且子问题不够精确。Opus 生成 8 个子问题;Sonnet 生成 4 个。由于底层调研更浅,最终的综合结果也不完整。

PR 未合并。团队有两个选项:接受更差的 trajectory 换取成本节省,或寻找另一条降低成本的路径。他们选择了第二条——探索将 Sonnet 用于综合步骤(Sonnet 胜任良好),同时保留 Opus 用于规划步骤(这里至关重要)。那是另一个 PR 的事。

当前 PR 以一条记录在案的学习笔记关闭:"Sonnet 4.5 不适合此智能体形态的编排者规划;trajectory_sensibility 下降 12 分。保留 Opus 用于编排者;替代降本路径正在探索中。"这条学习被记录在团队的评估笔记中。

这个案例揭示的规律

三个值得明确指出的观察:

门控发挥了作用。一个本可将成本降低 60% 的变更被测试、发现会造成质量回归,并在任何用户看到这个变更之前被拒绝。运行评估的成本(完整套件 14 美元)只是将回归发布到生产、一周后通过第三层发现的成本的一小部分。

快速子集方向正确,但需要确认。5 个查询的快速子集将 trajectory 的下降标记为一个隐患,但无法仅凭 5 个查询加以确认。升级到完整套件确认了这个信号。这正是预期中的关系——快速子集用于筛查,完整套件用于裁决。

学习被记录下来了。"不要在这个智能体上将 Sonnet 用于编排者"是团队通过代价高昂的试验发现、并在六个月后新工程师尝试同样做法时会忘掉的那类知识。记录在案的学习笔记将其保存下来。未来尝试类似变更的 PR 能看到历史背景,而不是重复同一个实验。

发布流程存在的意义是让拒绝代价低廉

整个门控系统正确的框架是:它的工作是让那些会造成质量回归的变更被低成本地拒绝。没有这些门控的团队同样会拒绝糟糕的变更,只不过那发生在变更已上线、破坏了某些东西、需要调查、回滚和恢复之后。有了门控,拒绝发生在 PR 阶段,评估花费 14 美元,并产生能防止日后犯同样错误的持久学习。门控比没有门控要慢;但远比在生产中学同样的教训要快。

End of chapter 3.4

交付物

对公开基准测试的深入理解(它们擅长什么、应打多少折扣、如何诚实地解读),对内部基准测试的掌握(何时构建、冻结子集模式、治理机制),以及智能体评估 CI 基础设施(将三层架构转化为每 PR 自动反馈的管道形态)。一个将所有门控组合成可放心运行的流程的发布过程。熟悉 2026 年特定的基准测试全景(SWE-bench、GAIA、WebArena、OSWorld、τ-bench、AgentBench、METR HCAST)及其应纳入考量的已记录局限性。完成本章,第三部分"评估"即告完整:你拥有了使一切成真所需的纪律(3.1)、架构(3.2)、评分机制(3.3)和运行时基础(3.4)。

  • 以适当的审慎态度解读公开基准测试分数(减去 10 分,将可靠性声明除以 1.3)
  • 基准测试分数仅用于能力校准,从不用作质量目标
  • 每季度与公开基准测试(SWE-bench / GAIA / 同类)进行跨检,用于能力追踪
  • 如果发布决策门控需要,构建内部基准测试冻结子集
  • 基准测试治理:版本提升附有原因,从不为让版本通过而进行修改
  • 第一层 CI 在每次推送时运行,在失败时阻止合并,耗时 60 秒以内
  • 第二层快速子集在每个 PR 上运行,发布包含与基线增量(delta)对比的固定记分板评论
  • 第二层完整套件在打标签或预合并 PR 上运行;发布门控要求 3 次运行取平均
  • 主分支上的夜间漂移检测,并向值班人员发送告警
  • 预发布检查清单已记录,由非变更作者签字确认
  • 每次发布记录回滚路径;提示词/配置变更使用功能开关
  • 发布后 24—48 小时观察期;如有任何遗漏,第三层信号回流至第二层