Agent / Subagent / Swarm 解析:ClaudeCode源码深度解读

0 阅读8分钟

Claude Code 的多智能体系统由三个递进层级构成:单次 Subagent(轻量委托)→ Fork Subagent(上下文克隆分身)→ Swarm / Team(多进程协作群)。它们共享同一个 runAgent() 核心,但在隔离策略、通信方式和生命周期上各有不同。

封面:Claude Code 多智能体系统


1. 三种模式一览

特性普通 SubagentFork SubagentSwarm Teammate
触发方式Agent(subagent_type=X)Agent() 不带 subagent_typeAgent(name=X, team_name=Y)
上下文继承空 / 只从 prompt 开始完整克隆父对话 + 工具结果独立进程,仅通过 mailbox 通信
运行位置父进程内(sync/async)父进程内,后台异步子进程(tmux/iTerm2)或 in-process
AbortControllersync: 共享父的; async: 独立独立独立(不受领导者中断影响)
权限提示async: 自动拒绝; sync: 气泡到父bubble → 气泡到父终端通过 permissionSync 文件路由
通信机制返回值 / Generator yield父 context 注入 + 返回值文件信箱(mailbox)
sidechain 转录
门控条件默认启用feature('FORK_SUBAGENT')isAgentSwarmsEnabled()

三种模式概览

2. Subagent(普通子代理)

2.1 实体:runAgent() 异步生成器

src/tools/AgentTool/runAgent.ts 的核心是一个 AsyncGenerator,外部通过 for await 消费其输出消息,实现流式透传。

关键设计决策:

parent → AgentTool.call()
          ├── 构建 agentToolUseContext(独立 AppState 视图)
          ├── resolveAgentTools()       ← 按定义过滤工具集
          ├── getAgentSystemPrompt()    ← 专属系统提示词
          ├── createSubagentContext()   ← 上下文隔离
          └── runAgent() → query()     ← 流式游历 API 轮次
                              ↓
                        yield messages → agentToolUtils.runAsyncAgentLifecycle()

权限模式继承规则(优先级从高到低):

  1. bypassPermissions / acceptEdits / auto — 父级总是赢,子代理不可覆盖
  2. agentDefinition.permissionMode — 定义文件中的声明
  3. isAsync=trueshouldAvoidPermissionPrompts=true(不弹 UI)
  4. canShowPermissionPrompts=trueawaitAutomatedChecksBeforeDialog=true(先等自动化检查)

Claude.md 裁剪优化:

Explore / Plan 等只读代理默认剥除 claudeMd(省 ~5-15 Gtok/week)和 gitStatus(节省上下文),通过 GrowthBook 门控 tengu_slim_subagent_claudemd

2.2 工具集解析 resolveAgentTools()

agentDefinition.tools: ['*']         → 继承父工具集
agentDefinition.tools: ['Bash','Edit'] → 精确子集
agentDefinition.tools: ['!Bash']      → 排除模式
useExactTools=true                   → 完全跳过过滤(fork 路径专用)

allowedTools 参数在作用域上替换父级 session 权限(保留 cliArg 层),避免父级许可泄漏给子代理。

子代理权限与工具集解析

2.3 生命周期 finally 清理

runAgent()finally 块确保即使抛出 AbortError 也执行:

  • MCP 服务器关闭(mcpCleanup()
  • Session hooks 清理(clearSessionHooks()
  • Prompt cache 追踪状态清理
  • 文件状态缓存释放(readFileState.clear()
  • Todos 孤立条目删除
  • 后台 Bash 进程杀死(killShellTasksForAgent()
  • Perfetto trace 注销

3. Fork Subagent(分叉子代理)

3.1 设计理念

Fork 是上下文克隆模式:子代理不是从空白对话开始,而是继承父对话的完整消息历史。核心思路是最大化利用父代理的 prompt cache——子代理与父代理共享相同的 API 请求前缀,命中缓存的概率极高。

启用条件:

// 互斥约束:不能与 coordinator 模式共用
feature('FORK_SUBAGENT') && !isCoordinatorMode() && !isNonInteractiveSession()

3.2 消息构建 buildForkedMessages()

父 AssistantMessage(含所有 tool_use 块)
    ↓ clone
[fullAssistantMessage]
+
user 消息:
  [tool_result x N (全为 FORK_PLACEHOLDER)]  ← 占位符让 API 认为工具已完成
  + text: <fork_directive>子任务说明</fork_directive>

FORK_PLACEHOLDER_RESULT 是固定字符串(保证 cache 命中),子代理看到的是已有工具结果和新指令。

Fork 子代理:字节级 Prompt Cache 最大化

3.3 useExactTools=true 稳定化

Fork 模式传 useExactTools=true

  • 直接使用父工具池,不经 resolveAgentTools() 过滤
  • 继承父的 thinkingConfig(思维链设置)
  • 继承父的 isNonInteractiveSession
  • 目的:让子代理的 API 请求前缀与父代理字节完全一致,命中 server-side prompt cache

3.4 递归 Fork 防护

// 双重检查,对抗 autocompact 改写消息后的 bypass
if (toolUseContext.options.querySource === `agent:builtin:${FORK_AGENT.agentType}`
    || isInForkChild(toolUseContext.messages)) {
  throw new Error('Fork is not available inside a forked worker.')
}

4. Swarm(群集多代理)

4.1 架构概览

Leader Process(claude REPL)
│
├── TeamFile (.claude/teams/{team}/team.json)
│     members: [{ agentId, agentName, color, backendType, paneId, ... }]
│
├── Mailbox (.claude/teams/{team}/inboxes/{agent_name}.json)
│     用于 peer DM、权限请求、shutdown、idle 通知
│
├── Backend(运行时自动选择)
│     ├── tmux        → 创建 tmux pane,发 claude CLI 命令
│     ├── iterm2      → it2 split pane
│     └── in-process  → AsyncLocalStorage 隔离,同进程运行
│
└── Leader Permission Bridge(leaderPermissionBridge.ts)
      module-level 全局 setter → 允许 in-process 队友使用 leader 的权限对话框

4.2 后端选择策略(registry.ts

优先级从高到低:

  1. tmux — 在 tmux 会话中且 tmux 可用
  2. iterm2 — 在 iTerm2 中且 it2 CLI 已安装
  3. in-process — 兜底(无终端分栏依赖)

选择结果在进程生命周期内固定缓存(cachedBackend)。

4.3 TeamFile:持久化团队状态

路径:~/.claude/teams/{team_name}/team.json

type TeamFile = {
  leadAgentId: string          // 唯一领导者
  members: TeamMember[]        // 包含 paneId、backendType、agentId
  hiddenPaneIds: PaneId[]      // 支持 hide/show
  allowedPaths: TeamAllowedPath[]  // 权限沙盒
}

写操作均通过 lockfile 序列化,防止并发的多个 Claude 进程竞争写:

const LOCK_OPTIONS = { retries: { retries: 10, minTimeout: 5, maxTimeout: 100 }}

4.4 文件信箱(Mailbox)通信协议

路径:~/.claude/teams/{team}/inboxes/{agent_name}.json

消息格式:

type TeammateMessage = {
  from: string       // 发送者名称
  text: string       // 消息内容
  timestamp: string
  read: boolean
  color?: string     // 发送者颜色(UI 显示用)
  summary?: string   // 5-10 字摘要(预览用)
}

消息类型(teammateMailbox.ts):

消息类型方向用途
peer_dm任意→任意SendMessageTool 的 DM
permission_requestworker→leader权限请求路由
permission_responseleader→worker权限决策回传
shutdown_requestleader→worker优雅关闭
idle_notificationworker→leader完成通知
sandbox_permission_request/responseworker↔leader沙盒权限

写操作原子性:通过 lockfile 包,10 次重试 + 指数退避,防止 N 个并发 Claude 进程同时写同一收件箱。

4.5 权限同步机制(permissionSync.ts

Worker 遇到需要确认的工具调用
    │
    ├─ [in-process 路径]
    │    leader 的 ToolUseConfirm 队列(内存 setter)
    │    ← leaderPermissionBridge.getLeaderToolUseConfirmQueue()
    │
    └─ [pane 路径]
         worker → writeToMailbox(leader, permission_request)
                      │
              leader 轮询检测 → UI 弹出确认对话框
                      │
              leader → writeToMailbox(worker, permission_response)
                      │
              worker 轮询收到决策 → 继续 / 拒绝

轮询间隔:PERMISSION_POLL_INTERVAL_MS = 500ms,由 useSwarmPermissionPoller hook 驱动。

4.6 In-Process Teammate 隔离

通过 AsyncLocalStorage 实现同进程内的上下文隔离:

spawnInProcessTeammate()
    ├── createAbortController()          ← 独立中止(不受领导者影响)
    ├── createTeammateContext()           ← AsyncLocalStorage 上下文
    ├── registerTask(taskState, setAppState) ← 在 AppState 中注册显示
    └── startInProcessTeammate()         ← runWithTeammateContext() 包裹
         └── runAgent() 在隔离上下文中执行

优点:无需额外进程,共享 API 连接和 MCP 服务器。 缺点:崩溃会影响主进程;不支持从 in-process teammate 内部再派生后台代理(限制:"In-process teammates cannot spawn background agents")。


5. Coordinator 模式

COORDINATOR_MODE 是另一套协调架构,与 Fork 互斥:

CLAUDE_CODE_COORDINATOR_MODE=1isCoordinatorMode()
    ├── getCoordinatorSystemPrompt()    ← 专属系统提示词
    ├── getCoordinatorUserContext()     ← 专属用户上下文
    ├── INTERNAL_WORKER_TOOLS          ← 仅限内部工具集
    └── 禁用 Fork subagent

设计目标:Coordinator 是轻量级调度者,不直接执行工具,而是向 worker 发任务;与 Fork(克隆并行)策略相对。


6. 通信流汇总

┌─────────────────────────────────────────────────────────┐
│                    Leader REPL                          │
│  ┌─────────────────────────────────────────────────┐   │
│  │  Mailbox Poller(500ms 轮询)                    │   │
│  │  · 收取 peer_dm → 注入为 attachment 消息          │   │
│  │  · 收取 permission_request → 弹 UI 对话框         │   │
│  │  · 收取 idle_notification → Task 状态更新         │   │
│  └─────────────────────────────────────────────────┘   │
│                       ↕ 内存信号                        │
│  ┌──────────────────────────────────────────────────┐   │
│  │  In-Process Teammate(AsyncLocalStorage 隔离)    │   │
│  │  permission via leaderPermissionBridge (内存 fn)  │   │
│  └──────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘
           ↕ 文件 I/O(mailbox + lockfile)
┌──────────────────────────────────────────────────────────┐
│              Pane Teammate(独立进程)                    │
│  claude CLI 以环境变量指定 team/agent 信息               │
│  · CLAUDE_CODE_AGENT_ID / NAME / TEAM_NAME / COLOR       │
│  · CLAUDE_CODE_LEADER_AGENT_ID                           │
└──────────────────────────────────────────────────────────┘

Swarm 三路通信全景

7. 设计亮点

7.1 Prompt Cache 最大化(Fork 设计)

Fork 子代理通过 useExactTools=true + 固定 placeholder result 确保 API 请求前缀与父代理字节完全一致,最大化 server-side prompt cache 命中率。这是性能优化的核心。

7.2 工具权限隔离(allowedTools 替换语义)

parent session rules: [A, B, C]
子代理 allowedTools: [X, Y]
→ 子代理实际拥有: [cliArg rules, X, Y]

父级 session 允许的工具不会自动泄漏给子代理,必须显式传递。cliArg 层(SDK --allowedTools)始终保留,因为这是外部用户的显式意图。

7.3 文件信箱 + lockfile 并发安全

多个 Claude 进程可同时写同一信箱,通过 lockfile 的重试+指数退避实现可靠串行化。比共享内存队列更健壮(跨进程),比数据库更轻量。

7.4 Backend 抽象层(TeammateExecutor 接口)

interface TeammateExecutor {
  spawn(config: TeammateSpawnConfig): Promise<TeammateSpawnResult>
  sendMessage(agentId, message): Promise<void>
  kill(agentId): Promise<void>
}

统一接口屏蔽 tmux / iTerm2 / in-process 差异,注册模式(registerTmuxBackend())避免循环依赖。

7.5 sidechain 转录(每个代理独立 JSONL)

每个子代理把消息写入 subagents/{agentId}.jsonl,支持会话恢复和调试查看,且使用增量追加(lastRecordedUuid 游标),不重写历史。

7.6 Claude.md 精简(读写代理分级优化)

只读代理(Explore、Plan)自动剥除 claudeMd 和 gitStatus,节省大量 token。按 agentDefinition.omitClaudeMd 字段在代理定义文件中声明,kill-switch 通过 GrowthBook 控制。


8. 可直接复用的设计模式

8 大可复用设计模式速览

8.1 文件信箱(Mailbox + lockfile)

场景:多进程 / 多 worker 异步通信,且不想引入消息队列中间件。

// 写:序列化写,带重试退避
await lockfile.lock(path, opts)
const msgs = JSON.parse(await readFile(path))
msgs.push(newMsg)
await writeFile(path, JSON.stringify(msgs))
lockfile.unlock(path)

// 读:轮询 + 标记已读
const msgs = readMailbox(agentName)
const unread = msgs.filter(m => !m.read)

8.2 权限决策气泡(bubble permission mode)

场景:子任务需要弹 UI,但希望对话框出现在主进程终端而非子进程。

  • permissionMode: 'bubble' + leaderPermissionBridge setter 注册
  • Pane 路径:mailbox 双向路由

8.3 AsyncLocalStorage 上下文隔离

场景:同进程并发运行多个"租户",每个租户有独立的 agentId、teamName、color 等上下文。

const teammateContext = new AsyncLocalStorage<TeammateContext>()
runWithTeammateContext(ctx, () => runAgent(...))
// 在任意调用深度取上下文:
getTeamName() → teammateContext.getStore()?.teamName

8.4 Generator 流式 + 终止信号

async function* runAgent(...): AsyncGenerator<Message> {
  const controller = isAsync ? new AbortController() : parent.abortController
  for await (const msg of query({ signal: controller.signal })) {
    yield msg  // 流式透传
  }
  if (controller.signal.aborted) throw new AbortError()
}

消费方调用 break 即可中止,不需要手动清理——finally 块统一清理。

8.5 懒加载工具 Schema(lazySchema()

const inputSchema = lazySchema(() => z.object({ ... }))
// 首次访问时才构建 Zod schema,配合 feature() 门控

避免在模块加载阶段就执行条件 feature() 检查(build-time DCE 友好)。

8.6 Backend 注册模式(避免循环依赖)

// registry.ts
let TmuxBackendClass: (new () => PaneBackend) | null = null
export function registerTmuxBackend(cls: new () => PaneBackend) {
  TmuxBackendClass = cls
}
// TmuxBackend.ts(在模块末尾)
registerTmuxBackend(TmuxBackend)

Backend 实现文件负责向 registry 注册自己,registry 不直接 import 实现,打破循环依赖。


9. 源码索引

职责文件
Agent 工具入口src/tools/AgentTool/AgentTool.tsx
子代理运行核心src/tools/AgentTool/runAgent.ts
Fork 消息构建src/tools/AgentTool/forkSubagent.ts
TeamFile & 锁src/utils/swarm/teamHelpers.ts
权限同步src/utils/swarm/permissionSync.ts
领导者权限桥src/utils/swarm/leaderPermissionBridge.ts
In-process 运行器src/utils/swarm/inProcessRunner.ts
In-process 生成src/utils/swarm/spawnInProcess.ts
文件信箱src/utils/teammateMailbox.ts
内存信箱src/utils/mailbox.ts
后端接口src/utils/swarm/backends/types.ts
后端注册表src/utils/swarm/backends/registry.ts
Swarm 门控src/utils/agentSwarmsEnabled.ts
Coordinator 模式src/coordinator/coordinatorMode.ts
通信时序图agent-swarm-communication.puml

PNG