跑通 eval harness
Day 1-2 立方法论、Day 3-4 接 token 与计费。今天把它们拧成一台能跑的机器:eval harness——给一个具名模型跑整套任务,输出 completion%(达标 outcome 占比)+ 总 cost + commit hash 三个可引用的数。这是 B1 的第一个「真产物」原型:从此评测不再是论述,而是一份带 commit 的报告。但诚实地说,真 cost 要 key;h
阶段: B1 · evals 方法论 + tokenization 起步(Day 1-10) 标签: #evals #harness #completion-rate #cohens-kappa
今日导引(由浅入深)
Day 1-2 立方法论、Day 3-4 接 token 与计费。今天把它们拧成一台能跑的机器:eval harness——给一个具名模型跑整套任务,输出 completion%(达标 outcome 占比)+ 总 cost + commit hash 三个可引用的数。这是 B1 的第一个「真产物」原型:从此评测不再是论述,而是一份带 commit 的报告。但诚实地说,真 cost 要 key;harness 的数学(κ + CI、graders、tokenizer/stats)已单测覆盖、离线可跑通。最小可判定产出:agent-evals/ 下首份自产报告(completion% + cost + commit),LLM-judge/真 cost 段标「待跑」。
1. 机理精读
completion% 的定义。agentEval.ts 里 completionRate = 任务套件中 judge.label === 'pass' 的占比。它是「这个模型在这套任务上达标 outcome 的比例」的单一数字——但单一数字会骗人(Day 23 会用 CI 修正),所以 harness 同时报 partialCreditMean(平均 partial 给分)、unknownRate、codePassRate。这套多指标而非单一通过率,正是 Day 1 outcome-over-process 的落地。
Cohen's κ 衡量判定一致性。cohensKappa.ts 用 Cohen's κ 衡量两套判定(如 code vs judge、judge vs human)超越随机的吻合度:κ = (p_o − p_e)/(1 − p_e),其中 p_o 是观测一致率、p_e 是随机期望一致率。它回答「这个 LLM-judge 的判分能不能信」——而不是默认信。含 bootstrap CI:点估计 κ 也会骗人,小样本下 CI 太宽不可下结论,所以 cohensKappaWithCI 重采样配对标签求 95% percentile CI(这是为什么 P1 要 N≥50,Day 20 收口)。
为什么 completion% + κ 是「双指标」。只报 completion% 而不报 κ,等于「我说我考了 79 分」但不告诉你「判卷的尺子准不准」。κ 校准判官、completion% 量产出质量——两者缺一,数字就不可采信。这是本仓评测体系的设计骨。
harness 报的不止两个数。TaskEvalReport 实际有 6+ 个字段,每个回答一个不同问题:
completionRate:judge=pass 占比 → 「达标率」。partialCreditMean:judge.score 均值 → 「即使没全对,平均接近多少」(rubric 部分给分的聚合)。unknownRate:judge=unknown 占比 → 「判官有多少次说不准」(高 unknownRate 是任务或判据有问题的信号)。codePassRate:code grader 通过率(仅对有 codeCheck 的任务)→ 「便宜确定性判据的视角」,与 judge 视角交叉验证。totalCostUsd:单位经济。judgeHumanKappa:判官可信度(有人工 labels 时)。 把这些一起看,才能区分「completion 低是模型差,还是判据太严,还是判官抖」——单一通过率做不到这种归因,Day 6 的失败归因正是建在这套多字段之上。
Cohen's κ 一个 30 秒手算。假设 judge 和 human 各对 10 条 trace 给 pass/fail:两者一致 8 条(po = 0.8)。judge 标了 7 pass/3 fail,human 标了 6 pass/4 fail,则随机期望一致率 pe = 0.7×0.6 + 0.3×0.4 = 0.42 + 0.12 = 0.54。于是 κ = (0.8 − 0.54)/(1 − 0.54) = 0.26/0.46 ≈ 0.565——落在「未达 0.6」区间,意味着这个 judge 还不够可信。注意:单看 po=0.8 会以为「judge 很准」,但扣掉随机一致后 κ 只有 0.565——这正是「只报准确率不报 κ 会骗人」的数值证据(类不平衡时 pe 高,准确率虚高)。
harness 离线可跑通的机理。runTaskEval 的 generate 与 judge 是可注入函数(GenerateFn/JudgeFn),所以聚合逻辑能用 fake 在无网络下单测;scripts/run-agent-eval.ts 才把真 OpenRouter 模型接进来。于是「harness 流程 + 数学」与「真模型调用」解耦——前者已测试绿,后者待 key。这种「核心数学纯函数 + IO 边缘可注入」的结构,是 AI 系统可测试性的范式:模型调用不可确定,但聚合、统计、κ、tokenizer 全是确定性纯函数,必须单测覆盖。
2. 推导 / 手算 / 代码走读
走读三件套:agentEval.ts、cohensKappa.ts、scripts/run-agent-eval.ts。真实符号:
runTaskEval(tasks, opts)(agentEval.tsL77):对每个 task 先opts.generate(task)出 output,再task.codeCheck?.(output)出 code 判,再opts.judge(...)出 judge 判,收进perTask。- 聚合(L94-100):
completionRate = passes/n(judge=pass 占比)、partialCreditMean(judge.score均值,clamp 到 [0,1])、unknownRate、codePassRate(仅对声明了codeCheck的任务求均值,无则 null)、totalCostUsd = Σ costUsd。 - κ 接入(L102-114):当传了
humanLabels,把 judge 标签与人工标签配对,配对数 ≥2 才调cohensKappaWithCI——所以无人工标签时judgeHumanKappa为 null(诚实地不假装有校准)。 cohensKappaWithCI(a, b, {bootstrap, rng})(cohensKappa.tsL53):先cohensKappa出点估计,再对 N 个配对有放回重采样 B 次(默认 2000),排序后取 2.5/97.5 percentile 成ci95。RNG 可注入 → 确定性可测。cohensKappa(a,b)(L22):算po(一致计数/n)与pe(各类别边际积之和),κ=(po−pe)/(1−pe);退化边际(两评分者都只用同一类别)时pe==1,约定完美一致返回 1、否则 0。run-agent-eval.ts:无 key → dry run(L83-89,打印「Would run」exit 0);有 key →runTaskEval后写agent-evals/reports/run-<stamp>.json,含commit(git rev-parse --short HEAD)、completion%、partial、codePass、unknown、cost、judge-human κ(有 labels 时)。- 末尾打印 QUOTABLE 行:
"On N=… tasks, provider:model completion (LLM-judge=pass) = …% [commit …]"——这就是「三数报告」的可引用形态。 - 退化边际的 κ 约定(
cohensKappa.tsL48):两评分者都只用同一类别时pe==1、分母为 0,约定「完美一致返回 1,否则 0」——避免 NaN 污染。这种边角处理是 κ 实现的常见坑,本仓显式处理了。 percentile线性插值(L85):CI 端点用预排序数组的线性插值 percentile(2.5 / 97.5),不是简单取整索引——小样本下更稳。- commit 落盘:报告含
git rev-parse --short HEAD的 commit hash(取不到时留'unknown'),保证每份报告可追溯到代码版本——这是「可验证资产」而非「一次性数字」的关键。
为什么三数都重要。completion% 答「质量」;cost 答「单位经济」(AISA 面试四问之一);commit 答「可复现 / 可追溯」。缺 commit 的 79% 是传闻,带 commit 的 79% 是证据——这套笔记的诚信底线就建在「带 commit 的数」上。
3. 今日实战
读报告时盯三件事(Day 6 失败归因的前置):哪些 task fail(结构化归因)/ cost 分布(哪几条吃大头)/ 判定噪声来源(code grader 漏判 vs judge 抖动 vs unknown 偏高)。今天先把这三件事的原始数据(per-task 报告)产出来。
执行步骤:
- 离线先跑通 harness:
pnpm test确认agentEval/cohensKappa/tokenizer/stats单测绿(无 key 需求)。 - 配好 key 后跑
pnpm eval:agent(脚本scripts/run-agent-eval.ts)首次出数:completion% + 总 cost + commit hash,落agent-evals/reports/。 - 无 key 时:用离线 fixture(fake
GenerateFn/JudgeFn或 code-grader-only)跑通管道,先出 completion% + commit,LLM-judge / 真 cost 段标注「待跑」。 - 不改 harness 代码——它已就位且测试绿;今天是「按下运行键」并落第一份报告。
4. 今日实测 / 产出
- 状态:待跑(需
OPENROUTER_API_KEY)出真实 cost。 - 可先用离线 fixture 跑通 harness 出 completion% + commit,LLM-judge 段标注待跑。
- 将产出:
agent-evals/下首份自产报告(completion% + cost + commit 三数)。 - 仓库现状锚点(可引用、非本日新测):harness 代码已在 repo 且测试绿(378 passing,tsc clean)。completion% + κ 的双指标设计符合当前 evals 实践。
- 不臆造数字:无 key 时 cost 不写真值、不写 0;fixture 只产 completion% + commit。
首份报告骨架(agent-evals/reports/run-<stamp>.json 关键字段,占位):
| 字段 | 离线 fixture | 真模型(待 key) |
|---|---|---|
commit | 可填(git short hash) | 同 |
n | 任务数(现 30,seed 锚点 29) | 同 |
completionRate | fixture judge 出(占位) | 真 judge 出 → 待跑 |
codePassRate | code grader 可离线出 | 同 |
totalCostUsd | n/a(无真调用) | 待跑(需 key) |
judgeHumanKappa | null(无 labels) | 待跑(需 ≥2 labels,目标 N≥50) |
离线先把 commit/n/codePassRate 三项跑实,其余标「待跑」——这就是本日「先出 completion%+commit、真 cost/judge 段标待跑」的落地形态。
4c. harness 在 B1→B18 曲线上的位置
今天这台 harness 不是一次性脚本,而是整条计划的度量主轴:B11/B12 的 GRPO 后训练要用它量「训练前后 completion% 是否真涨」;B17 的 A/B 仪表盘要用它对比模型 A vs B(同一批 task、不同模型的多组 trial);B13-B15 的 AML 深化要往 tasks.ts 里追加 FATF typology 任务再用它跑。换句话说,Day 5 建的是后面 175 天反复按下的那个「运行键」。所以今天哪怕只用离线 fixture 跑通、真 cost/κ 待 key,把管道立起来本身就是高杠杆产出——之后每次有 key、有 labels,同一条命令就能产出新的带 commit 的报告。
5. 常见误区 / 陷阱
- 只报单一通过率:不报判定一致性(κ)的 completion% 不可采信——尺子没校准。
- 静默写 cost=0:
makeModelGenerate在模型未定价 / usage 缺失时costUsd留undefined,报告显示「n/a」而非 0;别把 n/a 当 0 解读。 - 无 labels 还信 κ:
judgeHumanKappa在配对 <2 时为 null——这是诚实的「未校准」,不是 bug。 - 小 N 下信点估计 κ:N<50 时 bootstrap CI 太宽,不可下「judge 可用」结论(Day 19/20 正式处理)。
- 把 harness 测试绿当成「已出真实评测数」:测试绿证明数学/管道对,不等于跑过真模型——真 completion%/cost 仍待 key。
5b. 「三数报告」为什么是 B1 的真正里程碑
回看 B1 的 10 天:Day 1-2 立方法论、Day 3 学原理、Day 4 接计费。这些都是「准备」。Day 5 第一次把它们拧成一个能产出可引用证据的动作——一份带 commit 的 completion%/cost 报告。在 AISA 求职叙事里,「我读懂了 evals 方法论」和「我跑出了 N=30 任务、completion=X%、cost=$Y、commit=Z 的报告」是两个量级的资产:前者是笔记,后者是可链接、可复现、可被 hiring manager 追问到代码版本的证据。这正是 seed 铁律「只有笔记没有数 = 这天没学会」的终点——Day 5 是 B1 把「学」变成「数」的那一天。诚实地说,今天的「数」里 cost/κ 仍待 key,但 completion%+commit+code-pass 已可离线产出,里程碑的骨架已立。
6. 学习资源(每条带 YYYY-MM)
- Anthropic, Demystifying evals(2026-01)—— outcome + unknown + partial credit + κ 校准的方法论根。
- Evan Miller, Adding Error Bars to Evals(2024-11)—— bootstrap CI / 误差棒,Day 20 收口主线(κ 的 CI 设计同源)。
- 仓库代码:
src/agent/eval/agentEval.ts(runTaskEval聚合)、src/agent/eval/cohensKappa.ts(cohensKappa/cohensKappaWithCI)、scripts/run-agent-eval.ts(real-model wiring + dry-run + 报告落盘)。
SOTA检查 (2026-06 更新)
- 当前主流:completion% + judge-human κ + bootstrap CI 的双指标设计符合当前 evals 实践,仍 SOTA;多字段报告(partial credit / unknown rate / code-pass)已是 2026 默认而非加分项。
- 是否仍 SOTA:是。harness 代码已在 repo 且测试绿(378 passing,tsc clean)。可注入 generate/judge → 核心数学纯函数可单测,是 AI 系统可测试性的标准范式。
- 过时黑名单:
- 避免只报单一通过率而不报判定一致性(κ)。
- 避免无人工 labels 就信 judge 分数——κ 未达 0.6 不作准绳(Day 17-20)。
- 避免把「测试绿」当成「已出真实评测数」——测试证明管道/数学对,真 completion%/cost 仍待 key。
- 避免静默把未定价模型的 cost 记为 0(
makeModelGenerate留undefined,报告显示 n/a)。
- 下次复查:拿到 key 出首份真实报告后,复核 completion% 是否与离线 fixture 量级一致、cost 是否与 Day 9 token 估算可对账(Day 10);跟踪 Anthropic/Hamel/Miller 是否更新判官校准与误差棒最佳实践。
5c. 离线跑通到底跑通了什么
「离线 fixture 跑通 harness」常被误解为「假装跑了」。澄清它真正验证了什么:(1) runTaskEval 的循环、聚合、字段计算逻辑正确(用 fake generate/judge 喂确定输入,断言 completionRate/codePassRate/unknownRate 等于手算值);(2) cohensKappa/cohensKappaWithCI 的数学正确(注入固定 RNG,断言 κ 与 CI 端点);(3) tokenizer 的 encode/decode roundtrip 与 token 计量正确;(4) 报告落盘、commit 注入、QUOTABLE 行格式正确。这些都不需要真模型——它们是确定性纯函数 + IO。真模型只决定 output 内容与 cost,而那是 Day 4 的 key 一到就补上的最后一环。所以「离线跑通 + 单测绿」不是占位,是把 80% 的可验证逻辑先钉死,只把「真实模型输出」这一个变量留给 key。
衔接
- 昨天:Day 4 — OpenRouter 接入(input/output 计价 + 窗口;待 key ping)。
- 今天:跑通 harness,出 completion% + cost + commit 三数;harness 数学离线测试绿,真 cost 待 key。
- 明天:Day 6 — 解析首跑结果(失败归因到
failureTaxonomy.ts的结构化类别,top-3 失败类型及占比)。