AgentHarness 课程

第七章:状态管理与 Agent 协调

两层状态架构、7种后台任务、AgentTool子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.tsAppState Store
生命周期进程级组件级
访问方式直接 importReact Context
响应式是(触发重渲染)
使用场景非 UI 代码UI 组件
典型字段sessionId, cwd, tokenCountmessages, 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本地子 AgentAgent 完成时
RemoteAgentTask远程 Agent远程会话结束时
InProcessTeammateTask进程内协作者协作完成时
WorkflowTask工作流工作流结束时
MonitorMcpTaskMCP 监控持续运行
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
}

优势:

  1. 可追踪: 每次状态变更都产生新对象,可以通过引用比较判断是否变化
  2. 结构共享: ...state 展开操作只复制顶层,嵌套对象共享引用
  3. 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/Obootstrap/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 实现确认

思考题

  1. bootstrap/state.ts 有 100+ 个字段,这是否过于庞大? 如果要拆分,你会按什么维度拆分?

  2. AppState Store 的 34 行实现比 Zustand 精简得多,但它缺少了中间件支持(如 devtools、persist)。在什么场景下需要这些功能?

  3. Fork 模式共享 prompt cache,但如果子 Agent 的 System Prompt 与父 Agent 不同(比如子 Agent 有额外的安全约束),还能共享吗?

  4. Actor Model 中,任务之间通过 AppState 间接通信。如果两个任务需要实时协调(比如任务 A 的输出是任务 B 的输入),这种间接通信方式够用吗?

  5. Auto-Background 的阈值设为 10 秒,如果用户需要看到前 15 秒的输出,自动转为后台会丢失信息吗?


06-MCP与扩展 | 08-设计模式全景