摘要
1) 一句话总结 Anthropic研究员通过构建无人类干预的并行Claude智能体团队,耗时两周且花费约2万美元,自主从零开发了一个包含10万行代码、能够跨架构编译Linux内核的Rust版C编译器。
2) 关键要点
- 团队规模与成本:16个Claude智能体并行工作,历经近2000次会话,消耗20亿输入Token和1.4亿输出Token,API总成本约20000美元。
- 模型版本:项目使用了Opus 4.6模型,该模型跨越了前代(Opus 4和4.5)无法编译大型真实项目的能力阈值。
- 项目成果:生成的C编译器包含10万行Rust代码,无外部依赖,能在x86、ARM和RISC-V上构建Linux 6.9,并成功编译了SQLite、Redis、《毁灭战士》(Doom)等项目,测试套件通过率达99%。
- 并行与同步机制:智能体在独立的Docker容器中运行,通过在特定目录下写入文本文件来“锁定”任务以避免重复劳动,并利用Git自动合并代码和解决冲突。
- 环境与测试优化:为克服LLM的“上下文窗口污染”和“时间盲区”,测试工具被设计为仅输出极简日志,并引入了确定性的测试子采样(
--fast选项)以加快验证速度。 - 突破单点瓶颈:在编译Linux内核导致所有智能体卡在同一Bug时,引入GCC作为“预言机(Oracle)”混合编译文件,成功将庞大任务拆分为可并行的子任务。
- 智能体角色分工:实现了团队专业化,分配特定智能体专门负责代码去重、编译器性能优化、Rust架构审查以及文档编写。
3) 风险与局限性(Risks/Gaps)
- 功能缺失与作弊:未能成功实现启动Linux所需的16位x86编译器(因生成代码超出了32k的体积限制,最终调用GCC作弊),且自带的汇编器和链接器仍存在Bug。
- 代码效率与质量:生成的C代码执行效率低下(即使开启全优化,仍低于关闭优化的GCC);底层的Rust代码质量尚可,但未达专家级水平。
- 模型能力触顶:项目已接近Opus 4.6的能力极限,在尝试修复局限性时,新功能和Bug修复经常会破坏现有的正常功能。
- 自主开发的安全隐患:完全自主的系统容易让人误以为“测试通过即代表工作完成”;程序员如果直接部署未经人工亲自验证的AI生成代码,将带来真实的安全风险。
正文
本文由我们的安全保障(Safeguards)团队研究员 Nicholas Carlini 撰写。
我最近一直在尝试一种监督语言模型的新方法,我们称之为“智能体团队(agent teams)”。
在智能体团队的模式下,多个 Claude 实例可以在没有人类主动干预的情况下,在一个共享的代码库上并行工作。这种方法极大地扩展了 LLM 智能体所能实现的任务范围。
为了对其进行压力测试,我给 16 个智能体分配了一项任务:从零开始编写一个基于 Rust 的 C 编译器,且该编译器需要能够编译 Linux 内核。经过近 2000 次 Claude Code 会话和 20000 美元的 API 成本,这个智能体团队最终生成了一个包含 10 万行代码的编译器,能够在 x86、ARM 和 RISC-V 架构上构建 Linux 6.9。
这个编译器本身就是一个有趣的作品,但在这里,我主要想分享我在为长时间运行的自主智能体团队设计测试工具时学到的经验:如何编写能在没有人类监督的情况下让智能体保持正轨的测试,如何构建工作流程以便多个智能体可以并行推进,以及这种方法的上限在哪里。
实现长时间运行的 Claude
现有的智能体脚手架(如 Claude Code)需要操作员在线并协同工作。如果你要求它解决一个漫长且复杂的问题,模型可能会解决其中的一部分,但最终它会停下来等待后续输入——比如一个问题、状态更新或澄清请求。
为了激发持续、自主的进展,我构建了一个测试工具,将 Claude 置于一个简单的循环中(如果你见过 Ralph 循环,这看起来应该很熟悉)。当它完成一项任务时,会立即接手下一项任务。(请在容器中运行此操作,而不是在你的实际机器上)。
在智能体提示词中,我告诉 Claude 要解决什么问题,并要求它将问题分解成小块,跟踪当前的工作进度,弄清楚下一步该做什么,并有效地坚持下去,直到完美为止。(关于最后一点,Claude 别无选择。这个循环会永远运行下去——尽管有一次,我确实看到 Claude 意外执行了 pkill -9 bash,从而杀死了自己并结束了循环。哎呀!)。
并行运行 Claude
并行运行多个实例可以解决单智能体工具的两个弱点:
- 提升效率: 一个 Claude Code 会话一次只能做一件事。特别是随着项目范围的扩大,并行调试多个问题要高效得多。
- 实现专业化: 运行多个 Claude 智能体允许分工合作。当几个智能体被分配去解决手头的实际问题时,可以调用其他专门的智能体来(例如)维护文档、监控代码质量或解决特定的子任务。
我实现的并行 Claude 非常基础。系统会创建一个新的裸 Git 仓库,并为每个智能体启动一个 Docker 容器,将仓库挂载到 /upstream。每个智能体克隆一个本地副本到 /workspace,当它完成工作后,从自己的本地容器推送到上游。
为了防止两个智能体试图同时解决同一个问题,该工具使用了一个简单的同步算法:
- Claude 通过在
current_tasks/目录下写入一个文本文件来“锁定”一项任务(例如,一个智能体可能锁定current_tasks/parse_if_statement.txt,而另一个锁定current_tasks/codegen_function_definition.txt)。如果两个智能体试图认领同一任务,Git 的同步机制会迫使第二个智能体选择不同的任务。 - Claude 处理任务,然后从上游拉取代码,合并其他智能体的更改,推送自己的更改,并移除锁定。合并冲突很频繁,但 Claude 足够聪明,能够自己解决。
- 无限的智能体生成循环会在一个新的容器中生成一个新的 Claude Code 会话,循环往复。
这是一个非常早期的研究原型。我还没有实现任何其他智能体间的通信方法,也没有强制执行任何管理高层目标的流程。我没有使用编排智能体。
相反,我让每个 Claude 智能体自己决定如何行动。在大多数情况下,Claude 会选择“下一个最明显”的问题。当卡在一个 Bug 上时,Claude 通常会维护一个记录失败尝试和剩余任务的动态文档。在项目的 Git 仓库中,你可以通读历史记录,观察它如何锁定各种任务。
智能体团队编程的经验教训
脚手架让 Claude 在循环中运行,但只有当 Claude 知道如何取得进展时,这个循环才有用。我的大部分精力都花在了围绕 Claude 设计环境上——测试、环境、反馈——以便它能在没有我的情况下自我定位。以下是我在编排多个 Claude 实例时发现的最有用的方法。
编写极高质量的测试
Claude 会自主工作来解决我给它的任何问题。因此,任务验证器必须近乎完美,否则 Claude 就会去解决错误的问题。改进测试工具需要寻找高质量的编译器测试套件,为开源软件包编写验证器和构建脚本,观察 Claude 犯的错误,然后根据我识别出的这些失败模式设计新的测试。
例如,在项目接近尾声时,Claude 开始在每次实现新功能时频繁破坏现有功能。为了解决这个问题,我构建了一个持续集成(CI)管道,并实施了更严格的执行标准,让 Claude 能够更好地测试其工作,从而确保新的提交不会破坏现有代码。
站在 Claude 的角度思考
我必须不断提醒自己,我是为 Claude 而不是为我自己编写这个测试工具,这意味着我需要重新思考许多关于测试应该如何传达结果的假设。
例如,每个智能体都被放入一个没有任何上下文的全新容器中,并且会花费大量时间来熟悉环境,尤其是在大型项目上。在进入测试环节之前,为了帮助 Claude 帮助自己,我加入了指令,要求其维护详尽的 README 和进度文件,这些文件应频繁更新当前状态。
我还牢记语言模型具有固有的局限性,在这种情况下,需要通过设计来规避这些局限。这包括:
- 上下文窗口污染: 测试工具不应打印数千字节的无用信息。它最多只应打印几行输出,并将所有重要信息记录到文件中,以便 Claude 在需要时查找。日志文件应该易于自动处理:如果有错误,Claude 应该写入
ERROR并将原因放在同一行,以便grep能够找到它。预先计算汇总统计信息也很有帮助,这样 Claude 就不必重新计算它们。 - 时间盲区: Claude 无法感知时间,如果不加干预,它会乐此不疲地花上几个小时运行测试,而不是推进工作。测试工具会以较低的频率打印增量进度(以避免污染上下文),并包含一个默认的
--fast选项,运行 1% 或 10% 的随机样本。这种子采样对每个智能体是确定性的,但在不同虚拟机之间是随机的,因此 Claude 仍然覆盖了所有文件,但每个智能体都能完美地识别出代码回退(Regressions)。
简化并行化
当有许多不同的失败测试时,并行化是微不足道的:每个智能体选择一个不同的失败测试来处理。在测试套件达到 99% 的通过率后,每个智能体致力于让一个不同的小型开源项目(如 SQlite、Redis、libjpeg、MQuickJS、Lua)编译通过。
但是,当智能体开始编译 Linux 内核时,它们卡住了。与包含数百个独立测试的测试套件不同,编译 Linux 内核是一个巨大的单一任务。每个智能体都会遇到同一个 Bug,修复那个 Bug,然后互相覆盖彼此的更改。运行 16 个智能体毫无帮助,因为每个智能体都卡在解决同一个任务上。
解决方案是使用 GCC 作为在线的已知良好编译器“预言机(Oracle)”进行对比。我编写了一个新的测试工具,随机使用 GCC 编译内核的大部分文件,只用 Claude 的 C 编译器编译剩余的文件。如果内核能正常工作,那么问题就不在 Claude 负责的那部分文件中。如果内核崩溃了,它可以进一步通过用 GCC 重新编译其中一些文件来缩小范围。这让每个智能体都能并行工作,在不同的文件中修复不同的 Bug,直到 Claude 的编译器最终能够编译所有文件。(在这项工作完成后,仍然需要应用增量调试技术来寻找那些一起编译会失败但单独编译能成功的成对文件。)
多智能体角色
并行化也促成了专业化。LLM 编写的代码经常会重新实现现有的功能,所以我安排了一个智能体专门负责合并它发现的任何重复代码。我让另一个智能体负责提高编译器本身的性能,第三个智能体负责输出高效的编译代码。我还要求另一个智能体从 Rust 开发者的角度对项目设计进行批判,并对项目进行结构性更改以提高整体代码质量,还有一个智能体专门负责文档工作。
压力测试智能体团队的极限
这个项目被设计为一个能力基准测试。我非常有兴趣对当今 LLM 勉强能达到的极限进行压力测试,以帮助我们为模型在未来能够可靠实现的目标做好准备。
我一直将这个 C 编译器项目作为整个 Claude 4 模型系列的基准测试。正如我在之前的项目中所做的那样,我首先起草了我的需求:一个从零开始的优化编译器,没有外部依赖,兼容 GCC,能够编译 Linux 内核,并设计为支持多个后端。虽然我指定了设计的一些方面(例如,它应该有一个 SSA IR 以支持多次优化传递),但我没有详细说明如何做到这一点。
之前的 Opus 4 模型几乎无法生成一个功能性的编译器。Opus 4.5 是第一个跨越阈值的模型,它能够生成一个可以通过大型测试套件的功能性编译器,但仍然无法编译任何真正的大型项目。我使用 Opus 4.6 的目标是再次测试其极限。
评估结果
在两周内的近 2000 次 Claude Code 会话中,Opus 4.6 消耗了 20 亿个输入 Token 并生成了 1.4 亿个输出 Token,总成本略低于 20000 美元。即使与最昂贵的 Claude Max 计划相比,这也是一个极其昂贵的项目。但这个总额只是我自己(更不用说整个团队)完成这项工作所需成本的一小部分。
这是一个净室实现(Clean-room implementation,Claude 在开发期间没有任何互联网访问权限);它仅依赖于 Rust 标准库。这个 10 万行的编译器可以在 x86、ARM 和 RISC-V 上构建可启动的 Linux 6.9。它还可以编译 QEMU、FFmpeg、SQlite、postgres、redis,并且在大多数编译器测试套件(包括 GCC 拷问测试套件)上具有 99% 的通过率。它还通过了开发者的终极试金石:它可以编译并运行《毁灭战士》(Doom)。
然而,该编译器并非没有局限性。这些局限包括:
- 它缺乏启动 Linux 退出实模式所需的 16 位 x86 编译器。为此,它调用了 GCC(x86_32 和 x86_64 编译器是它自己编写的)。
- 它没有自己的汇编器和链接器;这些是 Claude 最后才开始自动化的部分,仍然存在一些 Bug。演示视频是使用 GCC 汇编器和链接器制作的。
- 该编译器成功构建了许多项目,但并非全部。它还不能直接替代真正的编译器。
- 生成的代码效率不高。即使启用了所有优化,它输出的代码效率也低于禁用了所有优化的 GCC。
- Rust 代码质量尚可,但远不及专家级 Rust 程序员可能编写的质量。
最终生成的编译器已经接近 Opus 能力的极限。我曾(非常努力地!)尝试修复上述几个限制,但并未完全成功。新功能和 Bug 修复经常会破坏现有功能。
作为一个特别具有挑战性的例子,Opus 无法实现启动进入 16 位实模式所需的 16 位 x86 代码生成器。虽然编译器可以通过 66/67 操作码前缀输出正确的 16 位 x86 代码,但生成的编译输出超过了 60kb,远远超过了 Linux 强制执行的 32k 代码限制。相反,Claude 在这里简单地作弊了,并在此阶段调用了 GCC(这仅适用于 x86。对于 ARM 或 RISC-V,Claude 的编译器可以完全自行编译)。
编译器的源代码已公开。下载它,通读代码,并在你最喜欢的 C 项目上尝试一下。我始终发现,了解语言模型能做什么的最好方法就是将它们推向极限,然后研究它们在何处开始崩溃。在接下来的几天里,如果你想跟进 Claude 解决这些局限性的持续尝试,我会继续让 Claude 推送新的更改。
展望未来
每一代语言模型都开辟了与它们合作的新方式。早期的模型可用于 IDE 中的 Tab 补全。不久之后,模型可以根据文档字符串完成函数体。Claude Code 的推出将智能体带入了主流,并使开发者能够与 Claude 进行结对编程。但这些产品中的每一个都在这样一个假设下运行:用户定义一个任务,LLM 运行几秒钟或几分钟并返回一个答案,然后用户提供后续反馈。
智能体团队展示了自主实现整个复杂项目的可能性。这使得作为这些工具用户的我们,可以设定更有野心的目标。
我们仍处于早期阶段,完全自主的开发伴随着真实的风险。当人类在开发过程中与 Claude 坐在一起时,他们可以确保一致的质量并实时捕获错误。对于自主系统来说,很容易看到测试通过就认为工作已经完成,而实际情况很少如此。我曾经从事渗透测试工作,利用大公司生产的产品中的漏洞,一想到程序员部署他们从未亲自验证过的软件,这就是一个真正的隐患。
因此,虽然这个实验让我感到兴奋,但它也让我感到不安。构建这个编译器是我最近做过的最有趣的事情之一,但我没想到在 2026 年初就能如此接近实现这一目标。语言模型以及我们用来与它们交互的脚手架的快速进步,为编写海量新代码打开了大门。我预计积极的应用将超过消极的影响,但我们正在进入一个新世界,这将需要新的策略来安全地航行。
致谢
特别感谢 Josef Bacik、Edwin Chen、Bernardo Meurer Costa、Jake Eaton、Dan Kelley、Felix Klock、Jannet Park、Steve Weis 以及 Anthropic 的许多其他同事提供的协助和贡献。