一声棒喝,本不立文字
偏要著録,已是二义

Anthropic · 工程博客

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

Building a C compiler with a team of parallel Claudes

二〇二六年五月八日 · 英文原文

Anthropic Safeguards 研究员 Nicholas Carlini 试验 agent teams:16 个 Claude 实例并行在共享 codebase 中开发 Rust C compiler。近 2,000 个 Claude Code session、约 20,000 美元 API 成本后,生成 100,000 行 clean-room compiler,可在 x86、ARM、RISC-V 构建 Linux 6.9,并用于研究 autonomous agent harness、测试与并行协作方法。

作者:Nicholas Carlini,我们 Safeguards 团队的研究员。

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

在 agent teams 中,多个 Claude 实例在没有人类主动干预的情况下,并行处理同一个共享 codebase。这种方法显著扩展了 LLM agent 能够完成的任务范围。

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

这个 compiler 本身是一个有趣的产物,但我在这里重点讨论的是,我在为长时间运行的 autonomous agent teams 设计 harness 时学到的东西:如何编写测试,让 agent 在没有人类监督的情况下保持方向;如何组织工作,使多个 agent 可以并行推进;以及这种方法的上限在哪里。

现有的 agent scaffold(如 Claude Code)要求 operator 在线并随时参与协作。如果你要求它解决一个漫长而复杂的问题,模型可能会解决其中一部分,但最终它会停下来等待后续输入——一个问题、一条状态更新,或一个澄清请求。

为了引出持续的自主进展,我构建了一个 harness,把 Claude 放进一个简单循环里(如果你见过 Ralph-loop,这看起来应该很熟悉)。当它完成一个任务后,会立即开始下一个任务。(请在 container 中运行,不要在你的真实机器上运行。)

在 agent prompt 中,我告诉 Claude 要解决什么问题,并要求它通过把问题拆成小块来推进,跟踪自己正在做什么,判断下一步该做什么,并持续推进,直到结果完美为止。(关于最后一点,Claude 没有选择。这个循环会永远运行——不过有一次,我确实看到 Claude 意外执行了 pkill -9 bash,于是杀掉了自己并结束了循环。Oops!)

并行运行多个实例可以解决 single-agent harness 的两个弱点:

我的 parallel Claude 实现非常基础。它会创建一个新的 bare git repo;对于每个 agent,会启动一个 Docker container,并把 repo 挂载到 /upstream。每个 agent 会 clone 一个本地副本到 /workspace,完成后从自己的本地 container push 到 upstream。

为了防止两个 agent 同时尝试解决同一个问题,harness 使用了一个简单的同步算法:

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

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

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

Claude 会自主地解决我交给它的任何问题。因此,任务 verifier 必须近乎完美,否则 Claude 会解决错误的问题。改进 testing harness 需要寻找高质量的 compiler test suite,为 open-source software package 编写 verifier 和 build script,观察 Claude 正在犯的错误,然后在识别出这些 failure mode 后设计新的测试。

例如,在项目接近尾声时,Claude 开始频繁地在实现新功能时破坏已有功能。为了解决这个问题,我构建了 continuous integration pipeline,并实现了更严格的 enforcement,让 Claude 能更好地测试自己的工作,确保新的 commit 不会破坏已有代码。

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

例如,每个 agent 都会被放入一个没有上下文的全新 container 中,并会花大量时间确定当前状况,尤其是在大型项目中。甚至在进入测试之前,为了帮助 Claude 帮助自己,我加入了指令,要求它维护详尽的 README 和进度文件,并频繁更新当前状态。

我也一直注意到语言模型固有的限制,在这个场景中,需要围绕这些限制进行设计。这些限制包括:

当有许多彼此不同的 failing test 时,并行化很简单:每个 agent 选择一个不同的 failing test 来处理。在 test suite 达到 99% 通过率后,每个 agent 负责让一个不同的小型 open-source project(例如 SQlite、Redis、libjpeg、MQuickJS、Lua)能够编译。

但当 agent 开始编译 Linux kernel 时,它们卡住了。与包含数百个独立测试的 test suite 不同,编译 Linux kernel 是一个巨大的单一任务。每个 agent 都会遇到同一个 bug,修复这个 bug,然后相互覆盖彼此的更改。运行 16 个 agent 并没有帮助,因为每个 agent 都卡在同一个任务上。

解决方法是使用 GCC 作为在线的 known-good compiler oracle 进行对比。我编写了一个新的 test harness,随机用 GCC 编译 kernel 的大部分文件,只用 Claude's C Compiler 编译剩余文件。如果 kernel 能工作,那么问题就不在 Claude 所负责的那部分文件里。如果它出错,就可以进一步把这些文件中的一部分重新用 GCC 编译来缩小范围。这让每个 agent 可以并行工作,在不同文件中修复不同 bug,直到 Claude 的 compiler 最终能够编译所有文件。(在这成功之后,仍然需要应用 delta debugging 技术,找出那些单独编译能工作、但组合在一起会失败的文件对。)

并行性还支持 specialization。LLM 编写的代码经常会重新实现已有功能,因此我让一个 agent 负责合并它发现的重复代码。我让另一个 agent 负责提升 compiler 本身的性能,让第三个 agent 负责输出高效的 compiled code。我还要求另一个 agent 从 Rust developer 的角度审视项目设计,并对项目进行结构性修改,以提升整体代码质量;另一个则负责 documentation。

这个项目被设计为一个 capability benchmark。我感兴趣的是对今天 LLM 勉强能够完成的事情进行压力测试,以帮助我们为未来模型能够可靠完成的事情做准备。

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

之前的 Opus 4 模型只能勉强生成一个可工作的 compiler。Opus 4.5 是第一个跨过某个门槛的模型,它能够生成一个可工作的 compiler,并通过大型 test suite,但仍然无法编译任何真正的大型项目。我对 Opus 4.6 的目标,是再次测试它的极限。

在两周内近 2,000 个 Claude Code session 中,Opus 4.6 消耗了 20 亿 input token,并生成了 1.4 亿 output token,总成本略低于 $20,000。即使与最昂贵的 Claude Max plan 相比,这也是一个成本极高的项目。但这个总额只是我自己完成同样工作所需成本的一小部分,更不用说组建整个团队了。

这是一个 clean-room implementation(Claude 在开发过程中任何时候都无法访问互联网);它只依赖 Rust standard library。这个 100,000 行的 compiler 可以在 x86、ARM 和 RISC-V 上构建可启动的 Linux 6.9。它还可以编译 QEMU、FFmpeg、SQlite、postgres、redis,并且在大多数 compiler test suite(包括 GCC torture test suite)上达到 99% 的通过率。它还通过了 developer 的终极 litmus test:它可以编译并运行 Doom。

不过,这个 compiler 并非没有限制。这些限制包括:

最终得到的 compiler 几乎已经达到了 Opus 能力的边界。我曾经(非常努力地)尝试修复上述几个限制,但没有完全成功。新功能和 bugfix 经常会破坏已有功能。

一个特别有挑战性的例子是,Opus 无法实现一个 16-bit x86 code generator,而这是启动进入 16-bit real mode 所必需的。虽然 compiler 可以通过 66/67 opcode prefix 输出正确的 16-bit x86,但生成的 compiled output 超过 60kb,远远超出 Linux 强制要求的 32k code limit。因此,Claude 在这里直接作弊,调用 GCC 来完成这个阶段。(这只发生在 x86 上。对于 ARM 或 RISC-V,Claude 的 compiler 可以完全自行编译。)

这个 compiler 的 source code 已经可用。下载它,阅读代码,并在你喜欢的 C 项目上试用。我一直发现,理解语言模型能做什么的最好方法,是把它们推到极限,然后研究它们从哪里开始失效。接下来几天,如果你想继续关注 Claude 试图解决这些限制的过程,我会继续让 Claude push 新的更改。

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

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

我们仍处在早期阶段,而 fully autonomous development 伴随着真实风险。当人类在开发过程中与 Claude 一起工作时,他们可以确保质量一致,并实时发现错误。对于 autonomous system,很容易看到测试通过就认为工作已经完成,但实际情况很少如此。我曾从事 penetration testing,利用大型公司产品中的漏洞;想到 programmer 部署自己从未亲自验证过的软件,确实让我担忧。

因此,虽然这个实验让我兴奋,但也让我感到不安。构建这个 compiler 是我最近最有趣的经历之一,但我没有想到在 2026 年初就已经接近可能做到这种程度。语言模型以及我们用来与其交互的 scaffold 都在快速进步,这为编写海量新代码打开了大门。我预计正面应用会超过负面影响,但我们正在进入一个新世界,需要新的策略来安全地应对。

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

译自 Anthropic · 工程博客 · 录于 二〇二六年五月八日