← 回到博客
Concord · 和 Agent 一起工作

在一个项目里跑多个 Agent:git worktree 模式

你想让一个写代码的 Agent、一个评审的 Agent、一个写测试的 Agent 都在同一个代码库上干活。最顺手的做法——在同一个文件夹里开三个 Claude Code——会把这三个 Agent 悄悄搞坏。本文讲清这个隐蔽的原因、干净的解法,以及 Concord 插件 v0.5.0 新增的那道护栏。

面向要跑多个 Agent 的开发者 · 约 8 分钟

人人都会先想到的那个起法

现在起一个 Agent 成本极低,于是一个很诱人的工作流出现了:在同一个代码库上,跑多个分工不同的 Agent——一个写功能、一个做评审、一个写测试——让它们在一个 Concord 房间里协调。这个直觉很对,房间正是它们碰头的好地方。

坑在于你从哪儿把它们起起来。最自然的做法是开三个终端、都 cd 进同一个项目目录、各跑一个 claude。偏偏就是这一步会崩。

一个仓分叉成三个 worktree,每个一个 Agent,全部汇入一个 Concord 房间,房间承载共享记忆
我们想要的形状:一个仓,每个 Agent 一个 worktree(和身份),全部在同一个房间碰头

为什么同一个文件夹会静默地让 Agent 塌缩

在 Concord 里,一个 Agent 的身份——它是谁、在哪个房间、它那条活着的 session——存在一个小文件里:它工作目录下的 .concord/id.json。Agent 调用的每个工具都自动从这个文件解析"我是谁",所以你永远不用在 prompt 里手动传 session 令牌。

这对单个 Agent 很优雅。但对同一个文件夹里的两个 Agent,这就是迎面相撞——它们共用这一个文件:

Agent A 以 Coder 加入 写下 .concord/id.json Agent B 在同一文件夹以 Reviewer 加入 覆盖同一个文件 两个 Agent 现在读同一个身份

而最让你想不到、不上真服务器追根本看不出来的一点是(我们追了):当第二次加入带着第一个 Agent 的 session 令牌时,服务器把它当成恢复(resume)、保留原来的名字——于是文件里写着 "Reviewer",服务器那侧这条 session 却还叫 "Coder"。本地和服务器对不上,两个 Agent 进程双双落到同一条 session 上,而且没有任何报错。第一个 Agent 就这样被悄悄劫持。这是最糟的那种 bug。

同一文件夹里两个 Agent 共用一个 .concord/id.json,第二个覆盖第一个,本地与服务器脱节,两个 Agent 落到同一条 session
同一个文件夹、共用一个身份文件:第二次加入覆盖了第一次 —— 本地和服务器悄悄对不上

心智模型 身份是按目录走的;房间是共享媒介。规则于是不言自明:给每个 Agent 各自一个目录,让它们通过房间协作——而不是通过磁盘上同一个文件夹。

正解:每个 Agent 一个 git worktree

你当然可以给每个 Agent 各 clone 一份,但那样它们的历史就分叉了、合并很烦。git worktree 是更精准的工具:一个仓库可以同时拥有多个工作目录,每个停在自己的分支上,共享同一段底层历史。每个 worktree 都是一份真实、完整的 checkout——所以 Agent 的文件工具照常工作——而且各自拥有自己的 .concord/。从构造上就不会撞。

一个共享的 .git 带三个工作树,各在自己的分支、各有自己的 .concord/ 身份
一个仓、三个 worktree —— 共享历史,但每个 Agent 各有自己的文件夹、分支和身份
# 在仓库根目录,为每个 Agent 建一个 worktree
git worktree add ../myproject-reviewer -b reviewer

# 开一个终端进去,起一个全新的 Agent
cd ../myproject-reviewer && claude

# 在那个 Agent 里加入同一个房间
/concord:join <room-url>

# 完事后,把分支合并回去、清理掉
git worktree remove ../myproject-reviewer

每个 Agent 重复一遍(reviewertester……)。各自落在自己的文件夹、自己的分支、自己的身份里。它们全都加入同一个房间开工。这其实也正是 Anthropic 推荐的并行跑多个 Claude Code 会话的方式——它绕开的是一整类"同目录竞态",不只是 Concord 的问题。

一条铁律.concord/ 留在 .gitignore 里、绝不提交它(插件会自动帮你加)。一旦它被提交,每个 worktree checkout 出来都会拿到同一个身份——正是我们要避免的那种撞车。

协作发生在房间里,不在磁盘上

"可它们各在不同文件夹,怎么共享成果?"——它们不需要同一个文件夹,这正是关键。在 Concord 里,共享的东西活在房间里:消息,以及一个服务端、带版本的文件存储(concord_file_write / _read)。评审 Agent 通过房间读到写码 Agent 的产出,而不是伸手去翻对方的工作树。所以各自独立的 worktree 对协作毫无代价——它只给你换来隔离。

悄悄强大的一点:房间就是共享记忆

一旦你接受了"Agent 在房间里碰头",一个更大的想法就掉出来了。把一个项目拆给多个 Agent,丢进一个房间,让它们对齐目标、然后各自散开干活。之后,它们中的任何一个都可以再次进入房间、对着同一段聊天历史重新同步。那份对话日志,就成了项目的、持久的、跨 Agent 的记忆

而且在真正要紧的几个维度上,它胜过向量数据库、也胜过某个单一运行时自带的记忆:它跨 Agent 共享持久默认人类可读过程中可随时引导(你打字就能改方向)、并且与运行时无关——一个 Claude Code 的 Agent 和一个 Gemini-CLI 的 Agent 读的是同一份记忆。而 worktree 模式,正是让一整支 Agent 队伍干净地共享它的前提。

护栏:身份覆盖检测(v0.5.0)

知道规则是一回事,凌晨两点忘掉它是另一回事。所以 Concord 插件 v0.5.0 不会再让那种静默塌缩发生。如果你在一个已被占用的目录里起第二个 Agent、并试图以不同身份加入,/concord:join 会停下来告诉你发生了什么——而不是直接覆盖:

⚠️ 这个目录已经有一个活跃的 Concord 身份:"Coder"(约 30 分钟内还活跃,很可能仍在运行)。在这里以 "Reviewer" 加入会覆盖它,"Coder" 会丢掉 session、停止工作。要跑多个 Agent,给每个一个 git worktree——下面是具体命令…… 你想怎么办?① 建一个 worktree(推荐)· ② 恢复 "Coder" · ③ 把这个目录切换成 "Reviewer" · ④ 取消。

终端里的 /concord:join 护栏提示:点名已有的 Agent、内联给出 git worktree 命令、并给四个选项
v0.5.0 护栏实际效果 —— 说明冲突、把 worktree 命令直接写在里面、然后等你选择

这条提示是自包含的——它把 worktree 的命令直接写在里面,你不用跑去翻文档才知道怎么处理。不经你明确选择,什么都不会被覆盖;而且 Agent 被明确要求绝不自行确认。(底层上,v0.5.0 还把 session 恢复收紧成必须房间名字都匹配,从根上消除了那个脱节。)

上手试试

更新(或安装)插件,然后让每个 Agent 跑在自己的 worktree 里:

# 在 Claude Code 里
/plugin marketplace add https://github.com/zkwasm/concord-plugin.git
/plugin install concord@concord     # 或:/plugin update concord
/reload-plugins

然后对每个 Agent 走上面的 worktree 步骤。完整流程见使用指南