8 分钟读完

3.5
第三部分 · 评估 · 把评测套件做成合入门禁

会跑但不阻断合入的评测套件是假的——分层模式(预提交阶段的廉价打分器、预览阶段的 LLM 评判器、月度节奏的评判器校准)是 2026 年经受住生产考验的纪律。

每个团队都会上评测套件;大多数团队不会用它阻断合入。结果就是一份"没人读的评测报告"——直到一次回归被放进生产。2026 年经受住考验的纪律是分层:便宜的代码打分器(毫秒级)跑在预提交钩子里,LLM 评判器(秒级)跑在预览部署上,评判器校准按月针对一份有版本的人类金标集运行。本章讲运维搭建——哪一层跑什么、怎么配置合入门禁、怎么让金标集不腐化。读完之后你会拥有一条真正会阻断坏合入的评测 CI 流水线,并知道会拆掉它的两种失败模式(评判器漂移与金标集腐化)。

STEP 1

三层。

评估驱动开发一章讲了节律;基准与 CI 一章讲了把节律带过 CI 的运行时基座。本章补上最后一块——把"评测通过"这份报告变成有强制力的合约的合入门禁策略。跑评测但从不阻断的团队有报告没门禁;对任何评测失败都阻断的团队有门禁但没吞吐。2026 年在生产里既不落其中一坑、又能真正走下去的模式是分层的,而分层的划分依据是单次运行成本,不是它测什么。

第一层是预提交层:毫秒级完成的确定性代码打分器。对模型输出跑正则、跑 JSON 模式校验、对生成的代码跑单元测试、对着一份固定的工具规格做契约检查。它们跑在 git 预提交钩子里,也会再跑一次在每次 push 的第一个 CI 任务里。它们始终是阻断性的,而这种阻断没有争议——模式失败不是口味判断。第二层是预览层:LLM 评判器针对一份小而有代表性的切片,在拉取请求的预览部署上运行。这一层耗时秒到分钟、单次运行几分钱到几美元,是真正在合入前把回归拦下来的层。第三层是月度层:针对一份有版本的人类金标集做评判器校准。这一层从不直接阻断某次合入——它阻断的是评判器在下一个月被信任,那是更强的一种门禁。

成本梯度就是全部要义。预提交打分器足够便宜到可以每次按键都跑;预览评判器足够便宜到可以每个 PR 都跑;校准贵到你一个月只跑一次、并把结果当作一份签字过的合约。任何把梯度倒过来的团队——预提交里放 LLM 评判器、月度里放代码打分器——不是花得太多,就是抓得太少。分层之所以存在,是因为评测预算有限,而失败模式是分层的。

STEP 2

预提交:便宜的代码打分器。

预提交层回答一个问题:这次改动是否违反了机器不用问模型就能检查的合约?这里每一个"是"都是硬阻断。检查在本地随 git 钩子跑,也在第一个 CI 任务里再跑一次,好让本地钩子被绕过时也不会悄悄逃出门禁。典型检查:智能体的结构化输出仍能解析为 JSON;每次工具调用都命中已注册的工具名;每条消息都带必要字段;智能体生成的代码能编译;黄金轨迹重放不抛异常。这些检查便宜、确定、乏味——正是三个让它们成为理想阻断门禁的性质。

把可用的预提交层和坏掉的预提交层分开的具体规则是:对一次典型改动必须在一分钟以内跑完。超过这个阈值,工程师会学会绕过它,而被绕过的钩子等于没有钩子。够到预算的办法是激进地缩范围:预提交打分器只对改动过的文件与其直接依赖跑,全量扫描留给 CI。同一任务的 CI 版本再对整棵树跑一遍、不缩范围,并在与本地钩子结论不一致时把 PR 硬打回来——这种不一致本身就是有人绕过了本地检查的信号,需要 CI 兜住钩子漏掉的部分。

具体来说,这一层就是一段带几十行断言的 YAML 任务。它是 CI 里对应类型检查器的位置:不判断你的代码好不好,只判断你的代码是否格式良好。这个定位值得抵御蔓延。每个季度都会有人提议往这一层加一个 LLM 评判器,理由是"也就多几秒"。拒绝。第一层里一旦持有一次网络调用,它的可用性就绑上了某个厂商的可用性,"预提交在本地永远通得过"的合约就坏掉了。

STEP 3

预览:LLM 评判器。

第二层是质量回归真正被抓住的地方。机制形状简单,细节不容马虎:每次拉取请求,预览部署把智能体跑在评测套件的一份固定采样切片上;一个 LLM 评判器沿评分量表的每一维打分;聚合分与主干基线比较;合入门禁在聚合低于阈值、或任何一维回归超过噪声底时阻断。用 LLM 作为智能体评判者这篇运维篇讲了让这一层可信的评分量表设计;合入门禁的意义在于让这份可信是被强制的,而不是愿景的。

三个实现决策决定这一层是真在工作还是在做戏。其一,采样切片必须由种子固定并在仓库里带版本——浮动的切片让基线变得毫无意义。其二,噪声底必须测量出来,不能拍脑袋。同一份切片针对同一份代码跑五次,取聚合的标准差,把阻断阈值定在两倍标准差。更紧就会产生假阻断;更松就会漏掉真回归。其三,阻断必须在分支保护那一层可强制。一个维护者点一下就能覆盖的失败检查会被覆盖,门禁就变成劝导剧场。

逃生阀——对评判器判错的少数正当情形给一个带标签的覆盖——是硬性要求,不是承认软弱。有了它,每一次覆盖都留下纸面痕迹、喂进第三层的校准通道。如果同一个覆盖理由连续三个月出现,就是评判器的评分量表需要修。

STEP 4

按月:评判器校准 + 金标集维护。

第三层是多数团队会略过、多数生产事故最后会追回到的一层。每月一次,评判器模型对着一份有版本的人类金标集跑——几百个手工标注的样例,代表智能体在生产中真正见到的分布。评判器的判决与人类标注比对;一致率低于阈值(85–90% 是多数已发表评分量表的上限,评判器校准与元评测深入解析讲了为什么更高的目标会自我拆台)就把评判器打回提示词工程。评估驱动的智能体开发运维篇讲了校准通道本身的操作手册;本章的贡献是把它命名为合入门禁的依赖,而不是"最好有"。

金标集按加法规则生长:每月加入一小把来自生产轨迹的样例——这些轨迹是由第二层逃生阀覆盖标出来的——并轮换出等量最老的样例。永不轮换的金标集会把评判器针对一个智能体已经不再服务的分布打分;被整批替换的金标集会丢掉与前几个月校准数字的连续性。飞轮是:生产轨迹喂逃生阀、逃生阀案例喂金标集、金标集校准评判器、评判器把关合入、合入塑造轨迹。任何一环断掉,这一层就退化。

STEP 5

失败模式:评判器漂移与金标集腐化。

两种失败模式会拆掉这条流水线,它们同时出现得足够频繁,值得当作一对来命名。评判器漂移是同一句评判器提示词、跑在同一份输入上,随时间产生了不同的分数分布——通常是因为厂商悄悄更新了底层模型。信号是:基线聚合分在几周内无故上下走,而没有相应的代码改动。修法:把评判器模型钉在带日期的快照上,就像你钉生产模型那样,并对任何一次月度校准聚合与上月相比偏离超过噪声底的运行报警。

金标集腐化是更慢的失败。六个月前无歧义的标注,会随着智能体能力变化变得有歧义——原本罕见的"擦边通过"变得常见,评分量表不再把擦边和清楚分开。信号是:如果你在金标集样本上保留两名标注员,标注员间一致率开始下滑;或者评判器与人类判决之间的差距在特定评分维度上变大。修法:某一维出现分歧扩大时,重写该维的评分量表文本,并按新定义重新标注受影响的切片。金标集不是不可变,但对它的每一次改动都必须有日志、有版本、有日期,好让校准历史仍可解读。

# .github/workflows/eval-gate.yml — the three-tier gate
name: eval-gate
on: [pull_request]

jobs:
  tier1-precommit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: make eval-precommit   # regex, schema, unit — ~ms, blocking

  tier2-preview:
    needs: tier1-precommit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: make eval-preview     # LLM judges on fixed slice — ~s, blocking
        env:
          JUDGE_MODEL: claude-opus-4-7-20260201   # pinned; drift guard
          EVAL_SLICE_SEED: 42                     # fixed; baseline guard

  tier3-monthly:
    # separate scheduled workflow — not shown; gates the judge, not the merge
$ gh pr checks 4171
tier1-precommit    pass    0m12s
tier2-preview      FAIL    3m41s
  aggregate: 0.847 → 0.821  (Δ -0.026, noise floor 0.014)
  axis: instruction_following   0.91 → 0.87   (Δ -0.04, blocking)
  axis: tool_selection          0.83 → 0.82   (Δ -0.01, within noise)
  axis: response_quality        0.80 → 0.78   (Δ -0.02, within noise)
verdict: BLOCKED  — instruction_following axis exceeds noise floor
override: use label `eval-override` with justification comment

上面这条轨迹就是一份可工作门禁在 PR 作者一侧看到的样子:某一维越过了噪声底、合入被阻断、覆盖路径是有据可查而不是藏起来的。这就是这份纪律的全部形状——流水线是乏味的,失败是响亮的,逃生阀是可审计的。其他一切都是维护。