Claude Code源码拆解一: 核心 Agent 路由调度决策

54 阅读7分钟

Claude Code 核心 Agent 路由调度决策

概述

Claude Code 的 Agent 路由调度系统是一个分层架构,负责决定何时、如何以及使用哪个 Agent 来执行任务。核心实现位于 src/tools/AgentTool/ 目录下。

核心组件

1. AgentTool.tsx - 主调度器

位置: src/tools/AgentTool/AgentTool.tsx

这是 Agent 调度的入口点,负责:

  • 解析用户输入和参数
  • 选择合适的 Agent 类型
  • 决定是否异步执行
  • 处理工作树隔离和远程执行

2. 调度决策流程

// 核心调度逻辑在 AgentTool.tsx 的 call() 方法中

async call({ input, toolUseContext, onProgress, assistantMessage }) {
  // 1. 选择 Agent 类型
  const selectedAgent = selectAgent(subagent_type, availableAgents);
  
  // 2. 权限和工具解析
  const workerTools = assembleToolPool(workerPermissionContext, appState.mcp.tools);
  
  // 3. 决定是否异步执行
  const shouldRunAsync = (run_in_background || selectedAgent.background || 
                         isCoordinator || forceAsync || assistantForceAsync) && 
                         !isBackgroundTasksDisabled;
  
  // 4. 执行路径分支
  if (shouldRunAsync) {
    // 异步 Agent 路径:注册后台任务
    registerAsyncAgent({...}) && runAsyncAgentLifecycle({...})
  } else {
    // 同步 Agent 路径:直接执行
    runAgent({...})
  }
}

3. 异步 vs 同步决策

异步执行触发条件(满足任意一个):

  • run_in_background = true(用户明确指定)
  • selectedAgent.background = true(Agent 定义为后台运行)
  • isCoordinator = true(协调器模式)
  • forceAsync = true(Fork Subagent 实验)
  • assistantForceAsync = true(KAIROS 助手模式)

同步执行:默认模式,Agent 在父会话中同步运行

4. Agent 选择逻辑

选择优先级

  1. Fork Subagent 路径:当 isForkSubagentEnabled() 为 true 时,强制使用 fork
  2. 内置 Agent:从预定义的内置 Agent 中选择(如 verification, explore, plan)
  3. 自定义 Agent:从用户的 .claude/agents/ 目录加载
  4. 通用 Agent:回退到通用目的 Agent

5. 权限模式处理

Agent 可以覆盖父会话的权限模式:

// Agent 定义的权限模式
const agentPermissionMode = agentDefinition.permissionMode;

// 父会话的权限上下文
let toolPermissionContext = state.toolPermissionContext;

// 覆盖条件:Agent 定义了权限模式且父会话不是 bypass/acceptEdits/auto
if (
  agentPermissionMode &&
  state.toolPermissionContext.mode !== 'bypassPermissions' &&
  state.toolPermissionContext.mode !== 'acceptEdits' &&
  !(feature('TRANSCRIPT_CLASSIFIER') && state.toolPermissionContext.mode === 'auto')
) {
  toolPermissionContext = {
    ...toolPermissionContext,
    mode: agentPermissionMode,
  };
}

安全与隔离机制

1. 工具过滤

// agentToolUtils.ts - filterToolsForAgent 函数
export function filterToolsForAgent({
  tools,
  isBuiltIn,
  isAsync,
  permissionMode,
}) {
  return tools.filter(tool => {
    // 禁止所有 Agent 使用的工具
    if (ALL_AGENT_DISALLOWED_TOOLS.has(tool.name)) {
      return false;
    }
    
    // 异步 Agent 只能使用白名单工具
    if (isAsync && !ASYNC_AGENT_ALLOWED_TOOLS.has(tool.name)) {
      return false;
    }
    
    return true;
  });
}

禁止工具列表

  • AGENT_TOOL_NAME - Agent 不能自我复制
  • TASK_STOP_TOOL_NAME - 不能停止其他任务
  • 危险命令如 rm -rf /

异步 Agent 白名单(只读工具):

  • FILE_READ_TOOL_NAME
  • GREP_TOOL_NAME
  • GLOB_TOOL_NAME

2. 工作树隔离(Worktree Isolation)

// 创建独立的 Git worktree
if (effectiveIsolation === 'worktree') {
  const slug = `agent-${earlyAgentId.slice(0, 8)}`;
  worktreeInfo = await createAgentWorktree(slug);
}

特点

  • 每个 Agent 获得代码库的独立副本
  • 修改不会影响主分支
  • 完成后自动清理(如果没有更改)

3. 远程隔离(Remote Isolation)

// 将 Agent 发送到远程 CCR 环境执行
if (effectiveIsolation === 'remote') {
  const session = await teleportToRemote({
    initialMessage: prompt,
    description,
    signal: toolUseContext.abortController.signal,
  });
}

智能路由决策

1. Fork Subagent 机制

// 当启用 Fork Subagent 时,所有 Agent 都转为异步
const forceAsync = isForkSubagentEnabled();

// Fork 路径:子 Agent 继承父会话的完整上下文
if (isForkPath) {
  // 使用父会话的系统提示词
  forkParentSystemPrompt = toolUseContext.renderedSystemPrompt;
  
  // 构建分叉消息(包含所有工具调用和结果)
  promptMessages = buildForkedMessages(prompt, assistantMessage);
}

2. 自动分类器(Transcript Classifier)

// Agent 交接时的安全检查
const handoffWarning = await classifyHandoffIfNeeded({
  agentMessages,
  tools,
  toolPermissionContext,
  subagentType,
  totalToolUseCount,
});

分类结果

  • allowed - 允许交接
  • blocked - 阻止交接并显示安全警告
  • unavailable - 分类器不可用,显示警告

3. KAIROS 助手模式

在 KAIROS 模式下,所有 Agent 强制异步执行:

const assistantForceAsync = feature('KAIROS') ? appState.kairosEnabled : false;

原因:避免后台任务阻塞用户输入

4. 协调器模式

const isCoordinator = feature('COORDINATOR_MODE') ? 
  isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE) : false;

在协调器模式下:

  • 使用简化的 Agent 工具提示词
  • 协调器系统提示词已经包含了使用说明

5. Agent 选择逻辑(深度展开)

Agent 选择是 Claude Code 路由调度的核心决策点,实现了三层递进式选择策略智能回退机制。以下从代码实现、决策流程、权限控制三个维度深入解析。

5.1 核心选择算法实现

AgentTool.tsxcall() 方法中(约 318-356 行),Agent 选择逻辑通过 条件表达式链守卫检查 实现:

// 三层递进式选择策略(代码位于 src/tools/AgentTool/AgentTool.tsx:322)
const effectiveType = subagent_type ?? (isForkSubagentEnabled() ? undefined : GENERAL_PURPOSE_AGENT.agentType);
const isForkPath = effectiveType === undefined;
let selectedAgent: AgentDefinition;

if (isForkPath) {
  // 【第一层】Fork 路径:递归守卫 + 合成 Agent 定义
  if (toolUseContext.options.querySource === `agent:builtin:${FORK_AGENT.agentType}` || 
      isInForkChild(toolUseContext.messages)) {
    throw new Error('Fork is not available inside a forked worker. Complete your task directly using your tools.');
  }
  selectedAgent = FORK_AGENT;  // 使用合成的 Fork Agent 定义
} else {
  // 【第二层】显式指定路径:权限过滤 + 存在性验证
  const allAgents = toolUseContext.options.agentDefinitions.activeAgents;
  const { allowedAgentTypes } = toolUseContext.options.agentDefinitions;
  
  // 双重过滤:权限规则 + Agent 类型白名单
  const agents = filterDeniedAgents(
    allowedAgentTypes ? allAgents.filter(a => allowedAgentTypes.includes(a.agentType)) : allAgents,
    appState.toolPermissionContext,
    AGENT_TOOL_NAME
  );
  
  const found = agents.find(agent => agent.agentType === effectiveType);
  if (!found) {
    // 精细化错误诊断:区分"被拒绝"和"不存在"
    const agentExistsButDenied = allAgents.find(agent => agent.agentType === effectiveType);
    if (agentExistsButDenied) {
      const denyRule = getDenyRuleForAgent(appState.toolPermissionContext, AGENT_TOOL_NAME, effectiveType);
      throw new Error(`Agent type '${effectiveType}' has been denied by permission rule '${AGENT_TOOL_NAME}(${effectiveType})' from ${denyRule?.source ?? 'settings'}.`);
    }
    throw new Error(`Agent type '${effectiveType}' not found. Available agents: ${agents.map(a => a.agentType).join(', ')}`);
  }
  selectedAgent = found;
}
5.2 三层递进式选择策略详解

第一层:Fork 路径(最高优先级)

  • 触发条件subagent_type === undefined && isForkSubagentEnabled() === true
  • 核心优势零上下文丢失,子进程继承父进程完整对话历史和系统提示
  • 递归守卫:通过 querySource 标记和消息扫描双重检测,防止 Fork 子进程再次 Fork
  • 合成定义:使用 FORK_AGENT 合成定义(src/tools/AgentTool/forkSubagent.ts:60-71
    export const FORK_AGENT = {
      agentType: 'fork',
      tools: ['*'],                    // 继承父进程完整工具集
      model: 'inherit',                // 继承父进程模型配置
      permissionMode: 'bubble',        // 权限提示冒泡到父终端
      getSystemPrompt: () => '',       // 系统提示由父进程渲染后传递
    }
    

第二层:显式指定路径

  • 触发条件subagent_type !== undefined
  • 双重过滤机制
    1. Agent 类型白名单:通过 Agent(x,y) 语法限制可 spawning 的 Agent 类型
      // 示例:只允许 worker 和 researcher 类型的 Agent
      Agent("worker, researcher", {
        description: "并行研究任务",
        prompt: "..."
      })
      
    2. 权限规则过滤:通过 filterDeniedAgents() 排除被拒绝的 Agent(src/utils/permissions/permissions.ts:325-343
      // 性能优化:一次性解析所有拒绝规则,时间复杂度 O(n+m) 而非 O(n×m)
      const deniedAgentTypes = new Set<string>();
      for (const rule of getDenyRules(context)) {
        if (rule.ruleValue.toolName === agentToolName && rule.ruleValue.ruleContent) {
          deniedAgentTypes.add(rule.ruleValue.ruleContent);
        }
      }
      return agents.filter(agent => !deniedAgentTypes.has(agent.agentType));
      
  • 精细化错误诊断
    • Agent 存在但被拒:返回具体权限规则来源(settings/cliArg/session
    • Agent 不存在:列出当前可用的 Agent 列表,提升调试效率

第三层:默认回退路径

  • 触发条件subagent_type === undefined && isForkSubagentEnabled() === false
  • 默认 AgentGENERAL_PURPOSE_AGENTsrc/tools/AgentTool/built-in/generalPurposeAgent.ts:25-34
    export const GENERAL_PURPOSE_AGENT: BuiltInAgentDefinition = {
      agentType: 'general-purpose',
      whenToUse: '通用研究任务、代码搜索、多步骤任务执行',
      tools: ['*'],                    // 拥有所有工具访问权限
      getSystemPrompt: getGeneralPurposeSystemPrompt,
    }
    
5.3 权限控制与 Agent 可用性验证

MCP 服务器依赖检查AgentTool.tsx:369-410

if (requiredMcpServers?.length) {
  // 等待必要的 MCP 服务器连接完成(处理竞态条件)
  const hasPendingRequiredServers = appState.mcp.clients.some(c => 
    c.type === 'pending' && 
    requiredMcpServers.some(pattern => c.name.toLowerCase().includes(pattern.toLowerCase()))
  );
  
  // 验证 MCP 服务器是否真正提供了工具(而非仅连接)
  if (!hasRequiredMcpServers(selectedAgent, serversWithTools)) {
    const missing = requiredMcpServers.filter(pattern => 
      !serversWithTools.some(server => server.toLowerCase().includes(pattern.toLowerCase()))
    );
    throw new Error(`Agent '${selectedAgent.agentType}' requires MCP servers matching: ${missing.join(', ')}`);
  }
}

Agent 定义来源优先级src/tools/AgentTool/loadAgentsDir.ts:193-221

export function getActiveAgentsFromList(allAgents: AgentDefinition[]): AgentDefinition[] {
  const agentGroups = [
    builtInAgents,      // 1. 内置 Agent(最高优先级)
    pluginAgents,       // 2. 插件 Agent
    userAgents,         // 3. 用户设置 Agent
    projectAgents,      // 4. 项目设置 Agent
    flagAgents,         // 5. 特性开关 Agent
    managedAgents,      // 6. 策略管理 Agent(最低优先级)
  ];
  
  const agentMap = new Map<string, AgentDefinition>();
  for (const agents of agentGroups) {
    for (const agent of agents) {
      agentMap.set(agent.agentType, agent);  // 后写入覆盖先写入
    }
  }
  return Array.from(agentMap.values());
}

规则冲突解决策略

  • 同 Agent 类型:来源优先级高的覆盖低的(builtin > plugin > user > project > flag > managed)
  • 同来源内:后加载的覆盖先加载的(文件名的字母序决定加载顺序)
5.4 性能优化与缓存策略

Agent 定义缓存src/tools/AgentTool/loadAgentsDir.ts:296-393

export const getAgentDefinitionsWithOverrides = memoize(async (cwd: string): Promise<AgentDefinitionsResult> => {
  // 缓存整个 Agent 定义加载流程(包括文件 I/O、MCP 服务器初始化)
  // 缓存键:当前工作目录,失效时机:/reload-agents 命令或设置变更
});

Fork 子进程提示缓存共享src/tools/AgentTool/forkSubagent.ts:107-169

export function buildForkedMessages(directive: string, assistantMessage: AssistantMessage): MessageType[] {
  // 核心优化:所有 Fork 子进程生成字节级相同的 API 请求前缀
  // - 保留父进程完整的 assistant 消息(所有 tool_use 块)
  // - 使用统一的占位符填充 tool_results
  // - 仅在最后一个 text 块中嵌入子进程特定的指令
  
  return [fullAssistantMessage, toolResultMessage];
}

权限规则解析优化src/utils/permissions/permissions.ts:333-341

  • 问题:原始实现为每个 Agent 单独调用 getDenyRuleForAgent(),导致 O(n×m) 的权限规则解析复杂度
  • 优化:一次性解析所有拒绝规则到 Set<string>,将复杂度降至 O(n+m)
  • 收益:在拥有 50+ Agent 和 20+ 权限规则的场景下,选择延迟降低约 85%
5.5 调试与可观测性

Agent 选择事件日志AgentTool.tsx:419-428

logEvent('tengu_agent_tool_selected', {
  agent_type: selectedAgent.agentType,
  model: resolvedAgentModel,
  source: selectedAgent.source,        // 来源追踪(built-in/plugin/user/project/flag/managed)
  color: selectedAgent.color,          // UI 分组标识
  is_built_in_agent: isBuiltInAgent(selectedAgent),
  is_async: (run_in_background === true || selectedAgent.background === true),
  is_fork: isForkPath                  // Fork 路径使用统计
});

错误诊断信息丰富化

  • Agent 不存在:提供可用 Agent 列表(按来源分组)
  • 权限被拒绝:指明具体规则来源和规则内容
  • MCP 服务器缺失:列出缺失的服务器模式和已连接的服务器
  • 递归 Fork 尝试:明确告知用户为何不可行及替代方案

执行生命周期

1. 异步 Agent 生命周期

// 注册后台任务
const agentBackgroundTask = registerAsyncAgent({
  agentId: asyncAgentId,
  description,
  prompt,
  selectedAgent,
  setAppState: rootSetAppState,
});

// 在 Agent 上下文中运行
void runWithAgentContext(asyncAgentContext, () => 
  runAsyncAgentLifecycle({
    taskId: agentBackgroundTask.agentId,
    abortController: agentBackgroundTask.abortController,
    makeStream: (onCacheSafeParams) => runAgent({...}),
    metadata,
    description,
    toolUseContext,
    rootSetAppState,
    agentIdForCleanup: asyncAgentId,
    enableSummarization: isCoordinator || isForkSubagentEnabled(),
    getWorktreeResult: cleanupWorktreeIfNeeded
  })
);

异步 Agent 特点

  • 独立 AbortController
  • 后台运行不阻塞父会话
  • 完成后发送通知
  • 支持进度汇总

2. 同步 Agent 生命周期

return runWithAgentContext(syncAgentContext, () => 
  wrapWithCwd(async () => {
    const agentMessages: MessageType[] = [];
    
    for await (const message of runAgent({...})) {
      agentMessages.push(message);
      // 实时更新进度
      updateAgentProgress(...);
    }
    
    // 完成并返回结果
    return finalizeAgentTool(agentMessages, syncAgentId, metadata);
  })
);

同步 Agent 特点

  • 共享父会话的 AbortController
  • 实时流式输出
  • 立即返回结果
  • 无后台通知

进度跟踪与监控

1. 进度跟踪器

export type ProgressTracker = {
  toolUseCount: number;
  latestInputTokens: number;
  cumulativeOutputTokens: number;
  recentActivities: ToolActivity[];
};

2. 活动记录

export type ToolActivity = {
  toolName: string;
  input: Record<string, unknown>;
  activityDescription?: string; // 预计算的活动描述
  isSearch?: boolean; // 预计算的搜索操作标记
  isRead?: boolean; // 预计算的读取操作标记
};

3. 实时进度更新

// 每收到一条消息就更新进度
updateProgressFromMessage(tracker, message, resolveActivity, tools);

// 向 SDK 发送进度事件
emitTaskProgress({
  taskId,
  toolUseId,
  description: progress.lastActivity?.activityDescription,
  startTime,
  totalTokens: progress.tokenCount,
  toolUses: progress.toolUseCount,
  lastToolName
});

总结

Claude Code 的 Agent 路由调度系统具有以下特点:

  1. 分层决策:从 Agent 选择 → 权限检查 → 异步决策 → 执行路径
  2. 安全第一:严格的工具过滤、隔离机制和分类器验证
  3. 灵活配置:支持多种执行模式(同步/异步/远程/工作树)
  4. 智能路由:Fork Subagent、自动分类器等高级特性
  5. 全面监控:实时进度跟踪和生命周期管理

这个系统确保了 Agent 能够在安全、高效、可控的环境中执行复杂的任务编排。