跳转到内容

Claude Code subagent:上下文隔离到什么程度、fork 和普通 subagent 差在哪、三种限额各管什么

约 28 分钟 难度:硬核 动手章

用 subagent 的判断标准是一句话:这个side任务会不会用一堆你之后不会再看的搜索结果、日志、文件内容淹没主对话。会,就交给 subagent——它在自己的上下文里干活,只把摘要返回。

但隔离是双向的。subagent 看不到你的对话历史、看不到你已经调用的 skill、看不到 Claude 已经读过的文件。所以它需要的背景信息必须在委派时说清楚。

fork 是这条规则的例外,也是它存在的理由:fork 继承整个对话,并且因为系统提示和工具定义与父会话完全一致,它的第一次请求能复用父会话的 prompt 缓存——所以对于需要相同上下文的任务,fork 比新起一个 subagent 更便宜。

agent模型工具用途
Explore继承主对话(Claude API 上封顶 Opus)只读,Write 和 Edit 被拒文件发现、代码搜索、代码库探索
Plan继承主对话只读plan 模式下的代码库调研
general-purpose继承主对话subagent 可用的全部工具需要探索加修改的复杂多步任务

Explore 和 Plan 跳过你的 CLAUDE.md 文件和父会话的 git status,其他所有内置和自定义 subagent 都加载这两样。

这个设计的理由是「让调研又快又便宜」:探索阶段不需要知道你的代码规范,加载它们只是浪费 token 和时间。

推论很实际:主对话是带着完整 CLAUDE.md 上下文读 Explore 和 Plan 的结果的,所以大部分规则不需要传到 subagent 里面。但如果某条规则必须到位,比如「忽略 vendor/ 目录」,就要在委派时的提示词里重述一遍。

Explore 的模型行为在 v2.1.198 变过:以前总是跑 Haiku,现在继承主对话的模型。Claude API 上继承值封顶 Opus——主对话在更高档时 Explore 跑 Opus,主对话在 Sonnet 或 Haiku 时跑同一个。想让探索固定在低成本模型上,定义一个自己的 Explore(用户或项目层)覆盖内置的,并写 model: haiku。

关掉内置 Explore 和 Plan:CLAUDE_CODE_DISABLE_EXPLORE_PLAN_AGENTS=1,之后 Claude 直接读文件而不委派。

位置范围优先级
managed 设置里的 .claude/agents/组织全体1(最高)
--agents CLI 标志当前会话2
.claude/agents/当前项目3
~/.claude/agents/你的所有项目4
插件的 agents/ 目录插件启用处5(最低)

项目 subagent 从当前工作目录往上走查找,直到仓库根,每一级 .claude/agents/ 都会被扫。多个嵌套目录定义了同名 subagent 时,用离工作目录最近的那个定义。

.claude/agents/ 和 ~/.claude/agents/ 是递归扫描的,可以用 agents/review/、agents/research/ 这样的子文件夹组织。子目录路径不影响 subagent 的标识和调用方式——身份只来自 name frontmatter 字段。

插件的 agents/ 目录不同:子文件夹会成为作用域标识符的一部分。插件 my-plugin 里的 agents/review/security.md 注册为 my-plugin:review:security。

name 里不能有 :,那是插件作用域标识符的保留字符。v2.1.218 之前这种名字是被接受的。

只有 name 和 description 是必填的。

字段说明
name小写字母和连字符组成的唯一标识。hook 收到的 agent_type 就是这个值。文件名不必匹配
descriptionClaude 何时该委派给这个 subagent
tools可用工具。省略时继承 subagent 可用的全部工具
disallowedTools要拒绝的工具,从继承或指定的列表里移除
modelsonnet / opus / haiku / fable / 完整 model ID / inherit。默认 inherit
permissionModedefault / acceptEdits / auto / dontAsk / bypassPermissions / plan
maxTurns停止前的最大 agentic 轮数
skills启动时预加载进上下文的 skill。注入的是完整内容,不只是描述
mcpServers这个 subagent 可用的 MCP 服务器。可以是已配置服务器的名字,也可以是内联定义
hooks限定在这个 subagent 生命周期内的 hook
memory持久记忆作用域:user / project / local
backgroundtrue 时总在后台运行。未设时 Claude 决定,v2.1.198 起默认后台
effort这个 subagent 活跃时的 effort level
isolation设 worktree 在临时 git worktree 里运行
color任务列表和 transcript 里的显示颜色
initialPrompt作为主会话 agent 运行时自动提交的第一轮用户输入

插件 subagent 不支持 hooks、mcpServers、permissionMode 三个字段,出于安全原因加载时被忽略。需要它们就把 agent 文件复制到 .claude/agents/ 或 ~/.claude/agents/。

这一节是 subagent 行为里最容易造成困惑的地方:同一份定义在前台和后台可能解析出不同的工具集。

subagent 继承主对话的内置工具和 MCP 工具,然后经过两道过滤。

第一道移除这些工具,即使你在 tools 里列了也一样:

  • Agent(在深度上限时)
  • AskUserQuestion
  • EndConversation
  • EnterPlanMode
  • ExitPlanMode(除非 permissionMode 是 plan)
  • ScheduleWakeup
  • TaskOutput
  • WaitForMcpServers
  • Workflow

第二道只对后台运行的 subagent 生效(这是默认)。后台 subagent 保留全部 MCP 工具,但内置工具只剩:Read、Grep、Glob、Bash、PowerShell、Edit、Write、NotebookEdit、WebFetch、WebSearch、TodoWrite、Skill、ToolSearch、EnterWorktree、ExitWorktree、Monitor、TaskStop、SendMessage、Artifact。

其他内置工具全被移除,无论是继承的还是在 tools 里列的。移除不报错,除非最后 tools 列表解析结果为空。

fork 跳过这两道过滤,拿到主对话的完整工具池。

限制工具用 tools 做白名单或 disallowedTools 做黑名单:

---
name: safe-researcher
description: Research agent with restricted capabilities
tools: Read, Grep, Glob, Bash
---
---
name: no-writes
description: Inherits the available tools except file writes
disallowedTools: Write, Edit
---

两个都设时:先应用 disallowedTools,再在剩余池里解析 tools。两边都列的工具被移除。

两个字段都接受 MCP 服务器级模式:mcp__<server> 或 mcp__<server>__* 授予或移除该服务器的全部工具。disallowedTools 里的 mcp__* 移除所有服务器的全部 MCP 工具。

tools 里没有任何条目解析成工具时(比如全拼错了),Claude Code 通常拒绝启动这个 subagent,Agent 工具返回一个点名未解析条目的错误。v2.1.208 之前那个 subagent 会带着零工具启动,返回空的或令人困惑的结果。

mcpServers 字段有一个很实用的用法:让一个 MCP 服务器完全不进主对话,避免它的工具描述在那里消耗上下文。

---
name: browser-tester
description: Tests features in a real browser using Playwright
mcpServers:
# 内联定义:只对这个 subagent 可见
- playwright:
type: stdio
command: npx
args: ["-y", "@playwright/mcp@latest"]
# 按名引用:复用已配置的服务器
- github
---
Use the Playwright tools to navigate, screenshot, and interact with pages.

内联定义的服务器在 subagent 启动时连接、结束时断开。字符串引用共享父会话的连接。

subagent 拿到工具,父对话拿不到。这比在 .mcp.json 里定义再想办法屏蔽要干净。

主会话适用的 MCP 限制同样覆盖 subagent frontmatter 里声明的服务器:--strict-mcp-config、--bare、企业 managed MCP 配置、allowedMcpServers 和 deniedMcpServers 策略。

subagent 继承主对话的权限上下文,可以覆盖模式,但有两个例外:

  • 父会话用 bypassPermissions 或 acceptEdits 时,这个优先,无法被覆盖
  • 父会话用 auto mode 时,subagent 继承 auto mode,frontmatter 里的 permissionMode 被忽略——分类器用与父会话相同的规则评估 subagent 的工具调用

这个设计防的是「通过 subagent 提权」:如果 subagent 能声明比父会话更宽的权限模式,那么权限模式这个机制就形同虚设了。

---
name: api-developer
description: Implement API endpoints following team conventions
skills:
- api-conventions
- error-handling-patterns
---
Implement API endpoints. Follow the conventions and patterns from the preloaded skills.

注入的是每个 skill 的完整内容,不只是描述。

这个字段控制的是「预加载哪些」,不是「能访问哪些」:不设它,subagent 仍然能在执行期间通过 Skill 工具发现并调用项目、用户、插件 skill。要彻底禁止,从 tools 里省掉 Skill 或加到 disallowedTools。

设了 disable-model-invocation: true 的 skill 不能被预加载,因为预加载取自「Claude 能调用的 skill」这同一个集合。内置的 /verify 和 /code-review 也在其中。

这和「在 subagent 里跑 skill」是互为反向的两种用法:

系统提示来自任务是
subagent 配 skills 字段subagent 的 markdown 正文Claude 的委派消息
skill 配 context: fork你指定的 agent 类型SKILL.md 内容

底层是同一套系统,区别在谁提供系统提示、谁提供任务。

memory 字段给 subagent 一个跨对话存续的目录:

---
name: code-reviewer
description: Reviews code for quality and best practices
memory: user
---
You are a code reviewer. As you review code, update your agent memory with
patterns, conventions, and recurring issues you discover.
作用域位置何时用
user~/.claude/agent-memory/<name>/跨所有项目记住
project.claude/agent-memory/<name>/项目特定且可通过版本控制共享
local.claude/agent-memory-local/<name>/项目特定但不进版本控制

project 是推荐的默认值,因为它让 subagent 的知识可以通过版本控制共享。

subagent 记忆是自动记忆的一部分:关掉自动记忆(autoMemoryEnabled 或 CLAUDE_CODE_DISABLE_AUTO_MEMORY),memory 字段就无效了。

启用时:

  • 系统提示包含读写记忆目录的指令
  • 系统提示还包含记忆目录里 MEMORY.md 的前 200 行或 25KB(先到者为准)
  • Read、Write、Edit 工具自动启用,让 subagent 能管理自己的记忆文件

把记忆指令直接写进 subagent 的 markdown 正文,让它主动维护自己的知识库:

Update your agent memory as you discover codepaths, patterns, library
locations, and key architectural decisions. Write concise notes about
what you found and where.

非 fork subagent 的初始上下文包含:

  • 系统提示:agent 自己的提示加上 Claude Code 附加的环境细节,不是完整的 Claude Code 系统提示
  • 任务消息:Claude 交接工作时写的委派提示
  • CLAUDE.md 文件:主对话加载的每一层,含 ~/.claude/CLAUDE.md、项目规则、CLAUDE.local.md、managed 策略文件。Explore 和 Plan 跳过
  • git status:父会话开始时的快照。不在 git 仓库里或 includeGitInstructions 为 false 时没有。Explore 和 Plan 无论如何都跳过
  • 预加载的 skill:skills 字段里点名的 skill 的完整内容
  • 兄弟名册:列出 main 和会话里其他所有具名 agent 的系统提醒,每个都是 SendMessage 的合法 to 值

有几样主对话状态永远不到非 fork subagent:

  • 输出样式:subagent 跑自己的系统提示,你的输出样式不影响它的回复
  • 自动记忆:主对话的自动记忆不加载。要给 subagent 自己的持久记忆用 memory 字段
  • 上下文窗口大小:subagent 的窗口由它自己的模型决定,不是父会话的。委派给窗口更小的模型,那个 subagent 就只有更小的窗口

最后一条容易忽略:给一个需要读大量内容的任务指定 model: haiku 省钱,可能因为窗口不够而失败。

这三个是独立的,各有自己的环境变量。混起来会误判问题在哪。

限额默认变量管什么
会话总量200CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION一个会话里累计能起多少个
并发20CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS同时运行多少个
嵌套深度3CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTHsubagent 能往下嵌几层

会话总量:已完成的 subagent 仍然计数。达到上限时 Agent 工具报 Subagent spawn limit reached,错误信息告诉 Claude 用自己的工具完成剩余工作。/clear 重置计数。

并发:达到上限时报 Concurrent subagent limit reached,错误告诉 Claude 不要重试。运行数降下来之后又能起。恢复一个已完成的 subagent 会拿一个新槽位且不检查限额,所以恢复操作可以把运行数推过上限。

嵌套深度:默认 3 层。到达深度上限时,Claude Code 对除 fork 以外的每个 subagent 收回 Agent 工具,所以在上限的 subagent 自己干完委派的活并返回一份摘要。设 1 关掉嵌套。

嵌套的历史默认值变过好几次:v2.1.172 到 v2.1.216 默认可嵌套最多 5 层且不可改;v2.1.217 到 v2.1.218 默认是 1;v2.1.219 提到 3。看老资料时注意这一点。

/subtask draft unit tests for the parser changes so far

fork 继承到目前为止的整个对话,而不是从零开始。它放弃了 subagent 本来提供的输入隔离:fork 看到与主会话相同的系统提示、工具、模型、消息历史,所以你可以把一个 side 任务交给它而不必重新解释情况。

fork 自己的工具调用仍然留在主对话之外,只有最终结果回来,所以主上下文窗口保持干净。

fork具名 subagent
上下文完整对话历史全新,带你传的提示词
系统提示和工具与主会话相同来自定义文件,后台运行时被过滤
模型与主会话相同来自 model 字段
prompt 缓存与主会话共享独立缓存

共享 prompt 缓存这一条是 fork 的成本优势来源:系统提示和工具定义与父会话完全一致,所以第一次请求复用父会话的缓存。

什么时候用 fork:具名 subagent 需要太多背景才能有用时,或者你想从同一个起点并行试几种方案时。

fork 出现在提示输入框下面的面板里,后台运行。控制键:

键动作
↑ / ↓在行之间移动
Enter打开选中 fork 的 transcript 并给它发后续消息
x移除已完成的 fork 或停止运行中的
Esc焦点回到提示输入框

打开某个 fork 的 transcript 时,后续消息和 skill 发给那个 agent,但内置命令仍在主对话里跑。v2.1.199 起,在那个视图里敲 /model 或 /fast 会提示它改的是主对话而不是所看的 agent。

fork 不能再 fork。

命令名变过:v2.1.212 起是 /subtask;v2.1.161 到 v2.1.211 是 /fork。现在的 /fork 在 agent view 开启时是把整个会话复制成一个新的后台会话,语义不同了。

自动委派不够用时,三种方式,从一次性建议升级到会话级默认:

自然语言:点名 subagent,Claude 决定是否委派。

Use the test-runner subagent to fix failing tests

@-mention:保证那个 subagent 为这一个任务运行。

@"code-reviewer (agent)" look at the auth changes

注意:你的完整消息仍然发给 Claude,由 Claude 根据你的要求写 subagent 的任务提示。@-mention 控制的是调用哪个 subagent,不是它收到什么提示词。

会话级:整个会话用那个 subagent 的系统提示、工具限制和模型。

Terminal window
claude --agent code-reviewer

这时 subagent 的系统提示完全替换默认的 Claude Code 系统提示,和 --system-prompt 一样。CLAUDE.md 和项目记忆仍然通过正常消息流加载。

要让它成为项目里每个会话的默认值,在 .claude/settings.json 里设 agent:

{ "agent": "code-reviewer" }

CLI 标志优先于设置。

输出扫描:一层容易忽略的防御

Section titled “输出扫描:一层容易忽略的防御”

Claude Code 在 Claude 读到之前扫描每个 subagent 的最终报告。

理由是:subagent 可能读了你从未审查的文件、网页或命令输出,那些来源的文本可以携带针对主对话的指令。

扫描从不删除或改写内容,只做两种可见的改动:

  • 插入反斜杠:模仿 Claude Code 自己输出的文本(如 <system-reminder> 标签,或以 Human: / Assistant: 开头的行)会被插入反斜杠,让模仿读作普通文本而不被当成对话的一部分
  • 标记行:报告模仿 <system-reminder> 这类标签、或提到 bypassPermissions、--dangerously-skip-permissions 这类权限设置时,前面加一行 [harness: subagent output matched instruction-shaped pattern(s):

扫描不判断内容是否恶意,也不改变报告里的指令能做什么:报告引导 Claude 做出的工具调用仍然经过会话的权限检查和沙箱。它不是「限制 subagent 能碰什么」的替代品。

需要 v2.1.210 或更高版本。

用主对话,当:

  • 任务需要频繁来回或迭代打磨
  • 多个阶段共享大量上下文(规划、实现、测试)
  • 你在做一个快速的定点改动
  • 延迟要紧。subagent 从零开始,可能需要时间收集上下文

用 subagent,当:

  • 任务产生你不需要留在主上下文里的冗长输出
  • 你想强制特定的工具限制或权限
  • 工作自成一体,能返回一份摘要

想要「可复用的提示词或流程,但在主对话上下文里跑」,用 skill 而不是 subagent。

对话里已有内容的快速提问,用 /btw:它看得到你的完整上下文但没有工具访问,答案会被丢弃而不加入历史。

每次调用创建一个新实例,上下文全新。要接着已有 subagent 的工作而不是重来,让 Claude 恢复它。恢复的 subagent 保留完整对话历史,包括之前所有工具调用、结果和推理。

内置 Explore 和 Plan 是一次性的,不返回 agent ID,因此不能恢复。需要接着做时用 general-purpose 或自定义 subagent。

transcript 存在 ~/.claude/projects/{project}/{sessionId}/subagents/,每个文件是 agent-{agentId}.jsonl。

subagent transcript 独立于主对话存续:

  • 主对话压缩时 subagent transcript 不受影响,它们在单独的文件里
  • transcript 在会话内存续,重启 Claude Code 后恢复同一个会话即可恢复 subagent
  • 超过 cleanupPeriodDays(默认 30 天)后被自动删除

v2.1.198 起,subagent 把来自启动它的 agent 的消息当作正常任务指导,包括任务中途的纠偏。两条限制始终成立:没有任何 agent 消息算作你对待处理权限提示的批准;没有任何 agent 消息能改变 subagent 的权限设置、CLAUDE.md 或配置。

tools 字段的粒度是「工具级」,有时不够——比如想允许 Bash 但只允许只读 SQL。这时用 PreToolUse hook:

---
name: db-reader
description: Execute read-only database queries
tools: Bash
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/validate-readonly-query.sh"
---
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -iE '\b(INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE)\b' > /dev/null; then
echo "Blocked: Only SELECT queries are allowed" >&2
exit 2
fi
exit 0

macOS 和 Linux 上记得 chmod +x,否则 hook 是失败而不是拦截——这个区别很关键,失败的 hook 拦不住任何东西。

系统提示里也告诉 subagent 拒绝写请求,hook 作为兜底。两层都要有:系统提示让它主动配合,hook 保证它做不到。

要让项目层 subagent 的 frontmatter hook 运行,需要接受该文件夹的 workspace 信任对话框。~/.claude/agents/ 里的用户层 subagent 和 --agents 传入的定义不需要这一步。v2.1.218 之前 frontmatter hook 可以从未信任的文件夹运行。

  • 让一个 subagent 跑测试套件,只报告失败的测试和错误信息,对比直接在主对话里跑测试后 /context 的差异。这是 subagent 上下文价值最直观的一次测量。
  • 定义一个自己的 Explore 覆盖内置的,写 model: haiku,看探索质量和成本的变化。
  • 用 /subtask 起一个 fork,同时在主会话继续工作,体会「共享缓存」在响应速度上的差别。
  • initialPrompt 字段的完整行为。文档说它作为主会话 agent 运行时自动提交为第一轮,命令和 skill 会被处理,但与用户提供的提示词如何拼接的细节没有核实。
  • isolation: worktree 的清理条件。文档说 subagent 没做改动时 worktree 会被自动清理,「有改动」的判定标准未见说明。
  • 兄弟名册(sibling roster)的完整触发条件。确认了它需要 SendMessage 在工具列表里且至少一个其他 agent 有名字,但名册内容的更新时机只知道是启动时快照。
  • 并发限额下「恢复已完成 subagent 不检查限额」是否会导致实际运行数无上限。文档陈述了这个行为,没有说明是否有其他兜底。
这一节有错或讲不清? 提个 Issue 直接改文档 请作者催更