跳转到内容

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 之前这种名字是被接受的。

只有 namedescription 是必填的。

字段说明
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
isolationworktree 在临时 git worktree 里运行
color任务列表和 transcript 里的显示颜色
initialPrompt作为主会话 agent 运行时自动提交的第一轮用户输入

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

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

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

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

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

第二道只对后台运行的 subagent 生效(这是默认)。后台 subagent 保留全部 MCP 工具,但内置工具只剩:ReadGrepGlobBashPowerShellEditWriteNotebookEditWebFetchWebSearchTodoWriteSkillToolSearchEnterWorktreeExitWorktreeMonitorTaskStopSendMessageArtifact

其他内置工具全被移除,无论是继承的还是在 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 配置、allowedMcpServersdeniedMcpServers 策略。

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

  • 父会话用 bypassPermissionsacceptEdits 时,这个优先,无法被覆盖
  • 父会话用 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 记忆是自动记忆的一部分:关掉自动记忆(autoMemoryEnabledCLAUDE_CODE_DISABLE_AUTO_MEMORY),memory 字段就无效了。

启用时:

  • 系统提示包含读写记忆目录的指令
  • 系统提示还包含记忆目录里 MEMORY.md 的前 200 行或 25KB(先到者为准)
  • ReadWriteEdit 工具自动启用,让 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 无论如何都跳过
  • 预加载的 skillskills 字段里点名的 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 直接改文档 请作者催更