跳到主要内容

第 3 章 · 为什么任意 CLI agent 都能接入

ClawTeam 的招牌卖点是「任何 CLI agent 都能当队员」。本章拆开这个声明背后的四件事:适配器抹平差异、提示词注入协作知识、保活循环、存活探测。

3.1 兼容性契约——门槛其实很低

README 写明了接入一个新 agent 只需满足四条(README.md:521-527):

  1. 命令在 PATH 上,且能脱离 ClawTeam 独立启动。
  2. 能在指定工作目录 / worktree 里运行。
  3. 能接受一个初始任务(命令行参数或交互输入皆可)。
  4. 如果是交互式的,进程要能在 tmux 里活着。

这条契约之所以低,是因为 ClawTeam 不要求 agent 懂任何协议——协作知识是 spawn 时注入进它提示词的(见 §1.4 与下文)。

3.2 适配器:一个类抹平各家差异

不同 CLI 的「跳过权限确认」flag、「传 prompt」方式、「设工作目录」方式都不一样。这些差异集中在 NativeCliAdapter.prepare_command(clawteam/spawn/adapters.py:34-146)。用一张表看它抹平了什么:

CLI跳过权限的 flag传 prompt 的方式
claude--dangerously-skip-permissions(root 下自动省略)交互式:启动后注入(post_launch_prompt)
codex--dangerously-bypass-approvals-and-sandbox交互式:启动后注入;子命令模式:作为位置参数
gemini--yolo-i <prompt>(交互)/ -p(非交互)
kimi--yolo--print -p <prompt>,并 -w <cwd> 设工作目录
qwen / opencode--yolo-p <prompt>
nanobot(normalize 成 nanobot agent)-m <prompt>,支持 docker 包装
openclaw无(设计极简)--message <prompt> + --session-id
pi无(设计极简)交互式直接追加;否则 -p

(出处:adapters.py 第 49-140 行;各 is_*_command 判别函数在第 156-237 行,靠 command_basename 取可执行名小写来识别)

两个非显然的细节:

  • claude 在 root 下会拒绝 --dangerously-skip-permissions,所以检测到 os.getuid()==0 时静默省略该 flag,否则 agent 根本起不来(adapters.py:52-55)。
  • 命令归一化 在更底层的 normalize_spawn_command 里(clawteam/spawn/command_validation.py),例如把裸 nanobot 提升成 nanobot agent(README.md:515 提到这点)。

直觉: 适配器就是「一个翻译官」。leader 只管说「用 codex 跑这个任务」,翻译官负责把它翻成 codex 真正认识的那串 flag。新增一个 agent = 多写一个 is_xxx_command + 在 prepare_command 里加一个分支。

3.3 提示词注入:让 worker 凭空学会协作

协作知识怎么进到一个「啥都不知道」的 CLI agent 脑子里?分两条路,取决于 CLI 怎么收 prompt:

  • 非交互 / 支持 -p:协作提示词作为命令行参数直接传进去。
  • 交互式 TUI(claude/codex):启动时无法用参数稳妥传多行提示词,于是走 post_launch_prompt——先等 TUI 就绪,再用 tmux paste-buffer 把提示词「贴」进输入框并回车(§1.4 已详述 _inject_prompt_via_buffer)。

「等 TUI 就绪」本身是个启发式:轮询 pane 文本,看到提示符字符(❯ > ›)或内容连续两轮不变就认为就绪(_wait_for_cli_ready,clawteam/spawn/tmux_backend.py:601-654)。

3.4 保活续跑:worker 别干完一单就跑

agent 的天性是「干完任务就退出」。但 ClawTeam 想要 worker 持续待命,干完继续领新活。这靠两层:

提示层:协作协议明确写「不要干完第一个任务就退出」「保持监控/上报循环在前台」(clawteam/spawn/prompt.py:96-106)。

进程层:tmux 启动的不是裸命令,而是一段 build_keepalive_shell_command 生成的 shell while 循环(clawteam/spawn/keepalive.py:53-91)。逻辑是:

while true:
跑 agent 命令
记下退出码,触发 lifecycle on-exit 钩子
if 退出码==0 且 `lifecycle should-keepalive` 通过:
把命令换成「续跑命令」(如 claude --continue),sleep 1,继续循环
else:
退出

「续跑命令」由 build_resume_command 按 CLI 给出(keepalive.py:11-35):claude 是 --continue、codex 是 resume --last、gemini 是 --resume latest…… 也就是用各家 CLI 自己的会话恢复机制接着上次干。这样一个 worker 可以反复被唤醒续跑,而不是每次都从零开始。

3.5 存活探测:三套办法判断 agent 死活

任务抢锁、死任务回收、leader 唤醒,全都依赖「这个 agent 还活着吗」。is_agent_alive 按后端分三套(clawteam/spawn/registry.py:55-79):

后端怎么判断活着兜底
tmuxtmux list-panespane_dead 标志;pane 只剩 shell(bash/zsh)也算死tmux target 失效时回退查 PID
subprocessos.kill(pid, 0) 探测 PID——
wshwsh blocks list --json 查 block 是否还在——

返回三态:True(活)、False(死)、None(查无登记)。三态很重要——「不确定」时抢锁逻辑选择保守不抢(§2.2),宁可让任务多等也不冒重复执行的险。

巧妙之处: tmux 探活会检查 pane_current_command,如果 pane 里跑的只是 bash,说明 agent 进程已退、只剩外壳——判定为死(_tmux_pane_alive,第 188-190 行)。这比单看「窗口还在不在」准确得多。

3.6 Profiles / Presets:换供应商不改命令

想用「Claude Code 跑在 Moonshot Kimi 后端」这种非默认供应商组合,不必每次手动 export 一堆环境变量。ClawTeam 提供两层抽象:

  • preset:可复用的「供应商模板」(端点、API key 映射等),如内置的 moonshot-cnminimax-global
  • profile:由 preset 生成、最终被 spawn/launch 使用的运行时对象。

spawn 时 --profile <name>apply_profile 把对应的命令前缀和环境变量注入(clawteam/cli/commands.py:3205-3215,调用 clawteam/spawn/profiles.py)。这让「任意 CLI agent」进一步扩展成「任意 CLI agent × 任意供应商端点」。

下一章看自动化编排与全局精华 → 04-internals.md