logo

构建黄金数据集:让评测从感觉变成证据

Published on

构建黄金数据集:让评测从感觉变成证据

“这个模型感觉比上一个好。”这是 AI 项目里最常听到、也最不可靠的一句话。开发者拿几个熟悉的问题试一下,回答顺了,就觉得优化成功;换一批用户问题,模型可能已经开始漏字段、乱引用,或者在没有答案时编造内容。

我很长时间也没有认真对待评测数据。大家把线上问题临时复制到一个表格里,谁有空谁补一个答案,过几个月表格里有几百行,却没人知道哪些题真的有代表性,哪些答案已经过时,更不知道一次 Prompt 修改到底改善了什么。

后来我才明白,黄金数据集不是“多收集一些题目”,而是给系统建立一把稳定的尺子。尺子不一定完美,但必须有刻度、有版本、有使用规则。否则每次评测都换一把尺子,结果再漂亮也没有可比性。

一、先定义数据集要回答什么问题

一个数据集不可能同时完美衡量所有能力。先写清楚它服务的任务:

type DatasetPurpose = {
  name: string
  system: 'chat' | 'rag' | 'agent' | 'extraction'
  targetBehavior: string[]
  excludedBehavior: string[]
  decisionItSupports: string
}

例如“客服 RAG 黄金集”要回答的是:系统能否根据授权知识回答常见问题,能否在没有证据时拒答,能否正确引用来源。它不需要拿来衡量诗歌创作,也不能被一句总体满意度替代。

目标越具体,样本和指标越容易设计。否则你会得到一套什么都有、却没有任何一项测得可靠的数据。

二、样本要覆盖真实分布和危险边界

黄金集不能只收集最常见的问题,也不能只收集最刁钻的问题。可以按几类组织:

  • 高频任务:真实流量里最常出现的问题。
  • 长尾任务:少见但业务重要的表达。
  • 边界任务:相似意图、缺字段、歧义和冲突信息。
  • 无答案任务:知识库没有足够证据的问题。
  • 对抗任务:Prompt Injection、越权和恶意输入。
  • 回归任务:历史上已经修复过的线上错误。
type GoldenCase = {
  id: string
  category: string
  input: string
  context?: string
  expected: string
  evidence?: string[]
  forbidden?: string[]
  risk: 'low' | 'medium' | 'high'
  source: 'production' | 'expert' | 'synthetic' | 'adversarial'
}

真实流量能反映用户怎么说话,但必须脱敏和授权;专家编写的样本能覆盖边界,但可能过于整洁;合成样本可以快速扩充,却可能继承生成模型的表达偏差。几种来源混合,才能既像真实用户,又能主动测试危险情况。

三、期望答案要写成可判断的标准

“回答正确”太模糊。对于事实问题,写出核心事实和允许的表达范围;对于 RAG,写出必须引用的片段;对于 Agent,写出允许调用的工具和最终状态;对于抽取,写出字段、类型和必填条件。

type AcceptanceCriteria = {
  requiredFacts?: string[]
  requiredEvidenceIds?: string[]
  requiredToolCalls?: string[]
  forbiddenClaims?: string[]
  schema?: string
  humanReviewRequired?: boolean
}

一个好标准不是要求模型说出唯一答案,而是定义什么不能错、什么可以有多种说法。允许范围写得太窄,评测会把正确答案误判为失败;写得太宽,又会让错误答案轻易通过。

四、标注需要规则,也需要人

标注前先写标注指南,包含概念定义、正反例、边界情况和冲突处理顺序。比如“退款”和“售后”在业务里可能很接近,如果不事先约定,两个标注员会根据自己的理解给出不同答案。

同一批关键样本最好由至少两个人独立标注,再计算一致性并讨论分歧:

问题 → 标注员 A:退款
     → 标注员 B:售后
     → 进入复核,更新标注指南

分歧不是噪音,它经常暴露了产品规则本身不清楚。不要简单地投票取多数,把少数意见丢掉;应该记录为什么分歧、最终采用什么规则,以及未来遇到同类问题如何处理。

自动标注可以降低成本,但高风险样本和关键测试集不能全部依赖模型。模型给出的答案需要人工抽查,尤其要关注它是否把自己的幻觉写进了标准答案。

五、时间切分和客户切分防止污染

黄金集最怕“看起来很高,实际上提前看过答案”。如果训练数据、Prompt 优化数据和测试数据包含相同或高度相似的内容,模型会记住样本而不是学会能力。

可以按时间、客户、文档来源或任务模板做隔离。线上新问题优先进入候选区,经过一段时间和人工确认后再进入正式回归集;不要因为某个版本在候选题上表现好,就立刻把它当作独立测试。

原始样本 → 脱敏 → 候选集 → 标注复核 → 冻结测试集
                           仅在版本发布时使用

正式测试集的访问要受限,答案不能随意暴露给所有 Prompt 调试流程。数据集越重要,越要防止它被优化过程反复“看见”。

六、版本化和变更记录不能省

黄金集会变化:产品规则更新,知识库过期,新增线上错误,旧样本失去意义。每次变化都要有版本、原因和影响范围:

type DatasetRevision = {
  version: string
  parentVersion?: string
  added: string[]
  changed: string[]
  removed: string[]
  reason: string
  reviewedBy: string[]
  createdAt: string
}

不要为了让新模型通过而随意删除失败题。先判断是标准错了、数据过时了,还是模型确实失败;如果删除,必须保留原因。数据集也应该像代码一样走审查,避免一个人悄悄改变评测标准。

七、指标要和数据集结构对应

总体准确率只是一个起点。按类别、风险、来源和语言切片,观察关键子集:

总体有效率:94%
高风险样本:98%
无答案拒答:91%
多跳问题:83%
历史回归集:100%

如果总体分数上升,但多跳问题下降,团队应该知道;如果高风险样本变差,不能被平均数掩盖。小样本切片还要注意统计波动,报告数量和置信区间,避免把三道题的变化当成确定结论。

对生成任务,可以结合规则、语义评审和人工复核。自动评审适合扩大覆盖,人工复核适合校准标准和发现评审偏差。评测模型也要有版本,不能换了裁判却继续比较旧分数。

八、黄金集要服务模型、RAG 和 Agent

同一套基础样本可以扩展成不同测试:

  • 模型测试:答案事实和格式是否正确。
  • RAG 测试:是否召回正确证据,是否引用过期或越权内容。
  • Agent 测试:工具顺序、参数、幂等和最终状态是否正确。
  • 系统测试:延迟、成本、失败重试和降级是否符合预算。
type CaseResult = {
  caseId: string
  modelVersion: string
  promptVersion: string
  retrievalVersion?: string
  toolTrace?: string[]
  quality: number
  latencyMs: number
  cost: number
  passed: boolean
}

这样,某个版本失败时,不会只得到“答案不好”,而能知道是模型、检索、工具还是系统约束出了问题。

九、数据集不是建完就结束

定期回顾数据集中的通过率。如果所有版本都接近 100%,可能说明样本太容易;如果失败样本从不被修复,可能说明标准不可执行。黄金集要随着真实系统变化,但每次变化都要留下证据。

可以设一个数据集健康检查:样本类别是否失衡,重复率是否上升,过期文档是否仍在引用,人工分歧是否集中在某个标签,线上高频失败是否还没有进入回归集。

总结:黄金数据集是一种工程记忆

我现在把黄金数据集看成团队给系统留下的错题本。它记录的不只是“正确答案”,还记录我们曾经在哪里失败,为什么认为某种行为不可接受,以及下一次改动必须守住什么。

一套好的黄金集不追求数量最多,而追求问题明确、证据可靠、边界完整、版本可追溯。它让模型升级、Prompt 修改、RAG 调整和 Agent 重构拥有共同的比较基线。

当有人说“这次感觉更好了”,你可以拿出数据问:哪类问题更好了?代价是什么?有没有新的失败?当有人说“这个版本已经可以上线”,你可以继续问:高风险样本通过了吗?无答案时会拒答吗?工具会不会重复执行?

这就是黄金数据集的意义。它不是为了给团队增加一张报表,而是把模糊的争论变成可以复核的证据。AI 系统会不断变化,只有把过去的失败认真保存下来,下一次进步才不会只是又一次感觉。

🤪 您也可以编辑此页: