中文内容
用一组并行的 Claude 团队构建 C 编译器
发布于 2026 年 2 月 5 日
我们让 Opus 4.6 使用智能体团队来构建一个 C 编译器,然后(基本上)放手不管。以下是它让我们了解到的关于自主软件开发未来的内容。
作者:Nicholas Carlini,我们 Safeguards 团队的一名研究员。
我一直在尝试一种监督语言模型的新方法,我们称之为“agent teams”。
在 agent teams 中,多个 Claude 实例在共享代码库上并行工作,无需人类主动干预。这种方法极大地拓展了 LLM 智能体所能实现的范围。
为了对其进行压力测试,我让 16 个智能体从零开始编写一个基于 Rust 的 C 编译器,该编译器能够编译 Linux 内核。在近 2,000 次 Claude Code 会话和 20,000 美元 API 成本之后,这个智能体团队产出了一个 10 万行的编译器,可以在 x86、ARM 和 RISC-V 上构建 Linux 6.9。
这个编译器本身就是一个有趣的成果,但我在这里重点讨论我在为长期运行的自治 agent teams 设计执行框架时学到的东西:如何编写测试,在没有人类监督的情况下让智能体保持方向;如何组织工作,使多个智能体能够并行推进;以及这种方法的上限在哪里。
启用长时间运行的 Claude
现有的代理脚手架(如 Claude Code)需要操作员在线并可随时共同工作。如果你要求它解决一个漫长而复杂的问题,模型可能会解决其中一部分,但最终会停下来等待进一步输入——一个问题、一条状态更新,或一个澄清请求。
为了引发持续的自主推进,我构建了一个工具框架,将 Claude 置于一个简单循环中(如果你见过 Ralph-loop,这应该看起来很熟悉)。当它完成一个任务后,会立即接手下一个任务。(请在容器中运行它,而不是在你的实际机器上运行)。
#!/bin/bash
while true; do
COMMIT=$(git rev-parse --short=6 HEAD)
LOGFILE="agent_logs/agent_${COMMIT}.log"
claude --dangerously-skip-permissions \
-p "$(cat AGENT_PROMPT.md)" \
--model claude-opus-X-Y &> "$LOGFILE"
done
在代理提示中,我告诉 Claude 要解决什么问题,并要求它通过将问题拆分成小块、跟踪自己正在处理的内容、弄清楚接下来要处理什么,并且实际上持续推进直到完美,来处理这个问题。(关于最后一点,Claude 没有选择。循环会永远运行——尽管有一次,我确实看到 Claude 不小心执行了 pkill -9 bash,从而杀死了自己并结束了循环。哎呀!)
并行运行 Claude
并行运行多个实例可以解决单代理框架的两个弱点:
- 一个 Claude Code 会话一次只能做一件事。尤其是随着项目范围扩大,并行调试多个问题要高效得多。
- 运行多个 Claude agent 可以实现专业化分工。当少数 agent 被分配去解决当前的实际问题时,可以调用其他专门的 agent 来(例如)维护文档、关注代码质量,或解决专门的子任务。
我的并行 Claude 实现非常基础。系统会创建一个新的裸 git 仓库,并为每个 agent 启动一个 Docker 容器,将该仓库挂载到 /upstream。每个 agent 会克隆一个本地副本到 /workspace,并在完成后,从自己的本地容器推送到 upstream。
为防止两个 agent 同时尝试解决同一个问题,该 harness 使用了一种简单的同步算法:
- Claude 通过向 current_tasks/ 写入一个文本文件来“锁定”某个任务(例如,一个 agent 可能锁定 current_tasks/parse_if_statement.txt,而另一个锁定 current_tasks/codegen_function_definition.txt)。如果两个 agent 尝试声明同一个任务,git 的同步机制会迫使第二个 agent 选择另一个任务。
- Claude 处理任务,然后从上游拉取内容,合并来自其他智能体的更改,推送自己的更改,并移除锁。合并冲突很常见,但 Claude 足够智能,能够解决这些问题。
- 这个无限智能体生成循环会在一个全新的容器中启动一个新的 Claude Code 会话,然后循环重复。
这是一个非常早期的研究原型。我还没有实现任何其他智能体之间的通信方法,也没有强制执行任何用于管理高层目标的流程。我不使用编排智能体。
相反,我把如何行动的决定权留给每个 Claude 智能体。在大多数情况下,Claude 会接手“下一个最显而易见”的问题。当卡在某个 bug 上时,Claude 通常会维护一份持续更新的文档,记录失败的方法和剩余任务。在该项目的 git 仓库中,你可以浏览历史记录,观察它如何对各种任务加锁。
与 Claude 智能体团队一起编程的经验教训
脚手架会让 Claude 在循环中运行,但只有当 Claude 能判断如何取得进展时,这个循环才有用。我的大部分精力都花在设计 Claude 周围的环境上——测试、运行环境、反馈——使其能够在没有我参与的情况下自行定位方向。以下是我在编排多个 Claude 实例时发现最有帮助的方法。
编写极高质量的测试
Claude 会自主工作,解决我交给它的任何问题。因此,任务验证器必须近乎完美,否则 Claude 就会解决错误的问题。改进测试框架需要寻找高质量的编译器测试套件,为开源软件包编写验证器和构建脚本,并留意 Claude 正在犯的错误,然后在识别出这些失败模式后设计新的测试。
例如,在项目接近尾声时,Claude 开始频繁地在每次实现新功能时破坏现有功能。为了解决这个问题,我构建了一个持续集成流水线,并实施了更严格的强制检查,使 Claude 能够更好地测试自己的工作,从而确保新的提交不会破坏现有代码。
设身处地为 Claude 着想
我必须不断提醒自己,我是在为 Claude 编写这个测试框架,而不是为我自己,这意味着需要重新思考我关于测试应如何传达结果的许多假设。
例如,每个 agent 都会被放入一个没有上下文的全新容器中,并且会花费大量时间来熟悉情况,尤其是在大型项目中。在我们甚至还没进入测试之前,为了帮助 Claude 自助,我加入了相关说明,要求维护详尽的 README 和进度文件,并应频繁更新当前状态。
我还记住了这样一个事实:语言模型具有固有局限性,在这种情况下,需要围绕这些局限进行设计。其中包括:
- 上下文窗口污染:测试框架不应打印成千上万字节的无用内容。它最多应打印几行输出,并将所有重要信息记录到文件中,以便 Claude 在需要时找到。日志文件应易于自动处理:如果有错误,Claude 应写入 ERROR,并将原因放在同一行,这样 grep 就能找到。预先计算汇总统计数据会有帮助,这样 Claude 就不必重新计算。
- 时间盲点:Claude 无法感知时间,如果放任不管,它会乐于花数小时运行测试,而不是取得进展。该框架以较低频率打印增量进度(以避免污染上下文),并包含一个默认的 --fast 选项,用于运行 1% 或 10% 的随机样本。该子样本对每个代理而言是确定性的,但在不同 VM 之间是随机的,因此 Claude 仍会覆盖所有文件,而每个代理都能准确识别回归。
让并行化变得容易
当存在许多不同的失败测试时,并行化很简单:每个 agent 选择一个不同的失败测试来处理。在测试套件达到 99% 的通过率后,每个 agent 开始让一个不同的小型开源项目(例如 SQLite、Redis、libjpeg、MQuickJS、Lua)完成编译。
但当这些 agent 开始编译 Linux 内核时,它们卡住了。不同于拥有数百个独立测试的测试套件,编译 Linux 内核是一个巨大的任务。每个 agent 都会遇到同一个 bug,修复这个 bug,然后相互覆盖彼此的修改。运行 16 个 agent 并没有帮助,因为每个 agent 都卡在解决同一个任务上。
解决办法是使用 GCC 作为在线的已知正确编译器 oracle 来进行对比。我编写了一个新的测试框架,随机使用 GCC 编译内核的大部分文件,只用 Claude's C Compiler 编译剩余文件。如果内核运行正常,那么问题就不在 Claude 所负责编译的那部分文件中。如果出错,则可以进一步细化:将这些文件中的一部分重新用 GCC 编译。这让每个 agent 能够并行工作,修复不同文件中的不同 bug,直到 Claude 的编译器最终能够编译所有文件。(在这套方法奏效之后,仍然需要应用 delta debugging 技术来找出那些单独编译可行、但组合在一起会失败的文件对。)
多个 agent 角色
并行还带来了专业化。LLM 编写的代码经常会重新实现已有功能,因此我安排一个 agent 负责合并它发现的任何重复代码。我让另一个 agent 负责提升编译器本身的性能,又让第三个 agent 负责输出高效的编译后代码。我请另一个 agent 从 Rust 开发者的视角评审项目设计,并对项目进行结构性修改以提升整体代码质量,还让另一个 agent 负责文档工作。
对 agent 团队的极限进行压力测试
这个项目被设计为一项能力基准测试。我感兴趣的是对当今 LLM 勉强能够达到的能力极限进行压力测试,以帮助我们为未来模型将可靠实现的能力做好准备。
我一直在将 C Compiler 项目作为整个 Claude 4 模型系列的基准测试。和之前的项目一样,我首先起草了我想要的内容:一个从零开始的优化编译器,无依赖、兼容 GCC、能够编译 Linux 内核,并设计为支持多个后端。虽然我指定了设计的某些方面(例如,它应该有一个 SSA IR 以支持多轮优化 pass),但我没有详细说明如何实现。
此前的 Opus 4 模型几乎无法生成一个可用的编译器。Opus 4.5 是第一个跨过某个门槛的版本,使其能够生成一个可以通过大型测试套件的功能性编译器,但它仍然无法编译任何真正的大型项目。我在 Opus 4.6 上的目标,是再次测试其极限。
评估
在两周内近 2,000 次 Claude Code 会话中,Opus 4.6 消耗了 20 亿个输入 token,并生成了 1.4 亿个输出 token,总成本略低于 20,000 美元。即便与最昂贵的 Claude Max 套餐相比,这也是一个极其昂贵的项目。但这个总额只是我自己完成这项工作所需成本的一小部分,更不用说整个团队了。
这是一个洁净室实现(Claude 在其开发过程中的任何时候都无法访问互联网);它仅依赖 Rust 标准库。这个 100,000 行的编译器可以在 x86、ARM 和 RISC-V 上构建可启动的 Linux 6.9。它还可以编译 QEMU、FFmpeg、SQlite、postgres、redis,并且在大多数编译器测试套件(包括 GCC torture test suite)上的通过率达到 99%。它还通过了开发者的终极试金石:它可以编译并运行 Doom。
然而,该编译器并非没有局限性。这些包括:
- 它缺少将 Linux 从实模式启动出来所必需的 16 位 x86 编译器。为此,它会调用 GCC(x86_32 和 x86_64 编译器则是它自己的)。
- 它没有自己的汇编器和链接器;这些是 Claude 开始自动化的最后一些部分,目前仍有些存在 bug。演示视频是使用 GCC 汇编器和链接器制作的。
- 该编译器可以成功构建许多项目,但并非全部。它尚不能作为真正编译器的直接替代品。
- 生成的代码效率不是很高。即使启用所有优化,它输出的代码也比禁用所有优化的 GCC 更低效。
- Rust 代码质量尚可,但远达不到专业 Rust 程序员可能写出的质量。
生成的编译器几乎已经达到了 Opus 能力的极限。我曾(非常努力地!)尝试修复上述几个限制,但并未完全成功。新功能和错误修复经常会破坏现有功能。
举一个特别具有挑战性的例子,Opus 无法实现一个用于启动进入 16 位实模式的 16 位 x86 代码生成器。虽然该编译器可以通过 66/67 操作码前缀输出正确的 16 位 x86,但生成的编译输出超过 60kb,远远超过 Linux 强制执行的 32k 代码限制。因此,Claude 在这个阶段实际上是作弊,转而调用 GCC(这仅适用于 x86。对于 ARM 或 RISC-V,Claude 的编译器可以完全自行编译)。
该编译器的源代码可供获取。下载它,通读代码,并在你喜欢的 C 项目上试用。我一贯认为,理解语言模型能力的最佳方式是将它们推向极限,然后研究它们从哪里开始失效。接下来几天,如果你想继续关注 Claude 对这些局限性的持续改进尝试,我会继续让 Claude 推送新的变更。
展望未来
每一代语言模型都会开启与其协作的新方式。早期模型可用于 IDE 中的制表符补全。没过多久,模型就能根据文档字符串补全函数体。Claude Code 的发布将智能体带入主流,并使开发者能够与 Claude 结对编程。但这些产品都基于一个假设:用户定义任务,LLM 运行几秒或几分钟并返回答案,然后用户再提供后续指示。
智能体团队展示了自主实现完整、复杂项目的可能性。这使我们作为这些工具的用户,能够为自己的目标设定更高的抱负。
我们仍处于早期阶段,完全自主的开发伴随着真实的风险。当人类在开发过程中与 Claude 一起工作时,他们可以确保质量一致,并实时发现错误。对于自主系统而言,很容易看到测试通过就认为工作已经完成,而实际情况很少如此。我曾从事渗透测试工作,利用大型公司产品中的漏洞;想到程序员部署他们从未亲自验证过的软件,确实令人担忧。
因此,尽管这个实验令我兴奋,它也让我感到不安。构建这个编译器是我最近经历过的最有趣的事情之一,但我没想到在 2026 年这么早的时候,这件事竟然已经接近可行。语言模型以及我们用来与其交互的脚手架都在快速进步,这为编写海量新代码打开了大门。我预计正面应用会超过负面影响,但我们正在进入一个新世界,需要新的策略来安全地应对。
致谢
特别感谢 Josef Bacik、Edwin Chen、Bernardo Meurer Costa、Jake Eaton、Dan Kelley、Felix Klock、Jannet Park、Steve Weis,以及 Anthropic 内部许多其他人的帮助和贡献。
