跳过正文

各司其职:SubAgent

·247 字·2 分钟
Chuck Chan
作者
Chuck Chan
分享技术、思考与生活

为什么需要SubAgent
#

我们先从一个常见场景开启今天的内容。

某天,你让 Claude Code 帮你跑一个测试套件,结果输出了 500 行日志;然后你想让它分析一下代码结构,又输出了 200 行;接着你想让它改一个 bug……这时候,你的对话上下文已经被各种“中间过程”塞满了,真正重要的信息被淹没在茫茫的输出海洋里。

其实,究其根本。如果你觉得 Claude Code 越用越“健忘”,并不是模型退化了,而是你的对话上下文,已经被一次次中间过程污染了,这正是子代理要解决的核心问题。因为只有子代理,才在系统层面天然拥有一个独立的上下文窗口。不是因为它“聪明”,不是因为它“更强”, 而是因为它是 Claude Code 里唯一一个,结构上允许“执行完即丢弃”的东西。

在前面的现象里,上下文里充斥着这些内容:

  • 跑测试 → 500 行日志。
  • 搜索代码 → 200 行 grep。
  • 分析错误 → 一堆中间推理过程……

这些信息有一个共同特征:它们对“当下执行”是必要的,但对“后续决策”是噪声。而 Claude Code 的主对话上下文是线性追加的,不会自动过期,默认“都当成重要记忆”。所以问题不是 Claude 突然不聪明了 ,而是它把临时执行数据,当成了长期决策记忆。 这在工程上,本来就一定会“炸”。

而子代理,通过上下文隔离,让专业的 Agent 做专业的事儿,不是为了让 Claude 做得更多, 而是为了让 Claude 记得更少,但记得对。

什么是SubAgent
#

SubAgent当于一个“专职小助手”,它拥有独立的:

  1. 规则
  2. 工具权限
  3. 上下文窗口

通过这些去完成某一类任务,然后把“结果摘要”带回来主代理(主代理即当前操作的主对话)。其编写格式如下:

固定内容
#

可以类比为子代理的元数据,其为YAML frontmatter 格式,其中name、description字段是强制要求的,其他字段都是可选的,但建议还是补充上。

---
name: 代理名称(英文小写,短横线)
description: 一句话说明这个代理是干嘛的
tools: 用哪些工具(固定 Read, Grep, Glob 即可)
model: sonnet (固定)
---

元数据字段参考:

建议内容
#

这部分不强制要求,主要是角色定义、执行步骤、执行准则、输出格式等,强烈建议都写上(否则子代理要做什么,难道要模型来猜吗?是吧)

角色定义(1 段话)
告诉 AI:你是谁、你负责什么、不负责什么。

核心职责(3–6 条)
告诉 AI:你要做哪几件事。

执行步骤 / 审核流程(必须结构化)
告诉 AI:第一步做什么、第二步做什么……

规则 / 标准(强制执行)
告诉 AI:哪些允许、哪些禁止、哪些必须做。

输出格式(固定 markdown 块)
告诉 AI:最终输出必须长什么样。

SubAgent的核心价值
#

子代理的工程价值,本质上就是三件事:隔离约束复用,下面具体展开说说

隔离
#

隔离,解决的是上下文污染问题——大量对当前执行有用、但对后续决策毫无价值的日志、搜索结果和中间推理,不应该进入主对话的长期记忆;子代理天然拥有独立上下文,执行完即丢弃,只把结论带回来,让 Claude 记得更少、但记得对。例如:

主对话的上下文:
┌─────────────────────────────────────────┐
│ 用户:帮我分析一下这个 bug                  │
│ Claude:好的,让我看看...                  │
│ [子代理去执行,产生 500 行日志]             │
│ [子代理返回:发现 3 个相关文件]             │
│ Claude:我发现问题在这三个文件...           │
└─────────────────────────────────────────┘

子代理的上下文(独立的,执行完就释放):
┌─────────────────────────────────────────┐
│ 任务:查找 bug 相关文件                    │
│ [搜索输出 500 行日志]                     │
│ [分析过程...]                            │
│ 结论:3 个相关文件                        │
└─────────────────────────────────────────┘

主对话只看到子代理所返回的结论,不需要承载 500 行的搜索过程。这意味着你的对话不会因为一次大搜索就把 token 配额用光,重要的讨论内容不会被中间输出淹没,Claude 在后续对话中能更好地“记住”关键信息。

约束
#

约束,解决的是行为不可控问题——通过工具权限边界,把“我希望你别这么做”变成“你物理上做不到”,让代码审查只能读、修 bug 才能写,角色职责不再依赖提示词自觉,子代理可以有精确的工具权限控制。

# 只读型子代理(代码审查)
tools: Read, Grep, Glob
# 它只能看,不能改任何东西

# 开发型子代理(bug 修复)
tools: Read, Write, Edit, Bash
# 它可以读写文件和执行命令

# 研究型子代理(技术调研)
tools: Read, WebFetch, WebSearch
# 它可以读本地文件和搜索网络

复用
#

复用,解决的是经验无法沉淀的问题—当子代理被定义成文件、放进版本控制后,好的使用方式就从一次性对话,变成了可共享、可迭代的工程资产。跟README.md一样,子代理也可以分为项目级、用户级(应该还有其他级别,但开发过程中最常用的就是这两种了,所以这里只提着两个级别了)。

  • 项目级:./claude/agents/xxx-sub-agent.md
  • 用户级:~/.cluade/agents/xxx-sub-agent.md
.claude/agents/
├── test-runner.md      # 测试运行专员
├── code-reviewer.md    # 代码审查专员
├── log-analyzer.md     # 日志分析专员
└── bug-fixer.md        # Bug 修复专员

这些配置文件就像公司里的“岗位说明书”,每个岗位职责清晰、权限明确,且随着业务的迭代,子代理会不断改善优化。

这三点,分别对应着三个经典的软件工程命题:内存管理、安全边界、组织效率。这三点合在一起,标志着 Claude Code 的使用方式,从“对话技巧”,正式跨入“工程系统”。

什么时候用SubAgent
#

并不是所有任务都值得动用子代理。子代理的价值,不在于“能不能用”,而在于“该不该用”。一个最直观的判断标准是:主对话到底需不需要承载执行过程本身。下面列举一些需要子代理的场景:

高噪声输出的任务
#

是高噪声输出任务的共同特点是:执行过程中会产生大量中间信息,但主对话真正关心的,往往只有一个结论。

例如跑一次测试会输出几百行日志,但你只想知道通过还是失败;扫描日志和错误栈时,真正有价值的可能只是一两条关键错误;在整个代码库里做搜索,最终只需要知道相关文件在哪里;生成一份长报告,主对话只需要摘要。

角色边界必须非常明确的任务
#

有些事情,你只希望 Claude “看”,而不希望它“动手”;有些操作,只能在特定目录、特定范围内发生;还有一些敏感操作,本身就需要和其他任务隔离开来。

可以并行展开的研究型任务
#

当你需要同时调研认证逻辑、数据库设计和 API 接口,或者对比几种技术方案、从多个视角分析同一个问题时,这些探索之间往往是相互独立的。与其在主对话里来回切换,不如让多个子代理各自去完成自己的探索,再把结果汇总回来。子代理在这里的价值,不只是隔离上下文,更是天然的并行加速器。

可以拆成清晰阶段的流水线式任务
#

比如先定位代码位置,再做代码审查,然后进行修改,最后跑测试验证。

Explore(找位置)
Reviewer(指出问题)
Fixer(修复)
Test-runner(验证)

当然,也有一些情况并不适合使用子代理。如果任务需要频繁来回确认需求、不断调整方向,那子代理这种“派出去干活再回来汇报”的模式反而会拖慢节奏;如果任务的各个阶段高度耦合,每一步都强依赖上一阶段的详细过程,那强行隔离上下文只会增加认知负担;还有非常简单的小任务,启动子代理本身就有开销,直接在主对话中完成,反而更高效。