Layer 6: 子 Agent 与 Swarm 协作
如何让多个 Agent 并行工作,同时保证隔离性(不互相干扰文件)、可通信性(能交换信息)和安全性(权限不泄露)?
子 Agent 系统是 Claude Code 中最复杂的子系统之一。它将单体 Agent 扩展为多 Agent 协作架构,支持进程内、本地子进程、远程三种模式。
1. Agent 定义体系
Section titled “1. Agent 定义体系”1.1 AgentDefinition 结构
Section titled “1.1 AgentDefinition 结构”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 // 系统提示词}1.2 内置 Agent 类型
Section titled “1.2 内置 Agent 类型”| Agent | 用途 | 工具集 | 模型 |
|---|---|---|---|
general-purpose | 通用多步骤任务 | 全部 | 继承父级 |
plan | 架构设计与实现规划 | 只读 + 搜索 | 继承 |
explore | 快速代码探索 | 只读 + 搜索 | 继承 |
verification | 验证实现正确性 | 只读 + Bash | 继承 |
claude-code-guide | Claude 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 只保留最高优先级的定义。
1.4 MCP 服务器过滤
Section titled “1.4 MCP 服务器过滤”// 只显示 MCP 需求可满足的 AgentfilterAgentsByMcpRequirements(agents, availableMcpServers): agent.mcpServers 中的每个服务器 → 必须在 availableMcpServers 中存在 → 否则该 Agent 不显示2. Agent 生成与隔离
Section titled “2. Agent 生成与隔离”2.1 AgentTool 入口
Section titled “2.1 AgentTool 入口”输入 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' // 隔离模式}2.2 三种执行模式
Section titled “2.2 三种执行模式”┌─────────────────────────────────────────────────────────┐│ 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 远程会话 |
2.3 createSubagentContext()
Section titled “2.3 createSubagentContext()”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 }, }}2.4 MCP 服务器继承
Section titled “2.4 MCP 服务器继承”父 Agent 的 MCP 服务器 ↓Agent 定义中的 MCP 服务器 ├─ 字符串引用 → 查找共享连接(不清理) └─ 内联定义 → 新建连接(Agent 退出时清理) ↓合并后的 MCP 客户端列表★ 设计洞察:引用型 MCP 连接不在 Agent 退出时关闭,因为父 Agent 可能还在用。只有 Agent 自己创建的内联连接才清理。这是典型的「所有权 = 清理责任」模式。
3. Fork 子代理
Section titled “3. Fork 子代理”3.1 Fork vs Spawn
Section titled “3.1 Fork vs Spawn”Spawn(指定 subagent_type): - 全新的系统提示词 - 全新的对话历史(只有 prompt) - 独立的工具集(按 Agent 定义过滤) - 适合:特化任务(探索、规划)
Fork(不指定 subagent_type): - 继承父对话的完整上下文 + 系统提示词 - 继承父的全部工具集 - 权限模式 = 'bubble'(冒泡给父) - 适合:分支式并行(做同一件事的不同部分)3.2 Prompt Cache 共享策略
Section titled “3.2 Prompt Cache 共享策略”这是 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 → 显著降低成本
3.3 递归 Fork 防护
Section titled “3.3 递归 Fork 防护”function isInForkChild(messages: Message[]): boolean { // 检查消息中是否有 <claude:fork-boilerplate> 标签 // 如果有 → 已经在 fork 子 Agent 中 → 拒绝再次 fork}防止 Fork 子 Agent 再次 Fork,避免指数级爆炸。
4. Swarm 多 Agent 协作
Section titled “4. Swarm 多 Agent 协作”4.1 Teammate 通信架构
Section titled “4.1 Teammate 通信架构”┌──────────────────────────────────────────────────┐│ Leader Agent ││ ││ injectUserMessageToTeammate(teammateId, message) ││ ↓ ↑ ││ ┌─────┴─────┐ ┌────┴─────┐ ││ │ Mailbox₁ │ │ Mailbox₂ │ ││ └─────┬─────┘ └────┬─────┘ ││ ↓ ↓ ││ ┌────────────┐ ┌────────────┐ ││ │ Teammate A │ │ Teammate B │ ││ │ (InProcess)│ │ (InProcess)│ ││ └────────────┘ └────────────┘ │└──────────────────────────────────────────────────┘4.2 进程内 Teammate 编排
Section titled “4.2 进程内 Teammate 编排”// 核心流程:
1. 创建 Teammate 上下文(克隆父上下文 + 独立 AbortController)2. 启动 query() 循环(与主 Agent 共享进程,但独立消息历史)3. 消息通过 Mailbox 传递: - Leader → Teammate: injectUserMessageToTeammate() - Teammate → Leader: Mailbox 通知4. 权限请求通过 UI Bridge 冒泡给用户4.3 跨进程权限同步
Section titled “4.3 跨进程权限同步”Teammate 需要权限 ↓createInProcessCanUseTool() 检查: ↓1. hasPermissionsToUseTool() → 基础决策 ↓2. 决策 = 'ask'? ├─ UI Bridge 可用? → setToolUseConfirmQueue(带 workerBadge 标识) │ → 用户在 REPL 中看到带颜色标记的权限对话框 │ → 用户决定 → 同步回 Teammate │ └─ UI Bridge 不可用? → sendPermissionRequestViaMailbox() → Leader 轮询 Mailbox → Leader 代理决策 → 响应写回 MailboxWorker Badge:每个 Teammate 有唯一的名字和颜色,权限对话框中会显示是哪个 Agent 在请求。
4.4 任务列表管理
Section titled “4.4 任务列表管理”// Teammates 可以:claimTask(taskId) // 认领任务updateTask(taskId, status) // 更新进度// Leader 可以:createTaskList(tasks) // 创建任务列表assignTask(taskId, teammateId) // 分配任务5. Git Worktree 隔离
Section titled “5. Git Worktree 隔离”5.1 为什么需要 Worktree?
Section titled “5.1 为什么需要 Worktree?”问题:多个 Agent 同时修改同一个文件 → 冲突解决:每个 Agent 获得独立的 Git 工作树副本5.2 工作树结构
Section titled “5.2 工作树结构”仓库根目录/├── .claude/worktrees/│ ├── feature-auth/ ← Agent 1 的工作树│ │ ├── .git (指针文件)│ │ ├── node_modules → 符号链接到父目录│ │ └── src/ ← 独立的文件副本│ └── feature-ui/ ← Agent 2 的工作树│ ├── .git (指针文件)│ ├── node_modules → 符号链接到父目录│ └── src/└── .git/ ← 主仓库5.3 路径安全性
Section titled “5.3 路径安全性”Slug 验证规则: - 最大 64 字符 - 每段匹配 [a-zA-Z0-9._-]+ - 拒绝路径遍历(..) - 拒绝绝对路径(/, C:\) - 嵌套路径扁平化:user/feature → user+feature(避免 git D/F 冲突)5.4 符号链接策略
Section titled “5.4 符号链接策略”// 避免在每个 worktree 中复制 node_modules(可能 GB 级别)symlinkDirectories(repoRoot, worktreePath, ['node_modules']): 创建符号链接 worktree/node_modules → repo/node_modules 错误处理: ENOENT → 源不存在(静默跳过) EEXIST → 已链接(静默跳过) 其他 → 警告日志5.5 快速恢复
Section titled “5.5 快速恢复”// 如果 worktree 已存在,跳过 git fetchconst existingHead = await readWorktreeHeadSha(worktreePath)if (existingHead) { return { worktreePath, existed: true } // 秒级恢复}5.6 凭证安全
Section titled “5.6 凭证安全”// 防止 git 命令挂起等待密码输入const GIT_NO_PROMPT_ENV = { GIT_TERMINAL_PROMPT: '0', // 禁止 TTY 提示 GIT_ASKPASS: '', // 禁用 GUI 密码框 stdin: 'ignore', // 关闭标准输入}6. 设计洞察总结
Section titled “6. 设计洞察总结”Fork vs Spawn:两种子 Agent 模型
Section titled “Fork vs Spawn:两种子 Agent 模型” 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。
权限冒泡 vs 权限继承
Section titled “权限冒泡 vs 权限继承”权限继承(Spawn 模式): 子 Agent 独立决策 → 可能过于宽松或严格 适合:有明确权限边界的特化 Agent
权限冒泡(Fork 模式 / bubble 模式): 子 Agent 请求 → 冒泡给父/Leader → 用户看到统一的权限对话框 适合:需要统一安全策略的并行任务
权限桥接(InProcess Teammate): Teammate 请求 → UI Bridge → REPL 权限对话框(带 worker badge) 用户看到是哪个 Agent 在请求,统一授权Mailbox 模式的工程取舍
Section titled “Mailbox 模式的工程取舍”为什么不用 SharedMemory / 全局事件总线? 1. 隔离性:Mailbox 天然隔离,一个 Agent 崩溃不影响其他 2. 可序列化:消息是纯数据,支持跨进程/远程传输 3. 背压控制:队列满了可以等,不会 flood 4. 可审计:所有通信都有记录
代价: - 延迟高于共享内存 - 需要轮询(polling)而非推送 - 消息需要序列化/反序列化与其他层的交互
Section titled “与其他层的交互”| 交互方向 | 说明 |
|---|---|
| ← L1 (Agent Loop) | 每个子 Agent 内部运行独立的 query() 循环 |
| ← L2 (Tool 系统) | AgentTool 是 Tool 系统中最复杂的工具实现 |
| → L3 (权限系统) | 权限冒泡 / 桥接 / 独立模式的选择 |
| → L5 (上下文管理) | Fork 子 Agent 的 compact 会利用 cache 共享策略 |
| ← Git 系统 | Worktree 隔离依赖 git worktree 功能 |
| ← MCP 系统 | Agent 可以定义专属 MCP 服务器 |