第 1 章 · 「agent 生 agent」端到端
本章讲清 ClawTeam 最核心的一支:
clawteam spawn一条命令背后发生的全部事情。这是项目价值的所在,也是工程含量最高的部分。
1.1 它要解决的小问题
你想让 leader agent 凭一句话再起一个 worker agent,而这个 worker 要满足三件事:
- 代码隔离:它改的代码不能直接污染主分支,也不能和别的 worker 撞车。
- 看得见:你能像看监控墙一样看它干活。
- 会协作:它一启动就知道自己叫什么、属于哪个队、该怎么报告进度。
ClawTeam 的答案分别是:git worktree、tmux 窗口、自动注入的提示词。
1.2 思路/直觉
把「派生一个 agent」拆成两半:
- 后端(backend) 负责「怎么把一个进程跑起来并能观察它」。默认是
tmux(每个 agent 一个 tmux 窗口),也有subprocess(后台进程)和wsh(Wave 终端块)。后端是可替换的,统一实现SpawnBackend抽象(clawteam/spawn/base.py:8,只有spawn()和list_running()两个抽象方法)。 - 编排 负责把「建队、起 worktree、拼提示词、登记存活」这些步骤按顺序串起来,并在任一步失败时回滚。这段编排就是 CLI 里的
spawn_agent函数。
1.3 图示:spawn 的流水线
怎么读:从左到右是 spawn_agent 的执行顺序;最后一步若返回 Error,会沿虚线回滚前面已做的副作用。
clawteam spawn --team my-team --agent-name alice --task "..."
│
▼
① 解析默认值 backend=tmux, command=[claude], skip_permissions=from config
│
▼
② 已有同名存活? 是 + 无 --replace → 报错退出;是 + --replace → stop_agent
│
▼
③ 建 worktree WorkspaceManager.create_workspace → 新分支 clawteam/<team>/<agent>
│ cwd = worktree 路径
▼
④ 登记成员 没队就 create_team(自己当 leader);否则 add_member
│
▼
⑤ 拼提示词 build_agent_prompt: 身份 + 工作区 + 任务 + 上下文 + 协作协议
│
▼
⑥ 后端 spawn TmuxBackend.spawn → 起窗口 + 等就绪 + 把提示词「贴」进去
│
▼
⑦ 登记存活 register_agent: 记 tmux target + pane PID + 命令
│
�└┄┄ 若 ⑥ 返回 Error ┄┄► 回滚:remove_member / cleanup_team + cleanup_workspace
(对应 clawteam/cli/commands.py:spawn_agent,第 3093-3331 行)
1.4 一步步看真实实现
③ 起 git worktree——隔离的基石
spawn 默认开启工作区隔离(config 里 workspace=auto)。真正建 worktree 的是 WorkspaceManager.create_workspace:它先清理可能残留的同名 worktree 与分支(崩溃恢复),再以当前分支为基底拉一个新分支:
# clawteam/workspace/manager.py:65-90 节选
branch = f"clawteam/{team_name}/{agent_name}"
wt_path = ensure_within_root(_workspaces_root(), team_name, agent_name)
# 崩溃残留清理:先删旧 worktree + 旧分支,避免 git 报「已存在」
if wt_path.exists():
git.remove_worktree(self.repo_root, wt_path) # 容错
git.delete_branch(self.repo_root, branch) # 容错
git.create_worktree(self.repo_root, wt_path, branch, base_ref=self.base_branch)
底层就是一句 git worktree add -b <branch> <path> <base_ref>(clawteam/workspace/git.py:create_worktree,第 48-58 行)。分支命名规则固定为 clawteam/{team}/{agent}——这让事后 git branch / git worktree list 一眼能认出谁是谁。
巧妙之处: worktree 的物理路径放在
~/.clawteam/workspaces/<team>/<agent>,不在用户的仓库目录里,但它仍是同一个.git的真分支。所以你既有完全隔离的工作目录,又能用普通git merge把成果合回主分支(merge_workspace→git.merge_branch,失败自动merge --abort,clawteam/workspace/git.py:86-104)。
⑤ 拼提示词——让 worker「天生会协作」
build_agent_prompt 把四块东西拼成一段 Markdown 提示词(clawteam/spawn/prompt.py:27-108):
- Identity:名字、ID、类型、队名、leader 名。
- Workspace:工作目录、分支,并明确告诉它「这是隔离 worktree,你的改动不影响主分支」。
- Task:leader 派的具体任务(也就是
--task的内容)。 - Coordination Protocol + Worker Loop:一串现成的
clawteam …命令模板,教它「怎么看任务、怎么报完成、怎么给 leader 发消息、闲了怎么上报」。
这段协作协议是 worker 行为的关键。它甚至写明了「完成任务前先 git commit」「不要干完第一个任务就退出,要循环检查新任务和信箱」(clawteam/spawn/prompt.py:88-106)。
注意一个设计取舍:「怎么用 CLI」的完整知识不在提示词里,而在 ClawTeam Skill 里(
skills/clawteam/SKILL.md)。提示词只给「够用的协作循环」,详细 CLI 参考交给 skill 文件,避免每次 spawn 都灌一大坨(见clawteam/spawn/prompt.py文件头注释)。
⑥ tmux 后端——把进程跑起来并喂提示词
这是最精细的一步(clawteam/spawn/tmux_backend.py:TmuxBackend.spawn,第 46-330 行)。挑几个非显然的点:
环境变量通过临时 env 文件注入,而不是塞进命令行。 因为 tmux 命令有约 16k 字符上限,且 WSL 上有 PROGRAMFILES(X86) 这种非法 shell 变量名。做法是:把合法变量写进一个 clawteam-env-*.sh,在 pane 里 source 它,文件里最后一行自删(clawteam/spawn/tmux_backend.py:140-154)。
解除 Claude 的嵌套检测。 如果 leader 自己就是个 Claude 会话,新起的 claude 会因为检测到「自己在 claude 里」而拒绝启动。所以启动命令前会 unset CLAUDECODE CLAUDE_CODE_ENTRYPOINT CLAUDE_CODE_SESSION(clawteam/spawn/tmux_backend.py:166)。
自动按掉启动确认弹窗。 在新 worktree 里,Claude/Codex/Gemini 会停在「信任这个目录吗」的弹窗;Claude 开 --dangerously-skip-permissions 时还有个「Yes, I accept」对话框。后端会截屏 pane 文本、识别弹窗、模拟回车或下箭头+回车把它点掉(_confirm_workspace_trust_if_prompted + _startup_prompt_action,第 435-494 行)。这是「让任意 CLI 无人值守启动」的脏活。
提示词用 paste-buffer 而不是 send-keys 注入。 多行 / 含特殊字符的提示词若用 send-keys 会被 shell 转义搞坏。做法是把提示词写进临时文件 → tmux load-buffer → paste-buffer 贴进 pane → 发两次回车提交(_inject_prompt_via_buffer,第 687-734 行)。
⑦ 登记存活信息——为日后探活做准备
spawn 成功后,把这个 agent 的「怎么找到它、怎么判断它还活着」记进 spawn_registry.json:
# clawteam/spawn/tmux_backend.py:300-308 节选 —— 记 tmux target + pane PID
register_agent(
team_name=team_name, agent_name=agent_name, backend="tmux",
tmux_target=target, # 形如 clawteam-my-team:alice
pid=pane_pid, # pane 的进程 PID,作为 tmux 失效时的兜底
command=list(final_command),
)
为什么同时记 tmux target 和 PID?因为 board attach 会把多个窗口合并成平铺 pane,tmux target 可能失效——这时用 PID 兜底判断存活(clawteam/spawn/registry.py:is_agent_alive,第 55-79 行;探活细节见 §3)。
1.5 关键细节 / 坑
- 回滚是事务性的。 若后端返回以
Error开头的字符串,CLI 会撤销刚才加的成员、必要时清掉刚建的队,并cleanup_workspace删掉 worktree(clawteam/cli/commands.py:3313-3325)。所以一次失败的 spawn 不会留下半截垃圾。 - 首个 spawn 自动建队。 如果 team 不存在,
spawn会把第一个 agent 当 leader 自动建队(commands.py:3223-3232)。这就是为什么 README 的「让 agent 驾驶」路径里,你只需给 leader 一句 prompt,它后续自己 spawn 就行。 --replace才能顶替同名存活 agent。 否则报错,防止你不小心起两个 alice(commands.py:3139-3163)。
下一章看这些 agent 起来之后,靠什么三块共享状态协作 → 02-coordination.md。