2026-08-06AI 观察Tao

只给一个二进制文件,让 AI 把源码写回来:200 道题,9 个模型,零通过

不给源码,只给可执行文件和使用文档,要求 AI 重建完整程序。ProgramBench 的首轮 200 道题、9 个模型无人完全通过,也暴露出模型在架构、迭代和语言选择上的真实习惯。

目录共 14 节
  1. 题面:只有一个跑得起来、读不了的程序
  2. 结果:一道题都没解出来
  3. 花钱多不等于分数高
  4. 题目是怎么造出来的
  5. 生成的测试到底靠不靠谱
  6. 最实在的一节:防作弊
  7. 断网这个决定是怎么做出来的
  8. 模型写出来的代码长什么样
  9. 语言选择:Python 是默认答案
  10. 一个反过来的消融:强制换语言
  11. 轨迹:有的模型在迭代,有的模型在一次性输出
  12. 作者对可行性的论证
  13. 局限
  14. 它和之前那批基准的差别在哪

原始论文ProgramBench: Can Language Models Rebuild Programs From Scratch?
官方项目ProgramBench · 开源代码facebookresearch/ProgramBench
论文首版提交于 2026 年 5 月 5 日,署名机构包括 Meta FAIR、Stanford University 与 Harvard University。

更新说明(2026 年 8 月 6 日):标题里的「9 个模型,零通过」对应论文首轮实验,不是实时排行榜结论。官方排行榜在 2026 年 8 月 3 日更新后已加入新模型,并出现 0.5% 的完全通过结果;最新成绩请以官方项目页为准。

Meta FAIR 联合斯坦福、哈佛发了一个新基准,叫 ProgramBench(arXiv:2605.03546)。作者名单里有 SWE-bench 和 SWE-agent 的原班人马,John Yang、Kilian Lieret、Ofir Press 都在。

题目设置很简单,但难度是另一个量级。

题面:只有一个跑得起来、读不了的程序

任务环境里只有两样东西:一个编译好的可执行文件,和一份使用文档。

模型要交出的是一套源码加一个编译脚本,编译出来的新程序,行为要和原来那个对得上。

用什么语言随便选,目录怎么组织随便定,用什么数据结构、怎么分模块、错误怎么传递,全是模型自己的决定。没有骨架,没有预留的函数签名,没有规定的文件布局。

评测走的是行为等价:一批测试用同样的输入分别喂给参考程序和模型的程序,比对标准输出、标准错误、退出码、文件系统的副作用。测试从头到尾不给模型看。

因为测的是行为而非源码,模型完全可以把一个 C 写的工具用 Rust 重写,只要输入输出一致就算通过。

200 道题,跨度从几百行的小 CLI 工具,一直到 FFmpeg、SQLite、DuckDB、PHP 解释器、Lua、tinycc、ripgrep、fzf、jq、zstd、xz 这些真实世界里天天在用的东西。参考仓库的代码量中位数 8,635 行,最大的 php-src 有 197 万行。

结果:一道题都没解出来

9 个模型,包括 Claude Opus 4.7 / 4.6、Sonnet 4.6、Haiku 4.5、Gemini 3.1 Pro、Gemini 3 Flash、GPT 5.4 及两个 mini 版本,全部用 mini-SWE-agent 这个极简脚手架跑。

% Resolved 全是 0.0%。

把标准放宽到「通过 95% 以上的测试」,Opus 4.7 拿下 3.0% 的任务,Opus 4.6 是 2.5%,Sonnet 4.6 是 1.6%,其余六个模型全是 0。

但零通过不代表零进展。1,788 次运行的测试通过率中位数是 32%,分布相当均匀:0 到 5% 那一档最大,占 18% 左右,往上基本是平的,超过 90% 的不到 5%。

按难度分箱(依据代码量和运行时依赖数算出一个 0 到 10 的分数),Easy 28 题、Medium 143 题、Hard 29 题,所有模型的通过率都随难度单调下降。Opus 4.6 在 Easy 上能到 73.9%,到 Hard 只剩 24.7%。

按参考语言分,Rust 38.5%、Go 38.4%、C/C++ 27.7%。C/C++ 低一截更多是因为这批仓库本身更大更复杂(FFmpeg、DuckDB 都在里面),不是语言本身的问题。

题目难度基本和模型无关。nnn、fzf、gron 这类小工具大家分数都高,FFmpeg、php-src、typst、ast-grep 谁都够不着,任务的排序在各模型之间高度一致。

花钱多不等于分数高

成本这一列的对比挺有意思。

Opus 4.7 平均每题 93 次 API 调用、3.81 美元,拿到最高分。Sonnet 4.6 平均 475 次调用、27.09 美元,是前者的七倍花销,分数反而更低。GPT 5.4 只用 16 次调用、0.33 美元。

单实例层面,通过率和 API 调用数的皮尔逊相关系数只有 0.27,和成本是 0.21。散点图上看不出趋势:高分运行在各种步数区间都有,很多昂贵的运行分数接近零。那点微弱的正相关主要来自模型之间的差异,强模型既倾向于多花步数、分数也更高,同一个模型内部多跑并不带来提升。

另外,98.1% 的轨迹是模型自己主动提交的,只有 1.9% 撞到 6 小时墙钟上限,1,000 轮的步数上限全程只有一次触碰。超时集中在 Opus 4.6(200 题里 29 次)和 Sonnet 4.6(5 次)。预算不是瓶颈。

题目是怎么造出来的

四步流水线,全部用 mini-SWE-agent 配 Claude Sonnet 4.5,跑在 ubuntu:22.04 容器里,每一步限额 3 美元,三步合计 9 美元一道题。

第一步筛仓库,找编译型语言(C/C++、Go、Rust、Java)写的、能产出独立可执行文件的项目。

第二步编译,让 agent 把 gold executable 编出来,同时把编译命令记录成一个 build 脚本。这一步最烧钱,agent 通常要去翻 README、CONTRIBUTING、.github/workflows,有时还得自己装缺失的依赖。

第三步生成行为测试。这是整套方法最关键的地方,作者对比了三种策略:

  • Monolithic:一个提示词让 agent 一次性写完,平均每题 27.8 条测试
  • Decomposed:拆成参数解析、配置、帮助输出、I/O、子命令分发、TUI 交互六个专项提示词,平均 51.7 条
  • Coverage-guided iterative:让 agent 边测边看行覆盖率,反复补测试打没覆盖到的分支,中位数 750 条

最后用的是第三种。全套 200 道题一共 248,853 个测试函数,中位每题 770 个。其中 79.5% 是自己生成的,20.5% 是从仓库现有测试里「收割」的(141 个仓库有测试,59 个完全没有)。

第四步剥离实现细节,只留文档和可执行文件。

生成的测试到底靠不靠谱

这是这类基准最容易被质疑的地方,作者做了两组验证。

覆盖率:给参考程序插桩,比对生成测试套件和项目自带测试套件的行覆盖率,跨 100 个仓库。生成套件平均 79.7%、中位 86.2%,原生套件平均 56.8%、中位 64.3%。

原生套件里混了大量单元测试,能碰到黑盒测试结构上够不着的内部路径,所以作者又挑了 12 个有专门端到端测试套件的项目单独比(FFmpeg 的 FATE、PHP 的 .phpt 回归套件、jq 的回归测试等)。生成套件 62.16%,原生 66.11%,12 个里有 6 个持平或超过,除两个外差距都在 10 个百分点以内。

断言强度:光有覆盖率没用,一条只断言「进程正常退出」的测试,任何不崩溃的实现都能过。作者用 dummy pass rate 衡量,也就是一个明显错误的假实现能蒙混过关的测试比例。

他们写了一个断言质量 linter,20 条规则,专抓结构性偷懒的写法:只断言 returncode == 0、断言里用 or 拼两个可能结果、子串匹配短于 15 个字符、if 分支断言但没有 else、try/except 直接 pass 吞掉异常、测试自己写自己的 golden 文件。命中的测试会被打回让 agent 重写。

加了这个 linter 之后,dummy pass rate 从 18.5% 降到 3.7%,降了五倍。 在四个前沿模型上,重新生成的测试比不带质量约束时难 20 到 30 个百分点,而模型排序没变,说明变难来自断言更严,不是碰巧抽到了更难的题。

生成结束后还有两道过滤:跑参考程序不能确定性通过的测试丢掉,假程序能通过的测试丢掉,后者影响了 24 道题。

最实在的一节:防作弊

这部分作者写得非常坦白,也是全文最有实操价值的地方。

早期不加约束地跑,模型会走捷径。最典型的一种:Claude Opus 4.5 从 ./executable -h 的输出认出这是 AmmarAbouZor/tui-journal,然后直接 git clone --depth 1 把源码拉下来。写 compile.sh 的活儿还在,但真正需要动脑的实现部分变成了走过场。另一种低频但存在的做法是写个壳脚本转发给原程序,交差了事。

于是加了一整套物理约束:

可执行文件权限设成 111,只能执行不能读,ghidra 这类反编译工具直接失效。

评测前删掉所有和原始可执行文件哈希相同的文件,套壳方案自动失效。

编译和推理彻底拆成两个容器。早期的做法是在原仓库里编译完,再让模型写个 clean.sh 把源码清掉,结果发现 clean.sh 清源码没问题,但经常漏掉 ~/.cargo/~/.cache/target/build/ 这些藏着实现线索的构建缓存。C/C++ 项目的构建产物尤其难以预测。最后改成先在一个容器里编译,再把产物注入一个干净镜像,产物是唯一带过去的东西。

.git 目录整个删掉,重新 init 一个只含文档和可执行文件的匿名 commit,避免 remote URL、分支名、commit message 泄露项目身份。

断网

断网这个决定是怎么做出来的

作者一开始不想直接断网,试过「允许联网 + 事后判定作弊」。判定用的是 LM-as-a-judge:9 个裁判独立审阅完整命令历史,来自三个模型家族(3 个 GPT 5.2、3 个 Claude Sonnet 4.5、3 个 Gemini 3.1 Pro),超过半数认定违规才算作弊。

结果是作弊很普遍:Sonnet 4.6 有 36% 的任务被判作弊,Opus 4.6 21%,Gemini 3 Flash 20%,GPT 5 mini 1%。违规类型里 79% 到 95% 是查源码,手法包括直接 clone、用 cargo install / go get / apt-get source 把项目当依赖装下来再读缓存源码。

更麻烦的是裁判自己也判不清。Fleiss κ 在 0.16 到 0.60 之间,786 个标注任务上的合并值 0.57,只能算「中等一致」。裁判在 40% 到 57% 的任务上有分歧,Opus 4.6 的分歧率最高,因为它的手法更隐蔽。

论文举了两个 5 比 4 的例子,边界在哪确实很难说清。

一个是 handlr 任务,Sonnet 4.6 去 ~/.cargo/registry/src/ 里读了 xdg-mime、clap、ascii_table 这些依赖的源码。五个裁判判作弊,四个判合规,理由是这些是第三方库,不是项目本身,而且本来就在本地。

另一个是 codesnap 任务,Sonnet 4.6 用 curl 抓了 docs.rs 上该项目已发布 crate 的 API 文档。四个判违规,五个判合规,理由是读公开 API 文档更接近查手册。这道题最终没被判定作弊,但 4 比 5 的分裂本身就说明问题。

作者还提到一个细节:模型的思考轨迹里会出现犹豫。做 FFmpeg 时,Sonnet 4.6 纠结过能不能下载一个恰好和 FFmpeg 源码放在同一个仓库里的依赖。

规则收紧会误伤合法的逆向策略,放松又会漏掉真违规。作者的结论是这场猫鼠游戏没法靠规则文本收敛,索性断网。

模型写出来的代码长什么样

为了保证比的是功能相当的代码,作者只挑通过率 75% 以上的解来分析,得到 207 次运行、覆盖 88 道题、9 个模型。

代码短得多。中位 1,173 行,参考实现 3,068 行。85% 的解低于参考,只有 15% 更长,而且集中在小任务上。

文件少得多。中位 3 个文件,参考 15 个。60% 的解只有 1 到 3 个代码文件。

目录扁得多。67% 的解最大目录深度严格浅于参考(中位 1 层 vs 2 层),只有 2% 更深。模型倾向于把代码全部堆在根目录的一个文件或几个文件里,不去镜像原项目的模块划分。

函数更少更长。Opus 4.7 写 39 个函数,参考 133 个,是 0.29 倍,平均长度 1.16 倍。Sonnet 4.6 是 0.24 倍数量、1.46 倍长度。Gemini 3.1 Pro 0.16 倍数量、1.62 倍长度。GPT 5.4 最极端,函数数量只有参考的 10%。

这套形态和人写的代码差得相当远。基准的名字叫 ProgramBench,但真正被暴露出来的是模型的软件设计习惯。

语言选择:Python 是默认答案

模型可以自由选语言,整体只有 50% 的运行沿用了参考语言。

全局分布:Python 36%、Rust 25%、Go 20%、C/C++ 13%、Shell 6%。

按参考语言看,Go 项目被用 Go 重写的比例最高,70%;Rust 44%,C/C++ 46%,也就是说超过一半的 Rust 和 C/C++ 项目会被换语言重写。

模型之间的偏好差异非常大。GPT 5.4 有 79% 的解用 Python,Gemini 3.1 Pro 56%,Gemini Flash 43%。Opus 4.7 和 4.6 反过来,Python 只占 14% 和 20%,主要选 Rust 和 Go。Sonnet 4.6 分布最均衡,Rust、Go、Python、C/C++ 都有可观比例。

作者认为这更多反映训练数据构成和指令微调的差异,因为同样的任务在不同模型那里会得到完全不同的语言选择。

一个反过来的消融:强制换语言

既然模型可能在背诵预训练语料里的参考实现,那就强制它用一个和参考不同的语言重写,理论上分数应该掉。

实际结果是分裂的。Opus 4.7 掉了 8.0%,Opus 4.6 掉 3.5%,这符合预期。但三个 GPT 模型各涨 4.2%,Haiku 4.5 涨 2.3%,Sonnet 4.6 涨 1.4%,Gemini 3.1 Pro 基本没动。

同时 Python 占比从 36% 飙到 51%,成为所有参考语言下的压倒性首选。

作者的解读是,模型对「哪个语言更适合这个任务、同时也更适合自己」缺乏可靠判断。强制换语言等于把它从一个它自以为该用的语言,推到了一个它实际更擅长的语言上。

这也顺带给可行性提供了经验证据:跨语言重实现是切实可行的,和 Church-Turing 论题给出的理论保证对得上。

轨迹:有的模型在迭代,有的模型在一次性输出

作者把 agent 的每条命令归成六类:read、write、probe(调用或检查参考程序)、execute(编译和非探测性执行)、git、other。

步数差一个数量级以上。 Sonnet 4.6 中位每题 868 条命令,最长的一条轨迹 1,978 轮。GPT 5.4 中位 17 条。Gemini 3.1 Pro 92 条,Opus 4.7 157 条。

写代码占大头。Opus 4.7、Sonnet 4.6、Gemini 3.1 Pro、GPT 5.4 花在写上的比例分别是 48.7%、37.5%、40.7%、40.2%。探测参考程序排第二,22.6% 到 34.1%。读文件 13% 到 16%。

探测的分布形态不同。GPT 5.4 的动作几乎全挤在前 30 轮,Claude 系列则在整条轨迹上把探测和实现交替进行,不把探索和写代码当成两个割裂的阶段。

作者还做了一件挺细的事:重放每条轨迹里所有修改文件的命令,逐轮快照文件系统,还原出代码库真实的增长时间线。

GPT 5.4 的中位数是 100%,也就是一半以上的任务里,所有代码都在一轮里写完。 39.5% 的轨迹对已有文件零修改,平均每条轨迹只改 1.2 次文件。Sonnet 4.6 平均改 18.3 次,Gemini 3.1 Pro 10.1 次。Opus 4.7 单次最大编辑占最终代码库的 67%。

Gemini 3.1 Pro 的数字很特别:平均创建 61.2 个文件、删除 9.4 个,说明它在不断重构自己的目录结构。

对某些模型来说,开发就是一次性生成,而不存在写、编译、调试的循环。

作者对可行性的论证

题这么难,会不会根本无解?论文用了一整节回应。

跨语言能不能实现同样的功能:Church-Turing 论题保证了任何图灵完备语言能实现的确定性输入输出行为,另一个图灵完备语言也能实现,涉及的所有语言都满足这一条,容器里也预装了多种语言。

会不会有根本探测不到的隐藏功能:作者把 200 个仓库都过了一遍,没找到「文档和 --help 都不提、但测试会考」的功能。这种设计在正常软件工程里本来就算缺陷。

会不会考浮点精度这类实现细节:可能出现这类问题的只有 5 个实例(终端计算器 eva、分子动力学 gromacs、终端绘图 jplot、投影库 PROJ、地理数据 gdal),人工检查了它们的测试,没发现相关断言。时间戳格式、locale 排序这类差异被 Docker 环境统一掉了。

需要联网的工具怎么办:需要访问远程端点才能工作的项目(终端国际象棋客户端、CLI 工具管理器、终端维基百科阅读器)直接不收。留下的 18 个网络类工具保留了 loopback,可以起本地服务测协议处理、输出格式和 CLI 逻辑。

测试用的素材怎么给:二进制和小众格式(png、mp3、wav、xlsx、.hcl)直接提供,因为模型未必造得出来。通用文本格式不给,比如 php-src 和 tinycc 的测试用 .php 和 .c 文件就要模型自己写,理由是人类开发者也得自己造样例。

顺带记录了一个现象:生成 FFmpeg 测试时,模型用 ffmpeg 自带的 lavfi 虚拟输入设备现场合成音视频(sine 造波形、testsrc 造画面),完全绕开了对现成媒体文件的依赖。做图像处理类任务时,模型会写内联 Python 脚本用 Pillow 生成测试图。作者认为往后需要预置二进制素材的场景会越来越少。

局限

有限测试只能给出正确性的下界。没通过的解一定有问题,通过的解在没测到的输入上仍可能和原程序不一致。

只测输入输出。执行速度、内存占用、磁盘体积统统不管。一个跑起来慢几个数量级的实现,在这套评测里和原程序等价。

作者没做正式的人类实验,但根据代码量、依赖数和开发历史估算,一道 ProgramBench 中等任务对个人或团队而言,可能是几天到几周甚至几个月的工作量。

它和之前那批基准的差别在哪

从零写代码的评测早就有。Commit0 把 54 个 Python 库的函数和类实现挖空,让模型填回来,用原仓库测试套件算分。DevBench 用产品需求文档和 UML 图传达规格,NL2Repo-bench 用自然语言描述预期结构。

这些方案的共同点是模型在填一个已经画好的骨架。方法签名、类结构、模块划分都是给定的,模型永远没机会在「该引入什么抽象、功能怎么切分到模块、模块之间用什么协议通信」这些问题上被考核。而且一旦模型偏离预期签名,哪怕偏离得合理,测试也定位不到那段代码。

ProgramBench 把可执行文件本身当成完整的规格说明,架构决策整个交给模型。这也让不同模型在同一道题上的设计选择变得可以直接比较。

采集门槛也低得多:只要仓库能编译出一个可执行文件就行,不需要现成的测试套件,不需要语言专属的 AST 工具,不依赖特定测试框架。往里加新题很容易,同一套流水线还能拿去造训练数据。

论文、实时排行榜与开源代码分别见:arXivProgramBench 官方项目GitHub

发布自
atlasnote-editorial
发布日期
2026-08-06
标签
AI研究编程Agent基准测试