第 04 章
第 1 周:跑 8 次,该看哪个数?
pass@k 与 pass^k:至少对 1 次和次次都对,差距随 k 越拉越大。一段讲解视频,一个可以拖参数的互动演示。
一个 Agent 单次成功率 90%。让它处理同一个请求 8 次,至少对 1 次的概率几乎是 100%,8 次都对的概率只有 43%。同一个 Agent,一个数字接近完美,另一个说明它一半时候靠不住。这一章讲这两个指标:pass@k 和 pass^k,以及什么时候该看哪个。
讲解视频
互动演示
拖动单次成功率 p 和运行次数 k,看两条曲线怎么分开。下面还有一个计算器:填入跑了几次、成功几次,算出无偏估计和区间。页面底部有自动判分的练习。
两个公式
设单次成功率为 \(p\),每次运行相互独立:
$$ \text{pass@}k = 1 - (1-p)^k \qquad\qquad \text{pass}^k = p^k $$- pass@k:k 次里至少成功 1 次,等于 1 减去"k 次全失败"。由 Kulal et al.(2019,SPoC)提出,OpenAI 的 Codex 论文(Chen et al., 2021)把它推广开并给出了无偏估计量,用来评代码生成:生成 k 份代码,有一份通过单测就算解出。
- pass^k(读作 pass hat k):k 次全部成功。出自 τ-bench(Yao et al., 2024),用来评客服类 Agent:同一个用户请求跑 k 次,要求每次都处理对。
| 单次成功率 p | pass@3 | pass@8 | pass^3 | pass^8 |
|---|---|---|---|---|
| 50% | 87.5% | 99.6% | 12.5% | 0.4% |
| 80% | 99.2% | >99.9% | 51.2% | 16.8% |
| 90% | 99.9% | >99.9% | 72.9% | 43.0% |
| 99% | >99.9% | >99.9% | 97.0% | 92.3% |
三个规律:
- k = 1 时两者相等,都是 p。
- k 越大,pass@k 越高,pass^k 越低。p = 90% 时,k = 3 差 27 个百分点,k = 8 差 57 个。
- pass^k 对 p 极其敏感。p 从 90% 提到 99%,pass^8 从 43% 涨到 92%。要让"8 次都对的概率 ≥ 90%",单次成功率得 ≥ 98.7%。
该看哪个:用户有没有重试机会
| 场景 | 指标 | 理由 |
|---|---|---|
| 代码生成,有单测把关 | pass@k | 生成 k 份,自动挑出通过测试的那份,失败的用户看不见 |
| 搜索、头脑风暴 | pass@k | 给 k 个候选,用户自己挑,有一个好的就行 |
| 客服、退款、订票 Agent | pass^k | 每个用户只有一次机会,同类请求来 k 次,每次都得对 |
| 改数据库、发邮件、执行命令 | pass^k | 操作不可撤销,错一次就是事故 |
Agent 上线后,同类请求会被成千上万个用户反复触发。pass@k 回答"它有没有能力做对",pass^k 回答"它能不能稳定地做对",可靠性说的是后者。τ-bench 的论文里,当时最强的模型任务成功率不到 50%,零售场景的 pass^8 掉到 25% 以下。
pass@k 在报告里很好看,因为它随 k 单调上升。看到别人报 pass@10,先问一句:产品上用户真的能重试 10 次吗?
实际怎么算:无偏估计量
真实的 p 不知道。做法是对每个任务跑 n 次(n ≥ k),数出成功 c 次。直觉上会直接把 \(\hat p = c/n\) 代进公式,但这样算出的 pass@k 系统性偏低;pass^k 正好相反,直接代入会系统性偏高。
Codex 论文给的无偏估计是:从 n 次结果里不放回地抽 k 次,算"抽到的 k 次全失败"的概率。pass^k 同理,算"抽到的 k 次全成功"的概率(τ-bench 用的就是这个):
$$ \widehat{\text{pass@}k} = 1 - \frac{\binom{n-c}{k}}{\binom{n}{k}} \qquad\qquad \widehat{\text{pass}^k} = \frac{\binom{c}{k}}{\binom{n}{k}} $$例子:跑 10 次,成功 3 次,求 pass@5。
- 无偏估计:\(1 - \binom{7}{5}/\binom{10}{5} = 1 - 21/252 \approx 91.7\%\)
- 直接代入:\(1 - 0.7^5 \approx 83.2\%\),低了 8 个多百分点
有多个任务时,每个任务单独算,再对所有任务取平均。不能先把所有任务的成功率平均成一个 p 再套公式,原因见「公式成立的前提」一节。
from math import comb
def pass_at_k(n: int, c: int, k: int) -> float:
"""n 次里成功 c 次,pass@k 的无偏估计(Chen et al., 2021)。"""
if not 0 <= c <= n or not 1 <= k <= n:
raise ValueError(f"需要 0 <= c <= n 且 1 <= k <= n,收到 n={n}, c={c}, k={k}")
if n - c < k: # 失败次数不够 k 次,抽 k 次必有成功
return 1.0
return 1 - comb(n - c, k) / comb(n, k)
def pass_hat_k(n: int, c: int, k: int) -> float:
"""n 次里成功 c 次,pass^k 的无偏估计(τ-bench)。"""
if not 0 <= c <= n or not 1 <= k <= n:
raise ValueError(f"需要 0 <= c <= n 且 1 <= k <= n,收到 n={n}, c={c}, k={k}")
return comb(c, k) / comb(n, k)
print(pass_at_k(10, 3, 5)) # 0.9167
print(pass_hat_k(10, 7, 3)) # 0.2917
p 本身就不准:和 rule of three、Wilson 的关系
上面的公式都把 p 当成已知。可 p 是跑出来的估计,本身带着不确定性,pass^k 会把这个不确定性放大。
因为 \(p^k\) 随 p 单调增,把 p 的置信区间两端各取 k 次方,就是 pass^k 的区间:
| 观测 | p 的 95% 区间 | pass^8 点估计(无偏) | pass^8 区间 |
|---|---|---|---|
| 30 次成功 27 次 | Wilson 双侧 95%:[74.4%, 96.5%] | 37.9%(直接代入 0.98 为 43.0%) | [9.4%, 75.5%] |
| 30 次全过 | 精确法单侧 95%:p ≥ 90.5% | — | ≥ 45.0% |
30 次成功 27 次,pass^8 可能低到 9%,也可能高到 75%。只报一个点估计,等于没说清楚。注意两行口径不同:第一行是双侧区间,第二行是单侧下界。
反过来算样本量:要证明 pass^8 ≥ 90%,单次失败率得 ≤ 1.3%。零失败的前提下,按精确法算至少要连续 228 次全过(rule of three 速算约 230 次)。
公式成立的前提
\(p^k\) 成立,要求 k 次运行相互独立,并且每次的成功率都是同一个 p。现实中常常不满足。
两个 Agent 在 10 个任务上的平均单次成功率都是 70%:
- Agent A:每个任务的成功率都是 70%,时好时坏。pass^8 = \(0.7^8 \approx 5.8\%\)。
- Agent B:7 个任务每次必对,3 个任务每次必错。pass^8 = 70%。
平均成功率一样,pass^8 差了 12 倍。A 是"不稳定",B 是"稳定地不会",修法完全不同。这也是 pass^k 必须按任务分别算再平均的原因。
同一个 prompt 重跑 k 次、换 temperature、换用例,算不算"独立的 k 次"?这是下一章「独立性假设」的内容。
常见错误说法
- “pass@10 达到 95%,Agent 很可靠”:pass@k 说的是能力,不是可靠性。
- “把 c/n 代进 1 − (1 − p)k 就是 pass@k”:有偏,系统性偏低,要用组合数公式。
- “所有任务平均成功率 70%,所以 pass^8 = 0.78”:任务之间成功率不一样,要逐任务算再平均。
- “跑了 8 次就能算 pass@8”:n = k 时估计值只能是 0 或 1,方差很大。Codex 论文每题采样 n = 200。