跑了 300 次,就是 300 个样本吗?

不一定。30 个用例各跑 10 次,如果失败总扎堆在几个难用例上,有效样本可能只有 80 个左右。

rule of three、Wilson 区间、pass^k 都默认每次运行相互独立。这个前提不成立时,算出来的区间会窄得离谱,名义 95% 实际可能只有 70%。

1前面的公式都有同一个前提

前面几个概念,每个公式都写着"每次试验相互独立、失败率相同"(统计上叫独立同分布,i.i.d.):

概念用到独立性的地方
rule of three\(P(\text{全过}) = (1-p)^n\):n 次全过的概率等于单次通过概率连乘
Wilson 区间标准误 \(\sqrt{p(1-p)/n}\):n 个独立样本平均后,波动按 \(1/\sqrt n\) 缩小
pass^k\(p^k\):k 次都成功的概率等于单次成功概率连乘

这些公式从头到尾只用到 n 这个数字。n 到底是多少,没有人替你检查。

2"同一个用例跑 30 次"和"30 个用例各跑 1 次"回答的是两个问题

同一个用例跑 30 次30 个不同用例各跑 1 次
估的是什么这一个用例上的不稳定率(flaky 率)Agent 在这类任务上的平均失败率
随机性来自模型采样、环境抖动用例本身的难度差异 + 采样
30 次全过能说明这个用例上失败率 ≤ 9.5%这类任务整体失败率 ≤ 9.5%(前提:用例是随机抽的)
不能说明换一个用例还行不行某个具体用例是否稳定
问题不在哪种跑法更好,而在结论要和跑法对得上。拿"同一用例跑 30 次全过"去宣称"Agent 失败率 ≤ 10%",就是把一个用例的结论推广到了所有用例。

实际评测一般两者都有:C 个用例,每个跑 m 次,一共 \(n = C \times m\) 次。麻烦在于:同一个用例的 m 次结果彼此相关,难的用例总是挂,简单的总是过。

3Agent 评测里,独立性是怎么被破坏的

来源表现后果
同一用例重跑用例难度不同,失败集中在少数难用例上失败扎堆,有效样本接近用例数,而不是运行次数
temperature = 0 / 缓存同样的输入给出几乎一样的输出,甚至命中 prompt 缓存或结果缓存30 次运行实际上是 1 次运行复制了 30 份
共享环境状态上一次运行留下的数据库记录、浏览器 cookie、文件前一次失败会连带后一次失败(或者反过来掩盖失败)
LLM Judge 系统性误判Judge 对某种输出格式总是判过,或者总是判错误判不会被平均掉,所有样本一起偏
同一时段 API 限流 / 故障某 10 分钟内的运行一起超时失败在时间上扎堆,和 Agent 本身能力无关

共同点:多次运行共享了某个随机因素(用例、种子、环境、Judge、时间段)。共享得越多,它们越像"同一次"。

4用数字衡量:簇内相关系数和设计效应

把同一个用例的 m 次运行看成一"簇"。簇内相关系数(ICC,记作 \(\rho\))衡量同一簇内两次运行的结果有多像:

$$\rho = \frac{\text{用例之间失败率的方差}}{\text{总方差}} = \frac{\operatorname{Var}(p_i)}{\mu(1-\mu)}$$

\(p_i\) 是第 i 个用例自己的失败率,\(\mu\) 是所有用例的平均失败率。

有了 \(\rho\),就能算出 n 次运行相当于多少个独立样本:

$$n_{\text{eff}} = \frac{n}{1 + (m-1)\rho}$$

分母 \(1+(m-1)\rho\) 叫设计效应(design effect)。区间宽度按 \(1/\sqrt{n}\) 变化,所以区间会变宽约 \(\sqrt{\text{设计效应}}\) 倍。

前提:每个用例重跑次数 m 相同、所有用例共用同一个 ρ、用例是随机抽的。m 不相等时用平均簇大小近似,或者直接用 cluster bootstrap。

例子:30 个用例 × 10 次 = 300 次,挂了 30 次

按独立算(n = 300)ρ = 0.3(n_eff ≈ 81)
设计效应11 + 9 × 0.3 = 3.7
95% Wilson 区间[7.1%, 13.9%][5.2%, 18.4%]
区间宽度6.8 个百分点13.3 个百分点(约 1.9 倍)
实际覆盖率按独立算的区间,只有约 70% 的时候罩住真实失败率(下面的模拟器可以验证)

加重跑次数,救不回来

固定 C = 30 个用例、\(\rho = 0.3\),只增加每个用例的重跑次数 m:

m1251030100∞
总运行次数30601503009003000∞
n_eff304668819398100

m 趋于无穷时,\(n_{\text{eff}} \to C/\rho = 30 / 0.3 = 100\)。跑 3000 次也只顶 98 个独立样本。想要更多有效样本,要加用例,不是加重跑。

5模拟器

每个用例的真实失败率从一个平均为 μ 的分布里抽(Beta 分布,簇内相关正好是 ρ),然后每个用例跑 m 次。所有随机数用固定种子,点"换一批"才会变。

场景

如果每次运行都独立(ρ = 0)

实际(ρ = 0.30)

每一行是一个用例,每一格是一次运行,红色是失败。两边平均失败率相同,右边的失败会集中在某几行。

总运行次数 n
设计效应 1 + (m−1)ρ
有效样本 n_eff

覆盖率:同样的设置重做 2000 次实验

每次实验重新抽用例、重新跑,看 95% 区间有多少次罩住真实平均失败率 μ。好方法应该贴着 95%。

按独立算的 Wilson(n)
按 n_eff 修正的 Wilson

金色竖线是 95%。修正用的是模拟器里真实的 ρ;实际工作中 ρ 要从数据里估,或者直接用按用例的 cluster bootstrap。

有效样本随重跑次数的变化

总运行次数 C × m 有效样本 n_eff 上限 C / ρ

6对策

做法解决什么
按用例聚类分析先算每个用例自己的失败率,再在用例层面汇总;报告里写清"C 个用例 × m 次",而不是只写总次数
多加用例,少加重跑有效样本的上限是 C/ρ,由用例数决定
分层抽样按任务类型、难度分层抽用例,避免某一类用例占满样本
cluster bootstrap重抽样时以用例为单位整组抽,不按单次运行抽,区间自动把簇内相关算进去
隔离环境每次运行用干净的数据库、新的浏览器上下文,避免状态串到下一次
每次运行换种子、关缓存避免"跑 30 次其实是同一次"
记录 trace,事后检查扎堆看失败是不是集中在某些用例、某个时间段、某种 Judge 输出;集中就说明独立性有问题

cluster bootstrap(按用例整组重抽)

import numpy as np

def cluster_bootstrap_ci(fails: np.ndarray, n_boot: int = 10_000, seed: int = 0):
    """fails: 形状 (C, m) 的 0/1 矩阵,每行一个用例。返回平均失败率的 95% 区间。"""
    if fails.ndim != 2 or fails.size == 0:
        raise ValueError("fails 需要是非空的 (用例数, 重跑次数) 矩阵")
    rng = np.random.default_rng(seed)
    per_case = fails.mean(axis=1)                  # 每个用例自己的失败率
    idx = rng.integers(0, len(per_case), size=(n_boot, len(per_case)))
    means = per_case[idx].mean(axis=1)             # 整组抽用例,再求平均
    return np.percentile(means, [2.5, 97.5])

关键是 per_case[idx]:抽中一个用例,就带上它的全部 m 次结果。如果按单次运行抽,就又回到了"假装独立"。

7面试要点与常见错误

一句话讲清楚

置信区间只认独立样本。同一个用例的多次运行彼此相关,有效样本是 \(n/(1+(m-1)\rho)\),上限是用例数除以 ρ;所以报告要写"几个用例 × 每个几次",区间要按用例聚类算,想提高精度要加用例。

追问准备

  1. ρ 怎么估?用单因素方差分析(ANOVA)的组间、组内方差估;或者绕开 ρ,直接用 cluster bootstrap 算区间。
  2. 什么时候可以不管?每个用例只跑 1 次(m = 1),设计效应就是 1;但这时要求用例本身是从任务分布里随机抽的。
  3. temperature = 0 就一定没用吗?对"这个用例稳不稳"没用,重跑 30 次几乎是同一个结果;但对"这批用例整体通过率"没影响,那时的样本是用例。
  4. 两个版本对比怎么办?让两个版本跑同一批用例,按用例配对比较(McNemar 检验、配对 bootstrap),用例难度的影响会在配对里抵消掉。这是下一步要学的。

常见错误说法

❌ "跑了 300 次,挂了 30 次,95% 区间 [7.1%, 13.9%]":没交代是几个用例 × 几次,默认成了 300 个独立样本。
❌ "同一个用例跑了 30 次全过,所以 Agent 失败率 ≤ 10%":这只说明这一个用例,换个用例不一定成立。
❌ "区间太宽了,每个用例再多跑 100 次":ρ 大的时候加重跑几乎没用,要加用例。
❌ "bootstrap 按单次运行重抽":抽样单位必须是用例,否则区间和按独立算的一样窄。

8练习

题 1(算):20 个用例,每个跑 5 次,一共 100 次。估出来 ρ = 0.5。有效样本是多少?
n_eff ≈
设计效应 = 1 + (5 − 1) × 0.5 = 3,n_eff = 100 / 3 ≈ 33.3。跑了 100 次,统计上只顶 33 个独立样本。
题 2(算):30 个用例 × 10 次,ρ = 0.3。和"按独立算"相比,95% 区间大约宽几倍?
约 倍
设计效应 = 1 + 9 × 0.3 = 3.7,宽度按 1/√n 变化,所以宽 √3.7 ≈ 1.92 倍。用 Wilson 公式实算是 1.94 倍(6.8 → 13.3 个百分点)。
题 3(算):还是 30 个用例、ρ = 0.3。每个用例重跑无限多次,有效样本最多能到多少?
n_eff 上限 =
m → ∞ 时,n_eff = Cm / (1 + (m−1)ρ) → C / ρ = 30 / 0.3 = 100。要突破这个上限,只能加用例。
题 4(概念):同事说:"我把同一个退款用例跑了 30 次,全过,所以退款 Agent 的失败率 95% 置信度下 ≤ 10%。"哪里有问题?
0 次失败用 rule of three 没错,B 不对。问题在于样本来自同一个用例,估的是这个用例的 flaky 率。要谈"退款 Agent 的失败率",得从退款任务里抽多个不同用例。
题 5(排查):评测报告显示 300 次运行挂了 30 次。翻 trace 发现 30 次失败里有 24 次集中在 3 个用例上,另有 5 次发生在同一个 10 分钟窗口内。最该先做什么?
失败扎堆说明 ρ 很大,A 加重跑几乎没用。C 是在挑数据,那 3 个用例恰恰是最有价值的信息。B 同时处理了两种相关:用例层面的聚类和时间上的共同故障。