Two metrics, two questions#
Let task have per-attempt success probability , and draw tasks from some population. The two metrics ask opposite questions about independent attempts:
pass@k, popularised for code generation by Chen et al. [1], asks whether at least one of attempts succeeds. It measures capability under search: if you can sample many times and verify, how often does a correct answer exist? pass^k, introduced with τ-bench by Yao et al. [2], asks whether all attempts succeed. It measures reliability: if the same customer request arrives times, does the agent handle it correctly every time? In τ-bench's retail domain, a strong function-calling agent with roughly 61% pass^1 fell below 25% at pass^8 [2].
At the two coincide. They diverge immediately afterwards, in opposite directions, and how fast they diverge depends on something a single-trial eval can't see: how success is distributed across tasks.
The hidden variable: heterogeneity across tasks#
Write the task-level success probabilities as having mean and variance . For ,
So for a fixed pass@1 , more variation across tasks gives higher reliability. That sounds backwards until you picture it. A polarised agent solves some tasks every time and others never; a homogeneous agent is a 60% coin flip on everything. The first is predictable, the second is not. For general , with ,
which decays like a power law in rather than geometrically. The homogeneous agent gets the geometric decay .
Simulation: same pass@1, very different reliability#
Setup. Three task populations, all with mean success 0.60: homogeneous (every task ); moderate spread, ; polarised, . 200,000 tasks each; metrics computed from the closed forms above by Monte Carlo over tasks.
pass_k.py.| Task population | pass@1 | pass@8 | pass^4 | pass^8 |
|---|---|---|---|---|
| homogeneous (all tasks p=0.6) | 60.0% | 99.9% | 13.0% | 1.7% |
| moderate spread (Beta conc. 10) | 60.0% | 99.3% | 17.6% | 5.3% |
| polarised (Beta conc. 1) | 60.0% | 87.3% | 37.4% | 28.8% |
Reading the result. The ordering flips between the two metrics. The homogeneous agent has the best pass@8 (99.9%) and the worst pass^8 (1.7%). The polarised agent is the reverse. A team optimising pass@k with best-of-n sampling and a team optimising customer-facing reliability can therefore prefer different models from the same leaderboard, and both are right for their own use.
Estimating pass^k without bias#
Running exactly trials and checking whether all succeeded is wasteful and noisy. Chen et al. [1] give an unbiased estimator for pass@k from trials with successes; the same combinatorial argument gives one for pass^k:
Both are averaged over tasks. The pass^k estimator is the probability that trials drawn without replacement from the observed are all successes, and its expectation is exactly . Here is a numerically safe implementation:
from math import comb
def pass_hat_k(n: int, c: int, k: int) -> float:
"""Unbiased estimate of P(all k trials succeed) from c successes in n trials."""
if c < k:
return 0.0
return comb(c, k) / comb(n, k)
# average over tasks: mean(pass_hat_k(n, c_i, k) for c_i in successes)
How many trials? We estimated pass^8 for 100 tasks drawn from the moderate-spread population (true value 4.29%) with trials per task, 4,000 replications.
Rule of thumb: use trials per task for a usable pass^k estimate on a 100-task suite. Since pass^k is small, the absolute error looks small, so report the interval as a ratio to the point estimate.
Estimating heterogeneity from your own data#
Since pass^k depends on how success varies across tasks, it helps to estimate that variation directly. With trials per task and observed success fractions , the sample variance across tasks mixes true heterogeneity with binomial noise:
A method-of-moments Beta fit then follows from and , via concentration , and gives a smooth pass^k curve for any , including values larger than . Treat extrapolation beyond as a model-based estimate and label it as one.
From pass^k to user experience#
pass^k connects directly to a user-level service objective. If a typical user brings the same kind of task times a month, the probability that they see at least one failure is . For the moderate-spread population, , so a user who repeats a task type eight times a month has a 95% chance of hitting a failure, even though the "accuracy" reported for the agent is 60%. Writing reliability targets in these terms ("95% of weekly users see no failure") makes the requirement on pass^k explicit and testable.
Which number to put in the launch memo#
- Customer-facing agents (support, claims, booking): report pass^k with matched to how often a typical customer or workflow repeats the task. An agent at 60% pass^1 and 5% pass^8 will fail every regular user eventually.
- Generate-and-verify pipelines (code with tests, search with a checker): pass@k is the right metric, because a reliable verifier turns any success into a usable one.
- Safety properties (no unauthorised refund, no data egress): the relevant quantity is the per-attempt failure probability compounded over volume, . We cover this in retry economics of prompt injection.
Limitations#
Trials are assumed independent given the task; in practice cached prompts, deterministic decoding and shared tool state create correlation, which raises pass^k relative to our model. Beta populations are a convenient family, not a fit to any specific agent. Synthetic simulation only. Code: methods.
Frequently asked questions#
What is the difference between pass@k and pass^k?
pass@k is the probability that at least one of k attempts succeeds, which measures capability with retries. pass^k is the probability that all k attempts succeed, which measures reliability. They are equal at k=1 and diverge after that.
How do you compute pass^k?
Run n ≥ k trials per task, count successes c, and average C(c,k)/C(n,k) over tasks. This estimator is unbiased for the per-task probability that k independent trials all succeed.
Why does pass^k matter for AI agents?
Customer-facing agents see the same kinds of request repeatedly, so a task solved 60% of the time will eventually fail every regular user. pass^k exposes that inconsistency, which a single-trial success rate hides.