返回博客列表

别用想象的 case 测模型:OpenRouter 从生产流量构建黄金评测集指南翻译

2026-10-01T14:30:00+08:00
OpenRouterEval黄金评测集回归测试Agent评测CI/CD生产流量

别用想象的 case 测模型:OpenRouter 从生产流量构建黄金评测集指南翻译

合成样本带着某人想象的失败,生产样本带着用户真实踩到的失败——这是评测集的第一性差异。

OpenRouter 这组教程的第三篇(前两篇我们已翻译:回归测试、工具调用准确性),回到了整个评测体系的源头问题:你的测试 case 从哪来?

教程开篇的场景每个做 Agent 的人都熟悉:你更新了一行 prompt,或者提供商在同一个模型 ID 下滚了个新 checkpoint,然后生产环境某些东西悄悄退化——最近一周的用户投诉看起来略有不同,但你的 CI 全绿。MMLU 这类通用基准抓不到这种问题,它测的是模型解学术题的能力,不是模型应对你的产品流量的能力。

黄金评测集(golden eval dataset)就是补这个缺口的:一组精选的生产输入,配上经过人工复核的预期输出,在 Git 里版本化,每次部署前运行。 这篇文章翻译它的完整五步流程。

本文提纲

  1. 核心摘要(Tl;dr)
  2. 黄金评测集是什么、不是什么
  3. 为什么生产数据优于合成数据
  4. 五步构建流程
  5. 跨模型基准测试
  6. 翻译后的几点解读

核心摘要

五条关键规则:

  • 黄金评测集 = 精选的生产输入 + 复核过的预期输出,作为每次有意义变更前的回归测试。
  • 集合建好后,通过一个 API 跨多个模型运行,用你自己流量上的证据选下一个模型——不是 MMLU,不是别人的排行榜。
  • 规模匹配用途:探索单个问题约 10 条;完整回归集 100 到 1,000 条。正确规模取决于指标、方差和你能察觉的最小差异。
  • 失败模式的覆盖比数量重要。生产样本携带用户真实踩到的失败;合成样本携带某人想象的失败。
  • 数据集、评分规则(rubric)和基线一起版本化——否则你分不清一次回归到底来自模型变更、prompt 变更还是数据集变更。

黄金评测集是什么、不是什么

黄金评测集是一批生产样本加上复核过的预期输出,从训练中隔离出来,在每个 release 候选上运行。有领域知识的人复核过每一条——这个复核正是集合有用的原因:没有它,当分数下降时,你分不清是变更让质量退化,还是第 23 条的标签本身是错的。

把黄金集当成行为的回归测试套件而不是代码的:它在 CI 里跑,有已知良好的预期输出,捕捉部署之间的漂移。

三个明确的"不是":

  • 不是基准(benchmark):基准测通用能力,黄金集测你的流量上的能力。
  • 不是训练集:训练集教模型,黄金集测模型——两者必须严格隔离。
  • 不是 A/B 测试:A/B 测的是新版本与旧版本,黄金集测的是任何变更前后的行为保持。

为什么生产数据优于合成数据

合成 eval 测的是"模型能不能回答某人想象的用户可能问的问题";你需要测的是"你的用户真实问过的问题,用他们的原话"。生产数据在三个维度上碾压:

一、分布是对的。 如果 70% 的流量在问定价,那 70% 的评测集就应该在问定价。从想象的边缘 case 里抽的合成集不会保持这个分布。

二、失败模式是你关心的那些。 用户会找到你想不到写进测试的失败方式:一条混着三种语言的消息、一个贴了整段错误日志的工单、一个引用了竞品产品功能的查询。教程的原话很扎心——"合成样本带着某人想象的失败,生产样本带着用户真实踩到的失败"。

三、样本跟着产品演进。 一个半年前只会处理密码重置的客服 bot,现在收到的是多账户问题、退款升级、竞品 UI 截图。从旧流量建的黄金集会持续过期——这也是它需要定期刷新的原因。

合成数据的正确位置是补充而非基础:用它填充已知失败模式里真实样本太少的覆盖缺口(比如用结构化输出生成、或对现有条目做转述),在元数据里标记 synthetic: true,并保持它们是集合的少数。

五步构建流程

Step 1:拉生产流量样本

从一两周的日志输入输出开始(功能有季节性或使用稀疏就拉更长窗口)。先随机采样,再看抽出来的是什么——如果流量有长尾,随机样本会过度代表头部,这通常正是你想要的,因为回归更多发生在头部。

记录所有以后可能想切分的字段:意图、功能区域、用户段、时间戳,以及产生响应的模型和 prompt 版本。

采样生产流量就是采样用户数据——检查你的服务条款,在数据进入评测 harness 之前跑 PII 清洗管道,并记录哪些样本做过处理。进入 Step 2 的原始池目标几百条——后面会大幅修剪。

Step 2:去重与聚类

生产流量高度重复。客服 bot 可能一天看到一百次"怎么重置密码",措辞各异——你只需要其中之一。

精确匹配去重抓不住转述。要抓转述,检查数据集是否已覆盖同一意图:对归一化后的输入做精确匹配,或用 embedding 相似度比较输入。

去重后按覆盖度采样:如果一半流量是某个意图,这个意图应该占黄金集大约一半——但不能占满,未覆盖意图的传播不足正是这套流程要解决的。

目标规模参考 Langfuse 的指引(数字是参考点,按自己的流量调整):

用途 典型规模
探索单个问题 约 10 条
测模型能力边界 约 10 条复杂未解样本
大变更的 CI 检查 100-1,000 条,覆盖生产分布
对抗 prompt 的护栏测试 大且持续增长,case 随发现追加

留一个几十到小几百条的子集给 PR 闸门保持快速,全套留给 release 分支或夜间运行。

Step 3:添加预期输出

每条输入都需要有资格的人写下"正确输出长什么样"。有时是一个字符串,更多时候是 rubric(评分规则):哪些事实必须出现、哪些可以缺席。

两个让评分更一致的选择:

  • 二元判据:每个判据要么 MET 要么 UNMET。
  • 分析式量规:每个判据单独打分,而不是整体一刀切。

让两个人独立标注一个子集。哪里有分歧,把 rubric 当成问题,而不是标注者的问题——修 rubric,直到两个评审对同一模型输出能达成一致。

并非每条都需要硬编码预期输出:只用无参考 evaluator 检查的条目(格式合法性、安全、语气)根本不需要预期输出。黄金集是输入、预期输出和评分规则的组合,三者按需出现。

Step 4:跑首次评估,修 rubric

在信任这套集合之前,用你当前的生产模型跑一遍,看每一个失败。把失败分成三类:

  • 真实失败:模型确实错了、预期输出是对的——保留。
  • rubric 问题:模型给了个合理回答但 rubric 没算及格——改 rubric。
  • 病态 case:两头都不对——删。

这一轮会砍掉一部分集合,砍多少取决于 Step 3 写得多仔细。修剪本身就是这个 pass 的意义——一套产生一堆可疑失败的黄金集,比没有黄金集更糟糕。

别跳过这一步:这里没抓住的 rubric 问题,会在真实部署时以"假回归"的面目再次出现。

Step 5:提交 Git、接 CI、持续迭代

黄金集属于你的代码库,与 prompt 和运行它的代码一起版本化——这是把"随意的质量检查"变成"回归测试"的关键。

最小目录布局:

/evals
  /golden
    dataset.jsonl        # 每行一个样本
    rubric.md            # 怎么评分
    run.ts               # 加载器与 harness
    baseline.json        # 当前生产模型的通过率
  /synthetic
    dataset.jsonl        # 稀有失败模式,标记 synthetic: true

每条样本是 JSONL 的一行:

{
  "id": "pw-reset-locked-account",
  "input": "i cant log in, tried resetting three times and its saying account locked. help",
  "expected": "The bot should acknowledge the lockout, ask for the account email, and route to the account-recovery flow. It must not offer to reset the password directly.",
  "rubric": "must_ask_email;must_route_recovery;must_not_reset_directly",
  "tags": ["password_reset", "edge_case"],
  "synthetic": false
}

每个字段服务不同的读者:id 给每条稳定引用(在 Git diff 里存活);input 是清洗后的生产原文;expected 是给 LLM judge 读的人类可读描述;rubric 是 judge 打分用的机器可检查判据;tags 让你按意图切分通过率;synthetic 让真实与合成样本在汇总指标里分开。

CI 接线:prompt 变更或模型切换时运行 harness,通过率跌破定义阈值就 fail build,某条要在失败状态下合并需要书面确认。一个在每个 PR 上调用 harness 的 GitHub Actions workflow 就够起步。

刷新节奏:每季度复查超过六个月的条目,逐条对照当前产品行为;产品变化快就每月。rubric 与数据一起版本化——失败 run 只有能诊断才有用,而 rubric 变更是事后最难抓的变更。

最重要的一条:别因为一条 case 一直通过就退役它。持续通过的 case 在证明行为仍然成立。只有当它测试的行为在产品里不复存在时才退役。

跨模型基准测试

有了 100 条带预期输出的样本,把同一套集合跑在不同模型上,测出的是该模型在你的工作负载上的表现——不是 MMLU,不是别人的排行榜。

跨厂商比较模型通常意味着不同的 SDK、认证、响应形状和限流。通过 OpenRouter,同一个 OpenAI 兼容请求体适用于整个模型目录:对共享接口且支持你所用参数的候选,改一行 model: "openai/gpt-5.1" 为 model: "anthropic/claude-fable-5.1" 重跑 harness 即可。模型在上下文长度、工具支持或支持参数上有差异时,每个候选会需要一些集成工作——models 端点列出了每个模型的上下文长度和 supported_parameters 数组,切换前先查。

教程给了一个最小 harness(TypeScript):读 JSONL、循环三个模型(GPT-5.1 / Claude Fable 5.1 / Gemini 3.8 Flash)、逐条打分、汇总 pass/total。

翻译后的几点解读

这是三部曲里我最推荐先读的一篇。 回归测试和工具调用准确性讲的是"怎么测",这篇讲的是"测什么"——case 从哪来、预期怎么写、rubric 怎么修。测什么错了,前面那些方法再精密都是空转。

"合成样本带着某人想象的失败"这句话值得印在评测文档第一页。 我们拆过的 Microsoft ai-agent-evals、DeepEval 都默认你有评测集,这篇回答了评测集从零到一的来源问题。实践中大多数团队的评测集确实是产品经理拍脑袋写的干净 case——难怪测着什么都能过。

"分歧时改 rubric 而不是怪标注者"是最容易被违反的原则。 两个评审对同一条打分不一致,直觉反应是"再培训一下标注者",实际上几乎总是 rubric 本身语义模糊。把这个原则写进流程,评测集的质量会完全不同。

"别退役一直通过的 case"是对回归测试本质的最好诠释。 每条 pass 的 case 都是"行为仍然成立"的活证据——删掉它等于拆掉一个探测器。这与传统软件"清理过时单测"的直觉相反,因为 LLM 的行为会静默漂移,而你的探测器是这个漂移环境下少数的稳定参照物。

三部曲连读的价值:黄金评测集(这篇)解决"测什么",工具调用准确性(上篇)解决"怎么断言",回归测试(前篇)解决"什么时候跑"。三篇加起来,加上 DeepEval/微软 ai-agent-evals 这样的框架,Agent 评测的完整工程体系已经齐了——现在唯一缺的是你动手建第一版 case 集。

参考链接

你的评测集是拍脑袋写的,还是从生产流量建的?上次模型切换你用了几条 case 验证?评论区聊聊,觉得有用点个赞让更多做 Agent 的人看到。


作者: itech001 来源: 公众号:AI人工智能时代(the-ai-era) 网站: https://www.theaiera.top/ 关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top

本文首发于 AI人工智能时代,转载请注明出处。

分享给朋友