评测驱动开发与 CI 中的回归评测

11 分钟读完

E5
深入解析 · 评估智能体

评测只有在"把关"时才配得上它的成本——一次回归就是一次红色构建,黄金集是带版本的源码,而 CI 门禁是对固定基线的配对显著性检验,绝不是对某个含噪单值的阈值。

一套你在发布前手动跑一遍的评测,到了下一周就什么都不告诉你了——那时某次静默的供应商更新、或一次被换掉的 retriever,悄悄让智能体回归,而没有任何一项基础设施指标发生变化。能抓住这种情况的纪律叫评测驱动开发(EDD):把评测当作测试、把黄金数据集当作带版本的源码、把回归当作构建失败。但智能体是非确定性的,所以单次运行的通过/失败只是噪声——门禁必须是对多份样本做的配对显著性检验,成本与延迟必须是一等预算,而且它在部署后还要继续在线上跑,因为你脚下的模型会漂移。这篇讲的就是"怎么接线"。

STEP 1

把评测当测试:活的黄金集。

评测驱动开发(EDD)的承重动作是:一份评测只有在"把关"时才配得上它的成本。没人会据以拦下部署的仪表盘只是装饰;一份像单元测试把关代码那样接进 CI 的评测,才是控制点。它的机制和一套测试套件是同样的三句话——把评测当测试、把评测数据集当带版本控制的源码、把回归当构建失败——只需针对单元测试无需操心的两件事做调整:成本(你不可能在每次敲键时都跑上万条打分用例)和噪声(单次运行会因与本次改动无关的原因而抖动)。这个框架在实务文献里被反复印证,从 Braintrust 关于发布把关的文章,到 arXiv 2411.13768《Evaluation-Driven Development and Operations of LLM applications》。evals-101 入门是这一切的地基。

负责把关的那份数据集就是黄金集(golden set):经人工标注、精挑细选的"输入→预期"配对,作为一条稳定基线,在模型、prompt 与供应商变化之间保持固定。它同时也是一件活的产物——纪律在于持续从生产中捞出"有意思的"和"失败的"轨迹,走一遍人工审核,再把校验过的那些加回去。新用例最好的来源不是合成灌水,而是你自己的生产失败。从你本就在收集的信号里去挖:

  • 点踩与用户显式纠正;
  • 被放弃或被重启的会话;
  • 升级给人工客服的场景;
  • 低置信度或自我标红的输出。

为每一个 bug 加一条用例。回归测试的类比是精确的:当一个失败被发现并修复,修复并没有算完——除非有一条能复现它的用例住进黄金集。这是唯一能保证"同一个失败永远无法悄无声息地卷土重来"的机制——记住它的,是评测这个制度,而不是你的记性。

这里有一处真实的张力:一份每周都在长大的数据集,无法同时充当稳定基线,因为对着移动靶算出的分数跨时间不可比。你要用每套严肃测试语料都用的办法来化解它——把黄金集严格地版本化(是 golden-v14,而不是"那份黄金集"),并把每次比较所跑的确切版本钉死。相对公开基准,一份私有的、带版本的黄金集还有第二重优势,而这正是基准全景一文用整篇篇幅讲的:它不会泄进训练语料而污染分数。你的生产失败是你自己的。

STEP 2

非确定性与配对显著性门禁。

把智能体 CI 与代码 CI 区分开的那个难题是:智能体及其底下的模型会随每次运行而变,所以单次运行的通过/失败,是一枚被你误当成测量的抛硬币。第一重防御是采样:每条用例跑 k 次试验,对分布而非对单点做推断。对这份分布有两种概括是要紧的,它们回答不同的问题。pass@k——k 次尝试里至少一次成功的概率——是能力上限:它告诉你智能体能不能做成这件事。pass^k("pass hat k")——k 次尝试全部成功的概率——是可靠性,也是生产真正在乎的那个数。它出自 τ-bench(Yao 等),以 c 次成功、n 次试验估计为 E[ C(c,k)/C(n,k) ]。两者的落差残酷而值得内化:在 τ-bench 上,一个智能体平均能上 60%(一个 pass@1 味道的数),而它的 pass^8 却跌破 25%——即便平均能力看着还行,可靠性也会在重复之下坍塌。这与 HAL 用它的一致性维度揭示的是同一个属性;而reading-benchmarks 入门正是内化"为何 pass@k 与 pass^k 不可互换"的地方。

采样修好的是测量,还不构成门禁。对单个候选的含噪分数设阈值会抖动——同一份代码,有的构建过、有的构建挂。站得住的门禁是配对且统计的:让候选与一条固定基线在同一批用例、同一批种子上重跑一遍,只在"统计显著"的变化上让构建失败,而不是在运行间波动带以内的变化上。配对正是它又便宜又锐利的原因——因为两个变体看到完全相同的用例,用例难度带来的方差相互抵消,剩下的就是这次改动真正造成的那个差值。说一次,钉到墙上:CI 门禁是显著性检验,不是对某个含噪单值的阈值。对分级(非二元)输出,用容差带而非精确匹配来比较。

eval-ci · paired regression gate — candidate vs pinned baseline
----------------------------------------------------------------
dataset: golden-v14 (218 cases)   samples/case: k=5   n=1090 runs
baseline: prod@2026-06 (pinned)   candidate: pr-3921

metric             baseline   candidate    delta
task_completion     0.842      0.851       +0.009
tool_correctness    0.910      0.888       -0.022
pass^5              0.612      0.549       -0.063
p95_latency_ms      4120       4890        +770
cost_per_task_usd   0.031      0.034       +0.003

paired bootstrap (10k resamples), same cases, same seeds:
  task_completion   +0.009   95% CI [-0.011, +0.028]   n.s.   -> ok
  pass^5            -0.063   95% CI [-0.101, -0.026]   p=0.004
----------------------------------------------------------------
GATE: FAIL — pass^5 regressed (significant), not one noisy run
      cost/latency within budget; task_completion delta is n.s.

从上往下读这份门禁。头条的 task_completion 了九千分之——而配对 bootstrap 说这个差值并不显著(它的置信区间跨过零),所以它并不证明任何事。构建挂在 pass^5 上:可靠性掉了 6.3 分,其区间整体落在零以下(p=0.004)。这恰恰是"对平均分设单次运行阈值"会挥手放行的那种失败——平均能力守住了,一致性却裂了。成本与延迟动了,但仍在预算之内,所以它们被报告、而非被把关。一个含噪单值本会把这次回归发出去;配对检验抓住了它。

STEP 3

把它接进 CI:promptfoo 与 DeepEval。

每次 PR 与每次 push 都跑评测;用上面那道显著性检验给部署把关。有两件工具的纯 CI 叙事最强。promptfoo 是这一领域里最 CI 原生的:声明式的 YAML 配置、assert 风格的检查,以及一个官方 GitHub Action(promptfoo/promptfoo-action),它在 pull request 上做评测,并把通过/失败的汇总评论直接贴回 PR,还内建红队。整道门禁就是一份你像审代码那样审的文件。

# promptfooconfig.yaml — declarative, assert-style; runs on every PR
prompts: [file://prompts/agent.txt]
providers: [openai:gpt-5.1, anthropic:claude-opus-4-8]
tests: file://golden/*.yaml        # the golden set — source-controlled
defaultTest:
  assert:
    - type: llm-rubric
      value: resolves the ticket, invents no refund policy
    - type: latency
      threshold: 6000            # ms — a gate-able budget, not an afterthought
    - type: cost
      threshold: 0.04            # USD/task

# .github/workflows/evals.yml — the official action gates the PR
steps:
  - uses: promptfoo/promptfoo-action@v1
    with:
      config: promptfooconfig.yaml
      # evaluates the diff, posts a pass/fail summary comment

DeepEvalconfident-ai/deepeval)走的是另一条 CI 原生路线:它长得像 pytest。你对用例写 assert_test()、跑 deepeval test run,于是评测在 CI 里就以你本地所用的同一条命令执行——不必再维护第二套要保持同步的框架。它自带 40 多项指标(G-Eval、任务完成、工具使用、答案相关性、幻觉、对话、RAG),并与 Confident AI 平台集成以做存储与仪表盘。

# test_agent.py — pytest-native; `deepeval test run` executes it in CI
from deepeval import assert_test
from deepeval.metrics import TaskCompletionMetric, ToolCorrectnessMetric

def test_refund_flow(case):
    metrics = [TaskCompletionMetric(threshold=0.8),
               ToolCorrectnessMetric()]
    assert_test(case, metrics)          # identical call locally and in CI

有两处调整能让它既不把你拖破产、也不抖动。给采样网格做预算:每次 PR 只在一份有代表性的子集上跑一个小 k,换取快速反馈,把"整份黄金集上的完整 n 次采样"留给夜间或发布前的任务。以及,输出是分级的地方就让断言保持分级——用一条 rubric 或容差带,而非字符串相等——因为对路径或改写做精确匹配,会误伤本来正确的智能体,这个陷阱由轨迹与过程一文完整拆解。

STEP 4

线下、线上、金丝雀,以及那些预算。

线下与线上评测不是对手;成熟的 2026 团队两者都跑。线下是部署前:对黄金集的批量运行,用逐用例的细颗粒度指标给发布把关,好让问题在任何用户碰到它之前就被抓住。线上是生产监控:它加进了你在沙盒里造不出来的那些坐标轴——真实延迟、吞吐、成本与用户反馈——并在真实流量上把回归实时浮出水面,那是线下集从未预料到的输入。前者拦住坏发布;后者是唯一能抓住"其糟糕只在真实输入面前才现形"的那类发布的东西。

护栏(Guardrails)是带内运行的线上评测:在答案抵达用户之前拦截或转移的"响应前打分器"——毒性、偏见、PII、幻觉检查,作为一道拦截步坐在智能体循环里(它们摆在哪里,见 the-agent-loop)。金丝雀(canary)是治理放量的线上评测:把一小撮流量导给新版本,只在这些会话上跑线上评测,与对照组比较,仅当质量守住或改善时才放量到 100%——一旦回归触发就即刻自动回滚。它就是 STEP 2 里那道显著性门禁,从 CI 搬到了活流量上。

当你真要比较两个版本时——一次 A/B 或一次金丝雀对对照——优先用配对(pairwise,"竞技场"式)评判,而非绝对打分。把两份输出一起递给评判器、问它哪个更好,会把它锚在这次比较上,从而绕开"绝对打分器的内部刻度在数周里游走"的漂移。已知的失败模式是位置偏置,所以每个配对裁决都要用位置互换一致性检查来兜底;评判器校准一文是让评判器保持诚实的完整协议。

成本与延迟是可设门槛的坐标轴,不是事后补的。到 2026 年,主流框架都把成本作为与正确性并列的一等信号发出——每任务的美元成本、p50/p95 延迟、token 消耗。把它们作为预算接进门禁:一个在同等质量下慢 3 倍或贵 2 倍的智能体,应当让 CI 失败,正如一次正确性回归那样。HAL 在基准层用它的每解一题的成本做过同样的主张。

STEP 5

漂移检测与工具全景。

线上评测之所以不容商量,原因是静默模型漂移。供应商几乎不打招呼就更新托管模型——2024 年 11 月的快照不是 2025 年 3 月的快照——而质量的变化可能落在某一片请求类别上,同时每一项基础设施指标都纹丝不动:延迟平、错误率平、质量却悄悄变差。静态的上线前测试无法抓住一个上线后才到来的变化,所以唯一的防御是上线后监控,加上对黄金集的定期重跑。这里的陷阱是以为"钉死一个模型版本号"就买来了稳定;它没有,因为名字背后的权重会在你脚下移动。像安排依赖更新那样安排重定基线。依赖漂移是往外一层的同一威胁:被换掉的工具、API、retriever 或上游 prompt,能在不碰智能体自身代码的情况下让它回归,所以只要有任何依赖变化,就重定基线。

2026 年的工具已经沉淀出可辨认的几条赛道——按你的门禁落在哪里来选:

  • promptfoo —— CLI 加 GitHub Action;最强的纯 CI 叙事,含红队。
  • DeepEval / Confident AI —— pytest 原生、本地与 CI 一致;40+ 指标。
  • LangSmith —— 实验、数据集、评估器模板、在线评测。
  • Braintrust —— 实验、autoevals、在线打分,以及明确的发布把关框架。
  • Arize Phoenix —— 开源、OTel 原生、可自托管。
  • Inspect / Inspect AI(UK AISI)—— Task = Dataset + Solver + Scorer,带沙盒;被 METR 与 Apollo Research 采用——面向安全与评测实验室的选项。
  • Weights & Biases Weave —— 可观测性加评测加 Guardrails。

把 OpenAI Evals 的消息读准。被弃用的是那个托管产品 / UI——约 2026 年 6 月宣布,2026 年 10 月 31 日起只读,2026 年 11 月 30 日关停。那等同于开源的 openai/evals 仓库与 Evals API graders,后者是另一套产品,仍然保留。"托管的 Evals 仪表盘正在落幕"是真的;"OpenAI Evals 死了"不是——别让这句省略语害你丢掉一个还能用的 grader。

失败模式是成簇的,每一簇都有一个有名字的修法。对单次含噪运行把关,换来的是抖动的 CI——用采样加配对显著性检验。一份没版本的"活"数据集,换来的是跨时间不可比的分数——把基线版本化并钉死。评判器漂移会腐蚀线上与 A/B 评测——改用配对加位置互换检查。把成本与延迟当事后补丁,发出去的是"质量过关却把预算打爆"的智能体——给它们设门槛。而一个钉死的模型版本号会哄你以为稳定,静默的供应商更新却在侵蚀它——安排重定基线。这些都不奇异;它不过是持续集成那套寻常纪律,套用在一个"每次问都可能答得不一样"的系统上。2026 年能发出可靠智能体的团队,就是那些不再用手跑评测、而开始让评测去弄挂构建的团队。