你用 Claude Code 的并行子 Agent 功能同时处理多个模块重构,完成后 git status 显示 src/utils/helpers.ts 有冲突,或者更糟——其中一个 Agent 的改动被另一个完全覆盖,没有任何冲突提示。在 AutoGen 的 GroupChat 里,两个 Coder Agent 同时收到了「更新配置文件」的任务,第二个写入的 Agent 把第一个的改动抹掉了。这类问题在 CrewAI 的 parallel tasks、LangGraph 的 fan-out 节点里都很常见,本质是多写者竞争同一资源。
常见原因
1. 任务分配没有按文件边界拆分
编排器把「重构认证模块」和「添加日志」两个任务同时分配给两个 Agent,但两个任务都需要修改 src/auth/middleware.ts。任务描述里没有明确的文件归属,两个 Agent 各自独立读取、修改、写回同一文件。
怎么判断:把两个 Agent 的任务描述列出来,检查它们是否涉及重叠的文件集合。如果有重叠,就存在冲突风险。
2. 写操作使用全量覆盖而不是增量修改
Agent 的写文件工具(如 Claude Code 的 Write 工具)会把文件全量覆盖。即使两个 Agent 修改的是不同函数,后写入的 Agent 也会用自己读取时的版本(不含第一个 Agent 的改动)覆盖文件。
怎么判断:查看两个 Agent 的 write 操作时间戳。如果写操作时间差在毫秒到秒级,且都是全量写入,就是这个问题。
3. 没有文件级锁或临界区保护
框架没有实现互斥机制,两个 Agent 可以同时持有同一文件的「写意向」。LangGraph 的 fan-out 节点默认不对文件系统加锁,CrewAI 的并行 tasks 同理。
怎么判断:检查 Agent 工具的实现代码或框架文档,确认是否有 filelock、mutex 或类似机制。
4. Git worktree 没有隔离
多个 Agent 共用同一个 git 工作目录,而不是各自使用独立的 worktree 或临时分支。任何一个 Agent 的 git checkout 或文件写入都会影响其他 Agent 的视图。
怎么判断:运行 git worktree list,如果只有一个 worktree,且多个 Agent 在同时操作,就有冲突风险。
5. 任务队列没有去重
相同的文件修改任务被加入了多次(例如由于重试或消息重复投递),两个 Agent 实例分别处理了同一个任务,导致对同一文件做了两次看似独立的修改。
怎么判断:检查任务队列的消息 ID,看是否有重复的 task_id 被两个不同的 worker 处理。
最短修复路径
Step 1:按文件(或目录)边界分配任务
# 在任务分配时显式声明文件所有权
tasks = [
{
"agent": "agent_a",
"description": "重构认证中间件",
"owns_files": ["src/auth/middleware.ts", "src/auth/tokens.ts"],
"readonly_files": ["src/types/auth.d.ts"]
},
{
"agent": "agent_b",
"description": "添加请求日志",
"owns_files": ["src/middleware/logger.ts", "src/utils/log.ts"],
"readonly_files": ["src/types/request.d.ts"]
}
]
# 运行前检查 owns_files 是否有交集
def check_file_conflicts(tasks: list[dict]) -> list[str]:
all_owned = []
conflicts = []
for task in tasks:
for f in task["owns_files"]:
if f in all_owned:
conflicts.append(f)
all_owned.append(f)
return conflicts
Step 2:为每个 Agent 创建独立的 git worktree
# 为每个并行 Agent 创建独立的工作目录
git worktree add /tmp/agent-a-workdir -b agent-a-branch
git worktree add /tmp/agent-b-workdir -b agent-b-branch
# Agent 完成后合并
git checkout main
git merge agent-a-branch --no-ff -m "merge: agent-a changes"
git merge agent-b-branch --no-ff -m "merge: agent-b changes"
# 清理
git worktree remove /tmp/agent-a-workdir
git worktree remove /tmp/agent-b-workdir
Step 3:用文件锁保护写操作
import filelock
def safe_write_file(path: str, content: str, timeout: int = 30):
"""带文件锁的安全写入,防止并发覆盖。"""
lock = filelock.FileLock(f"{path}.lock", timeout=timeout)
try:
with lock:
with open(path, "w", encoding="utf-8") as f:
f.write(content)
except filelock.Timeout:
raise RuntimeError(f"等待文件锁超时:{path},可能存在死锁")
Step 4:用增量 patch 代替全量覆盖
import subprocess
def apply_patch(file_path: str, diff_content: str) -> bool:
"""用 patch 命令做增量修改,而不是全量覆盖。"""
result = subprocess.run(
["patch", "--forward", file_path],
input=diff_content.encode(),
capture_output=True
)
if result.returncode != 0:
print(f"Patch 失败:{result.stderr.decode()}")
return False
return True
Step 5:冲突后的快速恢复
# 如果已经发生冲突,用 git 的三方合并恢复
git diff HEAD~2..HEAD~1 src/utils/helpers.ts > agent_a.patch
git diff HEAD~1..HEAD src/utils/helpers.ts > agent_b.patch
# 手动检查冲突区域
git log --oneline -5
git show HEAD~1:src/utils/helpers.ts > /tmp/before_agent_b.ts
# 对比两个版本,保留两者的改动
预防建议
- 任务分配阶段强制声明文件所有权,运行前做冲突检测,有冲突就拒绝启动
- 并行 Agent 默认使用独立的 git worktree,完成后由编排器统一 merge
- 所有写文件工具改为增量 patch 模式,只修改目标函数/类,不全量覆盖
- 在 CI 里对并行任务的输出做自动 diff,发现非预期改动时阻断合并
- 对共享配置文件(如
tsconfig.json、package.json)设置为只读,不允许 Agent 直接修改,改动必须通过人工审核 - 任务队列使用幂等
task_id,相同 ID 的任务只处理一次,防止重复投递导致双写 - 为并行 Agent 设置独立的临时工作目录,任务完成后再统一应用到主工作目录
常见问答 (FAQ)
Q: Claude Code 的并行子 Agent 会自动处理文件冲突吗? A: 目前不会。Claude Code 的子 Agent 共用同一个工作目录,并行写入相同文件时会直接覆盖。需要在任务设计层面确保文件边界不重叠,或者用 git worktree 隔离工作目录。
Q: LangGraph 的 fan-out 节点如何安全地并行修改文件? A: 推荐在 fan-out 节点里只做「读取 + 计算」,生成 patch 或 diff 对象而不是直接写文件,然后在 fan-in(reduce)节点里串行应用所有 patch。这样可以保证写操作有序,不存在竞态。
Q: 如果两个 Agent 修改的是同一文件的不同函数,能否自动合并?
A: 理论上可以,git merge 的三方合并算法能处理同一文件不同区域的改动。但前提是两个 Agent 的改动都是基于同一个 base 版本,且改动区域没有行重叠。用 git worktree 方案可以做到这一点,合并时让 git 自动处理非冲突区域。
Q: 这个问题在只读操作时也存在吗? A: 不存在。多个 Agent 并发读取同一文件是安全的,不会产生数据竞争。只有写操作(包括 append)才需要加锁或隔离。