评测的正确目标:不是“赢一次”,是“每次迭代都不退步”
榜单和 demo 的价值更多是市场与叙事;工程侧真正需要的是:
- 每次改 prompt / 换模型 / 调整检索 / 改工具调用之后
- 你能在 5~20 分钟内得到可信的回归结论:变好、变差、还是无显著变化
- 并且能定位“差在了哪里”(便于修复,而不是争论)
所以本文讨论的不是“怎么跑一次 evaluation”,而是怎么建立一个可复现、可版本化、可门禁的 Eval Harness。
最小 Eval Harness:5 个目录 + 2 条基线 + 1 条 CI 门禁
你不需要从复杂平台开始。一个足够用的最小结构如下:
eval/tasks/:任务定义(输入输出格式、判分方式、采样与权重)eval/datasets/:数据集(版本、来源、去重、泄漏检查)eval/metrics/:指标(质量/延迟/成本/安全),以及阈值与聚合方式eval/baselines/:两条基线baseline_current.json:当前线上/当前稳定版本baseline_target.json:本次迭代的目标基线(可选)
eval/reports/:每次运行的报告(可归档、可对比)
你真正要坚持的是三件事:
- 单一事实源:评测用的任务/数据/配置都落盘
- 可复现:任何人、任何机器都能重跑得到同样结论(或解释差异)
- 可门禁:退步会阻止合并,而不是“知道了但先合了”
任务定义:把“好不好”写成机器能执行的合同
评测失败最常见的原因不是模型不行,而是“任务定义不可判分”。
建议把每个任务写成一个结构化定义(YAML/JSON 均可)。例如:
# eval/tasks/qa_citation.yaml
id: qa_citation
name: 带引用的问答
weight: 1.0
input_schema:
question: string
output_schema:
answer: string
citations: array
scoring:
kind: rule_based
rules:
- name: must_have_citation
check: citations.length >= 1
- name: answer_not_empty
check: len(answer) >= 20两点经验:
- 先做“结构正确”再做“语义正确”:结构/格式稳定后,再引入更复杂的语义判分。
- 允许“人工评审”作为一等公民:对难自动化的任务(写作、对话),采用固定抽样 + 标准化评分表(rubric),不要靠感觉。
一个可用的最小 rubric 通常包含:
- 事实性(0-2)
- 覆盖度(0-2)
- 可读性(0-1)
- 风险项(是否出现幻觉/越权/敏感信息,Yes/No)
数据集:比模型更重要的是“样本的代表性与不作弊”
数据集要解决两个问题:
- 代表性:覆盖真实线上分布(长尾问题占比、语言分布、噪声输入)
- 可信度:不泄漏、不重复、不被提示词“投机取巧”
最低要求建议做到:
- 版本化:文件名或元数据包含版本号(如
dataset_v3.jsonl) - 去重:避免同题多次出现导致指标虚高
- 泄漏检查:至少检查是否与训练/检索语料高度重合(尤其是公开题库、热门问答)
一个工程上实用的做法是分两套集:
golden_set:小而硬(几十到几百条),覆盖关键能力,必须长期稳定shadow_set:更大、更贴近线上分布,可迭代更新,用于发现新问题
指标体系:别只看准确率,必须把“坏指标”纳入门禁
只看一个质量分数会让系统变得脆弱。建议把指标分四类:
- 质量(Quality):准确率/匹配率/人评得分
- 覆盖(Coverage):拒答率、无效输出率、格式错误率
- 效率(Efficiency):延迟(P50/P95)、吞吐
- 成本(Cost):每 1k 请求成本、token 使用
如果你的系统涉及工具调用/检索,还应加上:
- 工具调用成功率
- 检索命中率/引用率
- 关键异常率(超时、重试、限流)
门禁策略的核心不是“追求更高”,而是“阻止更差”。典型规则:
- 关键任务集质量:不得下降超过
X% - 拒答率:不得上升超过
Y% - P95 延迟:不得恶化超过
Z% - 成本:不得上升超过阈值(或必须解释)
基线(Baselines):你要对比的不是“昨天的你”,而是“线上稳定版本”
很多团队评测跑出来分数很好,但上线后反馈更差,常见原因是:
- 对比对象错了(拿一个弱基线当对手)
- 线上有更多噪声与边界输入
最低做法:
baseline_current固定指向线上稳定版本(配置、模型、检索参数一致)- 每次变更都与它对比
如果你在做“阶段性大改”(例如 v1→v2),可以增加一个 baseline_target,用于阶段性目标跟踪,但不要替代线上对比。
CI 门禁:让“退步”无法偷偷发生
你需要把 eval 接到 CI,成为合并前的硬门槛。
最小实现甚至可以只是一条脚本 + 一个阈值文件。例如:
node ./eval/run.js --dataset eval/datasets/golden_set_v1.jsonl --report eval/reports/${GIT_SHA}.json
node ./eval/compare.js --baseline eval/baselines/baseline_current.json --report eval/reports/${GIT_SHA}.json --threshold eval/thresholds.json阈值文件示例:
{
"quality.min_delta": -0.01,
"refusal_rate.max_delta": 0.02,
"latency_p95.max_delta_ms": 150,
"cost_per_1k.max_delta": 0.05
}经验是:
- 阈值别一开始设太严;先让流程跑起来、能稳定产出报告
- 但必须“可解释”:失败时输出具体失败样本与原因(不然团队会绕过它)
报告与定位:评测的价值在“可诊断”
一份有用的报告至少应包含:
- 版本信息:代码 commit、模型版本、参数、数据集版本
- 指标汇总:总体 + 按任务/按标签分桶
- 失败样本:Top N 回归失败案例(输入/输出/期望/判分原因)
工程上强烈建议输出 JSON(机器可读)+ Markdown(人可读)两份。
常见陷阱(以及规避方式)
- 为了过评测而过评测:提示词专门对付数据集,线上全崩
- 规避:golden_set 固定 + shadow_set 持续更新
- 只做一次人评:没有一致性,结论不可复现
- 规避:固定抽样比例 + rubric + 双人交叉评审
- 指标选择单一:准确率上去了,但拒答率/成本/延迟爆了
- 规避:把坏指标纳入门禁
- 缺少线上对齐:离线评测很好,线上反馈更差
- 规避:对齐线上日志分布,定期回灌“真实样本”
结语:榜单是营销,Eval Harness 是生产力
你不需要一开始就建一个评测平台。
只要先把这三件事做成制度:
- 可复现(任务/数据/配置落盘)
- 可回归(对比线上基线)
- 可门禁(退步阻止合并)
你就会从“靠感觉迭代”变成“靠证据迭代”。
