用并行Claude团队构建C编译器
Building a C compiler with a team of parallel Claudes
Nicholas Carlini 通过“代理团队”方法,使用16个并行运行的Claude实例,在近2000次Claude Code会话和2万美元API成本下,自主编写了一个10万行、基于Rust的C编译器。该编译器能在x86、ARM和RISC-V架构上编译Linux 6.9内核,并通过了GCC torture测试套件等多数测试(99%通过率)。Carlini 分享了设计长时间运行自主agent框架的经验,包括编写测试、组织并行工作及处理瓶颈。
代理团队:用16个Claude实例从头编写C编译器
作者:Nicholas Carlini,我们安全团队的研究员
我一直在试验一种监督语言模型的新方法,我们称之为"代理团队"(agent teams)。
在代理团队中,多个Claude实例在共享代码库上并行工作,无需人工主动干预。这种方法极大地扩展了LLM agent所能实现的范围。
为了进行压力测试,我让16个agent从头编写一个基于Rust的C编译器,要求能够编译Linux内核。经过近2000次Claude Code会话和2万美元的API费用,这个代理团队产出了一个10万行的编译器,能够在x86、ARM和RISC-V架构上构建Linux 6.9。
这个编译器本身就是一个有趣的产物,但我在这里重点讲述的是在设计用于长时间运行的自主代理团队的框架(harness)时学到的经验:如何编写测试让agent在无人监督的情况下保持正轨,如何组织工作让多个agent能够并行推进,以及这种方法在何处遇到瓶颈。
现有的agent脚手架(如Claude Code)需要操作者在线并能够协同工作。如果你要求解决一个长而复杂的问题,模型可能会解决其中一部分,但最终它会停下来等待后续输入——一个问题、状态更新或澄清请求。
为了引发持续、自主的进展,我构建了一个框架,让Claude处于一个简单的循环中(如果你见过Ralph-loop,应该会觉得眼熟)。当它完成一个任务时,立即开始下一个。(请在容器中运行,而不是你的实际机器)。
在agent prompt中,我告诉Claude要解决什么问题,并要求它通过将问题分解为小块、跟踪当前工作、确定下一步工作,并有效持续直到完美来解决。(关于最后一点,Claude别无选择。循环会永远运行——尽管有一次,我确实看到Claude意外地执行了pkill -9 bash,从而杀死了自己并结束了循环。哎呀!)。
并行运行多个实例可以解决单agent框架的两个弱点:
我的并行Claude实现非常简陋。创建一个新的裸git仓库,然后为每个agent启动一个Docker容器,将仓库挂载到/upstream。每个agent克隆一个本地副本到/workspace,完成后从自己的本地容器推送到upstream。
为了防止两个agent同时尝试解决同一个问题,框架使用了一个简单的同步算法:
这是一个非常早期的研究原型。我还没有实现任何其他agent间的通信方法,也没有强制执行任何管理高层目标的过程。我没有使用编排agent(orchestration agent)。
相反,我让每个Claude agent自行决定如何行动。在大多数情况下,Claude会选择"下一个最明显"的问题。当遇到bug时,Claude通常会维护一个运行文档,记录失败的方法和剩余任务。在项目的git仓库中,你可以浏览历史记录,看到它如何锁定各种任务。
脚手架让Claude在一个循环中运行,但这个循环只有在Claude知道如何取得进展时才有用。我的大部分精力都花在设计Claude周围的环境上——测试、环境、反馈——这样它就能在没有我的情况下自我定位。以下是我在编排多个Claude实例时发现最有用的方法。
Claude会自主地解决我交给它的任何问题。因此,任务验证器必须近乎完美,否则Claude会解决错误的问题。改进测试框架需要找到高质量的编译器测试套件,为开源软件包编写验证器和构建脚本,观察Claude犯的错误,然后在识别出这些失败模式时设计新的测试。
例如,在项目接近尾声时,Claude每次实现新功能时都频繁破坏现有功能。为了解决这个问题,我构建了一个持续集成(CI)流水线,并实施了更严格的执行机制,让Claude能够更好地测试其工作,使新提交不会破坏现有代码。
我必须不断提醒自己,这个测试框架是为Claude而不是为我自己编写的,这意味着要重新思考我对测试应该如何传达结果的许多假设。
例如,每个agent被放入一个没有上下文的新容器中,会花费大量时间进行自我定位,尤其是在大型项目上。在进入测试之前,为了帮助Claude自助,我包含了维护详尽README和进度文件的指令,这些文件应频繁更新当前状态。
我还牢记语言模型具有固有的局限性,在这种情况下需要围绕这些局限性进行设计。这些局限性包括:
当有许多不同的失败测试时,并行化很简单:每个agent选择一个不同的失败测试来处理。在测试套件达到99%通过率后,每个agent负责让不同的开源小项目(如SQlite、Redis、libjpeg、QuickJS、Lua)能够编译。
但当agent开始编译Linux内核时,它们卡住了。与包含数百个独立测试的测试套件不同,编译Linux内核是一个巨大的单一任务。每个agent都会遇到相同的bug,修复这个bug,然后互相覆盖彼此的更改。运行16个agent也无济于事,因为每个agent都卡在解决同一个任务上。
解决方案是使用GCC作为在线已知正确的编译器预言(oracle)进行对比。我编写了一个新的测试框架,随机使用GCC编译大部分内核,只使用Claude的C编译器编译剩余文件。如果内核能工作,那么问题就不在Claude负责的那部分文件中。如果内核崩溃,则可以通过用GCC重新编译其中一些文件来进一步缩小范围。这让每个agent能够并行工作,修复不同文件中的不同bug,直到Claude的编译器最终能够编译所有文件。(在这之后,仍然需要应用delta调试技术来找到那些单独工作但一起失败的文件对)。
并行化还实现了专业化。LLM编写的代码经常重复实现现有功能,所以我让一个agent负责合并它发现的任何重复代码。我让另一个agent负责改进编译器本身的性能,第三个负责输出高效的编译代码。我让另一个agent从Rust开发者的角度批评项目的设计,并对项目进行结构性更改以提高整体代码质量,还有一个负责文档工作。
这个项目被设计为一个能力基准(capability benchmark)。我有兴趣对LLM今天勉强能够达到的极限进行压力测试,以帮助我们为未来模型将可靠实现的能力做好准备。
我一直在使用C编译器项目作为整个Claude 4模型系列的基准。与之前的项目一样,我首先起草了我想要的目标:一个从头开始、无依赖的优化编译器,兼容GCC,能够编译Linux内核,并设计为支持多个后端。虽然我指定了设计的某些方面(例如,它应该有一个SSA IR以支持多个优化pass),但我没有详细说明如何实现。
之前的Opus 4模型几乎无法产生一个功能正常的编译器。Opus 4.5是第一个跨越门槛的版本,能够产生一个可以通过大型测试套件的功能编译器,但仍然无法编译任何真正的大型项目。我对Opus 4.6的目标是再次测试极限。
在两周内近2000次Claude Code会话中,Opus 4.6消耗了20亿个输入token,生成了1.4亿个输出token,总成本不到2万美元。与即使是最昂贵的Claude Max计划相比,这也是一个极其昂贵的项目。但这个总数只是我自己完成这项工作所需成本的一小部分——更不用说整个团队了。
这是一个洁净室实现(Claude在开发过程中从未访问过互联网);它仅依赖于Rust标准库。这个10万行的编译器可以在x86、ARM和RISC-V上构建可启动的Linux 6.9。它还可以编译QEMU、FFmpeg、SQlite、postgres、redis,并且在大多数编译器测试套件(包括GCC torture测试套件)上达到99%的通过率。它还通过了开发者的终极试金石:它可以编译并运行Doom。
然而,这个编译器并非没有局限性。这些局限性包括:
生成的编译器几乎达到了Opus能力的极限。我(努力地!)尝试修复上述几个局限性,但没有完全成功。新功能和bug修复经常破坏现有功能。
一个特别具有挑战性的例子是,Opus无法实现启动到16位实模式所需的16位x86代码生成器。虽然编译器可以通过66/67操作码前缀输出正确的16位x86代码,但生成的编译输出超过60kb,远远超过Linux强制执行的32k代码限制。相反,Claude在这里简单地作弊,调用GCC来完成这个阶段(这只适用于x86。对于ARM或RISC-V,Claude的编译器可以完全自行编译)。
编译器的源代码已公开。下载它,阅读代码,并在你喜欢的C项目上尝试。我一直发现,理解语言模型能力的最佳方式是将它们推向极限,然后研究它们在哪里开始崩溃。在接下来的几天里,如果你希望跟随Claude继续尝试解决这些局限性的过程,我会让Claude持续推送新的更改。
每一代语言模型都开辟了与之合作的新方式。早期模型在IDE中用于制表符补全。不久之后,模型可以根据文档字符串完成函数体。Claude Code的推出将agent带入了主流,使开发者能够与Claude进行结对编程。但这些产品都基于一个假设:用户定义一个任务,LLM运行几秒或几分钟并返回答案,然后用户提供后续指令。
代理团队展示了自主实现整个复杂项目的可能性。这使我们作为这些工具的用户,能够对自己的目标更加雄心勃勃。
我们仍处于早期阶段,完全自主的开发伴随着真正的风险。当人类在开发过程中与Claude一起工作时,他们可以确保一致的质量并实时捕获错误。对于自主系统,很容易看到测试通过就认为工作完成了,而实际情况很少如此。我曾经从事渗透测试工作,利用大公司产品的漏洞,想到程序员部署他们从未亲自验证过的软件,这确实是一个令人担忧的问题。
因此,虽然这个实验让我兴奋,但也让我感到不安。构建这个编译器是我最近最开心的事情之一,但我没想到在2026年初就能接近实现这一点。语言模型以及我们用来与之交互的脚手架的快速进步,为编写大量新代码打开了大门。我预计积极的应用将超过消极的,但我们正在进入一个需要新策略来安全导航的新世界。
特别感谢Josef Bacik、Edwin Chen、Bernardo Meurer Costa、Jake Eaton、Dan Kelley、Felix Klock、Jannet Park、Steve Weis以及Anthropic的许多其他人员的帮助和贡献。