给并行 agent 一人一个 worktree:让子代理真的敢同时改代码

· 约 4 分钟阅读

上一篇文章我建议并行前先按目录切边界,只读调查可以并发,会写文件的实现要尽量避开同一批文件。[4] 这是”任务切分”层面的解法。这周我把同一件事推进到了”运行环境”层面:与其靠提示词约束每个 agent 别碰别人的文件,不如让每个会写代码的子代理默认落在自己的 git worktree 里。

共享工作目录是并行的隐形杀手#

两个子代理同时干活时,它们默认共享主会话的同一个工作目录。[2] 两个进程读同一份文件、各自推理、再写回去:后写的覆盖先写的,没有冲突标记、没有退出码,先完成的那份改动就这么没了。而那个”赢家”agent 还会如实报告成功,因为从它的视角看,写入确实完成了。

这类 last-write-wins 竞态正是单独 checkout 要解决的。过去我用任务卡规定”可修改路径”,本质上是用纪律模拟隔离;现在 Claude Code 把隔离做成了第一方能力:子代理的 frontmatter 里加一行 isolation: worktree,它就在独立的 git worktree 里运行。官方文档对并行子代理的建议,也从”让它们各自探索独立区域”升级为显式的 worktree 隔离:让子代理在自己独立的 worktree 里运行,并行编辑才不会冲突。[2][1]

worktree 隔离的工作机制#

git worktree 是同一份仓库历史下的另一个工作目录:文件、索引、HEAD 各自独立,但对象数据库和 remote 共享,几乎不占额外磁盘。Claude Code 把子代理的 worktree 建在仓库根目录的 .claude/worktrees/ 下,默认从一个干净的分支起点开始:除非你在设置里把 worktree.baseRef 改成 "head",否则它从仓库默认分支(通常是 main)分叉,而不是从主会话那个带着一堆未提交改动的 HEAD 分叉。[1]

隔离不是一句提示词,而是有硬性执行的:Claude Code 会拦截编辑主 checkout 路径的文件写入;拦截工作目录落在主 checkout 的命令;拦截通过 git -C、--git-dir、GIT_DIR 或先 cd 回主目录再执行 git 的重定向;连命令文本无法静态证明 git 会留在 worktree 内的写法都会被拒绝。[1] 子代理的 Bash 和 PowerShell 都受这套检查约束。

两条收尾机制很省心:子代理没做任何改动就结束时,worktree 自动删除,所以只读调查几乎没有成本;改动了就留在磁盘上,由周期性清理兜底,不会丢工作。[1] 需要手动清理时 git worktree remove 即可,worktree 卡住就先 git worktree unlock。[1] worktree 与主 checkout 共享同一个 .git,所以在里面 git commit、git diff 都正常,agent 可以自己提交、留一份可复查的历史,最后你在主目录做常规的 code review 和合并。

合并仍然是人的工作#

隔离解决的是”写坏别人的文件”,不是”自动决定哪些改动该留”。并行结束后的合并仍然要人来做:每个 worktree 对应一条分支或一组提交,主会话逐条 review、测试、合并。把这条边界想清楚,就不会期待 worktree 模式替你拍板。它只是让”多个 agent 同时写代码”从事故变成可管理的流程:互不干扰是系统保证的,质量把关还是你的。

快速上手:先给写代码的子代理加隔离#

第一步,给常驻的”执行型”子代理加上 isolation: worktree。下面这份定义放在 .claude/agents/refactorer.md,专门处理跨文件的机械重构:

---
name: refactorer
description: 跨多个文件执行机械重构;只在用户要求重构时使用。
isolation: worktree
tools: Read, Grep, Glob, Bash, Edit, Write
---
对涉及的所有文件完成重构后,运行测试并报告结果。
提交改动到当前分支,输出:改动文件列表、测试命令与结果、未覆盖的风险。

第二步,派活时明确告诉主 agent 用并行子代理,例如:“用两个 refactorer 并行:一个把支付模块迁到新接口,一个拆分订单服务,各自在 worktree 里提交”。子代理之间的物理隔离由系统保证,不需要你在任务卡里再写一遍”别动 X 目录”。

第三步,子代理完成后在 git worktree list 里查看分叉,逐一 review 各自分支,测试通过后合并回主干,再统一删除临时 worktree。如果子代理需要基于你手头未提交的工作继续,把设置里的 worktree.baseRef 改为 "head",新 worktree 就会从当前本地 HEAD 分叉。[3]

几点提醒。worktree 是干净检出:gitignored 的文件(.env、node_modules)不会自动出现,需要在项目根目录加 .worktreeinclude 文件按需复制。[1] 需要长驻的独立任务,可以直接 claude --worktree <名字> 开一个隔离会话,效果相同但更接近一个完整 session。[1] 最后,别把隔离当成万能:任务本身耦合过深时(所有改动都落在同一个核心文件),worktree 再多也救不了,那种任务该串行就串行。

并行 coding agent 从”小心不要互相踩脚”,变成”各写各的 worktree,最后我来合并”,这一步省掉的不只是冲突,还有为防冲突写下的那一大堆限制性提示词。提示词越短,agent 越能把注意力放在代码本身。

Sources#

[1] https://docs.anthropic.com/en/docs/worktrees — Claude Code Docs: Run parallel sessions with worktrees [2] https://docs.anthropic.com/en/docs/claude-code/sub-agents — Claude Code Docs: Create custom subagents [3] https://docs.anthropic.com/en/docs/claude-code/settings — Claude Code Docs: settings [4] https://blog.knifelf.club/blog/parallel-agents-boundaries — Knifelf Blog: 别把 agent 当成并发槽位:并行开发要先切边界