繁

以 C 编译器实验为例,理解并行 Agent 怎样通过独立工作区、任务锁和 Git 协作,以及语义冲突、故障拆分、测试反馈和角色分工。

Parallel Coding Agents:工作区隔离、任务认领与代码集成
19 分钟
3898 字

多智能体研究主要交换资料与结论。共同开发代码时,交换的是会改变项目行为的补丁:一个 Agent 修改了接口,其他 Agent 的工作条件就可能随之变化。

因此,并行开发需要同时组织三件事:谁正在做哪项任务、代码变化怎样进入共同版本、组合后的项目怎样验证。 工作区、任务锁和测试分别参与其中,没有一种机制能单独覆盖全部问题。

Anthropic 的 C 编译器实验提供了一个具体案例:16 个 Agent 共同开发 Rust 实现的 C 编译器。这里借它理解协作机制:

  • 状态与隔离: 各自编辑和运行,怎样与共享仓库保持联系。
  • 认领与集成: 怎样减少重复劳动,怎样处理文本和行为冲突。
  • 并行性与反馈: 怎样把共同瓶颈拆成独立工作,再用测试与专门角色推进项目。

一、多个 Agent 怎样在同一项目中工作h2

先区分共享的项目状态与各自操作的工作空间,再看任务归属怎样传播给其他参与者。

1. Shared Repository:通过代码与任务记录协作h3

共享仓库协作,是让多个 Agent 根据共同可见的项目状态选择工作,再通过提交交换修改。协调信息可以存在源码、任务记录、测试结果和进度文件中,不一定由一个中心 Agent 逐项分派。

这个编译器实验没有 Orchestrator Agent。各 Claude 会话自行选择下一项工作;人和 Harness 负责建立总体目标、运行环境与验证条件。

这与 Lead / Subagent 研究架构的区别,是任务安排与整合责任放在了不同位置。研究 Lead 收回材料后形成报告;共享项目则持续把修改纳入同一个代码版本。

可以把它的工作循环理解为:

读取项目状态 → 认领任务 → 修改与测试 → 同步共同版本 → 更新任务与进度

这里同步的不只是源码。其他 Agent 还需要知道哪些问题已经解决、哪些任务有人处理,以及哪些失败已经调查过。代码和协作记录一起构成下一轮工作的起点。

2. Workspace Isolation:独立操作与版本同步h3

工作区隔离解决独立操作,版本同步解决变化传播。

如果几个 Agent 直接编辑同一个目录,A 正在测试时,B 可能已经改了输入文件;测试结果就难以对应到一份明确代码。独立工作区让各 Agent 可以分别编辑、构建和运行,知道当前操作针对哪一份状态。

实验中,每个 Docker 容器都挂载共享裸仓库到 /upstream,再克隆到自己的 /workspace。各 Agent 操作本地副本,通过 Git 交换提交。

图 1 · 独立工作区通过共享 Git 交换变化,不由中心 Agent 分派
共享 Git 与独立工作区 各 Agent 在独立容器的本地克隆中认领任务、修改和测试,再拉取上游、处理合并、推送变化并释放任务锁。共享 Git 保存源码、任务标记与进度,不是负责分派工作的 Orchestrator Agent。不同修改合并后仍需验证。 共享 Git 仓库源码 · 任务认领 · 进度记录 Agent A · 独立容器Agent B · 独立容器 认领任务 → 修改与测试认领任务 → 修改与测试拉取 / 合并 / 推送拉取 / 合并 / 推送释放认领,记录进度释放认领,记录进度 本地克隆 /workspace本地克隆 /workspace 读取与推送变化读取与推送变化认领不同任务 ≠ 修改互不影响任务锁协调工作,合并后的状态仍需验证

本地副本不会自动等于共同版本

A 的测试可能基于版本 V,B 此时已经推送了修改。A 本地通过,只能说明自己的这份状态通过。拉取 B 的提交以后,测试对象已经改变,需要重新检查组合状态。

由此可以分开两个判断:

  • 我的修改在当前工作区是否成立?
  • 我的修改与其他提交组合后,项目是否仍然成立?

这不是多跑一次同样的测试,而是在验证两份不同的代码状态。

进度记录补充源码没有表达的信息

代码可以说明现在实现了什么,却未必说明为何这样实现、已经排除了哪些假设。新会话需要借助 README、失败记录和进度文件恢复工作位置。

例如,“参数传递失败”还不足以接续调查;对应的目标架构、失败用例、相关提交与已尝试方案,才能减少重复定位。记录也应随项目更新,否则它会把旧判断传给下一轮。

3. Task Lock:认领任务与减少重复劳动h3

Task Lock 表达的是任务归属:当前这项工作已经有人处理。 它让其他 Agent 在选择任务前看到已有安排。

实验使用 current_tasks/ 下的文本文件记录认领。两个 Agent 争抢同一任务时,需要通过 Git 同步竞争认领状态,后者转向其他任务。工作完成后,修改与其他提交合并,再更新或移除认领标记。

理解这个机制,最重要的是区分本地意图与共享状态。A 只在自己的目录里写下“我要修复 X”,B 未必知道;认领信息进入大家采用的同步流程以后,才具有协调作用。

任务锁的粒度是任务,不是所有相关代码

“修复解析条件语句”和“调整表达式表示”可以是两个任务名,但可能同时修改一组类型。任务名不同,并不意味着涉及的文件或接口互不影响。

锁也不能自动识别两个失败用例是否来自同一根因。如果大家分别认领了不同测试,最后都去改同一个基础模块,重复工作仍可能发生。

因此,任务认领要配合进度与失败归因。它减少的是无意重复,代码能否组合还要由集成与验证决定。

二、代码冲突与共同故障怎样处理h2

独立工作不等于修改互不影响。代码需要整合,共同失败也需要拆成能够分别调查的问题。

4. Git Merge:整合补丁与检查语义冲突h3

Git Merge 整合不同提交,首先处理的是文件内容怎样组合。并行开发还需要检查组合之后的程序含义。

冲突怎样发生怎样发现与处理
文本冲突两份补丁修改相同代码区域,Git 无法自动合并对照两项任务的意图,手动调整补丁
语义冲突文本能够合并,但接口、假设或行为不兼容编译、集成测试和行为验证

例如,A 把一个函数的返回值改成“字节数”,B 在另一个文件中仍把它当作“元素数”。两份修改没有碰到同一行,Git 可能顺利合并,程序却已经改变含义。

这说明“没有冲突”有两种完全不同的意思:没有需要人工处理的文本冲突,不代表不存在行为冲突。

集成时需要核对的不只是自己的差异,还包括依赖条件是否变化。接口、公共类型和基础设施修改,尤其容易影响其他任务。专门角色与任务锁可以帮助分工,却不能省略共同版本的验证。

5. GCC Oracle:拆分可以独立排查的故障h3

并行性取决于能否拆出可分别调查的子问题。

几十个互不相关的失败测试,容易分派给不同 Agent;一次大型构建只暴露第一个失败点,则可能让所有 Agent 都停在同一个位置。此时增加 Agent,往往增加的是竞争修复。

GCC Oracle:用已知可用实现缩小失败范围

编译 Linux 内核时,实验确实出现了大家围绕同一个 bug 重复工作的情况。解决办法是改变验证方式:大部分文件交给 GCC 编译,只让剩余部分交给待测编译器,再检查混合构建结果。

如果失败,继续把可疑集合中的一部分改用 GCC,观察故障是否仍然出现。这样可以逐步缩小触发问题的范围。

这里的 Oracle 是提供参考行为的已知可用实现。它的作用不是让 Agent 阅读 GCC 源码,而是帮助控制变量:哪些文件由待测编译器负责,哪些变化与故障出现有关。

不同 Agent 因而可以调查不同文件集合。协作单位从“都让整个内核编译通过”,变成了“分别定位某个可复现故障”。

组合故障:单独通过不代表一起通过

实验还需要 delta debugging,寻找单独使用正常、组合时失败的文件对。独立子集的测试结果,不能完全代替组合行为。

我从这个例子提取的重点是:测试不仅能验收结果,还能改变任务的可拆分程度。能定位失败边界,才容易划分责任;始终只得到同一条“大任务失败”,就很难形成有效并行。

三、开发结果怎样验证与持续改进h2

测试提供修改是否成立的证据,专门角色则持续处理功能之外的质量目标。

6. Testing:局部验证、集成验证与回归检查h3

并行 Agent 要根据测试反馈选择下一项工作。验证器既影响“什么算完成”,也影响“接下来该查哪里”。

局部验证:确认本次修改确实解决目标问题

最小复现或专项测试,可以帮助把结果与当前补丁联系起来。没有这一步,只看到整个项目偶尔成功,很难说明本次修改产生了什么作用。

集成与回归:检查共同版本和已有能力

集成检查关注其他补丁加入后的组合;回归检查关注新增功能是否破坏过去正常的行为。实验后期出现功能反复退化,因此增加了更严格的 CI 验证。

这两类检查解释了为什么“我负责的测试通过”不等于“团队可以继续叠加修改”:后续 Agent 使用的是共同项目,而不是那一个孤立用例。

反馈表达:先给定位入口,再提供详细记录

终端可以返回失败项目、关键错误和日志位置,完整记录放到外部供按需读取。这样下一轮能够直接进入有关证据,而不必从海量输出中重新搜索。

快速抽样则缩短一次修改后的等待。实验让单个 Agent 内的样本保持稳定、不同虚拟机使用不同样本,兼顾前后比较与团队覆盖。抽样适合开发反馈,最终覆盖范围仍应按验收目标安排。

7. Specialized Roles:分工维护性能、结构与文档h3

任务分解按问题划分,角色分工则按持续职责划分。两者可以配合:功能 Agent 修复当前失败,性能或质量角色处理跨模块问题。

编译器里就存在两类不同性能目标:

  • 编译器自身性能: 编译项目需要多长时间、消耗多少资源。
  • 生成代码性能: 被编译出来的程序运行得怎样。

合并重复实现、审查 Rust 结构和维护文档,又是其他职责。分别关注这些目标,能减少团队只追逐眼前失败测试的倾向。

不过,跨模块工作也更容易改变其他人的条件。去重可能调整接口,结构修改可能影响正在开发的后端。因此,角色分工需要与任务认领、同步和验证一起使用。

这项实验在多种架构上构建了 Linux 6.9,但发布时仍有外部工具链依赖、生成代码效率与代码质量等限制。它展示了并行协作能够推进大型产物,也展示了测试覆盖、并行性和工程成熟度是不同的问题。实验报告与限制

几个关键问题,串起任务认领、代码集成与验证h2

独立工作区解决了什么,又没解决什么?

它让编辑和执行针对各自明确的本地状态。提交进入共享版本以后,修改仍会互相影响,因此不能替代集成验证。

为什么任务认领需要同步?

没有传播出去的认领只是本地记录。其他 Agent 需要在选择工作前取得共享状态,才能据此避开已有任务。

Task Lock 和 Git Merge 能否保证协作正确?

任务锁协调工作归属,Merge 整合文本版本;行为是否兼容仍要检查。两者都不能单独保证程序正确性。

没有文本冲突,为什么测试还会失败?

不同补丁可能对接口含义、数据格式或执行顺序作出不兼容假设。语义冲突可以发生在完全不同的文件里。

GCC Oracle 怎样帮助恢复并行性?

混合编译与逐步替换文件,让大型失败能够被归因到更小集合。不同 Agent 因而有了可以分别调查的故障范围。

为什么快速测试与完整验收要分开?

前者缩短反馈周期,帮助定位和比较修改;后者检查更完整的目标与组合。通过快速样本,说明的是这些样本没有暴露问题。

无中心协调 Agent,是否就等于 Handoff?

不是。这里通过任务记录与共享产物协调工作;Handoff指的是执行控制权从一个 Agent 转给另一个 Agent,机制不同。

参考资料h2