返回 文章 build CMS 文章

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

16 个 Claude 并行协作,用 2 万美元和两周时间从零写出能编译 Linux 内核的 C 编译器。

LLM智能体并行计算C编译器自主编程
成长分 / 100 80 综合收获、行动、留存与影响

用并行 Claude 智能体团队从零构建 C 编译器
为什么值得读了解一种全新的监督语言模型方法——智能体团队,让多个 Claude 实例在共享代码库上并行自主工作。

获取设计长时间运行自主智能体运行框架的实战经验,包括测试、环境与反馈机制。

关键洞察
  1. 智能体团队模式通过并行运行多个 Claude 实例,解决了单智能体框架需要人类持续干预和无法并行推进的弱点。
  2. 任务验证器必须近乎完美,否则 Claude 会解决错误的问题;测试框架需要为 Claude 而非人类设计。
  3. 并行化实现了专业化,例如指派不同 agent 负责合并重复代码、优化性能、改进代码质量或编写文档。
转成行动

深入阅读

正文与原文对照

原文保真覆盖:全文原文字符:12677

获取开发者简报

产品更新、操作指南、社区聚焦等。每月发送至您的收件箱。

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

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

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

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

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

现有的智能体脚手架(如 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

在 agent 提示词中,我会告诉 Claude 要解决什么问题,并要求它通过将问题拆解成小块来处理问题,跟踪它正在处理的内容,弄清楚接下来要处理什么,并有效地持续推进直到完美。(关于最后一点,Claude 别无选择。这个循环会永远运行下去——不过有一次,我确实看到 Claude pkill -9 bash

不小心把自己杀掉了,从而结束了循环。哎呀!)。

并行运行多个实例可以解决单 agent 框架的两个弱点:

我的并行 Claude 实现非常简陋。会创建一个新的裸 git 仓库,然后为每个 agent 启动一个 Docker 容器,并将该仓库挂载到 /upstream

。每个 agent 会将本地副本克隆到 /workspace

,完成后从自己的本地容器推送到 upstream。

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

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

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

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

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

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

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

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

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

--fast

运行 1% 或 10% 随机抽样的选项。这个子样本对每个 agent 是确定性的,但在不同 VM 之间是随机的,因此 Claude 仍然覆盖所有文件,但每个 agent 都能完美识别回归。当存在许多不同的失败测试时,并行化就变得很简单:每个 agent 选择一个不同的失败测试来处理。在测试套件达到 99% 通过率后,每个 agent 负责让一个不同的小型开源项目(例如 SQlite、Redis、libjpeg、MQuickJS、Lua)编译通过。

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

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

并行化还实现了专业化。LLM 编写的代码经常重新实现现有功能,因此我指派一个 agent 合并它发现的任何重复代码。我让另一个 agent 负责改进编译器本身的性能,第三个 agent 负责输出高效的编译代码。我让另一个 agent 从 Rust 开发者的角度批评项目的设计,并对项目进行结构性更改以提高整体代码质量,还有一个 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,并且在大多数编译器测试套件(包括 GCC 酷刑测试套件)上通过率达到 99%。它还通过了开发者的终极试金石:它可以编译并运行 Doom。

然而,这个编译器并非没有局限性。这些局限性包括:

由此产生的编译器几乎达到了 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 内外许多其他人的协助和贡献。

产品更新、操作指南、社区聚焦等。每月发送到您的收件箱。