用一组并行 Claude 构建 C 编译器
最近,我一直在尝试一种新的语言模型监督方式,我们把它称为:
Agent Teams,Agent 团队。
在 Agent Teams 中,多个 Claude 实例会同时在同一个代码库上并行工作,而不需要人类持续在线干预。
这种方式显著扩大了 LLM Agent 能够完成的任务规模。
为了做压力测试,我让 16 个 Agent 从零开始编写一个基于 Rust 的 C 编译器,并要求它能够编译 Linux Kernel。
经过近 2,000 次 Claude Code Session,以及约 20,000 美元 API 成本之后,这组 Agent 最终产出了一个约 10 万行代码的编译器。
它能够在以下架构上编译 Linux 6.9:
- x86;
- ARM;
- RISC-V。
这个编译器本身已经是一个很有意思的产物,但本文更关注我从中学到的关于长时间运行、自主 Agent Team Harness 设计的经验:
- 如何写测试,让 Agent 在没有人类监督的情况下保持正确方向;
- 如何组织工作,让多个 Agent 真正能够并行推进;
- 这种方法最终会在哪里撞到能力上限。
让 Claude 长时间持续运行
现有 Agent Scaffold,例如 Claude Code,通常默认用户在线,并与 Agent 共同工作。
如果让模型解决一个非常长、非常复杂的问题,它可能完成其中一部分,但最终通常会停下来等待用户继续输入,例如:
- 回答一个问题;
- 查看状态更新;
- 澄清需求。
为了让 Agent 能够持续、自主推进,我构建了一个非常简单的 Harness,把 Claude 放进一个无限循环里。
如果你见过 Ralph Loop,这个结构应该很熟悉。
一个任务完成后,它会立刻开始下一个任务。
请把下面的脚本运行在 Container 里,而不是你的真实机器上。
#!/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 Prompt 中,我会告诉 Claude:
- 需要解决什么问题;
- 把问题拆成较小部分;
- 持续追踪当前工作;
- 判断下一步应该做什么;
- 一直继续,直到整个项目足够完善。
最后这一点 Claude 实际上没有选择权。
因为循环会永远运行。
不过,有一次 Claude 意外执行了:
pkill -9 bash
结果把自己的父进程杀掉了,也顺便结束了无限循环。
算是一次小事故。
并行运行 Claude
并行运行多个实例,可以缓解单 Agent Harness 的两个弱点。
1. 单个 Claude Code Session 一次只能做一件事
随着项目规模扩大,如果存在多个 Bug,同时调试显然比串行处理效率高得多。
2. 多 Agent 可以产生专业化分工
除了让若干 Agent 直接解决核心问题之外,还可以让其他 Agent 专门负责:
- 文档维护;
- 代码质量;
- 性能;
- 特定子任务。
我的并行 Claude 实现非常简单。
系统会先创建一个新的裸 Git Repository。
然后:
- 为每个 Agent 启动一个 Docker Container;
- 把共享 Repository 挂载到:
/upstream
- 每个 Agent 把 Repository 克隆到自己的本地目录:
/workspace
- Agent 完成任务后,再从自己的 Container 把修改 Push 回共享 Upstream。
防止多个 Agent 同时做同一个任务
Harness 使用了一个非常简单的同步算法。
第一步:通过文件抢占任务锁
Claude 会在:
current_tasks/
目录中写一个文本文件,表示自己已经接下某项任务。
例如:
current_tasks/parse_if_statement.txt
另一个 Agent 可能同时锁定:
current_tasks/codegen_function_definition.txt
如果两个 Agent 同时尝试领取同一个任务,Git 的同步机制会迫使第二个 Agent 改选其他任务。
第二步:完成任务并合并
Claude 完成任务后,会:
- 从 Upstream 拉取最新改动;
- 合并其他 Agent 的修改;
- Push 自己的修改;
- 删除自己的任务锁。
Merge Conflict 很常见。
不过 Claude 足够聪明,通常能够自行处理。
第三步:重新启动一个全新 Session
无限 Agent Generation Loop 会在新的 Container 中创建一个新的 Claude Code Session。
然后继续重复整个循环。
这仍然只是一个非常早期的研究原型。
目前我还没有实现:
- Agent 之间的其他直接通信方式;
- 高层目标管理流程;
- Orchestration Agent。
我基本把决策全部交给各个 Claude Agent 自己。
大多数情况下,Claude 会选择:
下一个最明显的问题。
当它被某个 Bug 卡住时,Claude 经常会自己维护一份持续更新的文档,记录:
- 已经尝试过哪些失败方案;
- 还有哪些任务没有完成。
从项目 Git History 中,可以看到这些 Agent 如何不断:
- 抢占任务;
- 完成任务;
- 释放任务锁;
- 推进代码库。
使用 Claude Agent Team 编程时学到的经验
Harness 只是让 Claude 循环执行。
但只有当 Claude 能够判断:
怎样才算取得进展
时,这个循环才真正有意义。
我的大部分精力,其实都花在了围绕 Claude 设计环境:
- 测试;
- 执行环境;
- 反馈机制。
目标是让它能够在没有我参与的情况下自行定位当前状态。
下面这些方法,是我在编排多个 Claude 实例时最有帮助的经验。
编写极高质量的测试
Claude 会非常认真地自主解决你交给它的问题。
所以:
任务验证器必须几乎完美。
否则 Claude 会极其高效地解决错误的问题。
改进测试 Harness 的过程包括:
- 寻找高质量编译器测试套件;
- 为开源软件包编写验证器;
- 编写 Build Script;
- 观察 Claude 常犯哪些错误;
- 根据这些失败模式增加新的测试。
例如,在项目后期,Claude 经常会出现一种问题:
每实现一个新功能,就破坏一些已有功能。
为了解决这一点,我建立了 CI Pipeline,并加强了测试约束。
这样,Claude 能够更可靠地测试自己的工作,并阻止新 Commit 破坏已有代码。
站在 Claude 的角度思考
我必须持续提醒自己:
这套测试 Harness 是写给 Claude 用的,不是写给我自己用的。
这意味着必须重新思考很多关于测试输出方式的默认假设。
例如,每个 Agent 都会被放进一个:
- 全新的 Container;
- 完全没有上下文。
对于大型项目,它可能花很多时间重新理解项目现状。
所以,在真正进入测试之前,我就给 Claude 提供了一些帮助它理解项目的机制。
例如,我要求它持续维护:
- 大量 README;
- Progress File;
并频繁更新当前状态。
同时,还必须针对语言模型本身的一些天然限制做设计。
其中包括:
上下文窗口污染
测试 Harness 不应该打印几千字节没有价值的输出。
最多只应该打印:
- 少量几行摘要。
所有重要细节都写进日志文件中,让 Claude 在真正需要时再读取。
日志文件还应该方便自动处理。
例如:
如果发生错误,就应该在同一行中写:
ERROR
以及具体原因。
这样 Claude 就可以直接用:
grep
定位问题。
最好还提前计算汇总统计数据,避免 Claude 自己重新计算。
时间盲区
Claude 没有真正的时间感。
如果完全不加约束,它很可能花数小时一直运行测试,而不是继续推进任务。
因此,我的 Harness:
- 只偶尔打印增量进度,避免污染 Context;
- 默认提供:
--fast
选项。
这个选项只运行:
- 1%;
- 或 10%;
的随机样本。
这些抽样对于:
同一个 Agent 是确定性的
但在不同 VM 之间:
又是随机的。
这样一来:
- 不同 Agent 总体上仍然能够覆盖全部测试;
- 每个 Agent 又可以稳定识别自己修改造成的 Regression。
让并行化尽可能容易
当测试套件中有很多彼此独立的失败项时,并行化非常简单。
每个 Agent 只需要选择一个不同的失败测试进行修复。
当整个测试套件通过率达到:
99%
之后,我让不同 Agent 分别尝试让不同小型开源项目通过编译。
例如:
- SQLite;
- Redis;
- libjpeg;
- MQuickJS;
- Lua。
这种任务天然可以并行。
但当 Agent 开始尝试编译 Linux Kernel 时,系统卡住了。
原因在于:
与拥有数百个独立测试的测试套件不同,编译 Linux Kernel 是一个巨大的单体任务。
每个 Agent 都会:
- 遇到同一个 Bug;
- 尝试修复同一个 Bug;
- 最后互相覆盖修改。
所以即使同时运行 16 个 Agent,也没有真正提高效率。
使用 GCC 作为在线已知正确 Oracle
为了解决这个问题,我使用:
GCC
作为一个已知正确的编译器 Oracle。
我写了一套新的测试 Harness。
它会随机地:
- 使用 GCC 编译 Linux Kernel 中的大多数文件;
- 使用 Claude 的 C 编译器编译剩余文件。
如果最终 Kernel 能够工作:
说明问题不在 Claude 编译器负责的那部分文件中。
如果 Kernel 失败:
系统就可以继续缩小范围,把其中一部分文件改回 GCC 编译。
这样,每个 Agent 就可以在不同文件上并行工作:
- 定位不同 Bug;
- 修复不同 Bug。
最终,Claude 的编译器逐渐可以接管越来越多文件,直到能够编译全部文件。
即便如此,还需要使用:
Delta Debugging
进一步寻找这种特殊情况:
- 两个文件单独编译都没问题;
- 但一起编译时会失败。
多 Agent 角色分工
并行系统的另一个优势,是可以做专业化。
LLM 生成的代码经常会重复实现已有功能。
因此,我让一个 Agent 专门:
合并重复代码。
另一个 Agent 负责:
提高编译器自身性能。
第三个 Agent 负责:
让生成的机器码更加高效。
我还让一个 Agent:
从 Rust 开发者视角批评项目设计,并对整体结构进行调整,从而提升代码质量。
另外还安排一个 Agent 专门:
维护文档。
对 Agent Team 的能力上限做压力测试
这个项目本质上是一个:
Capability Benchmark。
我真正感兴趣的是:
测试 LLM 在今天刚刚勉强能够实现什么。
这样可以帮助我们提前理解:
未来模型能够稳定实现什么。
我已经在整个 Claude 4 系列模型上,把这个 C 编译器项目当作长期 Benchmark 使用。
和我之前做过的项目一样,我一开始只写了一份高层要求。
目标是构建:
- 从零实现;
- 带优化能力;
- 没有外部依赖;
- GCC Compatible;
- 能编译 Linux Kernel;
- 支持多个 Backend;
的 C 编译器。
我指定了一些设计要求。
例如:
应该拥有:
SSA IR
这样可以支持多个 Optimization Pass。
但我并没有详细告诉 Claude:
具体该怎么实现。
不同 Opus 版本的能力变化
早期 Opus 4 系列模型,只能勉强写出一个能工作的编译器。
Opus 4.5 是第一个跨过某个能力阈值的模型。
它能够:
- 构建真正可用的编译器;
- 通过大型测试套件。
但它依然无法编译大型真实项目。
所以,使用 Opus 4.6 时,我再次把目标设为:
继续探索它的能力边界。
评测结果
两周内,项目总共运行了:
近 2,000 次 Claude Code Session。
Opus 4.6 消耗了:
20 亿 Input Token
并生成了:
1.4 亿 Output Token。
总成本:
略低于 20,000 美元。
和最昂贵的 Claude Max 订阅相比,这当然是一个极其昂贵的项目。
但如果让我自己完成同样的工作,这依然只是人工成本的一小部分。
更不用说:
如果需要组建一个完整团队来完成它,成本还会更高。
最终编译器能力
这是一个 Clean-room Implementation。
Claude 在整个开发过程中:
完全没有互联网访问。
编译器只依赖:
Rust Standard Library。
最终,这个约 10 万行代码的编译器能够在以下架构上构建可启动的 Linux 6.9:
- x86;
- ARM;
- RISC-V。
它还能够编译:
- QEMU;
- FFmpeg;
- SQLite;
- PostgreSQL;
- Redis。
在大多数编译器测试套件中,它的通过率达到:
99%
其中包括:
GCC Torture Test Suite。
它还通过了开发者最经典的最终测试:
可以编译并运行 Doom。
编译器仍然存在的局限
当然,这个编译器并不完美。
它依然有很多明显限制。
1. 缺少 16-bit x86 编译器
从 Real Mode 启动 Linux 时,需要 16-bit x86 Code Generator。
目前这个阶段仍然会调用 GCC。
不过:
- x86_32;
- x86_64;
都是它自己实现的。
2. 没有完整自研 Assembler 和 Linker
Claude 在项目最后阶段才开始自动实现:
- Assembler;
- Linker。
目前它们依然存在不少 Bug。
演示视频使用的是 GCC 的:
- Assembler;
- Linker。
3. 并不能编译所有项目
它能够成功构建许多真实项目。
但并不是所有项目。
因此,目前还不能直接当作真正编译器的 Drop-in Replacement。
4. 生成代码效率较低
即使开启所有 Optimization:
它生成的机器码效率依然比:
完全关闭 Optimization 的 GCC
还要差。
5. Rust 代码质量只是“还可以”
整个 Rust Codebase 的质量合理。
但和真正专家级 Rust 程序员相比:
仍然有明显差距。
逼近 Opus 的能力极限
最终这个编译器已经非常接近 Opus 当前的能力上限。
我非常努力地尝试修复上面的一些限制,但没有完全成功。
继续开发时,经常发生:
- 添加新功能;
- 修复新 Bug;
- 却破坏已有功能。
其中一个特别困难的问题,就是:
16-bit x86 Code Generator。
Linux 从 16-bit Real Mode 启动时需要这部分能力。
Opus 无法真正实现一个符合要求的 16-bit x86 Code Generator。
它虽然能够通过:
66
和:
67
Opcode Prefix
生成正确的 16-bit x86 代码,
但最终输出大小超过:
60KB
而 Linux 对这一阶段代码大小的限制只有:
32KB
所以最终:
Claude 在这里“作弊”了。
它直接调用 GCC 完成这一阶段。
不过,这个限制只存在于:
x86。
对于:
- ARM;
- RISC-V;
Claude 的编译器可以完全自行完成整个编译流程。
为什么公开源码很重要
这个编译器的源码已经公开。
我建议:
- 下载它;
- 阅读代码;
- 用你喜欢的 C 项目测试它。
我一直认为,理解语言模型真正能力的最佳方法就是:
把模型推到极限,然后研究它在哪里开始崩溃。
接下来,我还会继续让 Claude 对这个项目提交新修改,继续尝试解决这些剩余限制。
展望未来
每一代语言模型,都会打开一种新的协作方式。
早期模型主要用于:
IDE Tab Completion。
没过多久,模型已经可以:
根据 Docstring 自动补完整个 Function Body。
Claude Code 的推出,则让 Agent 真正进入主流开发流程,使开发者能够与 Claude Pair Programming。
但这些产品都默认一个基本交互模式:
- 用户定义任务;
- LLM 运行几秒或几分钟;
- 返回结果;
- 用户继续给出下一步指令。
Agent Teams 展示了一种完全不同的可能性:
让模型自主实现完整、复杂的项目。
这意味着,作为这些工具的使用者,我们可以开始设定更加野心勃勃的目标。
自主开发也带来真实风险
我们仍然处在非常早期阶段。
完全自主开发,也带来非常真实的风险。
当人类一直和 Claude 一起开发时:
- 可以持续检查质量;
- 可以实时发现错误。
但在自主系统中,很容易看到:
测试通过
然后就认为:
工作已经完成。
但现实中,往往并不是这样。
我过去从事渗透测试工作。
我的工作就是利用大型公司产品中的安全漏洞。
因此,一想到开发者可能直接部署:
他们自己从未亲自审查过的软件
我就感到真正担忧。
所以,这次实验一方面让我非常兴奋,另一方面也让我有些不安。
构建这个编译器,是我最近做过最有趣的项目之一。
但我完全没有预料到:
2026 年这么早的时候,这种事情就已经接近可行。
语言模型,以及我们围绕它们构建的 Scaffold,都在快速进步。
这意味着:
未来我们可以生成数量极其庞大的新代码。
我仍然认为:
积极应用最终会超过负面影响。
但我们确实正在进入一个新的世界。
而这个世界需要新的安全策略来应对。
- 点赞
- 收藏
- 关注作者
评论(0)