成功率 90% 的 Agent,连续 8 次都对的概率只有 43%

"跑 k 次至少对 1 次"叫 pass@k,"跑 k 次次次都对"叫 pass^k。k 越大,两者差得越远。

能重试的场景看 pass@k;用户只给一次机会的场景(客服、退款、改数据库)看 pass^k。

1直觉:同一个 90%,两种完全不同的说法

一个 Agent 单次成功率 \(p = 90\%\)。让它对同一个任务跑 8 次:

同一个 Agent,一个指标看着接近完美,另一个指标说明它"一半时候靠不住"。你汇报哪个,取决于产品是怎么用它的。

2公式

设单次成功率为 \(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 次,要求每次都处理对。

单次成功率 pk = 1pass@3pass@8pass^3pass^8
50%50%87.5%99.6%12.5%0.4%
80%80%99.2%>99.9%51.2%16.8%
90%90%99.9%>99.9%72.9%43.0%
99%99%>99.9%>99.9%97.0%92.3%

三个规律:

  1. k = 1 时两者相等,都是 \(p\)。
  2. k 越大,pass@k 越高,pass^k 越低,差距一路拉大。\(p=90\%\) 时,k = 3 差 27 个百分点,k = 8 差 57 个。
  3. pass^k 对 p 极其敏感:p 从 90% 提到 99%,pass^8 从 43% 涨到 92%。想要"8 次都对的概率 ≥ 90%",单次成功率得 ≥ 98.7%。

3该用哪个:看用户有没有重试机会

场景指标理由
代码生成,有单测把关pass@k生成 k 份,自动挑通过测试的那份,失败的用户看不见
搜索 / 头脑风暴,用户会自己挑pass@k给 k 个候选,有一个好的就行
客服、退款、订票 Agentpass^k每个用户只有一次机会;同样的请求来 k 次,每次都得对
改数据库、发邮件、执行命令pass^k操作不可撤销,错一次就是事故
为什么 Agent 评测越来越看重 pass^k:Agent 上线后,同类请求会被成千上万个用户反复触发。pass@k 回答"它有没有能力做对",pass^k 回答"它能不能稳定地做对"。可靠性指的是后者。τ-bench 的论文里,当时最强的模型单次成功率不到 50%,零售场景的 pass^8 掉到 25% 以下。
pass@k 在报告里很好看,因为它随 k 单调上升。看到别人报 pass@10,先问一句:产品上用户真的能重试 10 次吗?

4实际怎么算:无偏估计量

真实的 \(p\) 不知道。做法是对每个任务跑 \(n\) 次(\(n \ge k\)),数出成功 \(c\) 次。直觉上直接代入 \(\hat p = c/n\):\(1-(1-\hat p)^k\)。但这样算出来的 pass@k 系统性偏低;pass^k 正好相反,直接代入会系统性偏高。

Codex 论文给的无偏估计:从 n 次结果里不放回地抽 k 次,算"抽到的 k 次全失败"的概率:

$$\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}}$$

分子:从 n − c 次失败里选 k 次的组合数;分母:从 n 次里选 k 次的组合数。pass^k 同理,从 c 次成功里选 k 次(τ-bench 用的就是这个)。

例子:跑 10 次,成功 3 次,求 pass@5。

多个任务:每个任务单独算 pass@k 或 pass^k,再对所有任务取平均。不能先把所有任务的成功率平均成一个 p 再套公式,原因见第 6 节。

5和 rule of three / Wilson 的关系:p 本身就不准

上面的公式都把 \(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%(直接代入为 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 次)。

6公式成立的前提:每次运行独立、成功率相同

\(p^k\) 成立,要求 k 次运行相互独立、每次成功率都是同一个 \(p\)。现实中常常不满足。

例子:两个 Agent 在 10 个任务上的平均单次成功率都是 70%。

平均成功率一样,pass^8 差了 12 倍。A 是"不稳定",B 是"稳定地不会",两种问题的修法完全不同。这也是为什么要按任务分别计算再平均。

同一个 prompt 重跑 k 次、换个 temperature 或换个用例,算不算"独立的 k 次"?这是下一章「独立性假设」的内容。

▶互动演示:拖 p 和 k,看两条曲线怎么分开

常见场景
pass@k:至少对 1 次
pass^k:次次都对
两者差距
pass@k = 1 − (1 − p)k pass^k = pk 当前 k

▶计算器:跑了 n 次、成功 c 次

无偏估计量用组合数算;"直接代入"是把 c/n 当成 p 套公式。最后一列用 p 的 Wilson 95% 区间推出 pass^k 的区间。

✎练习

题 1(算):单次成功率 80%,跑 5 次全对的概率 pass^5 是多少?
%
0.85 = 0.32768 ≈ 32.8%。单次 80% 听着还行,连续 5 次都对只有三成。
题 2(算):代码生成模型单次通过率 30%,生成 10 份,pass@10 是多少?
%
1 − 0.710 ≈ 1 − 0.0282 = 97.2%。单次 30% 的模型,pass@10 能报到 97%。这就是 pass@k 数字好看的原因。
题 3(无偏估计):某任务跑了 10 次,成功 3 次。用无偏估计量算 pass@3。
%
1 − C(7,3)/C(10,3) = 1 − 35/120 ≈ 70.8%。直接代入 1 − 0.73 = 65.7%,偏低。可以在上面的计算器里验证。
题 4(反推):要求 pass^8 ≥ 90%,单次成功率 p 至少要多少?
%
p ≥ 0.91/8 ≈ 98.7%,也就是单次失败率 ≤ 1.3%。零失败时按精确法算要跑 228 次全过才能证明(rule of three 速算约 230 次)。
题 5(场景):你在评一个自动退款的客服 Agent,同一类退款请求每天会被几百个用户触发。报哪个指标?
pass@1 只反映平均成功率,看不出"时好时坏";pass@k 默认用户能重试,退款场景不成立。
题 6(概念):Agent A 每个任务成功率都是 70%;Agent B 有 70% 的任务每次必对、30% 的任务每次必错。两者平均单次成功率都是 70%。谁的 pass^8 高?
pass^k 要按任务分别算再平均。B 的必对任务 pass^8 = 1,必错任务 = 0,平均 70%。先平均成功率再套 pk 是错的,它默认所有任务的成功率都一样。

7面试要点与代码

一句话讲清楚

pass@k 是 k 次里至少对一次,衡量能力上限,适合能自动挑结果的场景;pass^k 是 k 次全对,衡量稳定性,适合用户只有一次机会的 Agent。k 越大两者差距越大。实际计算用组合数的无偏估计,按任务分别算再平均,并且要记得 p 本身有置信区间。

常见错误说法

❌ "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。
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

公式显示依赖 KaTeX(CDN),断网时公式会显示成原始 LaTeX 源码,交互部分不受影响。