第 14 章 可复现评测系统:Evaluation Harness¶
开场问题:同一个模型、同一个 benchmark,两个团队为什么能跑出相差 8 分的结果?
14.1 harness 是实验协议,不只是脚本¶
一项任务应被封装为:
- 数据加载和版本。
- prompt/chat template 构造。
- 生成参数。
- 输出解析。
- 评分函数。
- 聚合和置信区间。
- 缓存、日志和可重放记录。
只有数据集名称相同远远不够。few-shot 示例、答案顺序、system prompt、是否要求 CoT 都能带来数分差异。
14.2 Task、Document、Request、Metric¶
一个清晰的 harness 把原始样本 Document 转成一个或多个 Request:生成文本、计算候选 log-likelihood、调用工具或运行 Agent 环境。模型产生 Response,再由 Metric 聚合。
分层的好处是同一任务可切换本地模型、API 模型和不同后端;评分逻辑不依赖执行方式。任务协议一旦和某个 SDK 写死,后续很难复现。
14.3 log-likelihood 与 generation¶
多选题可比较每个候选的条件 log-likelihood,不必真的生成;这种方法稳定、便宜,但长度不同的候选要考虑归一化。真实产品是生成答案,因此还应补 generation 评测,测试格式遵循和解释能力。
两种设置回答不同问题,不应拿 likelihood 结果冒充端到端生成效果。
14.4 缓存与可重放¶
模型调用昂贵,应按“模型制品哈希 + 模板版本 + 生成参数 + 输入”缓存原始响应。缓存原始输出,不只缓存分数,这样解析器或评分规则更新时不必重新推理。
每条结果要能追溯到完整请求和响应。只保留总分会让错误分析和 judge 复核无从进行。
14.5 并发、重试与 API 评测¶
外部 API 会限流、超时和返回瞬时错误。重试必须区分幂等错误与内容错误;不能因为解析失败就重新采样到一个能过解析的答案,这会暗中把 pass@1 变成 pass@k。
记录实际重试次数、模型版本和服务端可能变化的日期。闭源模型名称相同也可能滚动更新,结果无法永久复现。
14.6 Agent harness 为什么更难¶
普通评测是 prompt → response;Agent 评测是策略与环境多轮交互。harness 还要固定:
- 初始环境快照。
- 可用工具和权限。
- 每步观察格式。
- 最大步数、token 和时间预算。
- 环境状态转移。
- 成功判定和副作用清理。
若环境不确定,同一 Agent 的分数波动可能来自网页变化或服务故障,而不是模型。需要容器、模拟器或可重置 sandbox。
14.7 一个最小结果记录¶
每条样本至少保存:
- task/sample ID 与数据版本。
- 完整渲染 prompt/messages。
- 原始输出或完整轨迹。
- token 用量、延迟、工具次数。
- 解析结果、各指标分量。
- 模型、模板、推理配置和代码 commit。
这不是日志洁癖,而是未来能够解释“为什么这版涨了”的最低条件。
核心问答¶
Q:harness 与普通评测脚本的区别? harness 把任务协议、执行后端、解析和指标分离,并保证版本化、缓存和重放。
Q:为什么缓存原始输出而不是最终分数? 评分器和解析器会修复;有原始输出即可重算,没有就必须重新调用模型且结果可能变化。
Q:解析失败可以自动重试吗? 通常不可以。格式失败本身是模型失败,重试会把单次成功率偷偷变成多次尝试成功率。
Q:Agent 评测为什么需要环境快照? 环境变化会改变任务难度和结果,无法区分模型变化与外部变化。