我一直在尝试一种监督语言模型的新方法,我们称之为“智能体团队(agent teams)”。
通过智能体团队,多个 Claude 实例可以在同一个代码库上并行工作,而无需人工的积极干预。这种方法极大地扩展了 LLM(大语言模型)智能体所能实现的范围。
为了对其进行压力测试,我给 16 个智能体布置了一项任务:从零开始编写一个基于 Rust 的 C 编译器,并且要能够编译 Linux 内核。在经历了近 2,000 次 Claude Code 会话和花费了 20,000 美元的 API 成本后,这个智能体团队产出了一个 10 万行代码的编译器,它能够在 x86、ARM 和 RISC-V 架构上构建 Linux 6.9。
这个编译器本身就是一个有趣的产物,但我在这里主要关注我在设计长期运行的自主智能体团队的框架(harness)时学到了什么:如何编写无需人工监督也能让智能体保持正轨的测试,如何构建工作流程以便多个智能体可以并行取得进展,以及这种方法的天花板在哪里。
现有的像 Claude Code 这样的智能体支架(scaffolds)需要操作员在线并随时准备协同工作。如果你要求它解决一个漫长而复杂的问题,模型可能会解决其中的一部分,但最终它会停下来等待继续输入——比如一个问题、一个状态更新或是一个澄清请求。
为了引发出持续的、自主的进展,我构建了一个框架,将 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
在智能体提示词(prompt)中,我告诉 Claude
要解决什么问题,并要求它通过将问题分解成小块、跟踪当前正在处理的内容、弄清楚下一步该做什么,并有效地坚持下去直到完美为止。(关于最后一点,Claude
别无选择。这个循环会永远运行——尽管有一次,我确实看到 Claude
意外地执行了
pkill -9 bash,从而杀死了自己并结束了循环。哎呀!)。
并行运行多个实例可以解决单智能体框架的两个弱点:
我对并行 Claude 的实现是非常基础的。创建一个新的裸 git
仓库,对于每个智能体,启动一个 Docker 容器并将该仓库挂载到
/upstream。每个智能体将一个本地副本克隆到
/workspace,当它完成后,从其自己的本地容器推送到
upstream。
为了防止两个智能体试图同时解决同一个问题,该框架使用了一种简单的同步算法:
current_tasks/
写入一个文本文件来获取任务的“锁”(例如,一个智能体可能锁定
current_tasks/parse_if_statement.txt,而另一个锁定
current_tasks/codegen_function_definition.txt)。如果两个智能体试图认领同一个任务,git
的同步机制会强制第二个智能体选择另一个任务。
这是一个非常早期的研究原型。我还没有实现任何其他智能体之间的通信方法,也没有强制执行任何管理高层目标的流程。我没有使用编排智能体(orchestration agent)。
相反,我让每个 Claude 智能体自己决定如何行动。在大多数情况下,Claude 会选择“下一个最明显”的问题。当被某个 bug 卡住时,Claude 通常会维护一个包含失败尝试和剩余任务的运行文档。在项目的 git 仓库中,你可以通过历史记录看着它对各种任务加锁。
该支架在循环中运行 Claude,但只有当 Claude 知道如何取得进展时,这个循环才有用。我的大部分精力都花在了设计 Claude 周围的环境上——测试、环境、反馈——这样它就可以在没有我的情况下自我定位。以下是我在编排多个 Claude 实例时发现最有帮助的方法。
Claude 会自主地解决我给它的任何问题。所以,任务验证器必须近乎完美,这非常重要,否则 Claude 会解决错误的问题。改进测试框架需要找到高质量的编译器测试套件,为开源软件包编写验证器和构建脚本,并观察 Claude 犯的错误,然后在发现这些故障模式时设计新的测试。
例如,在项目快结束时,Claude 开始频繁地在实现新功能时破坏现有功能。为了解决这个问题,我建立了一个持续集成(CI)管道,并实施了更严格的强制措施,允许 Claude 更好地测试其工作,以确保新提交不会破坏现有代码。
我必须不断提醒自己,我是为 Claude 编写这个测试框架,而不是为我自己,这意味着要重新思考我对测试应如何传达结果的许多假设。
例如,每个智能体都被投放到一个没有上下文的新容器中,并且会花费大量时间来定位自己,尤其是在大型项目上。甚至在我们到达测试阶段之前,为了帮助 Claude 自助,我包含了维护大量 README 和进度文件的指令,这些文件应随当前状态频繁更新。
我还牢记语言模型具有固有的局限性,在这种情况下,需要围绕这些局限性进行设计。包括:
ERROR 并将原因放在同一行,以便
grep 可以找到它。预先计算聚合统计摘要很有帮助,这样
Claude 就不必重新计算它们。
--fast 选项,该选项运行 1% 或 10%
的随机样本。这个子样本对每个智能体是确定性的,但跨虚拟机是随机的,所以
Claude 仍然覆盖了所有文件,但每个智能体可以完美地识别回归错误。
当有许多不同的失败测试时,并行化是微不足道的:每个智能体选择一个不同的失败测试来处理。在测试套件达到 99% 的通过率后,每个智能体致力于让不同的小型开源项目(例如 SQlite, Redis, libjpeg, MQuickJS, Lua)能够编译通过。
但是当智能体开始编译 Linux 内核时,它们卡住了。与包含数百个独立测试的测试套件不同,编译 Linux 内核是一项巨大的任务。每个智能体都会遇到同一个 bug,修复那个 bug,然后覆盖彼此的更改。运行 16 个智能体并没有帮助,因为每个都在解决同一个任务。
修复方法是使用 GCC 作为在线的“已知良好编译器预言机(oracle)”进行对比。我编写了一个新的测试框架,随机使用 GCC 编译大部分内核,只用 Claude 的 C 编译器编译剩余文件。如果内核工作正常,那么问题就不在 Claude 的这部分文件中。如果它坏了,那么它可以通过用 GCC 重新编译其中一些文件来进一步缩小范围。这让每个智能体可以并行工作,修复不同文件中的不同 bug,直到 Claude 的编译器最终可以编译所有文件。(在这之后,仍然有必要应用差分调试(delta debugging)技术来找到那些成对失败但独立工作正常的代码文件。)
并行化也使得专业化成为可能。LLM 编写的代码经常重新实现现有的功能,所以我指派了一个智能体负责合并它发现的任何重复代码。我让另一个智能体负责提高编译器本身的性能,第三个智能体负责输出高效的编译代码。我要求另一个智能体从 Rust 开发者的角度批评项目的设计,并对项目进行结构性更改以提高整体代码质量,还有一个智能体负责文档工作。
该项目被设计为一个能力基准测试。我有兴趣压力测试 LLM 今天勉强能达到的极限,以帮助我们为模型在未来能可靠实现的目标做好准备。
我一直使用 C 编译器项目作为整个 Claude 4 模型系列的基准。正如我对之前的项目所做的那样,我首先起草了我想要的东西:一个从零开始的优化编译器,没有依赖项,兼容 GCC,能够编译 Linux 内核,并设计为支持多个后端。虽然我指定了设计的一些方面(例如,它应该有一个 SSA IR 以启用多个优化通道),但我没有详细说明如何做到这一点。
以前的 Opus 4 模型几乎无法生成一个功能性的编译器。Opus 4.5 是第一个跨越门槛的模型,允许它生成一个功能性的编译器,可以通过大型测试套件,但它仍然无法编译任何真正的大型项目。我对 Opus 4.6 的目标是再次测试其极限。
在两周内近 2,000 次 Claude Code 会话中,Opus 4.6 消耗了 20 亿个输入 token 并生成了 1.4 亿个输出 token,总成本略低于 20,000 美元。即使与最昂贵的 Claude Max 计划相比,这也是一个极其昂贵的项目。但这笔总额仅为我自己(更不用说整个团队)生产这个项目所需成本的一小部分。
这是一个“净室(clean-room)”实现(Claude 在开发期间的任何时候都没有互联网访问权限);它仅依赖于 Rust 标准库。这个 10 万行的编译器可以在 x86、ARM 和 RISC-V 上构建可启动的 Linux 6.9。它还可以编译 QEMU、FFmpeg、SQlite、postgres、redis,并且在大多数编译器测试套件(包括 GCC torture test suite)上拥有 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 中的 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 的许多其他人提供的帮助和贡献。