Claude Code 源码解析
返回目录
扩展与集成14

子代理系统

递归的 query() 调用

核心洞察

子代理通过递归调用 query() 实现,与主代理共享 MCP 连接但隔离上下文

学习

概念讲解与核心设计分析

14.1 AgentTool 与子代理生成

Claude Code 的子代理系统通过 AgentTool.tsx 提供统一的子代理生成入口。AgentTool 是一个特殊的工具,允许主代理在需要时创建独立的子代理来执行特定任务。每个子代理拥有独立的上下文窗口、工具集合和执行环境。

子代理通过 AgentDefinition 接口定义:

  • name — 代理名称,用于识别和日志记录
  • prompt — 代理的系统提示词,定义其角色和行为
  • tools — 允许使用的工具列表(可限制以保障安全)
  • maxTurns — 最大交互轮次限制
  • mcpServers — 代理专属的 MCP 服务器配置

14.2 runAgent 核心执行流程

runAgent.ts 是子代理执行的核心模块,实现了完整的代理运行循环:

  1. 创建子代理上下文 — 初始化独立的消息历史、工具注册表和权限范围
  2. 构建代理提示词 — 将 AgentDefinition 的 prompt 与系统指令、上下文信息合并
  3. 过滤工具集 — 根据代理定义限制可用工具,例如探索代理只能使用只读工具
  4. 递归调用 query() — 进入代理主循环,与 Claude 模型交互执行任务
  5. 产出进度 — 通过 yield 机制实时报告子代理执行进度
  6. 返回摘要 — 任务完成后生成执行摘要返回给父代理

14.3 分叉代理与缓存共享

forkSubagent.ts 实现了基于进程分叉的子代理生成。通过 fork 机制,子代理可以共享父进程的 KV 缓存和模型连接,大幅减少冷启动时间。这对于需要频繁创建子代理的场景(如多文件处理)尤为重要。

14.4 自定义与内置代理

loadAgentsDir.ts.claude/agents/ 目录加载用户自定义代理定义。系统同时内置了多个专用代理:

  • exploreAgent — 快速代码库探索代理,使用只读工具集快速理解代码结构
  • planAgent — 实现规划代理,制定详细的实现方案
  • generalPurposeAgent — 通用多步骤任务代理,处理不适合其他专用代理的任务
  • claudeCodeGuideAgent — Claude Code 使用指导代理
  • statuslineSetup — 状态栏配置代理
  • verificationAgent — 验证代理,用于验证任务执行结果

14.5 代理 MCP 与状态管理

代理的 MCP 配置支持两种引用方式:

  • 字符串引用 — 共享父代理的 MCP 连接,多个子代理复用同一连接,减少资源消耗
  • 内联定义 — 代理专属的 MCP 服务器,随代理生命周期创建和销毁

resumeAgent.ts 处理代理状态恢复,当代理因错误中断时可以从检查点恢复执行。agentColorManager.ts 为每个活跃代理分配唯一的显示颜色,方便在终端中区分不同代理的输出。SendMessageTool 实现代理间消息传递,允许子代理向父代理或兄弟代理发送消息。

架构

模块关系与设计决策

子代理系统架构图

主代理 (Parent Agent)

AgentTool.tsx
SendMessageTool

runAgent.ts 执行引擎

创建上下文
构建提示词
过滤工具
query() 循环
返回摘要

内置代理集合

exploreAgent
代码库探索
planAgent
实现规划
generalPurpose
通用多步骤
guideAgent
使用指导
verificationAgent
结果验证
自定义代理
.claude/agents/

MCP 连接策略 & 状态管理

字符串引用: 共享父连接
内联定义: 代理专属连接

源码

3 个关键代码示例

01
AgentDefinition 与 AgentTool
TypeScript
// AgentDefinition: 代理定义接口
interface AgentDefinition {
  name: string;
  prompt: string;
  tools?: string[];        // 允许的工具列表
  maxTurns?: number;       // 最大交互轮次
  mcpServers?: McpConfig;  // MCP 配置
  model?: string;          // 使用的模型
}

// 内置代理定义示例
const exploreAgent: AgentDefinition = {
  name: 'exploreAgent',
  prompt: '你是一个代码库探索专家。使用只读工具快速理解'
    + '代码结构、找到关键文件和理解架构设计。'
    + '不要修改任何文件。',
  tools: ['Read', 'Glob', 'Grep', 'LSP', 'Bash'],
  maxTurns: 15
};

const planAgent: AgentDefinition = {
  name: 'planAgent',
  prompt: '你是一个实现规划专家。分析需求后制定详细的'
    + '实现方案,包括文件修改计划、测试策略和风险评估。',
  tools: ['Read', 'Glob', 'Grep', 'LSP'],
  maxTurns: 10
};

// AgentTool: 子代理生成工具
const AgentTool = {
  name: 'Agent',
  description: '创建子代理来执行复杂的多步骤任务',
  inputSchema: {
    type: 'object',
    properties: {
      agent: { type: 'string', description: '代理名称' },
      task: { type: 'string', description: '任务描述' }
    },
    required: ['agent', 'task']
  },
  execute: async (input: AgentToolInput) => {
    const definition = resolveAgent(input.agent);
    return runAgent(definition, input.task);
  }
};
02
runAgent 核心执行循环
TypeScript
// runAgent.ts: 子代理核心执行流程
async function* runAgent(
  definition: AgentDefinition,
  task: string
): AsyncGenerator<AgentProgress, AgentResult> {
  // 1. 创建子代理上下文
  const context = createSubagentContext({
    parentContext: getCurrentContext(),
    agentName: definition.name
  });

  // 2. 构建代理提示词
  const systemPrompt = buildAgentPrompt(
    definition.prompt,
    context
  );

  // 3. 过滤工具集
  const tools = filterTools(
    getAllTools(),
    definition.tools  // 只保留允许的工具
  );

  // 4. 初始化消息
  const messages: Message[] = [
    { role: 'user', content: task }
  ];

  // 5. 代理执行循环
  let turns = 0;
  while (turns < (definition.maxTurns || 20)) {
    // 递归调用 query()
    const response = await query({
      systemPrompt,
      messages,
      tools,
      model: definition.model
    });

    // 产出进度更新
    yield {
      type: 'progress',
      agent: definition.name,
      turn: turns,
      content: response.content
    };

    // 检查是否完成
    if (!response.toolCalls || response.toolCalls.length === 0) {
      break;
    }

    // 执行工具调用并追加消息
    for (const call of response.toolCalls) {
      const result = await executeTool(call, context);
      messages.push(
        { role: 'assistant', content: response.content },
        { role: 'tool', content: result }
      );
    }
    turns++;
  }

  // 6. 返回执行摘要
  return {
    agent: definition.name,
    summary: messages[messages.length - 1].content,
    turnsUsed: turns
  };
}
03
forkSubagent 与自定义代理加载
TypeScript
// forkSubagent.ts: 基于进程分叉的子代理
async function forkSubagent(
  definition: AgentDefinition,
  task: string
): Promise<AgentResult> {
  // 分叉当前进程,共享 KV 缓存
  const child = fork(process.argv[1], {
    env: {
      ...process.env,
      AGENT_MODE: 'subagent',
      AGENT_NAME: definition.name,
      AGENT_TASK: task,
      // 共享父进程的缓存路径
      CACHE_DIR: getCacheDir()
    }
  });

  return new Promise((resolve, reject) => {
    child.on('message', (msg: AgentMessage) => {
      if (msg.type === 'result') {
        resolve(msg.data);
      }
    });
    child.on('error', reject);
    child.on('exit', (code) => {
      if (code !== 0) {
        reject(new Error(
          'Subagent exited with code ' + code
        ));
      }
    });
  });
}

// loadAgentsDir.ts: 加载自定义代理
async function loadAgentsDir(
  dir: string
): Promise<AgentDefinition[]> {
  const agentsPath = join(dir, '.claude', 'agents');
  const files = await readdir(agentsPath).catch(() => []);
  const agents: AgentDefinition[] = [];

  for (const file of files) {
    if (!file.endsWith('.md')) continue;
    const content = await readFile(
      join(agentsPath, file), 'utf-8'
    );
    // 解析 frontmatter 获取代理配置
    const { frontmatter, body } = parseFrontmatter(content);
    agents.push({
      name: frontmatter.name || basename(file, '.md'),
      prompt: body,
      tools: frontmatter.tools,
      maxTurns: frontmatter.maxTurns,
      mcpServers: frontmatter.mcpServers
    });
  }
  return agents;
}

// agentColorManager.ts: 代理颜色分配
class AgentColorManager {
  private colors = [
    '#e94560', '#0f3460', '#533483',
    '#48c9b0', '#f39c12', '#3498db'
  ];
  private assignments = new Map<string, string>();

  getColor(agentName: string): string {
    if (!this.assignments.has(agentName)) {
      const idx = this.assignments.size % this.colors.length;
      this.assignments.set(agentName, this.colors[idx]);
    }
    return this.assignments.get(agentName)!;
  }
}

互动

步进式流程演示

互动演示

子代理执行交互式演练

Step 1: 主代理决定委派任务

当主代理判断当前任务需要独立探索代码库时,调用 AgentTool 创建一个 exploreAgent 子代理。主代理提供明确的任务描述。

// 主代理调用 AgentTool
AgentTool.execute({
  agent: 'exploreAgent',
  task: '分析 src/services/ 目录的架构设计,找到所有服务间的依赖关系'
})

Step 2: runAgent 初始化子代理

runAgent 创建独立的执行上下文,构建针对探索任务优化的提示词,并将工具集限制为只读工具(Read、Glob、Grep、LSP)。子代理无法修改任何文件。

// 工具过滤
允许的工具: Read, Glob, Grep, LSP, Bash
禁止的工具: Write, Edit, NotebookEdit(只读模式)

Step 3: 子代理自主执行

子代理进入独立的执行循环,自主使用工具探索代码库。每个交互轮次会通过 yield 向父代理报告进度。子代理最多执行 15 轮(exploreAgent 的 maxTurns 限制)。

Step 4: 结果摘要返回

子代理完成探索后,生成结构化的执行摘要返回给主代理。摘要包含发现的架构信息、关键文件列表和依赖关系图。主代理据此继续后续工作。

// 返回结果
{
  agent: 'exploreAgent',
  summary: '发现 12 个服务模块,核心依赖链...',
  turnsUsed: 8
}

相关源文件

tools/AgentTool/tools/SendMessageTool/