第七章:状态管理与 Agent 协调
学习时间: 3 小时 | 难度: ⭐⭐⭐⭐ | 前置: 第二章、第三章
学习目标
完成本章后,学员将能够:
- 解释两层状态架构(bootstrap + AppState)的设计动机
- 描述后台任务系统的 7 种任务类型
- 理解 AgentTool 子 Agent 的 4 种运行模式
- 应用 Actor Model 和 Immutable State 模式
1. 两层状态架构
Claude Code 的状态管理采用两层架构,分别服务于不同层次的需求:
1.1 架构概览
┌─────────────────────────────────────────────────────────────┐
│ Layer 1: bootstrap/state.ts (1758行) │
│ 进程级全局单例 │
│ │
│ 字段: ~100+ 个 │
│ ├── sessionId: string 会话唯一标识 │
│ ├── cwd: string 当前工作目录 │
│ ├── projectRoot: string 项目根目录 │
│ ├── tokenCount: number 累计 token 消耗 │
│ ├── telemetry: TelemetryState 遥测状态 │
│ └── ... │
│ │
│ 特点: │
│ ├── 模块级单例(import 即获取) │
│ ├── 跨组件共享 │
│ └── 会话生命周期 │
└──────────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────────▼──────────────────────────────┐
│ Layer 2: AppState Store (~50 字段) │
│ UI 级状态(Zustand 风格,~34行实现) │
│ │
│ 字段: │
│ ├── messages: Message[] 对话消息列表 │
│ ├── tools: Tool[] 可用工具列表 │
│ ├── permissions: PermissionState 权限状态 │
│ ├── mcpConnections: Map MCP 连接 │
│ ├── tasks: Task[] 后台任务 │
│ └── ... │
│ │
│ 特点: │
│ ├── 响应式(React 组件自动更新) │
│ ├── 结构共享(Immutable State) │
│ └── 引用相等跳过(性能优化) │
└─────────────────────────────────────────────────────────────┘
1.2 为什么分两层
| 考量 | bootstrap/state.ts | AppState Store |
|---|---|---|
| 生命周期 | 进程级 | 组件级 |
| 访问方式 | 直接 import | React Context |
| 响应式 | 否 | 是(触发重渲染) |
| 使用场景 | 非 UI 代码 | UI 组件 |
| 典型字段 | sessionId, cwd, tokenCount | messages, tools, permissions |
1.3 极简 Store 实现
AppState 的 Store 实现极其精简,仅 ~34 行代码:
// 伪代码:Zustand 风格的 Store
function createStore(initialState, onChange) {
let state = initialState
const listeners = new Set<() => void>()
return {
getState: () => state,
setState: (updater: (state) => state) => {
const next = updater(state)
if (Object.is(next, state)) return // 引用相等跳过 ⭐
state = next
onChange?.({ newState: next, oldState: state })
for (const listener of listeners) {
listener() // 通知所有订阅者
}
},
subscribe: (listener: () => void) => {
listeners.add(listener)
return () => listeners.delete(listener) // 返回取消订阅函数
}
}
}
关键优化: Object.is(next, state) 引用相等检查。如果 updater 返回的对象引用与当前 state 相同,则跳过更新。这避免了不必要的重渲染。
2. 后台任务系统
Claude Code 支持 7 种后台任务类型,每种都是一个独立的 Actor:
2.1 任务类型
| 任务类型 | 说明 | 生命周期 |
|---|---|---|
| LocalShellTask | 本地 Shell 命令 | 命令执行期间 |
| LocalAgentTask | 本地子 Agent | Agent 完成时 |
| RemoteAgentTask | 远程 Agent | 远程会话结束时 |
| InProcessTeammateTask | 进程内协作者 | 协作完成时 |
| WorkflowTask | 工作流 | 工作流结束时 |
| MonitorMcpTask | MCP 监控 | 持续运行 |
| DreamTask | 后台思考 | 思考完成时 |
2.2 Actor Model
每个后台任务都是一个独立的 Actor:
┌─────────────────────────────────────────┐
│ Actor 模型 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │ Task 1 │ │ Task 2 │ │ Task 3 │ │
│ │ (Shell) │ │ (Agent) │ │ (MCP) │ │
│ │ │ │ │ │ │ │
│ │ 独立状态 │ │ 独立状态 │ │ 独立状态│ │
│ │ 独立执行 │ │ 独立执行 │ │ 独立执行│ │
│ └────┬─────┘ └────┬─────┘ └───┬────┘ │
│ │ │ │ │
│ └──────────────┼────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ AppState │ │
│ │ (消息总线) │ │
│ └─────────────┘ │
└─────────────────────────────────────────┘
Actor 通信: 任务之间不直接通信,而是通过 AppState 间接协调。
2.3 任务生命周期管理
任务创建
↓
注册到 AppState.tasks
↓
执行中
├── 前台运行: 显示在 REPL 中
└── 后台运行: 显示在 TaskPanel 中
↓
Auto-Background: 超过阈值自动转为后台
↓
任务完成/取消
↓
Grace Period Eviction: 保留 30s 面板宽限期
↓
从 AppState.tasks 移除
Auto-Background: 当任务执行时间超过阈值(如 10 秒),自动从前台转为后台,释放 REPL 给用户继续输入。
Grace Period Eviction: 任务完成后不立即从面板消失,保留 30 秒宽限期,让用户有机会查看结果。
3. AgentTool 子 Agent 管理
AgentTool/ (1397行) 实现了 Claude Code 的子 Agent 创建和管理机制。
3.1 四种运行模式
| 模式 | 说明 | 特点 |
|---|---|---|
| 同步模式 | 等待子 Agent 完成 | 简单直接,阻塞父 Agent |
| 异步模式 | 后台运行 | 不阻塞,通过 TaskPanel 查看 |
| Fork 模式 | 共享 prompt cache | 减少重复计算,性能最优 |
| 远程模式 (Teleport) | 远程执行 | 跨机器/环境执行 |
3.2 Fork 模式详解
Fork 模式是 Claude Code 最精妙的 Agent 协调机制之一:
父 Agent
├── System Prompt (已缓存)
├── 消息历史
└── 工具列表
↓
Fork 子 Agent
├── 共享 System Prompt 缓存 ← 关键优化
├── 独立的消息历史
└── 可能精简的工具列表
性能收益: System Prompt 通常有 ~900 行,通过 prompt cache 可以避免重复计算。Fork 模式让子 Agent 直接复用父 Agent 的缓存。
3.3 递归防护
// AgentTool 递归防护
function createSubAgent(prompt: string) {
// 检查是否在 Agent 内部
if (isInsideAgent()) {
throw new Error(
"AgentTool cannot be called inside another Agent. " +
"This prevents infinite recursion."
)
}
// 创建子 Agent...
}
为什么需要递归防护? 如果子 Agent 可以再创建子 Agent,可能导致:
- 无限递归
- 资源耗尽
- 难以追踪的执行链路
4. Immutable State 模式
AppState 使用 Immutable State 模式,确保状态变更可追踪:
// 不可变状态更新
function updateMessages(state: AppState, newMessage: Message): AppState {
return {
...state,
messages: [...state.messages, newMessage] // 新数组,不修改原数组
}
}
// 使用
const nextState = updateMessages(currentState, userMessage)
store.setState(() => nextState)
// Object.is 检查
if (Object.is(nextState, currentState)) {
// 引用相同,跳过更新
return
}
优势:
- 可追踪: 每次状态变更都产生新对象,可以通过引用比较判断是否变化
- 结构共享:
...state展开操作只复制顶层,嵌套对象共享引用 - React 优化: 引用相等检查避免不必要的重渲染
5. 状态管理架构图
┌─────────────────────────────────────────────────────────────┐
│ 状态管理系统 │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ bootstrap/state.ts (1758行) │ │
│ │ 进程级全局单例 │ │
│ │ │ │
│ │ sessionId | cwd | projectRoot | tokenCount | ... │ │
│ └──────────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼───────────────────────────┐ │
│ │ AppState Store (~34行实现) │ │
│ │ Zustand 风格响应式状态 │ │
│ │ │ │
│ │ messages | tools | permissions | mcp | tasks │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ Object.is(next, state) → 引用相等跳过 │ │ │
│ │ │ 结构共享 → 最小化内存占用 │ │ │
│ │ │ subscribe → React 组件自动更新 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 后台任务系统 (Actor Model) │ │
│ │ │ │
│ │ ShellTask | AgentTask | RemoteTask | WorkflowTask │ │
│ │ MonitorTask | DreamTask | TeammateTask │ │
│ │ │ │
│ │ Auto-Background → Grace Period Eviction │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
设计模式
本章涉及的模式:
| 模式 | 定义 | 应用位置 |
|---|---|---|
| Actor Model | 每个任务是独立 Actor,通过消息总线通信 | 后台任务系统 |
| Immutable State | 结构共享 + 引用相等跳过 | AppState Store |
| Memoized Singleton | 函数级缓存,首次调用执行 I/O | bootstrap/state.ts |
| Grace Period Eviction | 终止后保留宽限期 | 任务完成后的面板显示 |
| Auto-Background | 超过阈值自动转为后台 | 长时间运行的任务 |
| Fork with Shared Cache | 共享 prompt cache 的 Fork 模式 | AgentTool 子 Agent |
源码验证
- ✅ bootstrap/state.ts 1758行:行数确认
- ✅ AppState Store ~34行实现:Zustand 风格确认
- ✅ AgentTool 1397行:行数确认
- ✅ 7 种任务类型:全部在源码中找到
- ✅ 递归防护:isInsideAgent() 检查确认
- ✅ Object.is 引用相等检查:Store 实现确认
思考题
-
bootstrap/state.ts 有 100+ 个字段,这是否过于庞大? 如果要拆分,你会按什么维度拆分?
-
AppState Store 的 34 行实现比 Zustand 精简得多,但它缺少了中间件支持(如 devtools、persist)。在什么场景下需要这些功能?
-
Fork 模式共享 prompt cache,但如果子 Agent 的 System Prompt 与父 Agent 不同(比如子 Agent 有额外的安全约束),还能共享吗?
-
Actor Model 中,任务之间通过 AppState 间接通信。如果两个任务需要实时协调(比如任务 A 的输出是任务 B 的输入),这种间接通信方式够用吗?
-
Auto-Background 的阈值设为 10 秒,如果用户需要看到前 15 秒的输出,自动转为后台会丢失信息吗?