返回 AICAP-180
B1 · Day 5evals 方法论 + tokenization 起步

跑通 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.tscompletionRate = 任务套件中 judge.label === 'pass' 的占比。它是「这个模型在这套任务上达标 outcome 的比例」的单一数字——但单一数字会骗人(Day 23 会用 CI 修正),所以 harness 同时报 partialCreditMean(平均 partial 给分)、unknownRatecodePassRate。这套多指标而非单一通过率,正是 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 离线可跑通的机理runTaskEvalgeneratejudge可注入函数GenerateFn/JudgeFn),所以聚合逻辑能用 fake 在无网络下单测;scripts/run-agent-eval.ts 才把真 OpenRouter 模型接进来。于是「harness 流程 + 数学」与「真模型调用」解耦——前者已测试绿,后者待 key。这种「核心数学纯函数 + IO 边缘可注入」的结构,是 AI 系统可测试性的范式:模型调用不可确定,但聚合、统计、κ、tokenizer 全是确定性纯函数,必须单测覆盖。

2. 推导 / 手算 / 代码走读

走读三件套:agentEval.tscohensKappa.tsscripts/run-agent-eval.ts。真实符号:

  1. runTaskEval(tasks, opts)agentEval.ts L77):对每个 task 先 opts.generate(task) 出 output,再 task.codeCheck?.(output) 出 code 判,再 opts.judge(...) 出 judge 判,收进 perTask
  2. 聚合(L94-100):completionRate = passes/n(judge=pass 占比)、partialCreditMeanjudge.score 均值,clamp 到 [0,1])、unknownRatecodePassRate(仅对声明了 codeCheck 的任务求均值,无则 null)、totalCostUsd = Σ costUsd
  3. κ 接入(L102-114):当传了 humanLabels,把 judge 标签与人工标签配对,配对数 ≥2 才调 cohensKappaWithCI——所以无人工标签时 judgeHumanKappa 为 null(诚实地不假装有校准)。
  4. cohensKappaWithCI(a, b, {bootstrap, rng})cohensKappa.ts L53):先 cohensKappa 出点估计,再对 N 个配对有放回重采样 B 次(默认 2000),排序后取 2.5/97.5 percentile 成 ci95。RNG 可注入 → 确定性可测。
  5. cohensKappa(a,b)(L22):算 po(一致计数/n)与 pe(各类别边际积之和),κ=(po−pe)/(1−pe);退化边际(两评分者都只用同一类别)时 pe==1,约定完美一致返回 1、否则 0。
  6. run-agent-eval.ts无 key → dry run(L83-89,打印「Would run」exit 0);有 key → runTaskEval 后写 agent-evals/reports/run-<stamp>.json,含 commitgit rev-parse --short HEAD)、completion%、partial、codePass、unknown、cost、judge-human κ(有 labels 时)。
  7. 末尾打印 QUOTABLE 行"On N=… tasks, provider:model completion (LLM-judge=pass) = …% [commit …]"——这就是「三数报告」的可引用形态。
  8. 退化边际的 κ 约定cohensKappa.ts L48):两评分者都只用同一类别时 pe==1、分母为 0,约定「完美一致返回 1,否则 0」——避免 NaN 污染。这种边角处理是 κ 实现的常见坑,本仓显式处理了。
  9. percentile 线性插值(L85):CI 端点用预排序数组的线性插值 percentile(2.5 / 97.5),不是简单取整索引——小样本下更稳。
  10. 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 报告)产出来。

执行步骤:

  1. 离线先跑通 harness:pnpm test 确认 agentEval/cohensKappa/tokenizer/stats 单测绿(无 key 需求)。
  2. 配好 key 后跑 pnpm eval:agent(脚本 scripts/run-agent-eval.ts)首次出数:completion% + 总 cost + commit hash,落 agent-evals/reports/
  3. 无 key 时:用离线 fixture(fake GenerateFn/JudgeFn 或 code-grader-only)跑通管道,先出 completion% + commit,LLM-judge / 真 cost 段标注「待跑」
  4. 不改 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)
completionRatefixture judge 出(占位)真 judge 出 → 待跑
codePassRatecode grader 可离线出
totalCostUsdn/a(无真调用)待跑(需 key)
judgeHumanKappanull(无 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=0makeModelGenerate 在模型未定价 / usage 缺失时 costUsdundefined,报告显示「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.tsrunTaskEval 聚合)、src/agent/eval/cohensKappa.tscohensKappa/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(makeModelGenerateundefined,报告显示 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 失败类型及占比)。