榜单上那个 90.4 分,可能连它自己的置信区间都撑不住

0 阅读7分钟

一个 200 行的脚本,教你在信任何 Agent 榜单之前先算三件事:区间多宽、差显不显著、要多少题才够。

背景:这周榜单圈连着塌了三次

先把事实摆齐,全部可查:

时间事件
11-02Agent Index v1.2 预览:七项全有才计分,86 个模型仅 26 个合格,Opus 5.5 90.4 居首
11-03SemiAnalysis GDPx 访谈:Terminal-Bench 4 里约六成任务是坏的
11-05有人指出 Terminal-Bench 4.0.0 早在 9-04 就被 Epoch 判 Flawed(门槛:缺陷影响 ≥20% 总分)
11-09Terminal-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] 内,小样本和极端比例都稳。核心公式:

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 就能跑。

#!/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 一致):

一、每个分数的 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 题

三个结论,逐条看:

  1. 0.1 分的差距,半宽 2.6pp。 差比噪声小一个数量级。这不是"微弱领先",这是"无法区分"。
  2. 2.8 分的头名差距,z=1.42,不显著。 这条比第一条更反直觉——连榜首之争都撑不住。想坐实 2.8pp,每配置大约需要 900 题以上。
  3. 想分辨 3pp,每配置 769 题。 这就是 eval 预算的硬约束:不是"跑 30 条看着还行",是"要么 770 条,要么承认你测不出 3 个点"。

三、比模型差距大得多的是什么

跑第二段数据:

三、同一模型只换一环,分数就变了
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 值然后挑显著的讲。

# 多重比较的最小修法
m = 325
alpha_corrected = 0.05 / m
z_corrected = 3.02  # 双侧 alpha≈0.0025 对应的临界值

五、能直接抄进 PR 的三条规则

  1. 报告分数必须带 n。 不带 n 的百分比,不是数据,是观感。
  2. 任何"提升 X%"的结论,先算 MDE。 提升小于 MDE 的,一律写"未观察到显著差异",不写"略有提升"。
  3. 对比两个配置时,固定考卷和 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)。涉及单一榜单的结论已注明适用范围。