多 Agent 的文件与产物协作
产物是任务留下的可读取结果,例如代码文件、报告、数据集和图表。多 Agent 协作除了传递结论,还需要约定谁修改产物、基于哪个版本修改,以及各份结果如何组成最终交付。
假设写作 Agent 与审查 Agent 都在处理 report.md。写作 Agent 读到旧文件后重写第二节;审查 Agent 在同一节补了一条限定条件。如果两者都按各自读到的全文覆盖保存,后落盘的版本可能丢掉另一方的修改。即使工具采用局部 patch,两个修改也可能基于不同上下文,合在一起后出现重复或矛盾。
解决方式取决于任务能否分开:可以让不同 Agent 直接修改各自负责的文件,可以在隔离工作区中独立实现,也可以让它们提交 patch 后统一应用。没有一种策略适合所有任务。
先看哪些修改能够独立进行
| 策略 | 工作方式 | 适合的任务 | 合并时仍需检查什么 |
|---|---|---|---|
| 独立文件所有权 | 每个 Agent 直接修改指定文件,约定共享文件由谁处理 | 文档拆篇、边界稳定的模块修改 | 交叉引用、接口和共同约定是否一致 |
| 隔离工作区 | 每个 Agent 在独立目录或 Git worktree 中修改,最后汇总差异 | 跨多个文件的功能实现、需要各自运行验证的任务 | 文本冲突、接口变化、依赖及整体行为 |
| patch 队列 | 执行者提交修改和基线,合并者按顺序应用 | 多方需要改同一文件、写入范围需集中校验的任务 | patch 是否过期、是否越界、语义能否同时成立 |
例如整理一组教程时,A 可以负责 communication.md,B 负责 lifecycle.md,导航文件由协调者统一更新。这些文件可以并行直接落盘,没必要先把每次修改都转换成 patch。
但文件名分开不等于任务完全独立。A 将任务终态命名为 completed,B 使用 done,各自文档都能通过 Markdown 检查,整组教程仍然不一致。分派前应约定公共术语或接口,并明确这些公共定义变化后由谁通知其他执行者。
同文件的不同章节也可以划分所有权,不过标题位置和行号会随编辑变化。章节标识通常比“第 40—80 行”更稳定。自动格式化、批量重命名和全文件重写会越过分区,因此需要集中执行或串行处理。
隔离工作区隔离了什么
在独立工作区中,两个 Agent 可以从同一个基线版本开始,各自修改和验证,不会直接覆盖对方的工作文件。Git worktree 允许同一仓库拥有多个检出目录,适合把不同任务的文件状态分开;普通独立目录也能实现文件层面的隔离,但需要另外管理基线和差异。
共同基线 v1
├─ 工作区 A:修改资料读取模块 → 产物 A
└─ 工作区 B:修改报告生成模块 → 产物 B
↓
汇总两份差异
↓
检查接口衔接与集成后的行为隔离工作区仍可能共享 Git 对象库、包缓存、数据库、外部服务或测试端口。它提供的是文件修改空间,不自动等于权限隔离,也不保证运行中的副作用互不影响。需要并行运行程序时,应检查它们写入的实际位置和使用的服务资源。
汇总时保留任务基线、修改意图和验证结果,比只交一个目录路径更有用。如果 A 改了函数返回结构,B 还按旧结构调用,合并者需要协调接口。两个分支能被 Git 自动合并,只说明文本层面存在可用的合并结果。
patch 队列怎样应用一次修改
需要集中处理同一份报告时,可以让写作 Agent 提交修改差异。任务分派先带上目标文件、读取版本和允许修改的范围:
{
"task_id": "revise-handoff-section",
"artifact": "report.md",
"base_version": "v1",
"target_section": "上下文交接",
"intent": "解释结果交接需要保留的证据与未完成项",
"constraints": ["保留其他章节", "不改变已确认的任务条件"],
"deliverable": "unified diff 及修改说明"
}执行者读取这份基线后生成 patch,返回时仍保留基线标识:
{
"task_id": "revise-handoff-section",
"artifact": "report.md",
"base_version": "v1",
"patch_ref": "patches/revise-handoff.diff",
"intent": "补充证据引用和部分完成的交付方式",
"validation": {
"checked": ["目标章节的引用关系", "修改范围"],
"not_checked": ["与其他待合并修改的衔接"]
}
}v1 可以对应一份不可变快照,也可以由内容哈希或 Git 提交标识表示。行号只帮助定位,不能证明修改依据的内容仍然相同。patch_ref 指向的内容也要固定,不能在合并者检查之后悄悄替换。
合并者读取当前文件,核对任务范围与基线,检查 patch 是否可应用,再进行实际写入。如果另有程序或人也可能在检查之后修改文件,应使用能覆盖这些写入者的锁,或将版本比较与写入作为一个原子操作。只在写入前再次读取版本,依然留有被其他写入抢先的间隙。
应用后,新的文件版本、已处理的 patch ID 和检查结果一起记录,便于避免重复应用,也便于后续修改确定自己的基线。合并者可以是确定性的程序、负责集成的 Agent 或人,不要求每次普通写入都人工确认。
旧版本 patch 该怎样处理
沿用同一份教程,写作 Agent 调整“上下文交接”的结构,审查 Agent 补充来源标记。两者最初都读到 v1:
| 时刻 | 发生的动作 | 当前文件 |
|---|---|---|
| 1 | 写作 Agent 基于 v1 返回 patch A | v1 |
| 2 | 审查 Agent 基于 v1 返回 patch B | v1 |
| 3 | 合并者应用 A,第二节已经重新组织 | v2 |
| 4 | 合并者检查 B,发现修改区域与 A 重叠 | 保留 v2,B 暂不应用 |
| 5 | 审查 Agent 读取 v2,确认原意见是否仍然成立,生成 B2 | v2 |
| 6 | 合并者应用 B2,并检查前后文 | v3 |
B2 未必是把 B 换几个行号。如果 A 已经补上了部分说明,审查 Agent 只需要提出尚未解决的问题;如果 A 改变了表达方式,原来的插入位置也可能不再合适。
基线不同说明修改需要重新核对,不代表所有旧 patch 都必须废弃。如果两次修改作用于独立段落,可以比较基线、当前版本和目标修改,使用三方合并或在最新版上重新应用,再检查结果。范围重叠、事实前提改变或修改意图已被覆盖时,要求执行者基于最新版重新生成通常更清楚。
人中途编辑了文件,也按同样原则保留当前内容并比较意图。不能为了应用一个旧 patch,先把文件恢复到 Agent 最初读到的状态。
文本合并之后,还要看产物是否成立
文本冲突通常能被差异工具发现:同一行被改成两种内容,或者 patch 找不到预期上下文。语义冲突可能出现在完全不同的文件里。
| 改动组合 | 文本层面 | 需要处理的问题 |
|---|---|---|
| A 修改函数返回字段,B 新增调用方 | 可能自动合并 | 调用方是否仍按旧字段取值 |
| A 将报告预算改为 5500 元,B 在结论推荐 5999 元方案 | 可以没有重叠行 | 结论是否使用最新条件 |
| A 移动章节,B 新增到旧路径的链接 | 各自文件语法正确 | 链接是否失效 |
| A、B 各补一段相同机制说明 | 可以自动合并 | 是否重复、术语是否一致 |
验证要跟修改的关系走。代码需要覆盖受影响接口和组合后的行为;文档需要检查链接、示例前提、术语以及结论与证据的对应。执行者可以分别验证自己的部分,集成者还要检查相互依赖的部分。
当冲突涉及不同事实结论,应回查来源与适用条件;涉及不同实现方案,则按任务要求选择或组合,具体见审查、辩论与结果裁决。如果现有要求不足以决定取舍,才需要补充决策输入。通过格式检查不会自动解决这些判断。
交付时返回最终产物的版本、改动范围、已执行的检查与未完成事项,下游才能确认自己读取的内容与这次交付一致。这种产物引用也可以放入任务结果和共享状态,无须把整个文件复制进每个 Agent 的聊天记录。
参考资料
- Git:git-worktree:多个工作区的创建、状态与限制。
- Git:git-apply:patch 检查、应用和三方合并选项。
- Git:git-merge:合并行为与冲突处理。