跳到正文
原文
OpenRouter:Announcements(RSS)·· 2 小时前精选AI 评分65

OpenRouter 教程:如何从生产流量构建 golden 评测集并跨模型复测

Building a Golden Eval Dataset from Production Traffic

AI 导读

OpenRouter 发布教程,讲解如何从生产流量构建 golden 评测集,作为每次部署前的回归测试。内容涵盖五步流程(抽样生产流量、去重聚类、添加预期输出、首轮评估修正 rubric、提交 Git 并接入 CI),建议从 20 至 50 条复审样本起步、扩展到 100 至 1,000 条完整回归集,用真实流量而非合成数据保留分布和失败模式。

推荐理由

原文给出从生产流量构建 golden 评测集的五步流程和跨模型复测方法,可直接照做。

正文 · AI 翻译

你更新了一个提示词,或者某个提供商在同一个模型 ID 下推出了新的检查点,然后生产环境中的某些东西出现了回退。上周的用户投诉看起来和前一周略有不同。像 MMLU 这样的公开基准测试无法捕捉到这一点。它衡量的是模型在学术科目上的通用能力,而不是模型如何处理你产品的流量。

黄金评估数据集填补了这一空白。它是一个经过精心整理的生产输入集合,配以经过审核的预期输出,在 Git 中进行版本控制,并在每次部署前运行。它回答了基准测试无法回答的问题。这个变更对你所服务的流量是有帮助还是有损害?

本指南涵盖了什么是黄金集、为什么生产数据比合成数据是更好的基础,以及从实时流量构建黄金集的五步流程。它还涵盖了如何通过一个 API 将同一数据集针对多个候选模型运行,这样你就可以根据来自自己流量的证据来选择下一个模型,而不是依据排行榜排名。

简而言之

  • 黄金评估数据集是一组经过精心整理的生产输入,配以经过审核的预期输出,用作每次重要变更前的回归测试。
  • 一旦数据集建立起来,你就可以通过一个 API 将其针对多个模型运行,并根据来自自己流量的证据来选择下一个模型。
  • 让规模与任务相匹配。大约 10 个条目用于探索单个问题,100 到 1,000 个用于完整的回归集。合适的规模取决于指标、方差,以及你需要检测出的最小差异。
  • 对失败模式的覆盖比数量更重要。生产示例承载的是你的用户实际遇到的失败。合成示例承载的是某人想象出来的失败。
  • 将数据集、评分标准和基线一起进行版本控制。否则你无法判断一次回退是来自模型变更、提示词变更,还是数据集变更。

什么是黄金评估数据集

黄金评估数据集是一组带有经过审核的预期输出的生产示例,从训练中留出,并在每个发布候选版本上进行评估。具有领域知识的人确认了每个预期输出,或者判定该条目不需要预期输出,因为通过标准是无参考检查,如格式、安全性或语气。

正是这种审核让数据集变得有用。没有它,当分数下降出现时,你无法判断是变更导致了质量回退,还是第 23 个条目的标签有误。

把它当作行为层面的回归测试套件,而不是代码层面的。它在 CI 中运行,有已知良好的预期输出,并能捕捉部署之间的漂移。与单元测试的区别在于,预期输出是一种判断,因此测试框架必须做的不仅仅是比较字符串。

黄金集不是基准测试、训练集或 A/B 测试。基准测试衡量通用能力。黄金集衡量在你的流量上的能力。训练集教会模型。黄金集在模型回退时捕捉到它。A/B 测试衡量实时用户结果。黄金集衡量一个变更是否足够安全,以至于可以对其运行 A/B 测试。

为什么生产数据是更好的基础

合成评估测试的是模型回答某人想象中用户可能会问的问题的能力。你需要测试的是你的用户实际问过的问题,以及他们使用的措辞。生产流量是更好的基础,原因有三。

分布是正确的。如果你的流量中有 70% 与定价相关,那么你的评估集中也应有 70% 与定价相关。从想象中的边缘案例抽取的合成数据集无法保持这种分布。保持观察到的分布,才能让回归分数反映用户影响。

失败模式正是你所关心的那些。用户会发现的失败模式是你想不到去写的。一条混合三种语言的消息、一张粘贴了整段错误日志的支持工单、一个引用了你上季度重命名的产品功能的查询。除非有人想到这些,否则合成数据集不会包含它们。

示例会跟随你的产品演进。一个六个月前上线、处理密码重置的支持机器人,现在会遇到多账户问题、退款升级以及竞争对手 UI 的截图。从生产数据中抽取的黄金集会跟踪这种漂移。而冻结在上线时的合成数据集不会。

合成示例仍有其位置,但应作为扩展而非基础。用它们来填补你真实示例太少的已知失败模式的覆盖缺口。你可以通过使用结构化输出的模型调用来生成它们,使每个示例都符合你的数据集 schema,或者通过改写现有条目来生成。在元数据中将它们标记为合成,并让它们只占集合的少数。

五步流程

Diagram of the five-step process: pull production traffic, deduplicate and cluster, add expected outputs, run a first evaluation, then commit to Git and wire into CI, with the resulting frozen set used to benchmark across candidate models through one API

第 1 步:抽取生产流量样本

从一到两周的已记录输入和输出开始。如果该功能具有季节性使用量稀少,则使用更长的时间窗口。

先随机抽样,然后看看抽出了什么。如果你的流量有长尾,随机样本会过度代表头部。这通常是你想要的,因为头部的回归会影响最多用户,但要检查稀有且关键的意图是否出现。

记录你以后可能想要切分的每个字段。这包括意图、功能区域、用户分群、时间戳,以及生成你所抽样响应的模型和提示版本。你无法事后重建这些元数据。

抽样生产流量意味着抽样用户数据。检查你的服务条款,在数据到达评估框架之前运行 PII 清洗流水线,并记录你转换了哪些示例,以便日后可以复现清洗过程。

目标是进入第 2 步时有一个几百条示例的原始池。你会大幅删减。

第 2 步:去重和聚类

生产流量是重复的。一个支持聊天机器人可能每天看到同一个“如何重置密码”的问题一百次,措辞略有不同。你希望集合中有一个这样的问题,而不是五十个。

精确匹配去重会漏掉改写。要捕捉它们,检查数据集是否已经覆盖了相同意图,可以通过精确匹配规范化输入,或通过比较输入之间的嵌入相似度来实现。

一旦有了去重后的池,就在其中抽样以覆盖。如果你的流量有一半是某个意图,该意图应获得大约一半的黄金集,但不是全部。一个代表性不足且失败严重的意图,比一个代表性良好但失败温和的意图代价更高,因此要向你无法承受的失败模式倾斜。

关于目标规模,Langfuse 的指南是一个有用的起点。将这些数字视为参考点,并根据你的流量和评估目标进行调整。

目的典型规模
探索单个问题约 10 条
测试模型能力边界约 10 个复杂的未解决示例
对较大变更的 CI 检查覆盖生产分布的 100 到 1,000 个条目
使用对抗性提示进行护栏测试规模大且持续增长,出现新用例就添加

为拉取请求门禁保留几十到一百多个条目的子集,以保持其快速运行,并将完整集合留给发布分支或夜间运行。

第 3 步:添加预期输出

对于每个输入,都需要有资质的人写下正确的输出应该是什么样子。有时那是一个字符串。更多时候是一份评分标准。哪些事实必须出现,哪些主张不得提出,以及需要什么语气或格式。

两种评分标准选择能让评分更一致。使用二元标准,即每个标准要么满足(MET)要么不满足(UNMET)。使用分析式评分标准,即分别对每个标准评分,而不是使用给出一个总体分数的整体式评分标准。分析式评分标准能告诉你什么变差了,而不仅仅是是否有东西变差了。

让两个人独立标注一个子集。在他们意见不一致的地方,把评分标准视为问题所在,而不是标注者。修正评分标准,直到两位评审对同一模型输出给出相同的评分。

并非每个条目都需要硬编码的预期输出。仅由无参考评估器检查的条目,例如格式有效性、安全性或语气,完全不需要预期输出。黄金集可以混合这两种类型。

第 4 步:运行首次评估并修正评分标准

在你信任这个集合之前,用当前的生产模型对它运行评估,并查看每一个失败。

将失败分为三类。真实失败,即模型答错了而预期输出是对的。保留这些。评分标准问题,即模型给出了合理答案,而你的预期输出没有预料到。修正预期输出。任何合理评分标准都无法评分的模糊示例。删掉它们。

预计在这一轮中会删掉一部分集合。删多少取决于你在第 3 步中写预期输出时有多仔细。删减正是这一轮的意义所在。一个无论模型如何变化都产生相同通过率的黄金集,并没有在衡量任何东西。

不要跳过这一步。你在这里没有发现的评分标准问题,会在真实部署期间作为假回归在之后出现。

第 5 步:提交到 Git,接入 CI,迭代

黄金集属于你的代码库,与提示词和运行它的代码一起进行版本控制。这正是将它从临时的质量检查转变为回归测试的关键。

一个最小的目录结构如下所示。

/evals
  /golden
    dataset.jsonl        # one example per line
    rubric.md            # how to grade
    run.ts               # loader and harness
    baseline.json        # pass rates on the current production model
  /synthetic
    dataset.jsonl        # rare failure modes, marked with synthetic: true

将每个示例存储为一行 JSON,包含输入、预期输出、标签和元数据字段。将评分标准存储为评分器读取的人类可读文档,这样任何阅读拉取请求差异的人都能看出评分标准的变化是否就是推动通过率变化的原因。

dataset.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 差异中保持稳定的引用。input 是生产输入,经过清洗后逐字保留。expected 是人类可读的散文,LLM 评判员可以阅读。rubric 保存评判员据以评分的机器可检查标准。tags 让你按意图切分通过率。synthetic 在聚合指标中将真实条目和合成条目分开。

将测试框架接入 CI,在每次提示词变更或模型更换时运行。如果通过率下降超过定义的阈值,则使构建失败,并对任何较小的下降要求书面确认。一个在每次拉取请求时调用你的测试框架的 GitHub Actions 工作流就足以开始。

按固定节奏刷新集合。每季度审查一次超过六个月的条目,对照当前产品行为逐一检查,是一个合理的默认做法。如果产品变化很快,就每月审查一次。过时的黄金集最终将不再能预测生产环境中的行为。

将评分标准与数据一起进行版本管理。一次失败的运行只有在你能够诊断它时才有用,而评分标准的变更是最难事后发现的变更类型。如果有人为了修复看起来像是误报的回归而修改了某条标准,那么这次修改应该作为拉取请求差异中的一处可见变更出现,而不是在六周后表现为一次无法解释的通过率变动。

不要因为条目一直通过就将其淘汰。一个通过的条目正在证明该行为仍然成立。只有当它所测试的行为在产品中已不复存在,或者预期输出现在已错误时,才淘汰该条目。

通过一个 API 进行跨模型基准测试

一旦你有了 100 个带预期输出的示例,将同一集合在不同模型上运行,就能告诉你该模型在你的工作负载上表现如何。不是在 MMLU 上,也不是在别人的编码测试框架上。而是在你的用户提出的问题上。

跨供应商比较模型通常意味着每个候选模型都有不同的 SDK、不同的身份验证、不同的响应结构以及不同的速率限制。通过 OpenRouter,同一个 OpenAI 兼容的请求体可在模型目录中通用。对于共享同一接口并支持你的请求所用参数的候选模型,你可以通过将 model: "openai/gpt-5.1" 改为 model: "anthropic/claude-fable-5.1" 并重新运行测试框架来进行比较。当模型在上下文长度、工具支持或支持的参数上存在差异时,预计每个候选模型都需要一些集成工作。模型端点中的每个条目都列出了该模型的上下文长度和一个 supported_parameters 数组,因此你可以在切换前进行检查。

一个最小化的测试框架看起来是这样的。

import OpenAI from "openai";
import { readFileSync } from "fs";

const client = new OpenAI({
  apiKey: process.env.OPENROUTER_API_KEY ?? "",
  baseURL: "https://openrouter.ai/api/v1",
});

const dataset = readFileSync("./evals/golden/dataset.jsonl", "utf-8")
  .trim()
  .split("\n")
  .map((line) => JSON.parse(line));

const modelsToTest = [
  "openai/gpt-5.1",
  "anthropic/claude-fable-5.1",
  "google/gemini-3.8-flash",
];

const results: Record<string, { pass: number; total: number }> = {};

for (const model of modelsToTest) {
  results[model] = { pass: 0, total: 0 };

  for (const example of dataset) {
    const response = await client.chat.completions.create({
      model,
      messages: [{ role: "user", content: example.input }],
    });

    const output = response.choices[0].message.content ?? "";
    const passed = grade(output, example.expected, example.rubric);

    results[model].total += 1;
    if (passed) results[model].pass += 1;
  }
}

console.table(results);

grade 函数就是你的评分标准所在之处。对于字符串匹配类用例,它可以是 output.includes(expected)。对于基于评分标准的评分,它通常是另一次对强大的评判模型的 LLM 调用,传入评分标准和响应,询问该响应是否满足每一条标准。

发给评判模型的评分标准检查提示词看起来是这样的。

Rubric criteria (each MET or UNMET):
{criteria}

Response to grade:
{response}

For each criterion, output MET or UNMET on its own line. No prose.

评判模型针对每条标准返回一个 MET 或 UNMET,grade() 将其解析为:如果每条标准都是 MET 则通过,否则失败。二元标准以及“不要散文”的指令使评判模型的输出足够一致,以便在多次运行之间进行解析。

对于设计良好、采用二元标准并带有锚点示例的评分标准,LLM 评判模型在回归检测方面足够可靠。与人工评分者的一致性因任务、评分标准和评判模型而异,因此在依赖其评分之前,请用一组人工标注的示例来校准评判模型。评判模型的可靠性仅凭随机种子就会发生变化。一项研究让三个 LLM 评判模型对 BIG-Bench Hard 问题的响应各评分 100 次,除了种子之外什么都不改变,测得各评判模型之间的评分者间信度在多次重复中从 0.167 到 1.00 不等。在该量表中,1.0 表示完全一致。使用与被测模型不同的模型作为评判模型,以避免自我偏好偏差,并且不要在高风险决策中将单次评判运行视为绝对真理。

在每个模型上对同一集合评分。如果你在运行之间更改数据集,你就是在同时测量两件事。冻结集合,记录提交哈希,然后再更换模型。

同时关注成本和质量。在你的黄金集上最准确的模型,每次调用的成本可能是第二准确模型的 30 倍,而质量差距只有两个百分点。把这个权衡呈现给负责预算的人。我们的模型对比页面会在你运行完整基准测试之前,展示两个模型之间的价格差距。

当你需要可复现性时,使用提供商路由控制。同一个模型 slug 可能路由到不同的提供商,而提供商之间的行为可能略有差异。要将一次基准测试运行固定到某个提供商,请将 provider 对象中的 order 字段设置为该提供商的 slug,并将 allow_fallbacks 设置为 false。仅使用 order 时,如果所列的提供商不可用,我们仍会回退到其他提供商。

结论

黄金评估数据集能告诉你某项变更对你所服务的流量是有帮助还是有损害。用生产数据构建它,审查每一个预期输出,将数据集与其评分标准和基线一起进行版本管理,并在每次部署时运行它。

一旦你拥有了它,通过一个 API 在众多模型上运行同一数据集就是回报所在。对于兼容的候选模型,对比就变成了更改模型标识符并重新运行,同时针对每个候选模型检查上下文长度、工具支持以及支持的参数。

常见问题

黄金评估数据集应该有多大?

从 20 到 50 个经过审查、覆盖你最重要失败模式的条目开始。逐步扩展到 100 到 1,000 个条目,形成完整的回归集,并保留一个较小的子集用于每次 PR 检查。合适的大小取决于指标、流量分布以及你需要检测的最小质量差异。失败模式的覆盖比原始数量更重要。

我应该使用生产数据还是合成数据?

以生产数据为基础,用合成示例填补已知的覆盖缺口。真实流量保留了你的用户所产生的分布和失败模式。对于真实示例太少的罕见失败模式,添加合成用例,在元数据中将其标记为合成,并让它们只占数据集的一小部分。

我应该多久刷新一次黄金集?

每季度审查一次数据集,当提示词、模型或产品行为变化很快时,审查要更频繁。对照当前行为检查较旧的条目,添加新观察到的失败模式,并且只淘汰那些行为在产品中已不复存在的条目。保留之前的版本,以便你能复现历史回归。

我可以使用 LLM 作为评判者吗?

可以,但有两个条件。评分标准必须足够具体,以至于两个人类对同一输出评分时会给出相同的评分,并且你必须先根据人工标注的示例校准评判者,然后再依赖其评分。对于高风险决策,避免单次运行的评判,并且不要使用被测模型作为它自己的评判者。

黄金集和基准测试有什么区别?

基准测试针对公开测试衡量模型的通用能力。黄金集衡量模型在你自己的流量上的能力。基准测试帮助你缩小模型候选名单。黄金集告诉你候选名单中哪些模型适合你的产品。两者不能互相替代。

参考文献

来源:OpenRouter:Announcements(RSS) · openrouter.ai