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 选择逻辑
选择优先级:
- Fork Subagent 路径:当
isForkSubagentEnabled()为 true 时,强制使用 fork - 内置 Agent:从预定义的内置 Agent 中选择(如 verification, explore, plan)
- 自定义 Agent:从用户的
.claude/agents/目录加载 - 通用 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_NAMEGREP_TOOL_NAMEGLOB_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.tsx 的 call() 方法中(约 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 - 双重过滤机制:
- Agent 类型白名单:通过
Agent(x,y)语法限制可 spawning 的 Agent 类型// 示例:只允许 worker 和 researcher 类型的 Agent Agent("worker, researcher", { description: "并行研究任务", prompt: "..." }) - 权限规则过滤:通过
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 类型白名单:通过
- 精细化错误诊断:
- Agent 存在但被拒:返回具体权限规则来源(
settings/cliArg/session) - Agent 不存在:列出当前可用的 Agent 列表,提升调试效率
- Agent 存在但被拒:返回具体权限规则来源(
第三层:默认回退路径
- 触发条件:
subagent_type === undefined && isForkSubagentEnabled() === false - 默认 Agent:
GENERAL_PURPOSE_AGENT(src/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 路由调度系统具有以下特点:
- 分层决策:从 Agent 选择 → 权限检查 → 异步决策 → 执行路径
- 安全第一:严格的工具过滤、隔离机制和分类器验证
- 灵活配置:支持多种执行模式(同步/异步/远程/工作树)
- 智能路由:Fork Subagent、自动分类器等高级特性
- 全面监控:实时进度跟踪和生命周期管理
这个系统确保了 Agent 能够在安全、高效、可控的环境中执行复杂的任务编排。