Skip to content

Layer 6: 子 Agent 与 Swarm 协作

如何让多个 Agent 并行工作,同时保证隔离性(不互相干扰文件)、可通信性(能交换信息)和安全性(权限不泄露)?

子 Agent 系统是 Claude Code 中最复杂的子系统之一。它将单体 Agent 扩展为多 Agent 协作架构,支持进程内、本地子进程、远程三种模式。


tools/AgentTool/loadAgentsDir.ts
type AgentDefinition = {
agentType: string // 标识符(plan, explore, general-purpose 等)
whenToUse: string // 给模型的使用提示
tools?: string[] // 工具白名单('*' 表示全部)
disallowedTools?: string[] // 工具黑名单
mcpServers?: AgentMcpServerSpec[] // Agent 专属 MCP 服务器
hooks?: HooksSettings // Agent 专属 Hooks
model?: string // 模型覆盖
effort?: EffortValue // 复杂度设置
permissionMode?: PermissionMode // 权限模式
maxTurns?: number // 最大轮次
memory?: AgentMemoryScope // 持久记忆(user/project/local)
background?: boolean // 始终后台运行
isolation?: 'worktree' | 'remote' // 隔离模式
getSystemPrompt(): string // 系统提示词
}
Agent用途工具集模型
general-purpose通用多步骤任务全部继承父级
plan架构设计与实现规划只读 + 搜索继承
explore快速代码探索只读 + 搜索继承
verification验证实现正确性只读 + Bash继承
claude-code-guideClaude Code 使用指南只读 + Web继承

1.3 Agent 定义来源(优先级从高到低)

Section titled “1.3 Agent 定义来源(优先级从高到低)”
1. 内置 Agent(代码中硬编码)
2. 插件 Agent(plugins/ 目录注册)
3. 用户自定义 Agent(~/.claude/agents/ 目录)
4. 项目 Agent(.claude/agents/ 目录)
5. 策略 Agent(企业管理下发)
6. CLI Flag Agent(命令行指定)

去重规则:同一 agentType 只保留最高优先级的定义。

// 只显示 MCP 需求可满足的 Agent
filterAgentsByMcpRequirements(agents, availableMcpServers):
agent.mcpServers 中的每个服务器
→ 必须在 availableMcpServers 中存在
→ 否则该 Agent 不显示

tools/AgentTool/AgentTool.tsx
输入 Schema:
{
description: string // 3-5 词任务摘要
prompt: string // 任务指令
subagent_type?: string // Agent 类型
model?: 'sonnet' | 'opus' | 'haiku'
run_in_background?: boolean // 后台运行
name?: string // Teammate 名字
team_name?: string // 团队名
mode?: PermissionMode // 权限模式
isolation?: 'worktree' // 隔离模式
}
┌─────────────────────────────────────────────────────────┐
│ AgentTool.call() │
│ │
│ ┌─── subagent_type 指定? ───┐ │
│ │ │ │
│ │ Yes No (fork mode) │
│ │ ↓ ↓ │
│ │ loadAgent(type) forkSubagent() │
│ │ ↓ │ │
│ │ ┌────────────────┐ │ │
│ │ │ runAgent() │ │ │
│ │ │ → query() │ │ │
│ │ │ → 独立循环 │ │ │
│ │ └────────────────┘ │ │
│ │ │ │
│ │ team_name 指定? │ │
│ │ ↓ │ │
│ │ spawnTeammate() │ │
│ │ → InProcess/Local/Remote │ │
│ └────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

模式对比

模式进程通信隔离适用场景
InProcess同进程Mailbox共享状态快速轻量任务
Local子进程stdio/socket进程隔离需要独立环境
Remote远程HTTP/WebSocket完全隔离CCR 远程会话
tools/AgentTool/runAgent.ts
function createSubagentContext(parentContext, agentDefinition): ToolUseContext {
return {
...parentContext,
// 克隆文件状态缓存(隔离读取状态)
readFileState: parentContext.readFileState.clone(),
// Agent 专属的 MCP 客户端
mcpClients: mergedMcpClients,
// 过滤或继承的工具集
tools: resolveAgentTools(agentDefinition, parentTools),
// Agent ID(用于遥测和日志)
agentId: generateAgentId(),
// 查询跟踪(chainId + depth)
queryTracking: { chainId: parent.chainId, depth: parent.depth + 1 },
}
}
父 Agent 的 MCP 服务器
Agent 定义中的 MCP 服务器
├─ 字符串引用 → 查找共享连接(不清理)
└─ 内联定义 → 新建连接(Agent 退出时清理)
合并后的 MCP 客户端列表

★ 设计洞察:引用型 MCP 连接不在 Agent 退出时关闭,因为父 Agent 可能还在用。只有 Agent 自己创建的内联连接才清理。这是典型的「所有权 = 清理责任」模式。


Spawn(指定 subagent_type):
- 全新的系统提示词
- 全新的对话历史(只有 prompt)
- 独立的工具集(按 Agent 定义过滤)
- 适合:特化任务(探索、规划)
Fork(不指定 subagent_type):
- 继承父对话的完整上下文 + 系统提示词
- 继承父的全部工具集
- 权限模式 = 'bubble'(冒泡给父)
- 适合:分支式并行(做同一件事的不同部分)

这是 Fork 模式最精巧的工程细节:

目标:让所有 Fork 子 Agent 共享父 Agent 的 prompt cache
原理:API 的 prompt cache 按消息前缀匹配
→ 如果多个请求的消息前缀相同,cache 命中
实现:
父上下文: [...history, assistant(tool_use_A, tool_use_B, thinking, text)]
Fork 子 Agent 1(任务 D1):
[
...history, // ← 与父完全相同
assistant(tool_use_A, tool_use_B, ...), // ← 与父完全相同
user(
tool_result_A: "Fork started — processing in background", // 占位符
tool_result_B: "Fork started — processing in background", // 占位符
DIRECTIVE_D1 // ← 只有这里不同
)
]
Fork 子 Agent 2(任务 D2):
[
...history, // ← 与子1完全相同
assistant(tool_use_A, tool_use_B, ...), // ← 与子1完全相同
user(
tool_result_A: "Fork started — processing in background", // 相同!
tool_result_B: "Fork started — processing in background", // 相同!
DIRECTIVE_D2 // ← 只有这里不同
)
]

关键细节

  • 所有 tool_result 使用完全相同的占位符(不是每个子 Agent 不同)
  • 这使得前缀字节级相同,直到最后的 directive 文本
  • 结果:N 个 Fork 子 Agent 共享同一份 prompt cache → 显著降低成本
forkSubagent.ts
function isInForkChild(messages: Message[]): boolean {
// 检查消息中是否有 <claude:fork-boilerplate> 标签
// 如果有 → 已经在 fork 子 Agent 中 → 拒绝再次 fork
}

防止 Fork 子 Agent 再次 Fork,避免指数级爆炸。


┌──────────────────────────────────────────────────┐
│ Leader Agent │
│ │
│ injectUserMessageToTeammate(teammateId, message) │
│ ↓ ↑ │
│ ┌─────┴─────┐ ┌────┴─────┐ │
│ │ Mailbox₁ │ │ Mailbox₂ │ │
│ └─────┬─────┘ └────┬─────┘ │
│ ↓ ↓ │
│ ┌────────────┐ ┌────────────┐ │
│ │ Teammate A │ │ Teammate B │ │
│ │ (InProcess)│ │ (InProcess)│ │
│ └────────────┘ └────────────┘ │
└──────────────────────────────────────────────────┘
utils/swarm/inProcessRunner.ts
// 核心流程:
1. 创建 Teammate 上下文(克隆父上下文 + 独立 AbortController)
2. 启动 query() 循环(与主 Agent 共享进程,但独立消息历史)
3. 消息通过 Mailbox 传递:
- Leader → Teammate: injectUserMessageToTeammate()
- Teammate → Leader: Mailbox 通知
4. 权限请求通过 UI Bridge 冒泡给用户
Teammate 需要权限
createInProcessCanUseTool() 检查:
1. hasPermissionsToUseTool() → 基础决策
2. 决策 = 'ask'?
├─ UI Bridge 可用? → setToolUseConfirmQueue(带 workerBadge 标识)
│ → 用户在 REPL 中看到带颜色标记的权限对话框
│ → 用户决定 → 同步回 Teammate
└─ UI Bridge 不可用? → sendPermissionRequestViaMailbox()
→ Leader 轮询 Mailbox
→ Leader 代理决策
→ 响应写回 Mailbox

Worker Badge:每个 Teammate 有唯一的名字和颜色,权限对话框中会显示是哪个 Agent 在请求。

utils/swarm/teamHelpers.ts
// Teammates 可以:
claimTask(taskId) // 认领任务
updateTask(taskId, status) // 更新进度
// Leader 可以:
createTaskList(tasks) // 创建任务列表
assignTask(taskId, teammateId) // 分配任务

问题:多个 Agent 同时修改同一个文件 → 冲突
解决:每个 Agent 获得独立的 Git 工作树副本
仓库根目录/
├── .claude/worktrees/
│ ├── feature-auth/ ← Agent 1 的工作树
│ │ ├── .git (指针文件)
│ │ ├── node_modules → 符号链接到父目录
│ │ └── src/ ← 独立的文件副本
│ └── feature-ui/ ← Agent 2 的工作树
│ ├── .git (指针文件)
│ ├── node_modules → 符号链接到父目录
│ └── src/
└── .git/ ← 主仓库
utils/worktree.ts
Slug 验证规则:
- 最大 64 字符
- 每段匹配 [a-zA-Z0-9._-]+
- 拒绝路径遍历(..
- 拒绝绝对路径(/, C:\)
- 嵌套路径扁平化:user/feature → user+feature(避免 git D/F 冲突)
// 避免在每个 worktree 中复制 node_modules(可能 GB 级别)
symlinkDirectories(repoRoot, worktreePath, ['node_modules']):
创建符号链接 worktree/node_modules → repo/node_modules
错误处理:
ENOENT → 源不存在(静默跳过)
EEXIST → 已链接(静默跳过)
其他 → 警告日志
// 如果 worktree 已存在,跳过 git fetch
const existingHead = await readWorktreeHeadSha(worktreePath)
if (existingHead) {
return { worktreePath, existed: true } // 秒级恢复
}
// 防止 git 命令挂起等待密码输入
const GIT_NO_PROMPT_ENV = {
GIT_TERMINAL_PROMPT: '0', // 禁止 TTY 提示
GIT_ASKPASS: '', // 禁用 GUI 密码框
stdin: 'ignore', // 关闭标准输入
}

Fork Spawn
──── ─────
上下文继承 完整(父对话 + 系统提示词) 最小(只有 prompt)
工具集 父级全部 按 Agent 定义过滤
权限模式 bubble(冒泡给父) 独立(或继承)
Prompt Cache 共享(字节级相同前缀) 不共享
适用场景 分支并行做同一件事的不同部分 特化任务(探索、规划)
开销 低(复用 cache) 高(全新上下文)

★ 设计洞察:Fork 模式的 Prompt Cache 共享可以节省 90%+ 的输入 token 成本。假设父上下文有 50K tokens,5 个 Fork 子 Agent 共享 cache 后,实际新增输入只有 5 × ~1K tokens = 5K tokens,而不是 5 × 50K = 250K tokens。

权限继承(Spawn 模式):
子 Agent 独立决策 → 可能过于宽松或严格
适合:有明确权限边界的特化 Agent
权限冒泡(Fork 模式 / bubble 模式):
子 Agent 请求 → 冒泡给父/Leader → 用户看到统一的权限对话框
适合:需要统一安全策略的并行任务
权限桥接(InProcess Teammate):
Teammate 请求 → UI Bridge → REPL 权限对话框(带 worker badge)
用户看到是哪个 Agent 在请求,统一授权
为什么不用 SharedMemory / 全局事件总线?
1. 隔离性:Mailbox 天然隔离,一个 Agent 崩溃不影响其他
2. 可序列化:消息是纯数据,支持跨进程/远程传输
3. 背压控制:队列满了可以等,不会 flood
4. 可审计:所有通信都有记录
代价:
- 延迟高于共享内存
- 需要轮询(polling)而非推送
- 消息需要序列化/反序列化

交互方向说明
← L1 (Agent Loop)每个子 Agent 内部运行独立的 query() 循环
← L2 (Tool 系统)AgentTool 是 Tool 系统中最复杂的工具实现
→ L3 (权限系统)权限冒泡 / 桥接 / 独立模式的选择
→ L5 (上下文管理)Fork 子 Agent 的 compact 会利用 cache 共享策略
← Git 系统Worktree 隔离依赖 git worktree 功能
← MCP 系统Agent 可以定义专属 MCP 服务器