引子
想象这么一个场景:ClaudeCode在修复一个BUG时,读了500行错误日志,其中只有3行错误栈有用;跑了一次测试,200行输出里只有5行是关键。
可是,这500行日志、200行测试输出会原封不动地留在主上下文里赖着不走,挤占窗口、稀释关键信息,还会诱发更早的上下文压缩。
窗口越用越脏,回答质量随之下降。
这就是上下文污染。
那么:高噪声的子任务,怎样不污染、不耗尽主上下文?
ClaudeCode对这个问题的解法是SubAgent(子智能体):把高噪声的子任务委派到一个独立的上下文窗口里执行,过程留在那边,只把结论带回来。
在「大模型+工具调用循环+上下文窗口有限」这条主线上,SubAgent缓解的是"上下文窗口有限"这一环。循环还是照转,只是在别处转。
官方文档:https://code.claude.com/docs/en/sub-agents
原理
什么是SubAgent
SubAgent是在当前会话内运行的专用助手。
它有自己独立的上下文窗口,跑自己完整的工具调用循环(agent loop),自己读文件、自己执行命令、自己迭代。
关键点在于,它与主对话的交互只有两次:接收一段任务描述,返回一份结果摘要。
中间逐步的输出不会回传,它读过的几十个文件、翻过的几百行日志,随任务结束一起丢弃,主上下文始终干净。
正因为交互是"一次进、一次出",它有两个直接影响:
- 不适合需要来回讨论的活。
中途没有反馈通道,需要边做边看、逐步确认的任务(比如需要你一步步拍板的重构)应该留在主对话里做,而不是委派出去。 - 依赖主对话UI或会话状态的工具用不了。
例如AskUserQuestion(在主对话里向用户弹出一道选择题、用户点选后再继续)用不了。SubAgent和用户之间根本没有这样的交互通道,所以这类工具即便写进tools也不会生效。
注意:SubAgent的上下文窗口同样有限,接近上限时它会在自己的窗口内独立做上下文压缩,与主会话互不影响。
两种形态
Fork与Named
SubAgent按"带不带主对话的背景"分成两种形态。
| Fork(分支) | Named(具名) | |
|---|---|---|
| 上下文 | 继承主对话完整历史 | 全新上下文 |
| 系统提示 | 与主会话相同 | 定义文件的正文 |
| 工具与权限 | 全部继承主会话 | 定义文件的tools等字段控制 |
| 定义文件 | 无 | .claude/agents/*.md |
| 调起方式 | /fork命令 |
自动匹配description,或@agent-<name>显式点名 |
| 适用场景 | 需要全部背景的分身,比如从当前进度分一条线去起草测试 | 背景必须干净的专家,比如只读的代码审查员 |
Fork
Fork的入口只有/fork命令。
没有定义文件、没有自己的系统提示、没有路由,一切都是从主会话拷贝的。
本章其后所有内容(路由与硬约束、定义文件与字段、内置Agent)针对的都是Named形态,Fork不再单独讨论。
委派机制
description
是否委派、委派给谁,由ClaudeCode自动决定,依据就是description字段。
ClaudeCode会拿当前任务与每个SubAgent的description做匹配,匹配上就委派。
这与Skill靠description常驻匹配是同一个机制,差异只在Skill把正文注入当前上下文,SubAgent把任务送进一个独立的上下文。
再委派
SubAgent自己也能再委派SubAgent,但有两个限制:
- 嵌套上限5层。
超过5层无法继续生成。 - 一次性。
每个SubAgent执行完即结束,中间过程不保留。
比较
要复用指令,用Skill;要隔离噪声,用SubAgent。
| 维度 | SubAgent | Skill |
|---|---|---|
| 运行位置 | 会话内的独立上下文 | 主上下文内 |
| 上下文成本 | 隔离,只回传摘要 | 全部计入主对话 |
| 适用场景 | 隔离高噪声操作 | 跨任务复用指令 |
示例
随项目共享的具名Agent
例如,我们创建一个项目级的code-reviewer,把审查员定义进项目仓库,团队每个人都能用。
示例代码(.claude/agents/code-reviewer.md):
1 | --- |
解释说明:
tools只给了读取检索类工具加Bash(用来跑git diff),没有Edit和Write,审查员永远改不了代码。- 正文最后一句约束了回传内容,确保带回主对话的是结论而不是噪声。
只为当次会话临时定义的Agent
有些SubAgent只为当前这一次排查服务,不值得在仓库里留文件,用--agents在启动时临时定义。
会话结束即消失,不落盘、不污染项目配置。
例如,会话级的临时debugger,示例代码:
1 | claude --agents '{ |
带持久记忆的领域专家
SubAgent本身是一次性的,但配上memory字段,可以跨会话积累知识。
示例代码(.claude/agents/domain-expert.md):
1 | --- |
解释说明:
memory为这个SubAgent开启持久记忆。
每次任务结束时它把新知识写入.claude/agent-memory/domain-expert/,下次调用时自动注入上下文。
作用域有三种:project(随项目提交、团队共享)、user(跨项目个人)、local(仅本机,不提交)。- SubAgent的执行是一次性的,但沉淀下来的记忆是持久的。
实践
内置Agent
| Agent | 模型 | 工具 | 用途 | 可恢复 |
|---|---|---|---|---|
| Explore | 继承主会话(Claude API上封顶Opus) | 只读工具,无Write/Edit |
文件发现、代码搜索、快速探索 | 否 |
| Plan | 继承主会话 | 只读工具 | 计划模式下的前置研究 | 否 |
general-purpose |
继承主会话 | 全部工具 | 复杂多步任务、需要改动文件 | 是 |
statusline-setup |
Sonnet | Read、Edit |
服务于/statusline命令,配置状态栏 |
- |
claude-code-guide |
Haiku | Bash、Read、WebFetch、WebSearch |
回答ClaudeCode功能类问题 | - |
解释说明:
- 当我们想委派的是"接着干活"而不只是"探索"时,选
general-purpose。 statusline-setup和claude-code-guide是窄用途Agent,各自只服务表中那一件事。
Explore和Plan则有三个刻意的设计:
- 只读
两者都没有写工具,怎么调用都不会改动文件系统。 - 跳过
CLAUDE.md与git状态
为了快和省,它们启动时不加载CLAUDE.md层级,也不读git状态;其余所有SubAgent(内置或自定义)则两者都会加载。 - 一次性 不可恢复
执行完即销毁、不返回agent ID,因此无法恢复——为什么这是设计而非缺陷,见前文「路由与两条硬约束」。
此处容易有个疑问:
plan模式要产出计划,作为其中一环的Plan这个SubAgent怎能不读CLAUDE.md?
因为计划不在Plan这个SubAgent里定稿。
官方文档把Plan这个SubAgent定位为"计划模式下为出计划所做的代码库调研",它的产出回传给主对话;由带着完整CLAUDE.md的主对话读取,并在主对话保持只读的前提下最终向用户呈现计划。
也就是说,受CLAUDE.md约束的审阅与定稿落在主对话这一层,Plan跳过CLAUDE.md并不会让规则被绕过。
定义位置
自定义的SubAgent可以放在多个位置,同名时按优先级从高到低胜出。
| 优先级(1最高) | 位置 | 说明 |
|---|---|---|
| 1 | Managed设置目录内的.claude/agents/ |
组织统一下发 |
| 2 | CLI参数--agents传入的JSON |
仅当次会话有效 |
| 3 | 项目级.claude/agents/ |
随项目提交、团队共享,支持子目录递归 |
| 4 | 用户级~/.claude/agents/ |
个人所有项目可用 |
| 5 | 插件内的agents/目录 |
随插件分发 |
文件格式与字段
格式
定义文件由YAML front matter和Markdown正文组成。
YAML front matter是配置。Markdown正文是这个SubAgent的系统提示。
会话级的临时定义则是一段等价的JSON,见前文「会话级的临时debugger」
front matter字段
| 字段 | 必填 | 说明 |
|---|---|---|
name |
是 | 小写字母加连字符的唯一标识,在Hook(钩子)事件中以agent_type传递 |
description |
是 | 自动委派的路由依据 |
tools |
否 | 工具白名单,省略则继承主对话的全部工具 |
disallowedTools |
否 | 工具黑名单,与tools同时出现时在白名单基础上再剔除(黑名单优先) |
model |
否 | sonnet/opus/haiku/fable别名、完整模型ID或inherit,默认inherit |
permissionMode |
否 | default/acceptEdits/auto/dontAsk/bypassPermissions/plan(v2.1.200起manual为default的别名) |
maxTurns |
否 | 最大轮数上限 |
skills |
否 | 预加载的Skill列表,启动时注入完整内容,Skill机制见《3.Skill》 |
mcpServers |
否 | 可用的MCP(Model Context Protocol,模型上下文协议)服务器,支持内联定义或按名称引用,机制见《6.MCP》 |
hooks |
否 | 生命周期Hook配置,机制见《5.Hook》 |
memory |
否 | user/project/local,开启跨会话的持久化记忆目录,记忆机制见《2.Memory》 |
background |
否 | true时总是后台运行;省略时由Claude决定(v2.1.198起默认后台运行) |
effort |
否 | low/medium/high/xhigh/max,覆盖会话级设置 |
isolation |
否 | 设为worktree时在临时分支中运行,无改动的分支自动清理 |
color |
否 | UI显示颜色:red/blue/green/yellow/purple/orange/pink/cyan |
initialPrompt |
否 | 以--agent作为整会话身份运行时,自动提交的首轮提示 |
立即生效
ClaudeCode会监听~/.claude/agents/和.claude/agents/,在磁盘上新增或修改定义文件后,几秒内就会被检测到、下一次委派即用新定义,无需重启。
但是两种情况例外,仍需重启:
- 在会话启动时尚不存在的
agents目录里创建该目录的第一个定义文件(监听只覆盖启动时已存在的目录)。 - 用
--disable-slash-commands启动的会话(完全不监听)。
调用与管理
调起一个SubAgent,有六个入口:
| 方式 | 用法 | 说明 |
|---|---|---|
| 自动委派 | 正常提需求 | Claude按description匹配后自动委派 |
| 显式指定 | @agent-code-reviewer 看一下auth模块 |
用@agent-<name>点名,不经过自动匹配 |
/agents |
会话内输入/agents |
管理入口,行为随版本不同,见后文「排障速查」 |
--agents |
claude --agents '<JSON>' |
会话级临时定义,不持久化 |
--agent |
claude --agent code-reviewer |
整个会话以该Agent身份运行,此时initialPrompt作为首轮提示自动提交 |
/fork |
/fork draft unit tests |
开一个继承完整上下文的Fork分支去干活 |
解释说明:
- 自动委派是主路径:日常使用时不必记住每个SubAgent的名字,路由的依据见前文「路由与两条硬约束」。
- 显式指定和
/fork是补充:前者用于"我明确知道该谁干",后者用于"我要一个带着全部背景的分身"。