原文: English original · Anthropic/OpenAI 官方

用一组并行 Claudes 构建 C 编译器

发布于 2026 年 2 月 5 日

我们让 Opus 4.6 使用 agent teams 构建一个 C 编译器,然后(基本上)放手不管。下面是这次实验让我们看到的自主软件开发的未来。

作者 Nicholas Carlini,Anthropic Safeguards 团队研究员。

我一直在试验一种监督语言模型的新方法,我们称之为“agent teams”。

在 agent teams 中,多个 Claude instances 会在一个共享代码库上并行工作,不需要人类主动介入。这种方法极大扩展了 LLM agents 能够完成的任务范围。

为了做压力测试,我让 16 个 agents 从零开始编写一个基于 Rust 的 C 编译器,目标是能够编译 Linux kernel。在将近 2,000 个 Claude Code sessions、20,000 美元 API 成本之后,这个 agent team 产出了一个 100,000 行的编译器,可以在 x86、ARM 和 RISC-V 上构建 Linux 6.9。

这个编译器本身就是一个有趣的成果,但本文重点讨论的是,我在为长时间运行的自主 agent teams 设计 harness 时学到的东西:如何编写测试,让 agents 在没有人类监督的情况下保持方向;如何组织工作,让多个 agents 可以并行推进;以及这种方法会在哪些地方触及上限。

让 Claude 能够长时间运行

现有的 agent scaffolds,比如 Claude Code,要求 operator 在线,并且随时可以一起工作。如果你要求它解决一个漫长而复杂的问题,模型也许能解决其中一部分,但最终它会停下来等待后续输入:一个问题、一条状态更新,或者一次澄清请求。

为了诱导持续、自主的进展,我构建了一个 harness,把 Claude 放进一个简单循环里(如果你见过 Ralph-loop,这应该会很熟悉)。当它完成一项任务,就立刻接手下一项。(请在 container 里运行它,不要在你的真实机器上运行。)

#!/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

在 agent prompt 中,我会告诉 Claude 要解决什么问题,并要求它把问题拆成小块、跟踪自己正在做什么、判断接下来该做什么,并且实际上一直做下去,直到完美为止。(在最后这一点上,Claude 没有选择。这个循环会永远运行。不过有一次,我确实看到 Claude 不小心执行了 pkill -9 bash,于是杀掉了自己并终止了循环。哎呀!)

并行运行 Claude

并行运行多个 instances 可以解决单 agent harness 的两个弱点:

  • 一个 Claude Code session 一次只能做一件事。尤其当项目范围扩大时,并行调试多个问题的效率要高得多。
  • 运行多个 Claude agents 可以实现专门化。少数 agents 负责解决手头的实际问题,其他专门化 agents 则可以被调用来(例如)维护文档、关注代码质量,或解决专门的子任务。

我的并行 Claude 实现非常简陋。系统会创建一个新的裸 git repo;对于每个 agent,都会启动一个 Docker container,并把 repo 挂载到 /upstream。每个 agent 会把本地副本 clone 到 /workspace,完成工作后,再从自己的本地 container push 到 upstream。

为了避免两个 agents 同时试图解决同一个问题,harness 使用了一个简单的同步算法:

  1. Claude 通过向 current_tasks/ 写入一个文本文件来给任务加“锁”(例如,一个 agent 可能锁定 current_tasks/parse_if_statement.txt,另一个则锁定 current_tasks/codegen_function_definition.txt)。如果两个 agents 试图认领同一个任务,git 的同步机制会迫使第二个 agent 选择另一个任务。
  2. Claude 处理该任务,然后从 upstream pull,merge 其他 agents 的改动,push 自己的改动,并移除锁。Merge conflicts 很常见,但 Claude 足够聪明,能弄清楚怎么处理。
  3. 无限的 agent-generation-loop 会在一个新的 container 中启动一个新的 Claude Code session,然后循环继续。

这是一个非常早期的研究原型。我还没有实现 agents 之间的任何其他通信方式,也没有强制任何管理高层目标的流程。我也没有使用 orchestration agent。

相反,我把如何行动的决定留给每个 Claude agent。大多数情况下,Claude 会接手“下一个最明显”的问题。当卡在某个 bug 上时,Claude 经常会维护一份持续更新的文档,记录失败过的方法和剩余任务。在这个项目的 git repository 中,你可以浏览历史记录,观察它如何给各种任务加锁。

与 Claude agent teams 一起编程的经验

scaffolding 会让 Claude 在循环中运行,但只有当 Claude 能判断如何取得进展时,这个循环才有用。我的大部分精力都花在设计 Claude 周围的环境上:测试、环境和反馈,让它可以在没有我的情况下自行判断方向。下面这些方法,是我在编排多个 Claude instances 时发现最有帮助的。

编写极高质量的测试

Claude 会自主工作,解决我交给它的任何问题。因此,任务 verifier 必须近乎完美,否则 Claude 就会解决错误的问题。改进 testing harness 需要找到高质量的编译器测试套件,为开源软件包编写 verifiers 和构建脚本,观察 Claude 正在犯的错误,并在识别出这些 failure modes 之后设计新的测试。

例如,在项目接近尾声时,Claude 每次实现一个新功能,都会频繁破坏已有功能。为了解决这一点,我构建了一条 continuous integration pipeline,并实现了更严格的约束,让 Claude 能更好地测试自己的工作,确保新的 commits 不能破坏已有代码。

站在 Claude 的角度思考

我必须不断提醒自己:我是在为 Claude 编写这个 test harness,而不是为自己写。这意味着我要重新思考许多关于测试应该如何传达结果的默认假设。

例如,每个 agent 都会被投放到一个没有上下文的新 container 中,并且会花大量时间熟悉环境,在大型项目中尤其如此。甚至在进入测试之前,为了帮助 Claude 帮助自己,我加入了指令,要求维护详尽的 READMEs 和进展文件,并频繁更新当前状态。

我也一直记着,语言模型存在内在限制;在这个案例中,必须围绕这些限制进行设计。这些限制包括:

  • Context window pollution:test harness 不应该打印成千上万字节的无用内容。它最多应该打印几行输出,并把所有重要信息记录到文件里,让 Claude 在需要时能找到。Logfiles 应该易于自动处理:如果有错误,Claude 应该写下 ERROR,并把原因放在同一行,这样 grep 就能找到。预先计算聚合摘要统计也会有帮助,这样 Claude 就不必自己重新计算。
  • Time blindness:Claude 无法感知时间,如果放任不管,它会很乐意花数小时运行测试,而不是取得进展。harness 会以较低频率打印增量进展(避免污染 context),并包含一个默认的 --fast option,用于运行 1% 或 10% 的随机样本。这个 subsample 对每个 agent 是确定性的,但在不同 VMs 之间是随机的,因此 Claude 仍然会覆盖所有文件,同时每个 agent 都可以准确识别 regressions。

让并行变得容易

当存在许多彼此不同的 failing tests 时,并行化很简单:每个 agent 选择一个不同的 failing test 来处理。当测试套件达到 99% 通过率后,每个 agent 都转而让一个不同的小型开源项目(例如 SQlite、Redis、libjpeg、MQuickJS、Lua)能够完成编译。

但当 agents 开始编译 Linux kernel 时,它们卡住了。和拥有数百个独立测试的测试套件不同,编译 Linux kernel 是一个巨大的整体任务。每个 agent 都会遇到同一个 bug,修复那个 bug,然后覆盖彼此的改动。运行 16 个 agents 并没有帮助,因为每个 agent 都卡在同一个任务上。

解决方法是使用 GCC 作为在线的已知正确 compiler oracle 来进行比较。我编写了一个新的 test harness,随机用 GCC 编译 kernel 的大部分文件,只用 Claude’s C Compiler 编译剩余文件。如果 kernel 能工作,那么问题就不在 Claude 那部分文件里。如果它出错,就可以进一步细化:把这些文件中的一部分重新用 GCC 编译。

这让每个 agent 可以并行工作,修复不同文件中的不同 bug,直到 Claude 的编译器最终可以编译所有文件。(在这成功之后,仍然有必要应用 delta debugging 技术,找出那些单独工作正常、但放在一起会失败的文件对。)

多种 agent 角色

并行也带来了专门化。LLM 编写的代码经常会重新实现已有功能,因此我安排一个 agent 负责合并它发现的所有重复代码。我让另一个 agent 负责提升编译器自身的性能,又让第三个 agent 负责输出高效的编译后代码。

我还要求另一个 agent 从 Rust developer 的角度批判项目设计,并对项目做结构性修改,以提升整体代码质量;另一个 agent 则负责文档。

对 agent teams 的极限做压力测试

这个项目被设计成一个能力 benchmark。我感兴趣的是对当今 LLMs 勉强能够做到的事情进行极限压力测试,以帮助我们为未来模型将能稳定做到的事情做好准备。

我一直把 C Compiler 项目作为整个 Claude 4 model series 的 benchmark。和之前的项目一样,我先起草了自己想要的东西:一个从零开始的 optimizing compiler,没有 dependencies,与 GCC 兼容,能够编译 Linux kernel,并且设计上支持多个 backends。虽然我指定了设计中的某些方面(例如,它应该有 SSA IR,以支持多轮 optimization passes),但我没有详细说明该如何实现。

此前的 Opus 4 models 几乎只能勉强产出一个可工作的编译器。Opus 4.5 是第一个跨过某个门槛的版本,它能产出一个可工作的编译器,可以通过大型测试套件,但仍然无法编译任何真正的大型项目。我使用 Opus 4.6 的目标,是再次测试极限。

Evaluation

在两周内将近 2,000 个 Claude Code sessions 中,Opus 4.6 消耗了 20 亿 input tokens,并生成了 1.4 亿 output tokens,总成本略低于 20,000 美元。即使与最昂贵的 Claude Max plans 相比,这也是一个极其昂贵的项目。但这个总额只是我自己完成这件事所需成本的一小部分,更不用说一整个团队了。

这是一个 clean-room implementation(Claude 在开发过程中任何时候都没有 internet access);它只依赖 Rust standard library。这个 100,000 行的编译器可以在 x86、ARM 和 RISC-V 上构建可启动的 Linux 6.9。它还可以编译 QEMU、FFmpeg、SQlite、postgres、redis,并且在大多数编译器测试套件上都有 99% 的通过率,其中包括 GCC torture test suite。它也通过了 developer 的终极 litmus test:它可以编译并运行 Doom。

不过,这个编译器并非没有局限。这些局限包括:

  • 它缺少从 real mode 启动 Linux 所必需的 16-bit x86 compiler。为此,它会调用 GCC(x86_32 和 x86_64 编译器是它自己的)。
  • 它没有自己的 assembler 和 linker;这些是 Claude 最后才开始自动化的部分,目前仍然有些 buggy。演示视频是用 GCC assembler 和 linker 制作的。
  • 这个编译器成功构建了许多项目,但不是所有项目。它还不能作为真正编译器的 drop-in replacement。
  • 生成的代码效率不高。即使启用所有 optimizations,它输出的代码也不如禁用所有 optimizations 的 GCC 高效。
  • Rust 代码质量还算合理,但远远达不到专家级 Rust 程序员可能写出的质量。

最终得到的这个编译器几乎已经达到 Opus 能力的极限。我曾经(非常努力地!)尝试修复上述几个限制,但没有完全成功。新功能和 bugfixes 经常会破坏已有功能。

一个尤其具有挑战性的例子是,Opus 无法实现启动进入 16-bit real mode 所需的 16-bit x86 code generator。虽然这个编译器可以通过 66/67 opcode prefixes 输出正确的 16-bit x86,但生成的编译输出超过 60kb,远远超出 Linux 强制要求的 32k 代码限制。因此,Claude 在这里干脆作弊,调用 GCC 来完成这一阶段(这只适用于 x86。对于 ARM 或 RISC-V,Claude 的编译器可以完全由自己完成编译。)

这个编译器的 source code 已经公开。下载它,通读代码,并在你喜欢的 C 项目上试试。我一贯发现,理解语言模型能做什么的最好方式,就是把它们推到极限,然后研究它们从哪里开始崩溃。在接下来的几天里,如果你想继续关注 Claude 为解决这些限制而做的持续尝试,我会继续让 Claude push 新改动。

展望未来

每一代语言模型都会开启与它们协作的新方式。早期模型可用于 IDE 中的 tab-completion。没过多久,模型就能根据 docstring 补全函数体。Claude Code 的发布把 agents 带入主流,并让开发者能够与 Claude 结对编程。但这些产品都基于同一个假设:用户定义一个任务,LLM 运行几秒或几分钟并返回答案,然后用户再提供后续输入。

Agent teams 展示了自主实现完整复杂项目的可能性。这使我们这些工具用户能够设定更有野心的目标。

我们仍处在早期阶段,而完全自主开发伴随着真实风险。当人类在开发过程中与 Claude 坐在一起时,他们可以确保质量一致,并实时捕捉错误。对于自主系统来说,很容易看到测试通过就假定工作完成了,而实际情况很少如此。

我曾经从事 penetration testing,利用大型公司产品中的 vulnerabilities;想到程序员会部署他们从未亲自验证过的软件,这确实令人担忧。

所以,尽管这个实验让我兴奋,它也让我感到不安。构建这个编译器是我最近经历过的最有趣的事情之一,但我没想到在 2026 年初,这件事已经接近可行。语言模型以及我们用来与其交互的 scaffolds 都在快速进步,这为编写海量新代码打开了大门。我预计正向应用会超过负面应用,但我们正在进入一个新世界,需要新的策略来安全地穿行其中。

Acknowledgements

特别感谢 Josef Bacik、Edwin Chen、Bernardo Meurer Costa、Jake Eaton、Dan Kelley、Felix Klock、Jannet Park、Steve Weis,以及 Anthropic 内部许多其他人的帮助和贡献。