跳转至

2.5 Sub-agent

本节摘要

从State Management角度看,Sub-agent 机制回答的核心问题是:子 Agent 继承父 Agent 的哪些状态、隔离哪些、怎么把结果交还给父 Agent? 本节按「继承/隔离什么 → 怎么交换状态 → Coordinator 的状态分工」的主线展开。


一、Sub-agent 继承/隔离了哪些状态

Sub-agent 创建时,最关键的决策是:父 Agent 的哪些状态传给子 Agent、哪些隔离开。Claude Code 通过 Fork/Normal 两种模式提供不同的继承策略。

Fork vs Normal:两种状态继承模式

状态 Fork 模式 Normal 模式
System Prompt(静态区域) ✅ 共享(前缀对齐,命中 Prompt Cache) ❌ 独立构建
System Prompt(动态区域) ✅ 共享 ❌ 独立构建
userContext / systemContext ✅ 共享 ❌ 独立
对话历史 ✅ 继承父级消息 + 占位 tool_result ❌ 空白开始
工具定义 ✅ 继承(经过滤) ✅ 继承(经过滤)
readFileState 隔离(独立追踪读取的文件) ❌ 隔离
contentReplacementState 隔离(工具结果替换状态独立) ❌ 隔离
agentId 独立分配 ❌ 独立分配
abort 控制 同步共享 / 异步独立 独立
setAppState 条件共享(部分状态可回写父级) 独立

Fork 的核心价值:通过 CacheSafeParamsforkedAgent.ts)确保子 Agent 与父 Agent 的 API 请求在 system prompt + context + 工具定义 + 消息前缀上完全对齐,从而命中 Prompt Cache。buildForkedMessages 插入占位 tool_result 来维持消息序列的前缀一致性。

隔离的边界:即使在 Fork 模式下,readFileStatecontentReplacementState 也是隔离的——子 Agent 独立追踪自己读取了哪些文件、工具结果的替换状态,不会污染父 Agent 的状态。

工具集:三层过滤收紧

Sub-agent 看到的工具集是父 Agent 的子集,通过三层过滤逐步收紧:

层级 过滤规则 示例
1 ALL_AGENT_DISALLOWED_TOOLS — 全局黑名单 AgentTool 自身(防递归)
2 CUSTOM_AGENT_DISALLOWED_TOOLS — 按类型黑名单 特定 Agent 不允许的工具
3 ASYNC_AGENT_ALLOWED_TOOLS — 异步白名单 后台 Agent 仅允许特定工具

MCP 工具始终透传——用户主动配置的外部能力不被框架层过滤。

Memory:独立目录

每种 Agent 类型维护独立的Memory 目录(agentMemory.ts),按 user / project / local 三种 scope 映射到不同路径。不同类型的 Sub-agent Memory 互不干扰。

Attachment:部分共享

Sub-agent 共享全线程通用 attachment(Skill 列表、文件变更、工具 Delta 等),但不获取主线程独占 attachment(IDE 选中、诊断信息、token 用量等)。详见 2.6 Attachment 系统


二、Sub-agent 怎么与父 Agent 交换状态

Sub-agent 执行完毕后,需要把结果交还给父 Agent。这涉及两个问题:什么时候返回以什么形式返回

同步返回(默认)

Sub-agent 在前台执行,父 Agent 阻塞等待。结果直接作为 tool_result 返回到父 Agent 的对话中。

120 秒自动后台化

如果同步执行超过 120 秒,Sub-agent 自动转为后台任务——这是一个状态机转换

graph LR
    Sync["前台同步\n父 Agent 阻塞"] -->|"< 120s"| Return["tool_result\n直接返回"]
    Sync -->|"> 120s"| BG["自动后台化\nbackgroundSignal"]
    BG --> Notify["<task-notification>\n异步注入父会话"]
    Notify --> Done["最终结果回传"]

后台化后,Sub-agent 通过 <task-notification> XML 标签注入父 Agent 的对话——这实际上是在父 Agent 的消息历史中追加新的状态信息(任务进度、最终结果)。

异步 Sub-agent

从一开始就在后台执行,始终通过 <task-notification> 通信。与 120 秒后台化的区别是:异步 Sub-agent 有独立的 AbortController(父 Agent 中止不影响它)。


三、Coordinator 模式:状态的重新分工

Coordinator 模式从根本上改变了状态的分布——父 Agent(Coordinator)主动裁剪自己的状态空间,只保留编排能力:

状态维度 Coordinator Worker
可用工具 仅 4 个:AgentTool、SendMessage、TaskStop、SyntheticOutput 全量工具(30+)
职责 任务分解、分配、汇总 实际执行(文件读写、Shell 等)
Context 内容 任务描述 + Worker 返回的结果摘要 具体子任务的完整 Context

这是一种「状态分治」:Coordinator 的Context Window不被具体执行细节填满,可以专注于全局编排;Worker 的 Context只包含分配到的子任务。

Coordinator 与 Fork 互斥

Coordinator 模式下 Sub-agent 使用 Normal 模式(独立 Context),不使用 Fork——因为 Worker 的任务Context 与 Coordinator 完全不同,共享缓存没有意义。


四、补充机制

三条执行路径

路径 环境 适用场景
Local(默认) 当前进程内 大多数子任务
Remote (CCR) Claude Code Remote 远程环境 需要访问远程环境
Teammate tmux pane / 进程内后端 可见的并行协作

7 种任务类型

TaskRegistry(进程内 Map)管理的后台任务涵盖:LocalShellTask、LocalAgentTask、RemoteAgentTask、InProcessTeammate、LocalWorkflow、MonitorMcp、DreamTask。每种类型有独立的生命周期管理。

状态持久化

Sub-agent 状态是进程内的内存结构,不持久化到磁盘。CLI 场景下,进程就是会话——进程结束,Sub-agent 状态自然消失。这与 OpenClaw 的磁盘持久化注册表形成对比。


关键文件索引

文件 职责
src/utils/forkedAgent.ts CacheSafeParams + createSubagentContext(状态继承/隔离的核心)
src/tools/AgentTool/forkSubagent.ts Fork 模式 + buildForkedMessages(消息前缀对齐)
src/tools/AgentTool/AgentTool.tsx 三路径入口 + Fork/Normal 选择
src/tools/AgentTool/runAgent.ts Local Agent 主循环
src/coordinator/coordinatorMode.ts Coordinator 模式(工具裁剪)
src/constants/tools.ts 三层工具过滤规则
src/tools/AgentTool/agentMemory.ts Sub-agent 独立Memory 目录
src/tasks/LocalAgentTask/LocalAgentTask.tsx 后台化状态机(~680 行)