
最近我在折腾并行 AI Agent 开发时遇到一个特别拧巴的问题同一个代码仓库一个 Agent 重构核心模块一个 Agent 补测试还有一个 Agent 在改文档。听起来分工挺明确可真跑起来你会发现一个工作目录根本塞不下三个 Agent。改着改着git status 里全是别人的半成品A Agent 刚格式化完的代码B Agent 一小时后又按自己的规矩改了回来跑测试的时候两个 Agent 抢占同一个构建缓存直接把队友一脚踢翻。Git Worktree 是解决这个问题的标准答案但它裸用起来还是有点难受路径要自己想、分支名要靠记忆、清理的时候还得先回忆一堆命令。于是我把日常操作整理成了一个叫 Worktrunk 的 CLI 工具专门把 Git Worktree 包装成“任务 工作区”的心智模型。这篇就聊聊 Worktrunk 解决的是什么问题、我为什么这么设计、以及怎么用它把多个 AI Agent 真正并行跑起来。想搭一套干净并行开发流的朋友都可以直接参考即使你是刚接触 AI Agent 和命令行工具的新手这套思路也能帮你少踩不少坑。1. 为什么并行 AI Agent 工作流会卡在“共享一个目录”上1.1 三个 Agent 挤在一个工作区会发生什么很多人一开始对“并行 AI Agent”的理解是把几个 Agent 丢进同一个项目目录给它们分别交代任务它们就会各自干活、互不干扰。实际情况完全不是这样。我第一次试的时候两个 Agent 同时操作同一个工作目录结果惨不忍睹上下文污染最致命。Agent 看到的是整个工作区的状态它会无差别地把其他 Agent 留下的半成品、临时修改、未完成的接口当成“现状”。有一次重构 Agent 在改一个模块的返回值测试补充 Agent 已经基于旧返回值写了十几个测试用例两边一叠代码全都对不上。文件互相覆盖是常态。两个 Agent 可能同时格式化同一个文件或者一个改了公共配置另一个又从旧缓存里读了一遍直接覆盖回去。哪天你没有 commit 的习惯改着改着连哪个版本是谁写的都分不清。构建产物和缓存会互相踩踏。前端项目里的 .next、node_modules/.cache后端项目里的 target、pycache第一个 Agent 刚跑完一轮验证第二个 Agent 一跑测试缓存直接失效全都被重建一遍白白浪费算力。这就好比一个很小的厨房两个厨师共用一把刀、一个案板。不是切到手就是菜被拿错再不就是刚做好的半成品被另一个人当成垃圾倒掉。你需要的不是让厨师们“注意一点”而是直接把厨房隔成几个独立的操作台。1.2 Git Worktree 的机制一份 .git多张“工作桌”Git Worktree 就是那种“把厨房隔开”的天然解法。它允许你在同一个仓库里同时维护多个工作目录每个工作目录都有自己的文件、自己的 index、自己的 HEAD可以互不干扰地 checkout 不同分支。它的原理其实很巧妙所有 worktree 共享同一份 Git 元数据objects 目录、refs 引用但每个 worktree 的工作目录是独立的。也就是说你在 main 分支上正常开发同时还能开一个 ../agent-a 目录专门跑重构分支再开一个 ../agent-b 目录专门跑测试补充分支三个目录共存互不影响。裸用 git worktree 的关键命令并不复杂# 在指定目录创建一个新 worktree并基于默认分支创建新分支 git worktree add ../agent-a -b feat/agent-a # 查看所有 worktree 列表 git worktree list # 删除一个 worktree git worktree remove ../agent-a当多个 Agent 并行开发时这套机制的价值非常大每个 Agent 有独立目录git 状态不会被别人污染每个 Agent 可以 checkout 自己想要的分支不会互相阻塞构建产物、依赖目录相对独立测试时不会抢同一份缓存。不过这里要强调一点Git 对象是共享的所以仓库本体不会因为多开几个 worktree 就成倍膨胀但 node_modules、venv 这类依赖目录在每个 worktree 里都会单独出现。这个开销问题下面实操部分我会详细讲。1.3 裸用 Git Worktree 的隐形摩擦成本既然 Git Worktree 本身就能实现隔离为什么还要再做一层封装因为我实际用了两周之后发现裸用这套命令在并行 Agent 场景下有五个很现实的痛点路径和分支之间的关系全靠人脑记忆。我开了四五个 worktree 之后git worktree list 的输出又长又乱很难一眼看出“哪个目录对应哪个 Agent、哪个 Agent 正在处理什么任务”。任务信息完全丢失。worktree 只记录路径和分支不记录“这个 Agent 的目标是什么、基于哪个分支拉的、当前进展怎么样”。Agent 一多你根本不知道它到底在干嘛。生命周期管理太繁琐。Agent 干完活之后要合并、要删 worktree、要确认没有未提交内容每一步都得手动执行中途 Agent 进程没有正常退出时删除还会失败报一堆让人摸不着头脑的错误。并行场景下缺少统一视图。多个 Agent 同时跑你很难快速回答“现在有哪几个 Agent 在工作、他们分别在哪个目录、分支状态如何”。Agent 本身也不知道自己该在哪干活。给 Agent 一个裸的 worktree 目录它没有“任务上下文”还得靠你写一堆 prompt 反复交代。Worktrunk 就是为了解决这五个问题而生的它不重新发明 Git只是把“任务”和“worktree”绑定在一起让所有操作都围绕任务展开。2. Worktrunk 的设计思路从“管分支”变成“管任务”2.1 为什么是 CLI而不是 IDE 插件或一堆 Shell 脚本在设计 Worktrunk 之前我评估过三种实现方式纯 shell 脚本、IDE 插件、独立 CLI。最后选了独立 CLI核心原因是它和 AI Agent 工作流的契合度最高。先看一张对比表方案上手成本可脚本化/Agent可调用不绑定编辑器团队统一入口纯 Shell 脚本低中容易散落各处是差每个人改自己的IDE 插件低差否每个编辑器都要装一般独立 CLI中强是好命令统一AI Agent 的运行环境和人不一样它不一定会打开你的 IDE但它一定能在终端里执行指令。如果你的工作区管理工具是一个 CLI那么人可以用脚本可以用Agent 也可以直接调用。这是 IDE 插件很难替代的优势。但做成独立 CLI 并不意味着要重写 Git。Worktrunk 的核心非常薄它只是把 git worktree 命令包了一层加上任务元数据、命名约定、安全检查。换句话说底层还是 Git上层只是让人和 Agent 用一种更接近“任务管理”的方式去操作它。假设你已经编译安装好了 Worktrunk如果你构建的是自己的版本安装方式和普通 Go CLI 一致go install example.com/worktrunk/cmd/worktrunklatest worktrunk --version2.2 核心概念Task 即 WorktreeWorktree 即 Agent 工作区Worktrunk 最核心的设计是把“worktree”抽象成“任务Task”。你不需要告诉它“帮我开一个 worktree路径叫 xxx分支叫 yyy”你只需要说“我要开始一个叫 task-a 的任务”剩下的事情由 CLI 按约定完成。一套最基础的命令体系是这样设计的# 开始一个任务创建 worktree 创建任务元数据 worktrunk start task [--base branch] [--agent name] # 查看所有任务状态 worktrunk list # 输出某个任务的工作区路径 worktrunk path task # 切换到某个任务的工作区 worktrunk switch task # 结束一个任务清理 worktree 和任务元数据 worktrunk stop task [--merge --target branch] # 清理所有已失效的 worktree worktrunk prune你执行worktrunk start agent-a --base main --agent codex的时候它内部实际做了三件事调用git worktree add把新的工作区创建在统一的约定目录下比如.worktrunk/workspaces/agent-a基于指定的 base 分支创建并切换到一个以任务名命名的分支比如feat/agent-a在.worktrunk/tasks/下写一个任务元数据文件记录任务的完整信息。元数据文件是 Worktrunk 和裸 Git Worktree 最大的区别它的 JSON 结构大概长这样{ task: agent-a, branch: feat/agent-a, base: main, worktree: .worktrunk/workspaces/agent-a, agent: codex, created_at: 2025-01-12T14:30:0008:00, status: running }有了这个文件你随时可以回答前面提到的五个问题哪个 Agent 在哪个目录、基于什么分支、什么时候创建的、当前状态是什么。路径统一放在.worktrunk/workspaces/下面而不是散落在一堆别名的目录中管理和清理都会简单很多。2.3 给 Agent 一个能带进上下文的任务卡我用的 AI Agent 工具不止一个有 Claude Code、Codex CLI还有一个内部接的 Harness。它们各有各的启动方式但在一点上是共通的都需要一个清晰的“任务上下文”。如果一个 Agent 进到工作区之后还要靠猜“我现在在哪个仓库、该改什么、怎么验证”那它大概率会跑偏。Worktrunk 的export子命令就是为了解决这个问题的它把任务元数据渲染成一段可以直接喂给 Agent 的上下文。worktrunk export agent-a --format md输出结果大概是这样# Task: agent-a - Worktree: .worktrunk/workspaces/agent-a - Branch: feat/agent-a - Base: main - Agent: codex - Verify: npm test把这个任务卡作为启动 prompt 的一部分喂给 Agent它一进来就知道自己该在哪个目录工作、基于哪个分支、验证命令是什么不再需要你一遍遍在 prompt 里重复交代细节。更进一步如果 Agent 的 Harness 支持自定义工具也就是常说的 MCP、skill 这一层能力你完全可以把 Worktrunk 暴露成一个工具给 Agent 调用。Agent 自己执行worktrunk start和worktrunk stop等于它自己管理自己的工作区母 Agent 只需要负责拆任务。这个扩展方向后面我会单独讲。3. 实操用 Worktrunk 把两个 Agent 并行跑起来3.1 初始化仓库与约定配置在使用 Worktrunk 之前前提条件很简单你有一个已经存在的 Git 仓库代码默认在 main 分支上。如果你是从零开始的项目先正常把仓库初始化好、提交第一版代码。进入仓库后可以执行一次worktrunk init来做基础约定配置它会读取当前仓库信息生成一份默认配置比如默认的 base 分支、默认的 Agent 类型、工作区目录命名规则。不执行 init 也可以Worktrunk 会用一套合理的默认值base 分支是当前分支Agent 类型留空工作区统一放到.worktrunk/workspaces/。这里有一个小建议.worktrunk目录默认应该在.gitignore里任务元数据是给人和 Agent 看的运行状态不需要提交到仓库版本库。除非你想做团队级的任务审计那可以单独考虑提交一份匿名化的任务日志但千万不要把包含本地路径和临时状态的文件塞进主仓库。3.2 启动第一个 Agent 工作区现在假设我要跑一个重构任务让一个 Agent 去重构核心模块。执行worktrunk start refactor-agent --agent claude --base main执行成功后输出会告诉你这段信息Task refactor-agent started. Worktree: .worktrunk/workspaces/refactor-agent Branch: feat/refactor-agent Base: main此时Worktrunk 已经帮你完成了原本需要手动执行的git worktree add、分支创建、路径切换这一整套流程。你唯一需要做的就是进入这个工作区然后把 Agent 跑起来cd $(worktrunk path refactor-agent) claude注意cd $(worktrunk path refactor-agent)这个用法很实用它可以让你在任何位置快速跳到对应任务的工作区不用自己记那一长串路径。进入工作区后Agent 看到的是一个干净、独立、只属于它自己的目录没有其他 Agent 的干扰。3.3 启动第二个 Agent 工作区第一个 Agent 跑起来之后你再开一个终端启动第二个任务worktrunk start test-agent --agent codex --base main这时候你就有了两个并行的 Agent 工作区refactor-agent 在.worktrunk/workspaces/refactor-agenttest-agent 在.worktrunk/workspaces/test-agent。两个工作区基于同一个 main 分支的不同新分支运行彼此完全隔离。实际操作的时候最直观的感受就是两个 Agent 可以同时跑同一套测试命令各自的构建产物在自己的目录里不会再因为共享目录导致缓存被对方冲掉。如果两个 Agent 需要共享一些基础工具链比如同一个编译缓存、同一个依赖下载缓存那可以把这些缓存配置成外部公共目录但代码和构建输出一定不能互相串。这里还要提一个并行协作的关键场景如果第二个 Agent 需要等第一个 Agent 的代码完成之后再继续那不要让它基于 main 拉分支而是基于第一个任务的分支来创建任务worktrunk start test-agent --agent codex --base feat/refactor-agent这样 test-agent 的工作区里一开始就是 refactor-agent 的最新代码两个 Agent 的工作有上下游依赖关系。不过要注意这要求第一个 Agent 定期把代码提交并推送否则第二个 Agent 拉到的还是一个陈旧快照。3.4 查看总览与切换上下文并行跑起来之后最常用的命令就是worktrunk list。它会输出一张表格把当前所有任务的状态列清楚Task Branch State Changes Agent Worktree refactor-agent feat/refactor-agent running 12 claude .worktrunk/workspaces/refactor-agent test-agent feat/test-agent running 8 codex .worktrunk/workspaces/test-agent我实际使用中这个命令用的频率比任何其他命令都高。因为我经常是开着好几个终端跑着两三个 Agent一旦任务多起来脑子是记不住“哪个终端对应哪个工作区”的。worktrunk list一执行所有状态一目了然哪个任务改了哪些文件、用了什么 Agent、在哪个目录全部摆在那里。要切换上下文直接用worktrunk switch test-agent然后cd $(worktrunk path test-agent)进入对应目录。整个过程不需要 git stash、不需要手动记路径比裸用 Git Worktree 省心太多。3.5 合并与清理任务结束后的收尾工作Agent 把活干完之后第一件事不是直接跑stop而是先检查改动是否合理、测试是否通过然后手动或让 Agent 提交代码。确认没问题后执行worktrunk stop test-agent --merge --target main这个命令做的事情比较多内部逻辑是检查 worktree 是否有未提交的改动和未推送的提交如果有提示你先处理或确认是否强制结束把当前分支合并到目标分支 main删除 worktree清除任务元数据。如果不想自动合并只想把工作区清理掉直接执行worktrunk stop test-agent停止之后你可以用git worktree list验证一下之前多出来的测试 Agent 工作区应该已经消失了仓库恢复到一个相对干净的状态。4. 常见问题与排查技巧实录4.1 “我明明没在用这个 worktree为什么删除失败”这是并行 Agent 场景里最容易踩的坑。你以为 Agent 已经退出了实际上它的进程还挂在后台或者某个编辑器的终端还停留在那个目录里文件句柄被占用Git 就会拒绝删除 worktree。常见的报错信息里有 “contains modified or untracked files” 或 “being used by process” 之类的提示。排查思路先用worktrunk list看任务状态确认是不是有 Agent 还在运行再用系统命令检查目录占用比如lsof D .worktrunk/workspaces/test-agent确认没有活动进程后再执行删除。如果确实有进程占用但你想强制清理Worktrunk 提供了--force参数worktrunk stop test-agent --force这个命令等价于git worktree remove --force会跳过安全检查直接删除。使用前一定要确认没有未保存的重要改动强制删除是不可逆的。根据我个人经验比较稳妥的流程是先让 Agent 正常退出再等一两秒让它释放文件句柄最后才执行 stop。如果你用的 Agent 经常异常退出建议把 stop 命令接入 Agent 的退出回调里避免每次手动处理僵尸进程。4.2 并行跑测试时端口和缓存互相冲突两个 Agent 各自跑测试编译产物互相不影响了但端口冲突依然会出现。比如两个 Agent 都在各自的 worktree 里启动一个测试服务默认都监听 3000 端口第二个 Agent 一启动就会报 “port already in use”。解决办法是给每个任务注入唯一的运行时环境变量。Worktrunk 支持给任务记录环境变量worktrunk env test-agent PORT 43210写入之后这个环境变量会出现在任务元数据里并且在worktrunk export test-agent --format md时作为一条上下文提示输出。Agent 读取到任务卡后就知道该用哪个端口启动服务了。更完整的做法是不仅服务端口要隔离临时文件目录、数据库文件、缓存目录也最好按任务名隔离。你可以约定一个规则所有运行时临时文件都放在.worktrunk/workspaces/task/tmp/下面这样即使两个 Agent 同时跑同一套系统也不会把对方的临时数据覆盖掉。4.3 两个 Agent 基于同一个 base 并行改动合并时冲突满天飞并行开发必然伴随合并冲突这是物理规律不是工具能完全消除的。两个 Agent 同时改了同一个业务模块的同一个函数最后无论是往 main 合并还是把第二个 Agent 的分支合并到第一个 Agent 的分支上都会遇到一堆冲突标记。我的经验是先解决任务划分问题再解决合并技术问题。任务拆分时尽量让每个 Agent 负责独立的模块或独立的代码层比如一个 Agent 只改后端接口层另一个 Agent 只改前端展示层它们之间即使在同一时间点同时合并冲突概率也会小很多。如果两个任务确实有上下游依赖不要等最后再合并而是在中途就让下游 Agent 定期 rebase 到上游分支的最新版本。Worktrunk 虽然不帮你做冲突决策但它能帮你快速搞清楚任务之间谁基于谁通过worktrunk info task查看任务的 base 信息就能判断两个分支之间的关系方便规划合并顺序。4.4 多 Worktree 磁盘占用过大的问题有朋友问过我开五个 worktree是不是等于仓库复制了五份磁盘会不会爆炸这里解释一下Git 对象库是共享的仓库本体的代码历史不会重复存储所以 git 层面的开销不会成倍放大。真正占空间的是每个 worktree 里的依赖目录。一个 Node.js 项目node_modules 动辄几百兆一个 Python 项目venv 也是差不多的体量。开三个 worktree光依赖就是三个完整副本。解决方案有几种依赖管理器层面的共享缓存。比如 pnpm 有全局 store配合 workspace 配置可以大幅降低磁盘占用npm 和 yarn 也有 cache 目录可以复用。把依赖目录排除在 Git 管理之外然后用符号链接指向一个公共目录。例如ln -s /shared/venv .worktrunk/workspaces/test-agent/venv这样多个 worktree 共用一套依赖。不过要注意依赖版本一变链接会失效而且共享依赖时不同 Agent 同时更新依赖库也可能互相干扰建议只在紧急节省空间时用。构建产物和临时文件一定不要提交到 Git同时配置好 .gitignore。两个 Agent 各自生成的 build 目录如果被 Git 跟踪工作区之间会频繁出现冲突。4.5 快速排查速查表现象原因处理建议worktree 无法删除进程占用 / 有未提交改动检查 lsof确认无进程后stop --force端口冲突多个任务默认端口相同用worktrunk env注入唯一端口合并冲突集中爆发两个任务改动区域重叠重新规划任务边界按模块拆分磁盘占用明显增大依赖目录被重复创建启用包管理器共享缓存或符号链接依赖目录worktree 删了但git worktree list还有记录元数据不一致执行worktrunk prune清理失效记录worktrunk list显示的状态和实际不符手动在外部执行了 git 操作尽量所有操作都走 Worktrunk避免绕过封装5. 我的使用心得与可扩展方向5.1 不要盲目追求并行度2 到 3 个 Agent 是舒适区用过一段时间后我最大的体会是并行不是目的隔离才是。很多项目刚上手时会觉得“既然能并行那就多开几个 Agent任务拆得越细越好”。实际上我踩过坑之后发现同时跑 2 到 3 个 Agent 是比较舒适的范围超过 5 个之后维护心智负担会暴涨你很难快速判断每个 Agent 当前的状态两个任务同时改同一处代码的概率也大幅上升最后合并成本可能远超并行节省的时间。所以我现在的工作习惯是大任务优先拆成“重构、测试、文档”这类边界清晰的小任务每个任务对应一个独立 worktree三个 Agent 并行跑核心业务逻辑的修改宁可按顺序串行执行也不要同时让两个 Agent 去改同一个层。5.2 把 Worktrunk 暴露成 Agent 可调用的工具实现自我管理这算是我最近在试验的一个方向。如果你的 Agent 框架支持 MCP 或自定义 skill你可以把worktrunk start、worktrunk stop、worktrunk list这几个命令注册成 Agent 可调用的工具。这样母 Agent 收到一个复杂需求时它可以自己拆解子任务为每个子任务创建独立 worktree然后派生出子 Agent 到对应工作区干活等子 Agent 完成后自动执行 stop 和 merge。这套模式的效果是工作区的生命周期管理不再依赖人盯着Agent 自己就知道“我应该开一个工作区、干完活、合并代码、清理现场”。我实际跑下来比让一个 Agent 在一个目录里连续处理多个子任务要稳定得多每个子任务的前后文不会被别的任务污染。5.3 团队落地时的三个约定如果你想把 Worktrunk 这套思路引入团队或引入 AI Agent 开发流程我建议先定好三个约定命名约定任务名必须是 Agent 可读的唯一 ID不要用“test”“fix”这种过于通用的名字最好带上模块名比如refactor-user-service、test-payment-api。分支约定每个 worktree 对应一个分支分支名带上任务名比如feat/refactor-user-service这样从远程仓库看分支名就能知道是哪个任务的产出。生命周期约定Agent 完成任务后必须先提交、再合并、再清理不允许把 worktree 丢在那里不管。CI 层面可以加一个检查任务定期扫描那些长期未合并的 worktree 分支提前预警。最后再分享一个小技巧任务卡文件不要只生成完就丢建议把它放在 worktree 根目录下比如.worktrunk/task-card.md。这样不仅是 Agent 启动时能读到每次打开工作区的人或 Agent 也能快速了解这个任务的前因后果复盘的时候特别有用。我自己实际用了几个月最大的感受不是工具本身多炫而是它把“哪个 Agent 在哪个目录干什么”这个问题从脑内记忆变成了一条命令。尤其是同时开着两三个 Agent 时worktrunk list扫一眼所有状态清清楚楚比裸用 Git Worktree 安心得多。如果你也被并行 Agent 搞得头疼不妨从一个薄薄的 CLI 开始核心就一条给每个 Agent 一张自己的桌子。