非确定性统计基础
Day 31-32 解决了「该不该多步推理 / 该不该上 agent」的定性判定。但所有这些选型最后都要靠数字裁决,而 agent 输出是非确定的——同一任务跑两次可能一次过一次挂。今天补的是 B4 后半段一切实测的统计底座:为什么单跑不可信,以及 pass@k 与 pass^k 的根本区别。它直接决定 Day 34(self-consistency)、Day 39(pass@k+pass^k 实
阶段: B4 · reasoning/planning/multi-agent + 何时不用 agent(Day 31-40) 标签: #pass-at-k #pass-pow-k #variance #reliability
今日导引(由浅入深)
Day 31-32 解决了「该不该多步推理 / 该不该上 agent」的定性判定。但所有这些选型最后都要靠数字裁决,而 agent 输出是非确定的——同一任务跑两次可能一次过一次挂。今天补的是 B4 后半段一切实测的统计底座:为什么单跑不可信,以及 pass@k 与 pass^k 的根本区别。它直接决定 Day 34(self-consistency)、Day 39(pass@k+pass^k 实测)、Day 40(gate)能不能算出可信的可靠性数字。最小可判定产出:给 agentEval.ts 加 n-run 复跑封装(runNTimes),对 8 任务子集统计每任务 variance。
1. 机理精读
agent 输出非确定,单跑不可信,这是 B4 实测的前提。 LLM 在温度>0 下采样,同一 prompt 多次运行会给出不同输出;即便温度=0,浮点/路由/工具时序也会带来抖动。因此「跑一次过了」不能等于「这任务能做对」——你测的是一个随机变量的一个样本,而非它的期望。
pass@k(乐观口径):k 次运行里只要有 1 次过就算这条任务「通过」。它衡量的是能力上界——「模型在足够多次尝试下能不能做到」。pass@k 随 k 单调上升,看起来很漂亮,但它掩盖脆弱性:一个 5 次里只对 1 次的任务,pass@5 也记作「通过」,可生产环境里它 80% 的时候是错的。
pass^k(悲观口径,pass-power-k):k 次运行全部过才算通过。它衡量的是可靠性下界——「这任务能不能稳定复现」。pass^k 随 k 单调下降。当某任务 pass^k ≪ pass@k,说明它「偶尔能做对但不可复现」=脆弱任务,正是 agent 不该独立负责的(Day 39 会专门找出这类任务并标红)。
variance 本身是可靠性维度。 《Don't Pass@k》(2025-10) 的核心批评是:只报均值(或 pass@k)会掩盖输出的不稳定性,主张同时报 pass^k + variance。一个任务即使均值还行,若 per-run pass 计数方差大(这次 4/5、下次 1/5),它在生产里就是不可用的——抖动大 = 不可信赖。所以本日要对每个任务统计 std(标准差)作为可靠性的可量化代理。
关键权衡与边界。 pass@k 适合回答「能力天花板有多高」(研究/能力评估),pass^k 适合回答「上线后多可靠」(生产/选型)。AISA 选型必须用后者——这正是 Day 40 gate 里「任务非脆弱(pass^5 高)才允许用 agent」这一条的统计依据。边界:pass^k 对 k 极敏感,k 取多大要和延迟/成本预算一起定(Day 34/38 会算成本曲线),不能盲目堆 k。
为什么这一步对 AISA 重要。 「这个 agent 可靠吗」是 review 必问,而单跑给出的「跑通了」是最容易骗过自己和评审的假信号。把 pass^k + variance 作为标准报告口径,等于给可靠性论证装上防自欺机制——你能指着脆弱任务说「这条 pass^5 远低于 pass@5,所以它不能独立交给 agent,要么人审、要么降级为 workflow」。这比「我跑了几次感觉还行」强一个数量级,也是 Day 40 gate 阈值能成立的前提。
2. 推导 / 手算 / 代码走读
手算一个最小例子(设某任务 5 次运行结果,1=过 0=挂):[1,1,0,1,1]
- pass@5 = 「有≥1 次过」→ 1(通过),因为有 4 个 1。
- pass^5 = 「5 次全过」→ 0(不通过),因为第 3 次挂了。
- per-run pass 计数 = 4/5 = 0.8。
- 对「每次是否过」这个 0/1 序列算样本 std:均值 m=0.8,
Σ(x-m)²=4×(0.2)²+1×(0.8)²=4×0.04+0.64=0.8,var=0.8/(5-1)=0.2,std=√0.2≈0.447。 这就是单任务的 variance 列。pass@5=1 看着满分,但 std≈0.447 + pass^5=0 暴露了它其实脆弱——这正是《Don't Pass@k》要你看的东西。
代码走读(本日要新建的封装):
- 现有
src/agent/eval/agentEval.ts的runTaskEval(tasks, opts)是单跑一遍全任务:对每个 task 调一次opts.generate、一次opts.judge,聚合completionRate等。它没有复跑维度——这正是今天要补的缺口。 agentEval.ts的generate/judge是可注入的(GenerateFn/JudgeFn),所以聚合逻辑能用 fake 离线单测、不触网——这让runNTimes的封装层本身可离线绿。- 现成的
src/agent/eval/stats.ts已提供mean(xs)与sd(xs)(样本标准差,n<2时返回 0);本日的 per-task std 列直接复用sd(),无需另造统计原语。 - 现状核对:仓库内目前没有
runNTimes/runNRuns符号(grep 无命中),与 seed 标注的「待跑/待建」一致——本日产出是新增封装 + 离线单测,真实 5×8 模型跑需 key。
runNTimes(task, n) 的契约设计:对单个 task 复跑 n 次 generate+judge,返回 per-run 的 pass 布尔序列 + 聚合的 pass 计数与 sd()。8 任务各跑 n=5,吐出 8 行 {taskId, passCount, std}。
再手算一个对照任务(某稳定任务 5 次全过 [1,1,1,1,1]):
- pass@5 = 1,pass^5 = 1(全过),per-run 计数 5/5=1.0。
- std:均值 1,
Σ(x-m)²=0→ var=0 → std=0(sd()在此返回 0)。 - 对比 Day 2 的脆弱任务(pass^5=0、std≈0.447):同样 pass@5=1,但 pass^5 与 std 把「稳定」与「脆弱」干净分开——这正是 pass@k 单看会漏掉的信号。
封装的不变量(写进单测):
runNTimes(task, 1)应退化为runTaskEval([task], …)的单跑结果(n=1 边界)。- per-run pass 序列长度恒等于 n;passCount ∈ [0, n]。
- 全过 ⇒ std=0;全挂 ⇒ std=0;半数过 ⇒ std 取最大(
sd([1,1,0,0,1])之类)。 - generate/judge 用 fake(确定性输出)时,结果完全可复现、不触网——这是封装离线绿的根据。
pass@k vs pass^k vs variance 速查表
| 指标 | 定义 | 衡量 | 随 k | 选型用途 |
|---|---|---|---|---|
| pass@k | k 次有 ≥1 次过 | 能力上界(乐观) | 单调升 | 研究/天花板 |
| pass^k | k 次全过 | 可靠性下界(悲观) | 单调降 | 生产/上线门槛 |
| variance(std) | per-run pass 计数的离散度 | 稳定性/可信赖度 | — | 抖动越大越不可用 |
判读口诀:pass^k ≪ pass@k ⇒ 脆弱任务(偶尔对、不可复现),正是 agent 不该独立负责的(Day 39 标红、Day 40 入 gate)。
3. 今日实战
- 在
src/agent/eval/agentEval.ts(或同目录新文件)加runNTimes(task, n, opts)封装:循环 n 次opts.generate+opts.judge,聚合 per-task pass 计数与sd()(复用stats.ts)。 - 选 8 任务子集(如
aml-structuring-classic、planning-investigation、injection-memo-instruction、honesty-refuse-insufficient等覆盖多类别)。 - 对 8 任务跑 5×8,统计每任务 variance(std 列)。
- 新增测试到
src/agent/__tests__/eval/,用 fake generate/judge 验证封装聚合正确,跑pnpm test。 - 离线 fixture 模式下:喂确定性 fake 输出,验证 8 行
{taskId, passCount, std}表能产出并 commit;真实 5×8 模型跑留待 key。 - 把 8 任务子集固定写入脚本(而非随机抽),保证后续 Day 34/35/39 复用同一子集——同任务集才能配对作差(Day 35 bootstrap 的前提)。
4. 今日实测 / 产出
- 复跑封装与 variance 表为 「待跑(需 OPENROUTER_API_KEY)」——5×8 真实模型跑需 key。
- 可先用离线 fixture 跑通封装出 8 任务 std 列 + commit。
- 封装单测本身离线绿。
- 将产出:8 任务 std 数字。
- 状态:封装+离线单测属本日要建/可建;真实 8 任务 std 数字属待跑(需 key),不臆造具体数值。
与 B4 后续的接力
- Day 34 用
runNTimes的多采样做 self-consistency(同 prompt 投票降方差),只对今天 std 大的任务定向开。 - Day 35 把 single vs k=5 的两组结果喂
pairedBootstrap算 Δ 的 95% CI(需同 8 任务集配对)。 - Day 39 对今天标的脆弱子集跑 n=10,算 pass@1/pass@5/pass^5,找
pass^5 < pass@5的任务标红。 - Day 40 把 pass^5、variance、CI 一起塞进「何时不用 agent」决策 gate。 今天定下的 8 任务子集要在这四天里保持不变,否则配对作差失去意义。
5. 常见误区 / 陷阱
- 只报均值不报 variance:旧 eval 习惯,《Don't Pass@k》2025-10 明确批评——抖动大的任务均值再好也不可上线。
- 用 pass@k 充门面:pass@k 乐观掩盖脆弱性,把「5 次对 1 次」记成通过;生产口径必须 pass^k。
- n 取太小:n=5 是入门量级,per-task std 估计噪声仍大;做最终结论时 n 需配合 bootstrap CI(Day 35)一起看,别拿单点 std 下硬结论。
- 把封装离线绿当成真实结论:fixture 跑通只证明聚合逻辑对,真实 std 数字仍需 key 出——状态不可升级。
- 温度=0 就以为确定:即便温度=0,路由/浮点/工具时序仍带抖动,「单跑可信」依然不成立;复跑是必须的。
- 用 judge 的随机性掩盖被测的随机性:复跑时若 judge 自身也抖(LLM-judge),要分清是被测模型不稳还是裁判不稳——必要时固定 judge 或退回 code grader(
codeCheck)做对照。
自测题(面试演练)
- Q:为什么生产选型必须用 pass^k 而不是 pass@k? A:pass@k 是「足够多次尝试能不能做到」的能力上界,乐观且掩盖脆弱;生产只跑一次,要的是「每次都对」的可靠性下界 = pass^k。把 5 次对 1 次的任务记成「通过」会让上线后 80% 时间出错。
- Q:variance 为什么算可靠性维度而非附属指标? A:均值相同但抖动大的任务(这次 4/5、下次 1/5)在生产里不可信赖;《Don't Pass@k》2025-10 要求与 pass^k 并列报 variance,否则均值会粉饰不稳定。
- Q:n=5 复跑够不够下结论?
A:不够。n=5 的 per-task std 估计噪声仍大,必须配 bootstrap CI(Day 35)一起看,单点 std 不能下硬结论;想收窄要么加 n、要么用
requiredNForDelta反推所需 N。
6. 学习资源(每条带 YYYY-MM)
- 《Don't Pass@k》(2025-10) — 批评 pass@k 掩盖脆弱性,主张报 pass^k + variance,本日主线。该文也提示:在 RL/agent 训练里只优化 pass@k 会鼓励「碰运气」的高方差策略,进一步说明生产口径须用 pass^k。
- 本仓代码
src/agent/eval/agentEval.ts(runTaskEval/GenerateFn/JudgeFn)— 复跑封装的注入点。 - 本仓代码
src/agent/eval/stats.ts(mean/sd)— per-task std 计算原语。 - Anthropic《Demystifying evals》(2026-01) — 多维度量(含 variance)作为可靠性维度的方法论依据。
- 本仓代码
src/agent/__tests__/eval/(新增的runNTimes单测)— 离线验证复跑聚合的确定性产物。
SOTA检查 (2026-06 更新)
- 当前主流:《Don't Pass@k》2025-10 是近期主线,现行有效。
- 过时黑名单:避免只报均值不报 variance 的旧 eval 习惯;pass^k 为 2025 推荐口径。
- 下次复查点:是否出现 pass^k 的标准化报告格式/工具;推理模型(extended thinking)是否改变 per-task variance 的典型量级(与 Day 34 self-consistency 收益变小关联)。
衔接
- 昨天:Day 32 — agent vs workflow(四规则把 29 任务分 workflow N / agent M)。
- 今天:补非确定性统计底座——pass@k vs pass^k vs variance,建
runNTimes封装出 8 任务 std。 - 明天:Day 34 — self-consistency 机制(用
runNTimes+ 多数投票,跑 single-shot vs k=5 对比)。