两个智能体同时改一个文件会发生什么?
在多 Agent 协作开发系统中,如果 Agent A 正在重构 auth.ts,而 Agent B 也决定向 auth.ts 添加一个新方法,两者如果不加协调,最后提交的一方就会完全覆盖前者的劳动成果。
分支隔离与乐观并发设计模式
- 绝对分支隔离:任何 Agent 均无权直接向
main或共享开发分支写入代码。每个任务启动时自动克隆出专属临时分支(Feature Branch); - 文件级悲观意向锁(Intent Locks):在重构大型核心文件前,Agent 必须通过 Redis 分布式锁向集中调度器声明修改意向;
- 自动化三路合并与语义冲突仲裁:当分支合并遇到传统 Git 冲突时,系统自动抽取冲突块的前后上下文,交由专门的 Merge Arbiter Agent 进行语法级调和与单元测试核验。
落地示例:一次并发任务的完整生命周期
以两个 Agent 同时改认证模块为例,调度器的执行序列通常是固定的:
# 任务开始前:为每个 Agent 建立独立工作副本
git worktree add ../agent-a agent/auth-refactor
git worktree add ../agent-b agent/add-session-method
# 声明意向锁(键名按调度器规范,需带过期时间)
redis-cli SET lock:src/auth.ts agent-a NX EX 600
# 回收阶段:先变基到最新基线,再走合并提交
git -C ../agent-a rebase origin/main
git merge --no-ff agent/auth-refactor
关键点是锁必须有租约(TTL)。Agent 可能在持有锁时被杀掉,没有过期时间的锁会让整条流水线永久卡住。锁续期由调度器心跳负责,执行端不要自己持有裸锁。
三种并发控制策略的取舍
| 策略 | 适用场景 | 代价 | 失败表现 |
|---|---|---|---|
| 分支隔离加事后合并 | 改动分散、文件重叠少 | 合并冲突需仲裁 | 冲突块增多 |
| 文件级意向锁 | 核心大文件重构 | 吞吐下降、串行化 | 等待或锁超时 |
| 全量悲观锁(仓库级) | 迁移类一次性任务 | 几乎无并发 | 排队时间过长 |
经验值:当单次任务平均触碰的文件数小于五、且并发 Agent 在个位数时,分支隔离加语义仲裁的吞吐明显优于加锁;一旦多个 Agent 高频改同一个巨型入口文件,就要退回到文件级锁。
验证方法
冲突控制只能靠演练验证。构造三个用例:两个 Agent 修改同一函数的不同行,确认合并后两处改动都保留;两个 Agent 删除同一函数,确认仲裁器不会把删除复活;让持锁 Agent 在执行中途被强杀,确认租约到期后锁能自动释放且任务被重新派发。再补一条静态检查看门狗:合并后跑全量单测与类型检查,任一失败即回滚合并提交。
常见故障速查
| 现象 | 原因 | 处理 |
|---|---|---|
| 合并后某Agent的改动消失 | 直接推送到共享分支绕过隔离 | 服务端禁止非快进推送并强制走仲裁 |
| 同一文件反复触发冲突 | 锁粒度过粗或租约过短 | 按符号级拆分锁并延长续期 |
| 仲裁结果通过但编译失败 | 只做文本合并未做语法校验 | 仲裁后强制过类型检查再落库 |
| 流水线长时间无任务完成 | 持锁 Agent 崩溃未释放锁 | 依赖 TTL 自动过期加死信队列 |
| 单测在合并分支上抖动 | 基线过旧导致依赖行为差异 | 合并前先变基到最新基线 |
小结
判断并发控制是否到位,标准很直接:随机挑十次已合并的提交,逐一比对每个参与任务的改动是否在最终代码里存活,且没有任何一方被静默覆盖。只要出现一次丢失,就说明锁租约、变基步骤或仲裁器的核验门禁中有一环是摆设。多数团队可以按 Multi-Agent 通信拓扑:中心化编排(Orchestrator)vs 对等协作(P2P) 先把调度器职责定清楚,再引入锁。