Agentic Drift:一次成为多个开发者很难
I’m happy to translate the article for you, but I need the actual text you’d like translated. Could you please paste the content (or the portion you want translated) here? I’ll keep the source line and all formatting exactly as you specify.
Agentic Drift
我一直在并行运行多个 AI 编码代理——一次五、六,有时甚至八个工作区,每个都在同一个代码库上处理不同的功能或修复。这种方式在短时间内非常高效。感觉就像雇了一支小团队。然后你停下来看看实际产出,事情就变得奇怪了。
一个代理添加了 dynamic model discovery。另一个在不同工作区解决不同问题的代理也添加了 dynamic model discovery——版本略有不同,类名也不同。第三个代理在实现其功能时需要模型列表,既没有看到前两个实现,又自行内联了实现。于是我在三个分支中拥有了同一概念的三个版本,彼此之间毫不知情。
这就是我所说的 agentic drift:当并行的自主代理在缺乏协同的情况下处理代码库的相关部分时,逐渐、隐形的分歧。它不是 Git 意义上的合并冲突——文件可能可以顺利合并。它是 语义冲突。代码可以编译,测试可以通过,但你实际上把同一件事实现了三次,每个版本对其工作方式的假设略有不同。
它是如何发生的
产生这种情况的工作流很诱人,因为一开始感觉非常好。
- 你确定了六件需要完成的事。
- 你启动了六个代理。
- 每个代理得到一个工作区——一个干净的分支、一个聚焦的任务、完全的自治。
一个小时后,每个代理都取得了实质性进展。Pull Request 开始出现。你感觉自己像个 CTO。
问题出现在任务并非真正独立时。而且它们几乎从不独立。软件是 图,而不是列表。
- 功能 A 需要一个工具函数。
- 功能 B 需要一个类似的工具函数。
- 功能 C 重构了应该放置该工具函数的模块。
这些代理之间没有任何交流。它们各自做出在局部看来合理的决定,却在全局上不一致。
最终的表现大致如下:
| 症状 | 描述 |
|---|---|
| Duplicate implementations(重复实现) | 同一概念被多种方式实现,有时使用相同的名称,有时则不同 |
| Architectural divergence(架构分歧) | 一个分支简化系统,另一个分支扩展系统。两者单独看都合理,但合在一起就矛盾 |
| Cross‑pollination artifacts(交叉渗透产物) | 负责特性 X 的代理在模块 Y 中修复了一个 bug,负责特性 Z 的另一个代理也以不同方式修复了同一个 bug,导致两个无关 PR 中出现相同 bug 的两个修复 |
| Phantom dependencies(幻影依赖) | 你记得某个特性已经实现,于是认为它已经存在,但实际上它在另一个工作区。你正在合并的分支并没有它,导致莫名其妙的破坏 |
等待整合的时间越长,问题越严重。每个工作区与其他工作区的偏离越来越大。最终的合并不是简单的叠加——它更像是考古。你必须从分叉的时间线中重建意图。
整合成本
我刚在 Glue 上经历了这种情况——这是我正在构建的一个基于终端的编码代理。使用 Conductor(它让并行启动代理变得异常容易)进行了一段时间的并行工作后,我出现了:
- 4 个打开的 PR,其中两个出现了合并冲突
- 10+ 个没有 PR 的特性分支,每个分支都有实质性工作
- 在已经有 PR 的分支上,另一个工作区中还有未提交的更改
- 3 个空分支,工作从未开始
- Ollama 模型发现、技能加载和会话回放的实现出现重叠
- 一个 PR 删除了另一个 PR 所依赖的缓存系统
弄清楚要先合并哪些内容、合并顺序以及如何调和这些矛盾,花的时间比构建任何实际功能都要长。
Source: …
个体特性。这就是 集成成本——为并行性付出的代价,而且是非线性的:
- 2 个并行代理 → 大约 1.5 倍的集成工作量
- 8 个并行代理 → 大约 5 倍的集成工作量
每个单独的 PR 看起来都不错:有测试、描述清晰、代码整洁。只有把它们全部摆出来并追踪共享的表面时,才会看到混乱。
- 功能 B 假设功能 A 从未被构建。
- 功能 D 删除了功能 E 所扩展的内容。
- 模型注册表被一个代理重构,而另外三个代理保持不变。
一个提示实验:理想化差分
除了漂移问题之外,我一直在尝试一种用于代码改进的提示技术,我认为它可能有助于集成阶段。该技术很简单:
- 看看这段代码。
- 想象它实际上是优秀的——结构良好、优雅地处理边缘情况、数据流清晰、抽象明确。
- 详细描述这个想象中的版本。
- 将其与我们实际拥有的进行比较。
我把它称为 理想化差分。与其问“这段代码有什么问题?”(往往只会得到表面的挑剔)或“重构这段代码”(往往只会产生增量改动),不如先让模型构建一个完整的理想版本的心象,然后利用理想与实际之间的差距作为结构化的改进计划。
假设: 当你给模型一个具体的代码库作为参考时,“想象中的更好版本”会保持在实际约束之内。它可以看到真实的限制——例如,需要处理粘贴的 TUI、具有向后兼容要求的会话存储。理想化版本在尊重这些约束的同时改进架构。如果没有代码库作为参考,模型会产生幻觉细节或输出通用的东西。
早期结果令人鼓舞。 在合并冲突分支后的模块上使用,它往往会提出正确的问题:
“这两个实现的目的相同,但对 X 的假设不同——应该这样统一它们。”
这本质上是把想象力当作一种代码审查方式,但它产生的是 目标状态,而不是一系列孤立的问题列表。
投诉
该技术作为重构的前期工作。你并不是直接执行理想化的版本——它是一个 北极星,帮助你在开始编辑之前弄清楚合并后的代码 应该 什么样子。可以把它看作是先写测试后写代码的架构等价:在动手之前先定义好期望的形态。
其他人也遇到这个问题
我并不是唯一一个碰到此类情况的人。随着人们扩大并行代理的工作,这个问题正在各处显现:
- Clash – 一个 CLI 工具,在 Git worktree 产生冲突之前就检测合并冲突,使用三路合并模拟。它的出现正是因为“代理彼此看不见对方的改动”,冲突只有在大量工作浪费之后才会显现。
- Multi‑Agent Coordination Framework – 记录了一种在 5,100+ 测试中验证过的方法,通过在 100+ 会话中让 Claude 和 GPT 代理在零共享内存的情况下协同工作。他们的做法是:协议、交接清单、一致性闸门以及结构化备忘录,而不是共享状态。
- EQengineered 的 Ed Lyons 也写到了同样的担忧:“由于代理以不同方式修改同一文件而导致的丑陋冲突”,以及难以管理的审查工作量。他的结论是:将代理限制在划分明确、易于理解的任务中。
- Google 2025 年的 DORA 报告发现,AI 采用率提升 90 % 与 9 % 更多的 bug、91 % 更多的代码审查时间以及 154 % 更大的 PR 相关。吞吐量确实提升了,但集成成本同样显著。
- MCP Agent Mail 为代理提供身份、收件箱和文件预留租约——本质上是为编码代理提供的 Gmail,后端使用 Git 和 SQLite。代理可以在编辑前声明对文件的独占锁,并发送消息进行协同。从理论上它解决了协同问题;但在实际使用中感觉像是仪式感——又要搭建一个系统、又要制定一套协议、又会出现新的故障点。我还没有足够深入使用它来断言它不值得,但直觉上,让每个代理在写代码前先检查邮件的开销可能会抵消它带来的协同收益。
与 Beads 的感受相似——设计周到,但对大多数工作流来说,设置成本可能超过问题本身的成本。
工具正在跟进,但目前协同问题大多仍未解决——现有工具要么提前检测冲突,要么加入协同协议,却并未阻止导致冲突的语义漂移。
我在考虑的缓解措施
1. 更短的集成周期
最大的杠杆。
- 尽早合并,频繁合并。
- 不要让五个分支运行一天——每隔几小时就集成一次。
- 集成成本会累积,因此频繁合并可以保持其低水平。
2. 共享上下文文件
为所有代理提供一个活文档,描述当前的架构、最近的决策以及进行中的工作。
- 示例:
AGENTS.md或CLAUDE.md。 - 每个工作区都会读取此文件。
- 这并不能阻止漂移,但可以减小其范围。
3. 早期冲突检测
使用挂接到代理工作流的工具,在写入可能与另一个工作树冲突之前发出警告。
- 示例:Clash。
- 它并不能解决漂移,但能足够早地捕获机械冲突以进行重定向。
4. 基于主干的代理开发
不要使用长期存在的特性分支,而是让代理在短期分支中工作,并快速合并到 main。
- 每个分支对应一个特性,每小时一个分支。
- 这与“启动六个代理”的工作流冲突,但可能整体上是正面的。
5. 合并后理想化差异比较
在合并一批分支后,对被多个分支触及的每个模块运行理想化提示。
- 让模型识别合并代码中的矛盾或冗余。
- 然后有意识地进行清理。
6. 架构边界
任务之间共享的表面区域越少,漂移就越少。
- 如果代理 A 负责 CLI 入口点,代理 B 负责可观测性,它们大多数情况下不会相互干扰。
- 如果它们都修改
app.dart——而且会,因为上帝类是漂移的磁铁——就会出现问题。
仍然值得
我不想对并行代理过于苛刻——它们的吞吐量是真实的。原本需要一周专注单独工作才能完成的功能,可能在一天内就能交付。质量往往出乎意料地好;每个单独的代理都会进行细致、经过测试的工作。问题纯粹出在集成层面。
这与真实工程团队面临的权衡相同,只是把时间压缩到了数小时而不是冲刺。Brooks’s Law 说,向一个已经延期的项目中加入人手会让它更晚。对应的代理版可能是:向一个耦合的代码库中添加代理会让合并更困难。代理本身很快,但合并仍然是手动的,仍然需要了解全局情况,仍然落在你身上。
答案不是减少代理数量,而是:
- 更好的集成纪律,
- 更好的共享上下文,
- 也许——如果理想化的差异化技术站得住脚——更好的工具,用于在你开始拼接之前推断组合输出应是什么样子。
让人不舒服的问题:如果隔离本身是问题呢?
有一种可能性我一直在反复思考:也许整个 worktree‑per‑agent 模型本身就是错误的,答案其实很简单……不要对它们进行隔离。
如果所有代理都在同一个目录、同一个分支上工作,就没有合并步骤。代理 A 编写一个工具,代理 B 立即看到,代理 C 在此基础上继续构建。没有分歧,没有“幽灵”依赖,也没有在最后进行考古式合并。漂移问题消失,因为只有一个现实。
实际操作中的样子
代理之间的相互干扰比你想象的要少。它们可以以逻辑块提交各自的更改,而且没有集成成本,因为根本没有需要集成的东西。
优点
- 立即可见彼此的工作。
- 不需要对分叉的历史进行最终合并。
- 当任务紧密耦合时工作流更简单。
缺点
- 对于编译型语言来说,代理在进行特性开发时会出现半成品、破损的状态。
- 如果两个代理同时修改同一个界面或模块,其中一个会面对一个不断变化的目标。
- 你无法仅预览代理 A 的工作而不看到代理 B 的未完成更改。
- 提交历史会变得混乱——不同特性的更改交叉出现,导致干净的回滚变得困难。
权衡
| 模型 | 隔离程度 | 干净提交 | 漂移风险 | 中间状态混乱 |
|---|---|---|---|---|
| 每个代理的工作树 | 高 | 高 | 高 | 低 |
| 共享工作区 | 低 | 低 | 低 | 高 |
两种方法都没有明显的优势。正确的选择可能取决于:
- 语言(解释型 vs. 编译型)
- 代码库规模
- 任务重叠程度
一个可能的折中方案
我猜真正的答案介于两者之间——也许两三个代理共享一个工作区,而第四个代理在真正独立的任务上保持隔离。我还没有找到那个最佳平衡点,但我很想听听任何已经奏效的经验。
目前,我正回到合并那八条都修改了同一个文件的分支。