主题
第 14 章:Claude Code Agent 与编排系统
← 返回目录 | 上一章:Claude Code 概述与定位 | 下一章:Claude Code 工具系统 →
上一章我们从宏观角度了解了 Claude Code 的技术栈和架构布局。本章将深入其核心:查询循环、工具编排、任务系统和 Coordinator 模式。这些机制共同构成了 Claude Code 的"大脑",决定了它如何接收指令、调度工具、管理上下文,并最终完成复杂的编程任务。
14.1 查询循环(Query Loop):核心引擎
Claude Code 的所有智能行为都源自一个函数:query.ts 中的 query()。这个文件长达 1729 行,是整个系统最核心的模块。
14.1.1 AsyncGenerator 架构
query() 被设计为一个 AsyncGenerator 函数,它不是一次性返回结果,而是通过 yield 持续向调用方输出流式事件:
typescript
export async function* query(params: QueryParams):
AsyncGenerator<StreamEvent | Message | TombstoneMessage> {
const terminal = yield* queryLoop(params, consumedCommandUuids)
return terminal
}query() 本身是一个薄包装层,真正的逻辑在 queryLoop() 中。这个函数内部是一个 while (true) 无限循环,每一轮迭代对应一次与 Claude API 的交互。
14.1.2 循环状态机
每轮迭代共享一个可变的 State 对象:
typescript
type State = {
messages: Message[] // 完整的对话历史
toolUseContext: ToolUseContext // 工具执行上下文
autoCompactTracking: ... // 自动压缩追踪
maxOutputTokensRecoveryCount: number // 输出超限恢复计数
turnCount: number // 当前轮次
transition: Continue | undefined // 上一轮为何继续(调试用)
// ...
}每轮迭代的执行流水线如下:
Snip(历史裁剪) → Microcompact(工具结果压缩)
→ Context Collapse(上下文折叠) → Autocompact(全量摘要)
→ API 调用 → 流式接收响应 → 工具执行 → 判断继续/终止如果模型的响应包含 tool_use 块,循环继续下一轮;如果没有,循环终止并返回最终结果。
14.1.3 与 Opencode 和 OmO 的对比
Opencode 的 Agent 循环位于 session/processor.ts,逻辑类似但简洁得多:发送消息、等待响应、执行工具、重复。它没有多层压缩、没有流式工具执行、没有特性开关驱动的分支逻辑。
OmO 在 Opencode 的循环之上增加了异步后台任务层,但底层的单轮执行逻辑并未改变。Claude Code 则从根本上重新设计了这个循环,使其成为一个功能完备的状态机,内置了错误恢复、上下文管理和预算追踪等能力。
| 特性 | Opencode | OmO | Claude Code |
|---|---|---|---|
| 循环类型 | 同步 while 循环 | 同步循环 + 异步任务层 | AsyncGenerator 状态机 |
| 上下文压缩 | 单次 compaction | 单次 compaction | 四层压缩流水线 |
| 工具执行 | 逐个串行 | Agent 级并行 | 工具级流式并行 |
| 错误恢复 | 基础重试 | 基础重试 | 多策略恢复(reactive compact 等) |
| Token 预算 | 无 | 无 | 内置预算追踪器 |
14.2 工具编排:流式并行执行
在传统的 AI Agent 实现中,工具执行通常是串行的:模型生成一个 tool_use 块,系统执行它,再把结果送回模型。Claude Code 打破了这个范式,引入了两层并行机制。
14.2.1 分区策略
toolOrchestration.ts 中的 partitionToolCalls() 函数负责将模型返回的一组 tool_use 块分区为多个批次(Batch):
typescript
type Batch = { isConcurrencySafe: boolean; blocks: ToolUseBlock[] }分区规则非常直观:连续的只读工具(如文件读取、搜索)被归入同一个并发批次;遇到非只读工具(如文件写入、Shell 命令)则单独成为一个批次,串行执行。这种设计确保了读操作的高吞吐量,同时保证写操作的顺序一致性。
并发上限通过环境变量 CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY 控制,默认值为 10。
14.2.2 流式工具执行器
更激进的优化来自 StreamingToolExecutor(530 行)。传统做法是等模型的完整响应结束后再执行工具,而这个执行器在模型流式输出的过程中就开始执行已完成的工具块:
typescript
class StreamingToolExecutor {
private tools: TrackedTool[] = []
// 模型每输出一个完整的 tool_use 块,立刻加入执行队列
addTool(block: ToolUseBlock, assistantMessage: AssistantMessage): void
// 收集已完成工具的结果,按原始顺序输出
async *getRemainingResults(): AsyncGenerator<MessageUpdate>
}每个工具经历四个状态:queued → executing → completed → yielded。并发安全的工具可以与其他并发安全工具并行执行;非并发安全工具则获得独占访问权。
一个精巧的细节是"兄弟中止控制器"(sibling abort controller):它是 toolUseContext.abortController 的子控制器。当一个 Bash 工具报错时,其他正在执行的兄弟子进程会被立即终止,避免浪费资源。但这个中止不会向上传播到父控制器,所以不会结束当前轮次。
14.2.3 三系统的工具执行对比
Opencode 的工具执行是最朴素的形式:一个工具执行完毕,才执行下一个。OmO 通过后台任务系统实现了 Agent 级别的并行(多个 Agent 同时工作),但单个 Agent 内部的工具执行仍然是串行的。
Claude Code 的创新在于将并行下沉到了工具级别。模型说"同时读这 5 个文件",系统真的会并行读取,而不是排队等待。这种设计在处理大型代码库时的性能优势尤为明显。
14.3 七大任务类型
Claude Code 通过 tasks/types.ts 定义了一个统一的 TaskState 联合类型,涵盖了所有可能的计算单元。每种任务类型都有独立的生命周期管理和状态追踪。
14.3.1 LocalShellTask(local_bash)
最基础的任务类型。直接在本地终端执行 Shell 命令,管理子进程的生命周期。支持前台/后台切换,用户可以将一个长时间运行的 Shell 任务放到后台继续执行。
14.3.2 LocalAgentTask(local_agent)
本地后台智能体。它拥有自己独立的查询循环(通过 runAgent() 启动),在后台自主执行任务。LocalAgentTask.tsx(683 行)实现了细致的进度追踪:
typescript
type ProgressTracker = {
toolUseCount: number // 工具调用次数
latestInputTokens: number // 最新输入 token(累计值)
cumulativeOutputTokens: number // 累计输出 token
recentActivities: ToolActivity[] // 最近 5 个工具活动
}每个工具活动(ToolActivity)不仅记录工具名和输入参数,还通过 ActivityDescriptionResolver 生成人类可读的描述(如"Reading src/foo.ts"),这些信息会实时显示在 UI 的任务进度面板中。
14.3.3 RemoteAgentTask(remote_agent)
远程智能体任务,在 Anthropic 的云端环境(CCR,Claude Code Remote)中执行。这是 Claude Code 最复杂的任务类型之一,RemoteAgentTask.tsx 长达 856 行。
它支持五种远程任务子类型:
| 子类型 | 用途 |
|---|---|
remote-agent | 通用远程 Agent |
ultraplan | 远程规划,支持浏览器审批流程 |
ultrareview | 远程代码审查 |
autofix-pr | 自动修复 PR 问题 |
background-pr | 后台 PR 处理 |
远程任务通过 WebSocket 轮询(pollRemoteSessionEvents)获取执行状态。RemoteTaskCompletionChecker 模式允许为不同子类型注册自定义的完成检测逻辑。任务的元数据会持久化到磁盘,确保会话恢复时不会丢失正在运行的远程任务。
14.3.4 InProcessTeammateTask(in_process_teammate)
这是 Claude Code 最独特的任务类型。与 LocalAgentTask 在独立进程中运行不同,InProcessTeammate 运行在同一个 Node.js 进程中,通过 AsyncLocalStorage 实现隔离。
每个队友都有团队感知的身份标识:
typescript
type TeammateIdentity = {
agentId: string // 如 "researcher@my-team"
agentName: string // 如 "researcher"
teamName: string
planModeRequired: boolean
parentSessionId: string
}这种设计的优势是通信延迟极低(进程内函数调用),劣势是所有队友共享同一个进程的内存空间。源码注释中记录了一个真实的生产事故:某个会话(9a990de8)在 2 分钟内启动了 292 个 Agent,内存峰值达到 36.8GB。为此,团队引入了 TEAMMATE_MESSAGES_UI_CAP = 50 的消息上限,只在 AppState 中保留最近 50 条消息用于 UI 显示,完整对话历史则存储在磁盘上。
InProcessTeammate 还支持计划模式审批(awaitingPlanApproval)和空闲/活跃的生命周期切换,使其在需要人工介入审查的工作流中也能流畅运作。
14.3.5 其他任务类型
LocalWorkflowTask:执行预定义的本地工作流序列,通常是针对特定场景(如代码重构、文档生成)优化的多步骤指令。
MonitorMcpTask:监控 MCP 服务器的连接状态和工具可用性,确保外部工具源的稳定集成。
DreamTask:最富想象力的一种任务类型,用于"记忆整理"。它是一个后台运行的记忆巩固子 Agent,分为 starting(启动)和 updating(更新记忆文件)两个阶段。DreamTask 会回顾历史会话,提炼出值得长期记住的信息,并写入 MEMORY.md 文件。它追踪已审阅的会话数、修改的文件列表和最近 30 轮的对话摘要。
14.3.6 任务系统对比
Opencode 只有一种任务形态:通过 Task 工具同步调用子 Agent,父 Agent 阻塞等待结果。OmO 在此基础上引入了后台任务和 category 分类系统,但任务类型本身没有分化。
Claude Code 的七种任务类型体现了一种"为每种计算场景定制运行时"的理念。本地 Shell、本地 Agent、远程 Agent、进程内队友,各自有最适合的执行策略和资源管理方式。这种精细的分化使得系统能在不同场景下做出最优的调度决策。
14.4 Coordinator 模式:多 Worker 编排
Coordinator 模式是 Claude Code 最接近 OmO Sisyphus 编排器的功能,但它的实现路径截然不同。
14.4.1 触发与切换
当环境变量 CLAUDE_CODE_COORDINATOR_MODE 被设为真值时,Claude Code 的主 Agent 从"全能执行者"变为"纯编排者"。这个切换是运行时的,不需要重启:
typescript
export function isCoordinatorMode(): boolean {
if (feature('COORDINATOR_MODE')) {
return isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)
}
return false
}系统还会追踪会话的模式状态。当恢复一个旧会话时,matchSessionMode() 会自动将当前模式切换到会话创建时的模式,避免 Coordinator 会话在普通模式下被恢复(或反过来)。
14.4.2 Coordinator 的行为规范
进入 Coordinator 模式后,主 Agent 的系统提示词被完全替换为 getCoordinatorSystemPrompt() 生成的专用提示词。这段提示词定义了严格的行为规范:
角色定位:Coordinator 只做三件事:与用户沟通、指挥 Worker、综合结果。它不直接使用文件读写工具,所有实际工作都委派给 Worker。
可用工具:仅限 AgentTool(创建 Worker)、SendMessageTool(向已有 Worker 发送后续指令)和 TaskStopTool(终止 Worker)。
工作流阶段:
| 阶段 | 执行者 | 目标 |
|---|---|---|
| Research | Worker(并行) | 调查代码库,理解问题 |
| Synthesis | Coordinator | 阅读 Worker 发现,制定实施方案 |
| Implementation | Worker | 按方案修改代码,提交 |
| Verification | Worker | 测试验证改动是否正确 |
其中 Synthesis 阶段是 Coordinator 最核心的职责。系统提示词中明确要求:不允许写"based on your findings"这类偷懒的委派语句。Coordinator 必须阅读 Worker 的研究结果,理解问题本质,然后用具体的文件路径、行号和修改方案来指导下一步工作。
14.4.3 Worker 上下文隔离
一个关键的设计约束是:Worker 看不到 Coordinator 与用户的对话。每次创建 Worker 或发送后续消息时,提示词必须是自包含的,包含 Worker 完成任务所需的全部信息。
Worker 可用的工具列表由 ASYNC_AGENT_ALLOWED_TOOLS 常量定义,并过滤掉内部工具(TeamCreate、TeamDelete、SendMessage、SyntheticOutput)。如果有 MCP 服务器连接,Worker 也能访问对应的 MCP 工具。
Coordinator 模式还支持 Scratchpad 目录,这是一个共享的临时文件目录,Worker 可以在其中读写文件而无需权限确认。这为跨 Worker 的知识传递提供了一个轻量级的持久化通道。
14.4.4 Continue vs Spawn 决策
系统提示词中详细规定了何时继续使用现有 Worker(通过 SendMessageTool),何时创建新 Worker:
- Continue:Worker 之前探索的正好是需要修改的文件;纠正失败或延续近期工作
- Spawn fresh:研究范围广但实现范围窄(避免拖入无关上下文);验证他人代码(需要新鲜视角);第一次实现方向完全错误(避免锚定效应)
决策依据是上下文重叠度:重叠高则继续,重叠低则重新创建。
14.4.5 与 OmO Sisyphus 的对比
OmO 的 Sisyphus 编排器和 Claude Code 的 Coordinator 模式解决的是同一个问题:如何让一个 AI 指挥其他 AI 高效协作。但两者的实现哲学大相径庭:
| 维度 | OmO Sisyphus | Claude Code Coordinator |
|---|---|---|
| 编排逻辑 | 嵌入在系统提示词中的行为指令 | 独立的运行时模式,替换整个系统提示词 |
| Worker 类型 | 按 Category 分类(visual、ultrabrain 等),每类对应不同模型 | 统一的 Worker 类型,使用默认模型 |
| 探索策略 | 专用的 Explore/Librarian 侦察 Agent | Worker 既能探索也能实现 |
| 知识传递 | 通过 session_id 延续对话上下文 | 通过 Scratchpad 目录或 SendMessage 继续 |
| 计划审查 | Metis(分析需求)→ Momus(审查计划) | Coordinator 自身负责 Synthesis 阶段 |
简言之,OmO 倾向于"专人专事",为每种职能配备专属 Agent;Claude Code 倾向于"通用 Worker + 精确指令",让 Coordinator 的提示词质量决定最终效果。
14.5 上下文压缩层级
长时间的编程会话会不断积累对话历史,最终超出模型的上下文窗口。Claude Code 为此设计了一套多层级的压缩体系,每一层针对不同的场景和粒度。
14.5.1 第一层:Snip(历史裁剪)
特性开关 HISTORY_SNIP 控制。snipCompact.ts 负责对过早的历史消息进行轻量级裁剪。它是最快的压缩手段,直接移除旧消息,返回释放的 token 数量(tokensFreed)。裁剪后如果 token 数降到阈值以下,后续更重的压缩步骤可以跳过。
14.5.2 第二层:Microcompact(工具结果压缩)
针对工具调用结果的精细压缩。工具返回的结果(如完整的文件内容、Shell 输出)通常占据大量 token,但对后续推理的价值递减。Microcompact 按 tool_use_id 逐条压缩这些结果,保留关键信息。
它还有一个缓存感知的变体(特性开关 CACHED_MICROCOMPACT),在压缩时考虑 Anthropic API 的提示缓存机制,避免因内容变化导致缓存失效。
14.5.3 第三层:Context Collapse(上下文折叠)
特性开关 CONTEXT_COLLAPSE 控制。这是一种介于 Microcompact 和 Autocompact 之间的策略。它将一组相关消息折叠为摘要,但以"提交日志"的方式管理这些折叠操作,使得折叠可以在多轮迭代间持久化。applyCollapsesIfNeeded() 在每轮迭代时重放折叠日志,确保投影视图的一致性。
Context Collapse 在 Autocompact 之前运行。如果折叠就能将 token 数控制在阈值内,Autocompact 就无需触发,从而保留更细粒度的上下文信息。
14.5.4 第四层:Autocompact(全量摘要)
当 token 数超过 上下文窗口 - 13000(AUTOCOMPACT_BUFFER_TOKENS) 时触发。autoCompact.ts 会调用 compactConversation() 将整个对话历史压缩为一段简洁的摘要。
为防止摘要本身过长,系统预留了 20000 个 token 的输出空间。如果 Autocompact 连续失败 3 次(MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES),触发断路器停止重试,避免在不可恢复的情况下浪费 API 调用。
14.5.5 第五层:Reactive Compact(响应式压缩)
最后一道防线。特性开关 REACTIVE_COMPACT 控制。它并非按阈值提前触发,而是在 API 返回 prompt_too_long 错误时才启动。这意味着前四层的预防措施都未能阻止超限,系统需要紧急响应。
14.5.6 压缩体系对比
Opencode 和 OmO 都只有单一的 compaction 机制:一个专用的 compaction Agent 将对话压缩为摘要。这种方式简单直接,但颗粒度粗,且只有一种触发条件。
Claude Code 的五层压缩体系体现了"渐进式降级"的思想:优先使用轻量级手段(Snip、Microcompact),保留尽可能多的原始上下文;只有在轻量手段不够时才动用重型压缩(Autocompact);最后保留一个紧急恢复机制(Reactive Compact)。这种设计在保持上下文质量和控制 token 成本之间取得了精妙的平衡。
14.6 Agent 运行器(runAgent)
所有子 Agent 的实际执行都汇聚到 tools/AgentTool/runAgent.ts 这个 973 行的核心函数。无论是 LocalAgentTask、InProcessTeammateTask 还是 Coordinator 模式下的 Worker,最终都通过 runAgent() 启动。
14.6.1 隔离与递归
runAgent() 为每个子 Agent 创建独立的执行上下文:
- 独立的
AbortController(可以单独终止子 Agent 而不影响父级) - 独立的工具集合(根据 Agent 定义和权限模式筛选)
- 独立的权限模式(Worker 的权限不继承父 Agent 的临时授权)
- 独立的 transcript 路径(每个子 Agent 的对话记录保存在单独的文件中)
最关键的是,runAgent() 本身也是一个 AsyncGenerator,它内部调用的正是 query() 函数。这构成了一个递归架构:主查询循环通过工具调用启动子 Agent,子 Agent 运行自己的查询循环,子 Agent 甚至可以再启动孙 Agent。整个系统是自相似的。
14.6.2 参数丰富度
runAgent() 的参数列表反映了 Claude Code 对子 Agent 行为的精细控制能力:
agentDefinition:Agent 的定义(系统提示词、工具、模型)model:可选的模型覆盖maxTurns:最大轮次限制worktreePath:Git worktree 隔离路径(支持在独立分支中工作)allowedTools:工具白名单(替换而非追加父级权限)contentReplacementState:内容替换状态(用于恢复中断的 Agent 会话)preserveToolUseResults:是否保留工具结果(InProcessTeammate 需要,因为用户可以查看其 transcript)
与 Opencode 的 Task 工具相比,差异非常明显。Opencode 的 Task 工具只需三个参数:Agent 名称、提示词、和一个可选的附件。Claude Code 的 runAgent() 则提供了近 20 个参数,涵盖了从资源隔离到缓存策略的方方面面。
14.7 小结
Claude Code 的 Agent 与编排系统是一个精心设计的多层架构:
- 查询循环是一切的基础,一个带状态的 AsyncGenerator 无限循环
- 工具编排将并行下沉到工具级别,通过流式执行最大化吞吐量
- 七大任务类型为不同计算场景提供定制化的运行时
- Coordinator 模式将主 Agent 提升为纯编排者,形式化了多 Worker 协作流程
- 五层压缩体系确保长时间会话的上下文质量
- runAgent 递归架构使整个系统具备无限嵌套的子 Agent 能力
如果说 Opencode 的 Agent 系统是一辆手动挡汽车(简单、可控、透明),OmO 给它加上了自动变速箱和巡航控制(后台任务、分类调度),那么 Claude Code 就是一辆自动驾驶汽车(流式并行、多层压缩、形式化编排)。三者各有适用场景,但 Claude Code 在系统复杂度和工程投入上显然领先一个量级。
下一章我们将深入 Claude Code 的工具系统,看看那 50 多个工具是如何通过统一的 buildTool 工厂函数构建,以及权限模型如何在安全性和便利性之间取得平衡。