Cluade code:Subagents (子代理)

当一个 Agent 需要处理复杂任务时,它如何避免把自己「撑死」?答案是:生出一个儿子,让儿子去干,自己只看结果。

问题的起点

Agent 在执行任务时,消息历史会不断增长。假设父代理需要「调查一下项目用的是什么测试框架」,这个子任务本身可能需要十几次工具调用:读文件、搜索、分析......

如果所有这些中间步骤都堆在主对话里,会发生什么?

  • Token 快速消耗:每次 LLM 调用都要携带全量历史

  • 推理质量下降:大量无关的中间信息干扰后续判断

  • 上下文被稀释:主任务的目标越来越模糊

子代理(Subagent)就是解决这个问题的核心机制。

核心设计:子代理就是一个 Tool

Claude Code 里,子代理不是一个独立进程,而是通过 AgentTool 实现的------它本质上是一个嵌套的 Agent Loop,和 BashTool、FileReadTool 并列注册在工具列表里。

javascript 复制代码
// src/tools/AgentTool/AgentTool.ts - 简化
export const AgentTool = buildTool({
  name: 'Agent',
  maxResultSizeChars: 50_000,

  inputSchema: z.object({
    prompt: z.string(),           // 子代理的任务描述
    description: z.string(),      // 短描述 (3-5 词)
    subagent_type: z.string().optional(),  // 代理类型
    model: z.enum(['sonnet', 'opus', 'haiku']).optional(),
    run_in_background: z.boolean().optional(),
    isolation: z.enum(['worktree']).optional(),
  }),

  // 子代理可以并发执行
  isConcurrencySafe() { return true },

  // 子代理本身是只读的 (它内部的工具调用有自己的权限检查)
  isReadOnly() { return true },

  async call(input, context) {
    // 创建一个新的 QueryEngine 实例 ------ 独立的消息列表
    const childEngine = new QueryEngine({
      tools: getChildTools(input.subagent_type),
      systemPrompt: buildSubagentPrompt(input),
      // 关键: messages 从空开始
      messages: [],
    })

    // 运行子代理的 Agent Loop
    let result = ''
    for await (const message of childEngine.submitMessage(input.prompt)) {
      if (message.type === 'text') {
        result += message.text
      }
    }

    // 只返回最终文本给父代理
    return { data: result }
  },
})

隔离与共享:父子代理的边界

子代理和父代理之间有清晰的边界划分:

1.完全隔离(不共享)

|----------|-------------------------------|
| 状态 | 说明 |
| 消息历史 | 子代理 messages 从空开始,父代理的历史对它不可见 |
| Token 计费 | 独立追踪,子代理的消耗不合并到父代理 |
| 压缩状态 | 子代理有自己的 Micro/Auto-Compact 周期 |

2.共享(父子均可访问)

|------|---------------------|
| 状态 | 说明 |
| 文件系统 | 子代理读写的文件,父代理下一步就能看到 |
| 工作目录 | 相同的 cwd |
| 权限规则 | 子代理继承父代理的权限设置 |

这是典型的「进程内线程」思路:共享内存(文件系统),但各自有独立的执行栈(消息历史)。

子代理工具集

1.防止无限递归:

最关键的设计细节:子代理的工具列表里没有 AgentTool

javascript 复制代码
function getChildTools(subagentType?: string): Tool[] {
  // 子代理的基础工具集 ------ 没有 AgentTool!
  const baseTools = [
    BashTool,
    FileReadTool,
    FileWriteTool,
    FileEditTool,
    GlobTool,
    GrepTool,
    WebFetchTool,
    // 注意:没有 AgentTool → 防止无限递归
  ]

  // 不同类型的子代理有不同工具
  switch (subagentType) {
    case 'Explore':
      // 探索型:只有搜索和读取工具
      return [FileReadTool, GlobTool, GrepTool, WebFetchTool]
    case 'Plan':
      // 规划型:只有读取工具 + 任务工具
      return [FileReadTool, GlobTool, GrepTool, TaskCreateTool]
    default:
      return baseTools
  }
}

2.子代理类型

Claude Code 预定义了多种子代理类型,每种类型的工具集不同:

|-----------------|-------------------------------|------------|
| 类型 | 可用工具 | 适用场景 |
| general-purpose | 所有工具(除 Agent) | 复杂多步骤任务 |
| Explore | Read / Glob / Grep / WebFetch | 快速探索代码库,只读 |
| Plan | Read / Glob / Grep | 设计方案,不允许修改 |
| code-reviewer | 所有工具 | 代码审查 |

Explore 类型为什么没有 Write/Edit?因为「找信息」不需要修改能力,减少工具集也减少了犯错的可能。这是最小权限原则的工程实践。

源码示例如下:

javascript 复制代码
// 从系统提示词中提取的子代理类型
const SUBAGENT_TYPES = {
  'general-purpose': {
    description: '通用代理,适合复杂多步骤任务',
    tools: '*',  // 所有工具 (除了 Agent)
  },
  'Explore': {
    description: '快速探索代码库',
    tools: ['Read', 'Glob', 'Grep', 'WebFetch'],
    // 不能编辑文件
  },
  'Plan': {
    description: '设计实现方案',
    tools: ['Read', 'Glob', 'Grep'],
    // 只读 + 任务工具
  },
  'code-reviewer': {
    description: '代码审查',
    tools: '*',
  },
  // ... 更多专用类型
}

执行模式:

前台执行(默认):父代理等待子代理完成后继续。适合结果强依赖的场景。

javascript 复制代码
const result = await AgentTool.call({
  prompt: "分析项目的测试覆盖率",
  description: "分析测试覆盖率",
})
// 父代理等待子代理完成

后台执行:父代理立即继续,子代理异步跑,完成后通知。适合耗时长但结果不紧急的场景。

javascript 复制代码
const result = await AgentTool.call({
  prompt: "运行所有测试并报告结果",
  run_in_background: true,  // ← 父代理立即继续
})

一图总结

参考信息

深入Cluade code源码:

https://nangongwentian-fe.github.io/learn-claude-source/s06-subagents.htmlhttps://nangongwentian-fe.github.io/learn-claude-source/s06-subagents.html

感兴趣的宝子可以关注一波,后续会更新更多有用的知识!!!

相关推荐
Dust-Chasing1 小时前
Claude Code源码剖析 - ShellTool与真实动作
人工智能·python·ai
木白CPP1 小时前
Claude Code 自用高效插件
ai·ai编程
aXin_ya1 小时前
乐尚代驾,总结
java
仙俊红1 小时前
Java JUC:CompletableFuture 详解,多个任务并行执行并等待全部完成
java·python·spring
JAVA面经实录9171 小时前
MongoDB(文档型 NoSQL)
java·数据库·mongodb·nosql
cfm_29141 小时前
JVM类加载机制初步了解
java·jvm
吴佳浩 Alben1 小时前
Hermes vs OpenClaw:基于源码的 Agent Loop 全面分析
人工智能·ai·transformer
让我上个超影吧1 小时前
Cluade code:上下文压缩
java·服务器·ai
plainGeekDev1 小时前
批量写入 → Room 事务
android·java·kotlin