多Agent协作控制面
症状:开了多 agent,总要人工告诉别的 agent 当前进度、规则变了没;写了文档但别处没更新;做过的东西反复造轮子。
问题定义收敛成四层
| 层 | 解决什么 | 关键词 |
|---|---|---|
| 0 统一入口 | Claude / Codex / Cursor 读到的规则不一样 | 所有 agent 读同一套规则 |
| 1 共享当前进度 | 谁在干什么、做到哪、下一个怎么接 | 当前、临时、跨 worktree |
| 2 独立协作 | 多 agent 不互相踩代码、不混改、不覆盖 | 一人一个 worktree + branch |
| 3 复用知识 | 做过的找不到、相似 bug 反复踩 | 长期知识、可搜索、跟 Git 走 |
这四层不要混:把「当前状态」写进「长期知识」,或反过来,都会让下一层失真。
每层对应一种文件,职责单一
入口层:agent.md / AGENTS.md / CLAUDE.md → 统一入口
当前层:~/.agent-state/<proj>/STATUS.md → 现在谁在做什么
隔离层:git branch + git worktree → 并行施工隔离
历史层:journal.md → 为什么当时这么做
知识层:.agent/registry/ → 组件 / bug / 惯例索引
复盘层:lessons/ → 心智模型怎么改
规格层:specs/ + .specify/ → 要做什么、怎么做
一句话:status 管现在,journal 管过去,registry 管可复用,lesson 管教训,branch/worktree 管隔离。
status.md不是日志,是「当前战场地图」:当前分支 / 目标 spec / 正在动的文件 / 不要碰的文件 / 最近一次验证结果 / 阻塞点 / 下一步 1-3 件 / 最后更新人。要短、可覆盖。registry不要写成百科。组件条目只回答「干什么、在哪、什么时候复用、有什么坑」;bug 条目必须写「症状 → 根因 → 修法 → 防复发信号」,只写「修好了」等于没写。lesson不要被 registry 顶替:registry 负责「下次能搜到」,lesson 负责「为什么会发生、之后心智模型怎么改」。复杂 bug 可以两边都写。
跨 worktree 的依赖与共享配置
隔离层只解决「不互相踩」,不解决「怎么共享」。依赖的分发规则:
- 共享依赖、vendor 库、共享配置默认落 main,feature 分支通过
merge/rebasemain 拿到; - worktree 之间只共享提交,不共享工作区:staged 但未 commit 的依赖不算进仓库,别的 worktree 看不到 —— 所以「我装好了」不等于「别人有了」;
node_modules不进 git;国内项目不建议外部 CDN,依赖应本地化 vendor。
配套的防撞车机制(隔离层的执行细则):开工前写 session 卡、声明 touching 与 do-not-touch、看到别人声明在改的文件就不碰、一 agent 一 worktree 一 branch。
版本线与分支模型
main = 稳定发布版;大版本用一条集成分支(如 1.0version)承载;小功能分支一律从集成分支开出,做完合回集成分支;集成分支整体测试稳定后,再一次性 merge 进 main。
main
└─ 1.0version ← 大版本集成区
├─ 1.0-toc
├─ 1.0-ai-panel
└─ 1.0-settings
- 不要让所有人直接改
main,也尽量不要所有人直接改集成分支 —— 集成分支是汇总区,不是公共工作区。 - 多个分支各有未提交改动时,不要把未收口的状态直接塞进
main:先把各自的改动提交或明确丢弃,再评估大功能分支。 - 落后的分支不要在落后状态上继续堆,基于最新基线重开一个小分支单独合。
- 「在分支中开分支」= 从当前点开新分支:在 worktree 里
git switch -c <new>,或从主目录git worktree add <dir> -b <new> <base>(后者更适合一 agent 一 worktree,不占用原 worktree)。
推倒重来之前:先开保险分支,不要拿 stash 当主备份
要 reset --hard upstream/main + 重装依赖这种「推倒重来」时,第一步不是 pull,而是先把本地改动完整落成提交:
git switch -c codex/backup-before-rebuild
git add -A
git commit -m "wip: save local work before rebuild"
# 之后才切回 main、fetch upstream、reset --hard、重装依赖
git stash不适合做主备份:它不覆盖未跟踪的新文件,也不形成一个可随时回来挑改动的基线。- 备份分支的价值是「可挑拣」:重建后从 backup 分支里按需重新合入需要的改动(登录页修正、schema 文档、skill、测试……),而不是全量回放。
- 推倒重来前先清点本地有哪些未提交改动(含未跟踪文件),确认都进了备份分支再 reset。
领先/落后只是提交计数,不是先进程度
A---B---C main
\
D---E old-branch
old-branch 对 main「领先 2」,同时「落后 2」。领先只说明有 main 没有的提交,不等于更新、更先进,也不代表应该直接合。
所以判断一条老分支要不要处理,不能看领先数,要看 diff 内容和产品是否还需要:可能是(a)有价值但还没合、(b)已被别处用另一种方式实现、(c)旧实验/废弃,可直接清理。
worktree 的两个坑
- 分支不是目录,不能
cd到分支。worktree 已经把每条分支 checkout 到一个固定文件夹,要进的是文件夹。在主目录里git checkout <branch>会被拒绝,因为该分支已被某个 worktree 占用 —— 这正是「两个目录不会同时改同一条分支」的保护。看别的 agent 的改动用cd <worktree>或git -C <worktree> diff。 .git/worktrees/<旧名>是 Git 内部元数据,别手删。分支改名后元数据目录名可能还是旧的(旧名对应新分支),Git 照样能对应上;worktree 目录里的.claude/settings.local.json是该 worktree 自己的配置,也不是旧分支残留。
入口分裂的典型症状
根因往往很蠢:AGENTS.md 里写「事实进 .Codex/memory/」,但目录里实际存在的是 .claude/。于是:
- 入口索引指向不存在的路径 → 死链;
- Codex 读
AGENTS.md、Claude 读CLAUDE.md,两个 agent 进来读到两套世界; - 规则「写了」但没生效,因为没人指向它。
修法是让 AGENTS.md 和 CLAUDE.md 退化成薄壳,都指向同一份真实源(.agents/START_HERE.md 或 agent.md)。工具专属配置才留在 .claude/ / .codex/。
.agents 和 .agent 为什么不是重复
.agents = 工具箱(能力目录) → 有哪些 skill / 命令可以调用,如 speckit-*
.agent = 项目白板(协作目录) → 现在什么状态、谁在哪个分支、修过什么 bug、有什么可复用
有 .agents 仍然需要 .agent:能力目录不存当前进度、不存 worktree 状态、不存 bug/组件索引。名字太像时,协作目录可改名 .agent-collab/。
Git 能替代多少
Git 擅长回答:改了什么(diff)、谁在哪个分支(branch/log)、文件历史(log -- path)、bug 哪次引入/修复(bisect/blame)、哪些没合并。
Git 不擅长回答:「现在下一个 agent 该接什么」「这个 bug 当时为什么这么修」「这个组件能不能复用」「这条规则刚变了要不要重读」「这个分支卡在用户拍板还是测试失败」。
所以分支表、最近变更、bug 索引都能部分由 Git 生成(写好 commit message,git log --grep 就是 bug 历史库),但**「当前意图」和「未完成状态」Git 不知道**,STATUS.md 不能省。
同一 worktree 内的并发写冲突
隔离层(一 agent 一 worktree)只挡住「跨 worktree 踩代码」,挡不住同一个工作区里并行跑多个 session —— 同一批文件被两个 session 同时写时,症状是「我的改动被回退了 / 文件被 linter 改了」。
分诊:先确认被怀疑的工具是否有写能力(lint 脚本只有 eslint .、没有 --fix,就不可能是它),再去看哪些文件同时带着多个来源的 diff —— 那些就是冲突点。
修复:暂停所有在改同一批文件的 session → 保存对方的 diff → 按 hunks 合并,不 reset 整个文件(否则删掉的是别人的改动)→ 只跑 lint/build 验证,不跑任何带 --fix、格式化、checkout、restore 的命令。
结论:并发写冲突要用「声明 + 按 hunk 合并」处理,不要用「整文件回滚」。 更根本的解法是让每个 session 有明确的文件所有权(touching / do-not-touch),同一时刻只有一个 session 写同一文件。
与 project-setup skill 的关系
原理相同:都是先建项目协作骨架,让 agent 不靠对话记忆工作。区别是规模——从「单 session 项目初始化」升级成「多 agent / 多 worktree 协作系统」。
相关
- 远端主线可能与本地主线无共同祖先 —— 重建/重置工作区前要先分清两套历史
- 通用脚本要靠工具适配器接入 —— 入口统一之后,自动化 brief 还得有工具适配层
- 入库前先分层清洗 —— 同样是「按职责分层,不混」的思路
- 远端主线可能与本地主线无共同祖先 —— 推倒重来前先确认远端与本地是不是同一套历史
- 文件被回退先分清是工具还是并发会话 —— 本节的分诊与修复细则