跳到正文
Anthropic Engineering·· 2026-02-05精选AI 评分76

用并行 Claude 智能体团队构建 C 编译器

Building a C compiler with a team of parallel Claudes

AI 导读

Anthropic 研究员 Nicholas Carlini 用 16 个并行 Claude 智能体,在近 2000 次 Claude Code 会话和约 2 万美元 API 成本下,从零写出 10 万行 Rust C 编译器,可在 x86、ARM、RISC-V 上构建 Linux 6.9。

推荐理由

Anthropic 研究员复盘 16 个 Claude 智能体并行写 C 编译器的工程细节,可迁移到长时自主智能体的测试与并行设计。

正文 · AI 翻译

由我们 Safeguards 团队的研究员 Nicholas Carlini 撰写。

我一直在尝试一种监督语言模型的新方法,我们称之为“智能体团队”。

借助智能体团队,多个 Claude 实例可以在共享代码库上并行工作,无需人类主动干预。这种方法极大地拓展了 LLM 智能体所能达成的范围。

为了对它进行压力测试,我让 16 个智能体从零开始编写一个基于 Rust 的 C 编译器,要求它能够编译 Linux 内核。在近 2,000 次 Claude Code 会话和 20,000 美元的 API 成本之后,这个智能体团队产出了一个 100,000 行的编译器,能够在 x86、ARM 和 RISC-V 上构建 Linux 6.9。

这个编译器本身就是一件有趣的产物,但我在这里关注的是我在为长时间运行的自主智能体团队设计测试框架过程中学到的东西:如何编写测试,使智能体在没有人类监督的情况下保持正轨;如何组织工作,使多个智能体能够并行推进;以及这种方法在哪里触及上限。

让 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 智能体可以实现专业化。虽然少数智能体被分配去解决手头的实际问题,但其他专门的智能体可以被调用来(例如)维护文档、关注代码质量,或解决专门的子任务。

我对并行 Claude 的实现非常简陋。创建一个新的裸 git 仓库,并为每个智能体启动一个 Docker 容器,将仓库挂载到 /upstream。每个智能体将本地副本克隆到 /workspace,完成后从自己的本地容器推送到上游。

为了防止两个智能体同时尝试解决同一个问题,该测试框架使用了一种简单的同步算法:

  1. Claude 通过向 current_tasks/ 写入一个文本文件来对任务加“锁”(例如,一个智能体可能锁定 current_tasks/parse_if_statement.txt,而另一个锁定 current_tasks/codegen_function_definition.txt)。如果两个智能体试图认领同一个任务,git 的同步机制会迫使第二个智能体选择另一个任务。
  2. Claude 执行任务,然后从上游拉取,合并其他代理的更改,推送自己的更改,并移除锁。合并冲突频繁发生,但 Claude 足够聪明,能够解决。
  3. 无限代理生成循环在一个全新的容器中启动一个新的 Claude Code 会话,然后循环重复。

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

相反,我让每个 Claude 代理自行决定如何行动。在大多数情况下,Claude 会选择“下一个最明显”的问题。当卡在某个 bug 上时,Claude 通常会维护一个记录失败方法和剩余任务的运行文档。在项目的 git 仓库中,你可以翻阅历史记录,观察它对各种任务加锁。

从 Claude 代理团队编程中获得的经验

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

编写极高质量的测试

Claude 会自主工作来解决我交给它的任何问题。因此,任务验证器几乎完美非常重要,否则 Claude 会解决错误的问题。改进测试框架需要找到高质量的编译器测试套件,为开源软件包编写验证器和构建脚本,并留意 Claude 犯的错误,然后在我发现这些失败模式时设计新的测试。

例如,在项目接近尾声时,Claude 每次实现新功能时都开始频繁破坏现有功能。为了解决这个问题,我构建了一个持续集成流水线,并实施了更严格的执行,让 Claude 能够更好地测试其工作,从而使新提交不会破坏现有代码。

设身处地为 Claude 着想

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

例如,每个代理被放入一个没有上下文的全新容器中,会花费大量时间进行自我定位,尤其是在大型项目上。甚至在进入测试之前,为了帮助 Claude 自助,我加入了维护详尽 README 和进度文件的说明,这些文件应经常更新以反映当前状态。

我还牢记语言模型具有固有局限性,在这种情况下,需要围绕这些局限性进行设计。这些局限性包括:

  • 上下文窗口污染:测试框架不应打印数千个无用的字节。它最多应打印几行输出,并将所有重要信息记录到文件中,以便 Claude 在需要时查找。日志文件应易于自动处理:如果有错误,Claude 应写入 ERROR 并将原因放在同一行,以便 grep 能找到它。预先计算汇总统计数据会有所帮助,这样 Claude 就不必重新计算它们。
  • 时间盲区:Claude 无法感知时间,如果放任不管,它会很乐意花几个小时运行测试而不是推进进度。测试框架会不频繁地打印增量进度(以避免污染上下文),并包含一个默认的 --fast 选项,运行 1% 或 10% 的随机抽样。这个子样本对每个 agent 是确定性的,但在不同 VM 之间是随机的,因此 Claude 仍然覆盖所有文件,但每个 agent 都能完美识别回归。

让并行变得简单

当存在许多不同的失败测试时,并行化就很简单:每个 agent 选择一个不同的失败测试来处理。在测试套件达到 99% 通过率后,每个 agent 负责让一个不同的小型开源项目(例如 SQlite、Redis、libjpeg、MQuickJS、Lua)编译通过。

但当 agent 们开始编译 Linux 内核时,它们卡住了。与拥有数百个独立测试的测试套件不同,编译 Linux 内核是一个巨大的单一任务。每个 agent 都会遇到相同的 bug,修复那个 bug,然后互相覆盖彼此的更改。运行 16 个 agent 也无济于事,因为每个都在解决同一个任务。

解决办法是使用 GCC 作为在线的已知良好编译器预言机来进行对比。我编写了一个新的测试框架,使用 GCC 随机编译内核的大部分内容,仅用 Claude 的 C 编译器编译剩余文件。如果内核能正常工作,那么问题就不在 Claude 编译的那部分文件中。如果内核崩溃,则可以通过用 GCC 重新编译其中一些文件来进一步缩小范围。这让每个 agent 都能并行工作,修复不同文件中的不同 bug,直到 Claude 的编译器最终能够编译所有文件。(在这之后,仍然有必要应用增量调试技术来找出那些一起失败但单独工作正常的文件对。)

多 agent 角色

并行化也促成了专业化。LLM 编写的代码经常重新实现现有功能,因此我让一个 agent 负责合并它发现的任何重复代码。我让另一个 agent 负责改进编译器本身的性能,第三个 agent 负责输出高效的编译代码。我让另一个 agent 从 Rust 开发者的角度批评项目的设计,并对项目进行结构性更改以提高整体代码质量,还有一个 agent 负责文档工作。

对 agent 团队极限的压力测试

这个项目被设计为一个能力基准。我有兴趣对 LLM 今天勉强能达到的极限进行压力测试,以帮助我们为模型未来能够可靠实现的目标做好准备。

我一直在整个 Claude 4 模型系列中使用 C 编译器项目作为基准。正如我之前的项目一样,我先起草了我想要的东西:一个从零开始、无依赖的优化编译器,与 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 标准库。这个 10 万行的编译器可以在 x86、ARM 和 RISC-V 上构建可启动的 Linux 6.9。它还可以编译 QEMU、FFmpeg、SQlite、postgres、redis,并且在大多数编译器测试套件上通过率达到 99%,包括 GCC torture 测试套件。它还通过了开发者的终极试金石:它可以编译并运行 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 运行几秒或几分钟并返回答案,然后用户提供后续输入。

Agent 团队展示了自主实现完整、复杂项目的可能性。这让我们作为这些工具的用户,能够在目标上变得更加雄心勃勃。

我们仍处于早期阶段,完全自主的开发伴随着真实的风险。当人类在开发过程中与 Claude 坐在一起时,他们可以确保一致的质量并实时捕捉错误。对于自主系统而言,很容易看到测试通过就认为工作完成了,而实际情况很少如此。我以前从事渗透测试工作,利用大公司生产的产品中的漏洞,一想到程序员部署他们从未亲自验证过的软件,就让我感到真切担忧。

所以,尽管这个实验让我兴奋,它也让我感到不安。构建这个编译器是我最近经历过的最有趣的事情之一,但我没想到这在 2026 年这么早的时候就近乎可能。语言模型以及我们用来与它们交互的脚手架的快速进步,为编写大量新代码打开了大门。我预计积极应用会超过消极应用,但我们正在进入一个新世界,需要新的策略来安全航行。

致谢

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

来源:Anthropic Engineering · anthropic.com