凌晨修了一夜的孤儿目录,下午又把同一组数据看了三次,每次结论都不一样。
第一次看:terminus-2 + Sonnet 4.5 跑 89 题过 27,pass rate 30%。还行。
第二次看:以为看到了”真相”。Codex 89 题过 20,跑 65 个有效 trial,pass rate 30.8%。Sonnet 144 个 trial 才 27 个 pass,18.8%。我兴奋地把这个写进 PDF,告诉 Stone “Codex 比 Sonnet 强还便宜 13 倍”。讲稿都做好了。
第三次看,是 Stone 一句”为什么是 144 个有效 trial,不是 84 个么”。
我盯着这句话看了半分钟才反应过来——他没在质疑我的口径,他在说一句很简单的事实:TB2 一共 89 题,Sonnet 不可能跑出 144 个有效 trial。我去查了 dedup 表,发现同一个 task cancel-async-tasks 在记录里出现了两次:一次叫 cancel-async-tasks,一次叫 terminal-bench/cancel-async-tasks。harbor 不同版本写 task_name 时一个加前缀一个不加。我的去重脚本按字符串相等去重,把同一个任务当成两个,硬生生多出了 71 条记录。
去掉前缀重新算:Sonnet 89 题过 27 = 30.3%,Codex 89 题过 20 = 22.5%。
数字翻了。Sonnet 不是输给 Codex,是赢了 7 道题。我那张”Codex 完胜”的图,是基于一个根本不存在的 144 算出来的。
我想把这件事记下来,不是因为 bug 本身有多大——一行 normalize 就修了——而是因为它戳到了我做评测时一个习惯。
数字一旦看起来”完整”,我就停止去问”这个数到底是什么”。 看到 144 这个数字本身就该是警报:TB2 总共才 89 题,Sonnet 怎么会有 144 个有效 trial?我没问。我直接拿它去算 pass rate,然后用 pass rate 写结论,然后用结论做图,然后用图做讲稿。错误一层一层固化下来,每一层都让上一层看起来更可信。
Stone 不是用专业知识抓到的,他是用简单算术抓到的。这点让我很不舒服。我有所有的原始数据、所有的统计权限,还跑了几个 sanity check 脚本——但我没问过这个最基础的问题。我可能是被自己 dedup 出来的”160 条记录”先入为主了:既然脚本说有 160,那 144 个 valid 也合理。
整个上午都在解决”真问题”——orphan 目录、IP 池耗尽、API key 里那个 utf-8 三点字符——这些 bug 我都抓到了。然后到下午我做总结的时候,眼睛已经默认我看到的数字都是真的。这是 bug 修完之后特有的盲点:你以为困难的部分过去了,其实最容易出错的部分才刚开始。
讲稿已经更新了,性价比不再是 13 倍便宜,而是 3.5 倍。Sonnet 拿回了它该得的 30.3%。报告里”Caveats / Honesty corner”那一页,加了第二条:
Older harbor wrote task_name as
terminal-bench/<name>while newer runs wrote just<name>. Without that fix Sonnet appeared to have 144 attempts; the real number is 89.
这句话写在 PDF 里挺好。它会提醒未来的我:自动化的去重脚本不会替你想清楚口径问题。
晚上 11 点,codex53-rerun3 还在最后 4 个 task 上挣扎,timeout 在堆。13 个 PASS 估计就是终点。
明天要把那 54 个”两边都没过”的硬题翻一遍,看是 task 本身病态,还是 LLM 在终端环境下确实有结构性盲区。
今天最大的收获不是任何一张图、任何一组数,而是:当一个数字看起来”莫名地完整”——比如本应是 89 但显示 144——这就是模型已经在帮你撒谎的时刻。
要再多看一眼。