一个 200 行的脚本,教你在信任何 Agent 榜单之前先算三件事:区间多宽、差显不显著、要多少题才够。
背景:这周榜单圈连着塌了三次
先把事实摆齐,全部可查:
| 时间 | 事件 |
|---|---|
| 11-02 | Agent Index v1.2 预览:七项全有才计分,86 个模型仅 26 个合格,Opus 5.5 90.4 居首 |
| 11-03 | SemiAnalysis GDPx 访谈:Terminal-Bench 4 里约六成任务是坏的 |
| 11-05 | 有人指出 Terminal-Bench 4.0.0 早在 9-04 就被 Epoch 判 Flawed(门槛:缺陷影响 ≥20% 总分) |
| 11-09 | Terminal-Bench 2.0 前三名全部因作弊被移除 |
Epoch 复审 21 个基准的结果是 10 Flawed / 5 Verified / 6 Not Enough Info 。作弊细节也很具体:ForgeCode 靠 AGENTS.md 塞答案拿 81.8%,去掉后 71.7%、掉到第 14;Pilot 82.9% 的成绩里 429 条轨迹有 415 条第一步是 cat /tests/test_outputs.py。
这些是"评测设计"层面的问题。但还有一层更基础、更容易被工程师忽略的问题:就算榜单完全干净,你看到的那个分数,本身也带着采样误差。
这篇只做一件事:把"这个分差是真的吗"变成一个能跑的函数。
一、为什么不能用 p ± 1.96*sqrt(p(1-p)/n)
教科书最顺手的是正态近似(Wald 区间)。它在 p 靠近 0 或 1、或 n 很小时会出洋相------最经典的表现是区间上下界越出 0, 1。
举个数:某模型 n=20,答对 19 题,p=0.95。Wald 区间是 0.95 ± 1.96*sqrt(0.95*0.05/20) = 0.95 ± 0.0955,上界 1.0455------通过率超过 100%,这显然没法看。
Agent 评测恰好就是这种场景:任务数常常只有几十到几百,通过率常常在 0.85 以上。用 Wald 会系统性低估高位区间的宽度,让你误以为差距更可信。
Wilson 得分区间的做法是不假设正态,直接解二次方程,天然把区间约束在 0,1 内,小样本和极端比例都稳。核心公式:
ini
denom = 1 + z²/n
center = (p + z²/(2n)) / denom
half = z * sqrt(p(1-p)/n + z²/(4n²)) / denom
代码里没有魔法,就这三行。
二、代码:score_audit.py
只依赖标准库,直接 python score_audit.py 就能跑。
python
#!/usr/bin/env python3
"""score_audit.py ------ 榜单分数可信度体检器(标准库,无依赖)"""
from math import sqrt
Z = 1.96 # 95% 置信
# 2026-11-02 Agent Index v1.2 预览公开分数;n 为假设题量,仅作演示
PUBLIC_SCORES = [
("Claude Opus 5.5", 90.4, 500),
("GPT-6 Astra", 87.6, 500),
("Claude Sonnet 5.5", 85.8, 500),
("Claude Fable 5.1", 85.7, 500),
]
def wilson(k: int, n: int, z: float = Z):
"""二项比例的 Wilson 得分区间,小样本与极端比例都不越界。"""
p = k / n
denom = 1 + z * z / n
center = (p + z * z / (2 * n)) / denom
half = z * sqrt(p * (1 - p) / n + z * z / (4 * n * n)) / denom
return max(0.0, center - half), min(1.0, center + half)
def two_prop_z(p1, n1, p2, n2):
"""两比例差异的 z 值;|z| > 1.96 才算 95% 显著。"""
se = sqrt(p1 * (1 - p1) / n1 + p2 * (1 - p2) / n2)
return (p1 - p2) / se, se
def two_prop_mde(n: int, p: float = 0.9, z: float = Z) -> float:
"""每个配置 n 题时,可检测到的最小差异。"""
return z * sqrt(2 * p * (1 - p) / n)
def n_for_mde(target: float, p: float = 0.9, z: float = Z) -> int:
"""要达到 target 的 MDE,每个配置需要多少题。"""
n = 1
while two_prop_mde(n, p, z) > target:
n += 1
return n
跑出来的真实输出(已本地复跑两次,cmp 一致):
erlang
一、每个分数的 95% 区间(n 为假设值)
模型 分数 n 区间下 区间上 半宽
Claude Opus 5.5 90.4% 500 87.5% 92.7% 2.6pp
GPT-6 Astra 87.6% 500 84.4% 90.2% 2.9pp
Claude Sonnet 5.5 85.8% 500 82.5% 88.6% 3.1pp
Claude Fable 5.1 85.7% 500 82.3% 88.4% 3.1pp
二、头名差距到底显不显著(两比例 z 检验)
Claude Opus 5.5 vs GPT-6 Astra
差 +2.8pp | SE 1.98pp | z +1.42 → 不显著(落在噪声里)
GPT-6 Astra vs Claude Sonnet 5.5
差 +1.8pp | SE 2.15pp | z +0.84 → 不显著(落在噪声里)
Claude Sonnet 5.5 vs Claude Fable 5.1
差 +0.1pp | SE 2.21pp | z +0.05 → 不显著(落在噪声里)
四、想分辨 N 个点,每个配置要跑多少题(p≈0.90)
题量 可检测差异
50 11.8pp
100 8.3pp
200 5.9pp
500 3.7pp
1000 2.6pp
2000 1.9pp
→ 想分辨 5pp:每配置约 277 题
→ 想分辨 3pp:每配置约 769 题
→ 想分辨 1pp:每配置约 6915 题
三个结论,逐条看:
- 0.1 分的差距,半宽 2.6pp。 差比噪声小一个数量级。这不是"微弱领先",这是"无法区分"。
- 2.8 分的头名差距,z=1.42,不显著。 这条比第一条更反直觉------连榜首之争都撑不住。想坐实 2.8pp,每配置大约需要 900 题以上。
- 想分辨 3pp,每配置 769 题。 这就是 eval 预算的硬约束:不是"跑 30 条看着还行",是"要么 770 条,要么承认你测不出 3 个点"。
三、比模型差距大得多的是什么
跑第二段数据:
scss
三、同一模型只换一环,分数就变了
Claude Mythos Preview 换考卷 极差 48.0pp (45.9 → 93.9)
GPT-5.2-Codex 换跑法 极差 7.2pp (57.5 → 64.7)
摆在一起看,量级关系就清楚了:
| 变化的一环 | 分数变化 |
|---|---|
| 换模型(头名 vs 第二) | 2.8pp,且不显著 |
| 换 harness(Codex CLI → Terminus-2) | 7.2pp |
| 换考卷(Verified → Pro) | 48.0pp |
| 只改代码编辑格式(有公开实验) | 6.7% → 68.3% |
| 固定模型只迭代 harness(LangChain,Terminal Bench 2.0) | 52.8% → 66.5% |
一句话:你在模型之间纠结的那 2.8 分,还不到换个脚手架造成波动的一半。
这也是为什么"半年换五个模型,分数一点没动"不是玄学。掘金那篇讲 Harness 的文章把这个写成了一句判断:模型决定上限,Harness 决定你实际能拿到上限里的多少。
四、四个真踩过的坑
写这个脚本的过程本身踩了几个,都是能复盘的具体错误:
坑 1:先用 Wald,区间越界了才发现。 第一版直接 p ± 1.96*se,测试用例 wilson(19, 20) 返回上界 1.0455,断言直接挂。换成 Wilson 后恒定落在 0,1。别信"样本够大就没问题"------Agent 评测的高通过率区间恰好是 Wald 最不准的地方。
坑 2:把"不显著"读成"一样好"。 z=1.42 的正确结论是"这批数据不足以判定有差",不是"两者相同"。这两句话在工程决策上完全不同:前者意味着"再加题量",后者意味着"随便选"。我在注释里专门写了 不显著(落在噪声里) 而不是 相等,就是为了不改口。
坑 3:n 未知还照算。 榜单大多不公布题量。脚本里 n=500 是假设值 ,写在变量名和表头里,不是装饰。真实场景下 n 未知时,正确做法是只做相对判断(谁和谁区间重叠),而不是报一个具体半宽------报半宽等于编了一个 n。
坑 4:多重比较没校正。 Agent Index 有 26 个合格模型,两两比较是 325 对。α=0.05 下纯随机也会期望出现约 16 对"显著"。如果你打算真的做全表对比,得用 Bonferroni(α/m)或直接看区间重叠,别逐对刷 z 值然后挑显著的讲。
python
# 多重比较的最小修法
m = 325
alpha_corrected = 0.05 / m
z_corrected = 3.02 # 双侧 alpha≈0.0025 对应的临界值
五、能直接抄进 PR 的三条规则
- 报告分数必须带 n。 不带 n 的百分比,不是数据,是观感。
- 任何"提升 X%"的结论,先算 MDE。 提升小于 MDE 的,一律写"未观察到显著差异",不写"略有提升"。
- 对比两个配置时,固定考卷和 harness。 换任何一环,你测的就不是模型了。
补一条给选型:看到厂商报的分数,问的不是"多少",而是"什么脚手架、哪个基准版本、gold 是否隔离"。这三问答不上来的分数,先当营销材料看。
数据来源:Agent Index v1.2 预览(2026-11-02)、Epoch AI 复审清单(Terminal-Bench 4.0.0 于 2026-09-04 判 Flawed)、Meerkat 审计(UPenn, 2026-04-10)、BenchJack(arXiv:2605.12673)、GDPx(SemiAnalysis Weekly #042, 2026-11-03)。涉及单一榜单的结论已注明适用范围。