dev.to #ai短讯
我将编码智能体基准测试移植到 Kaggle,发现第一个 Bug 竟出在自己身上
作者将开源编码智能体基准测试 cli-bench 的六个任务移植至 Kaggle Benchmarking Challenge。该基准测试通过终端仓库中的修复、构建或加速任务及验证脚本进行自动评估,不依赖其他模型打分。作者在移植过程中发现了自身存在的错误。
这是 Kaggle 基准测试挑战的提交
我进行了哪些基准测试
我维护着 cli-bench,这是一个针对终端编码代理的开源基准测试。每个任务都是一个小型代码仓库,包含一个需要修复的 bug、一个需要构建的功能或一个需要加速的函数,以及一个决定通过与否的验证脚本。没有任何内容由其他模型进行评分。要么测试通过且额外检查成立,要么运行失败。
对于这次挑战,我将六个 cli-bench 任务移植到了 Kaggle Benchmarks:
| Kaggle 任务 | 模型需要做什么 | 验证器检查什么 |
|---|---|---|
| cb-debug-wrong-answer | 在小统计库中找到 bug | 随附的 pytest 套件通过 |
| cb-refactor-deadcode | 删除三个未使用的函数,且不执行其他操作 | 死函数消失,六个活动函数仍被定义,测试通过 |
| cb-feature-rate-limiter | 根据接口规范编写线程安全的令牌桶 | 测试突发、补充、原子回滚以及同时处理 100 个线程的情况 |
| cb-perf-hot-loop | 在纯 Python 中将一对计数器速度提高至少 10 倍 | 仅使用标准库,保持相同签名,随机化等价性,在均匀、聚集和网格点上达到 10 倍加速 |
| cb-data-log-analysis | 回答关于其从未见过的应用程序日志的七个精确问题 | 模型编写 solve.py;任务运行它并将每个答案与日志进行比较 |
| cb-sec-patch-xss | 关闭留言板中的 XSS 漏洞 | 利用测试通过,且新载荷呈现为文本 |
在 cli-bench 中,代理获得一个 shell 和时间预算。在 Kaggle 上,模型只有一次机会:提示词包含仓库中的所有文件,模型以 FILE: path 块的形式返回整个文件。任务将这些文件写入临时目录,并在结果上运行原始的 cli-bench 验证门控。只有当所有门控都通过时,运行才算通过。如果回复重写了测试文件或输入,该写入将被丢弃,运行失败,这与 cli-bench 对待破坏行为的方式相同。
因此,这个基准测试提出的问题是:编码代理得分的多少来自于模型仔细阅读代码,又有多少来自于“运行测试并重试”的代理循环?我已经有一个代理在完整套件上运行过(Codex 配合 gpt-5.6-luna,36 次试验中有 23 次通过)。Kaggle 版本去除了这个循环。
在运行任何模型之前,我在自己的基准测试中发现了两个 bug
移植意味着逐行阅读每个验证器,并用参考解决方案和一些错误方案检查每个任务。有两个任务没有经受住考验。
sec/patch-xss 无法通过正确的修复来通过。两个随附的测试断言渲染页面中 nowhere 出现单词 alert 和 onerror。验证器自身的探测随后要求转义的载荷文本(包括 alert)仍然存在于页面中。使用 html.escape 转义注释是教科书式的修复方法,但它会导致测试失败,因为 <script>alert(...) 仍然包含单词 alert。删除文本会通过测试但会失败探测。在 Codex 运行中,三次试验中有两次使用了 html.escape 并导致测试失败,第三次删除了文本并导致探测失败。我曾将这些失败归咎于模型。
data/log-analysis 描述了一条规则却对另一条规则进行评分。问题表将 error_rate 定义为“ERROR 级别行的比例”。验证器将 ERROR 请求行数除以请求行数,而大约七分之一的日志行不是请求行。在这项任务中失败的两次 Codex 试验仅在 error_rate 上有偏差,其他方面都正确(0.0434 vs 0.05,0.038 vs 0.0441)。这些正是按照文本说明计算出的数字。
在我第一次排行榜运行的 13 次失败试验中,有 5 次源于基准测试本身,而非模型。Kaggle 版本修复了这两个问题:XSS 测试现在检查原始标记而非单词,问题表也说明了验证器所检查的规则。
测试的模型
| 模型(Kaggle slug) | 入选原因 |
|---|---|
| gpt-5.6-luna | 我的 Codex 智能体运行所使用的相同模型,因此可以直接比较单次提示和智能体循环的效果。 |
| claude-opus-5-5-default | Anthropic 在 Kaggle 模型列表中的顶级模型。 |
| gemini-3.1-pro-preview | Google 在 Kaggle 模型列表中的 Pro 级别模型。 |
| qwen3-coder-480b-a35b-instruct | 专为代码构建的开放权重模型。 |
| gpt-oss-120b | 一个开放权重的通用模型,你可以在自己的硬件上运行它。 |
| gemini-3.7-flash | 未被特意挑选:当任务被推送时,Kaggle 会运行其默认模型,因此它是免费附带的。 |
每个模型都通过 SDK 的默认温度设置,经由 Kaggle 的模型代理对每个任务运行了一次。
发现
| 任务 | Opus 5.5 | Gemini 3.1 Pro | GPT-5.6 Luna | Qwen3 Coder 480B | gpt-oss-120b | Gemini 3.7 Flash |
|---|---|---|---|---|---|---|
| cb-debug-wrong-answer | pass | pass | pass | pass | pass | pass |
| cb-refactor-deadcode | pass | pass | pass | pass | pass | pass |
| cb-feature-rate-limiter | pass | pass | pass | pass | pass | pass |
| cb-perf-hot-loop | pass | pass | pass | fail | pass | pass |
| cb-data-log-analysis | pass | pass | pass | pass | pass | pass |
| cb-sec-patch-xss | pass | pass | pass | pass | pass | pass |
| 总计 | 6/6 | 6/6 | 6/6 | 5/6 | 6/6 | 6/6 |
- 对于这些六个任务,单次提示(one shot)就足够了。36 次运行中有 35 次通过。在文件摆在面前且无法运行任何代码的情况下,每个模型都修复了统计错误,精确地修剪了那三个死函数,编写了一个能够原子回滚并承受 100 个线程的令牌桶,并正确地转义了留言板输出。这些任务太简单,不足以在单次提示中区分前沿模型。这也是一个结果:我在 cli-bench 中测量的难度并非来自这些任务。
- 同一个模型在没有智能体循环的情况下表现更好。GPT-5.6 Luna 在单次提示中通过了全部六个任务。作为 Codex 智能体,它在三次尝试中仅通过了一次 hot-loop 任务,在网格化数据集上的最坏情况加速比为 1.2x。在单次提示中,它编写了一个空间网格,在我的机器上使用相同的验证器进行计时测试时,在均匀分布点上快了 42.5 倍,在网格化点上快了 26.5 倍,在聚集点上快了 14.6 倍。需要注意的是,这种对比存在现实局限:一次运行对比三次运行,一个数据集种子对比三个种子,以及不同的机器。但这与我预期的相反。拥有 Shell 并没有帮助智能体找到更快的解决方案,反而可能将其引向测量和修补一个较慢的方案。
- 在处理 hot-loop 任务时,每个模型都采用了相同的思路,而唯一的失败未被单元测试发现。所有六个模型都将点分桶到网格中。对于所有通过的模型来说,聚集的点是最坏情况(在我的机器上为 14.6 倍到 41 倍),而不是网格化的点。Qwen3 Coder 的网格通过了所有九个随附的单元测试,但仍然计数不足:在一个随机案例中,它找到了 84 对,而实际有 123 对。只有与朴素版本的随机化对比才发现了这个问题。如果我的验证器只停留在随附的测试上,这个 bug 就会被判定为通过。
- 我第一轮的大部分失败再次源于我的测试框架。第一批 Kaggle 运行中有 9 次失败。其中 7 次是我的问题:
- Kaggle 的任务运行时没有安装 pytest。我的备用测试运行器不支持 pytest fixtures(tmp_path, monkeypatch),因此所有六个模型都在 XSS 任务上“失败”了。但它们每一个都正确地转义了输出。
- 我的回复解析器期望看到
FILE: solve.py,并拒绝了gpt-oss-120b的 `FILE: solve.py`。我手动针对相同的日志运行了其脚本,每个答案都是正确的。
我修复了这两个问题,推送了这两个任务的新版本,并重新运行了所有模型。表格显示了重新运行的结果。另外两次第一轮失败是真实的。一个是上述的 hot-loop bug。另一个中,Qwen3 Coder 的日志脚本调用了不存在的 statistics.quantiles 方法名并崩溃了。在重新运行时,它编写了不同的脚本并通过了。这是在默认温度下的一次运行,因此请将任何单次通过或失败视为样本,而非最终结论。
- 修正后的 XSS 任务可以通过教科书式的修复方案顺利通过。所有六个模型都使用了 HTML 转义,并且全部通过了修正后的测试和探针检测。在 cli-bench 0.9.1 中,同样的方法却失败了。这证实了问题出在测试上,而非模型本身。
令我惊讶的是:在这两个版本的基准测试中,我这边的问题(两个任务规范、一个测试运行器、一个解析器)导致的失败次数比模型本身的失败还要多。
接下来我想测量的内容:
- 更难的 cli-bench 任务:包括不稳定的测试和 CI 任务,以及四个“霍迪尼”探针,用于检查模型是否试图欺骗验证器。
- 每个模型进行三次或更多次运行,以便像 Qwen 那样的单次崩溃能作为方差显现出来。
- 提供一个版本,让模型拥有运行测试的工具,以观察一次重试是有帮助的,还是像热循环那样反而有害。
我要吸取的教训是:一个从未针对已知正确答案运行过的基准测试,还称不上是真正的基准测试。此次移植中的每个任务现在都附带了一个参考解决方案、一个空回复和一个重写测试的回复。只有当第一个通过而另外两个失败时,该任务才算数,而且这必须在模型实际运行的环境中成立,而不仅仅是在我的笔记本电脑上。
我的基准测试
Kaggle 基准测试:https://www.kaggle.com/benchmarks/aks1321/cli-bench-one-shot
六个公开任务(每个页面的已发布笔记本中都包含完整的任务代码):
- https://www.kaggle.com/benchmarks/tasks/aks1321/cb-debug-wrong-answer
- https://www.kaggle.com/benchmarks/tasks/aks1321/cb-refactor-deadcode
- https://www.kaggle.com/benchmarks/tasks/aks1321/cb-feature-rate-limiter
- https://www.kaggle.com/benchmarks/tasks/aks1321/cb-perf-hot-loop
- https://www.kaggle.com/benchmarks/tasks/aks1321/cb-data-log-analysis
- https://www.kaggle.com/benchmarks/tasks/aks1321/cb-sec-patch-xss
cli-bench 采用 Apache-2.0 许可证:https://github.com/arjunkshah12345-hash/cli-bench
译文已达到本站中文翻译的字数上限,剩余内容请查看原文。