Claude Code 学习站

子代理与上下文隔离

考点 1.3 · Subagents & context isolation · 所属域权重 27% · 智能体架构与编排

本页目录5

这个考点是什么

子代理(subagent)是协调者(orchestrator)用来委派任务的执行单元,在 Claude Code 和 Claude Agent SDK 中通过 Agent 工具派生——该工具在 Claude Code v2.1.63 之前称为 Task,现行文档与 SDK 一律以 Agent 作为 tool_usename,只在 system:init 的工具列表、result.permission_denials[].tool_name 等历史兼容字段里还能看到 Task 这个旧名。

子代理唯一的定义性特征是上下文隔离(context isolation):它拥有自己独立的 context window、独立的 system prompt,以及独立的工具集(通过 AgentDefinition.tools/disallowedTools 配置)——它看不到协调者此前的对话历史,协调者也看不到它执行过程中的中间细节(搜索结果、日志、文件全文),只会收到它任务结束时返回的一段总结。

这种隔离带来的收益是双重的:一是保护主上下文不被大量中间产物挤占;二是让不同子代理可以配置不同的工具白名单、路由到不同模型(比如限制为只读工具,或改用更便宜的 Haiku),各自独立探索、互不干扰路径依赖。

代价则是子代理之间不共享任何状态——一个子代理的 context 从零开始,协调者传给它的唯一内容就是 Agent 工具调用里的那段 prompt 文本,所以下游子代理(比如做 synthesis)需要的所有信息都必须由协调者显式写进 prompt。

考点还覆盖两处容易混淆的机制细节:

  • 一是“子代理能不能被调出来”取决于顶层的 allowedTools(自动放行列表)有没有包含 Agent,这与限制子代理自身工具集的 AgentDefinition.tools 是两个不同的开关;

  • 二是“多个独立子任务能不能并行”取决于协调者是否在同一轮响应里一次性发出多个 Agent 调用,逐个等待结果再发起下一个本质上仍是串行。

为什么考

出题角度集中在“子代理拿到什么、拿不到什么”这条边界上的具体推演,而非抽象定义复述。

常见考法包括:

  • 给一个协调者的工具配置,让你判断它为什么无法派发子代理(核心是自动放行列表里缺了派生子代理用的工具);

  • 给一个“多个独立子任务”场景,考察并行的触发条件是“同一轮发出多个工具调用”还是“扩大 context window / 用异步批处理接口”这类似是而非的选项;

  • 给一个 synthesis 子代理的场景,考它该如何拿到前序子代理的产出(结构化嵌入 prompt,而非读取不存在的共享 session);

  • 以及给一个工具结果里混入“新指令”的场景,考子代理的信任边界。

这些题目共享的出题逻辑是:先建立“独立上下文/独立权限”这条正确直觉,再用具体机制反问是否真的理解其运作方式。

核心辨析

  1. 1

    “上下文隔离”具体隔离掉了什么

    子代理有自己独立的 context window、system prompt 和工具集,协调者看不到它读了哪些文件、搜了哪些关键词,只拿到最终返回的一段总结;反过来子代理也不继承协调者此前的对话历史,它的 context 从一段全新的 prompt 文本开始。

    判断某个设计是否符合子代理隔离,先看它是否把中间过程留在子代理自己的上下文里、只把结论传回去。

  2. 2

    派生子代理靠的工具当前名为 Agent(Claude Code v2.1.63 前称 Task,旧名仅残留在 system:init 工具列表和 permission_denials[].tool_name 等兼容字段里)。

    要让这个调用自动放行、不弹审批,需要把 Agent 加进顶层的 allowedTools;这与 AgentDefinition.tools/disallowedTools(限制子代理自己能用哪些工具)是两个不同的字段,前者管“调用 Agent 这一步要不要走审批”,后者管“子代理内部的工具白名单”。

    没把 Agent 放进 allowedTools 不代表绝对调不出子代理,而是会落进权限确认回调,或在免确认模式下被直接拒绝。

  3. 3

    并行的判定标准是“同一轮里发了几个工具调用”,不是“能力上能不能并行”

    协调者若在一条响应里一次性发出多个 Agent 调用,这些子代理会并发执行,总耗时约等于最慢的那一个,而不是各任务耗时之和;先发一个、等结果回来再发下一个,即使任务彼此独立也是串行。

    扩大 context window、改用异步批处理接口等选项都不改变“是否同轮发出”这条判定,是典型的干扰项;若要协调的子任务从几个扩展到几十上百个,则超出了单轮 Agent 派发的定位,官方指引是转用 Workflow 这类把编排移出对话上下文的机制,而不是硬塞进一轮里。

  4. 4

    子代理之间没有共享状态,synthesis 之类的下游子代理必须靠协调者显式喂料

    子代理的 context 从空白开始,协调者传给它的唯一内容就是那次 Agent 调用的 prompt 文本,不存在可读取的共享 session 或记忆。正确做法是协调者把完整发现连同来源、置信度等元数据,以内容与元数据分离的结构化格式直接写进 prompt;重新调用工具或把结果压缩成一段散文,都会丢失下游需要的可追溯性。

  5. 5

    给子代理的指令该是目标导向还是过程式,取决于任务是否需要适应性

    如果子代理频繁“报告结果不足”却不尝试替代方案、覆盖不了新出现的情况,根源往往是协调者下发了过于具体的过程指令(精确查询词、逐步顺序、来源优先级),压制了它自主调整的空间。修复方式是把指令换成目标和质量标准,而不是简单加大算力或调用次数上限——那只是买更多计算去重复同一套已经失败的步骤。

  6. 6

    子代理面对工具返回内容里出现的“指令”要一律当作数据,不当作命令

    工具结果是不可信的外部输入,子代理的任务权威只来自协调者的指示,它没有直接面向用户的信任关系,也不该转而去问用户,而应拒绝执行、按原计划继续或上报给协调者。

    注意这与“子代理输出里出现的指令状文本”是另一个方向的问题——官方文档描述的是协调者一侧会对子代理返回的最终消息做扫描,防止子代理的文本冒充系统级控制标记,这保护的是协调者不被子代理的输出污染,而不是子代理该如何对待工具结果里的指令,两者不要混为一谈。

反模式对照

常见做法

只在 system prompt 里写明协调者可以派生子代理,却没检查顶层 allowedTools 是否包含 Agent

正确做法

Agent 显式加入顶层 allowedTools,并与 AgentDefinition.tools/disallowedTools(子代理自身的工具白名单)区分开配置

没加入 allowedTools 不等于绝对调不出子代理,而是调用会落进权限确认或在免确认模式下被拒绝;站内“调不出子代理”类题目的根因几乎总是这里配置缺失,而不是 prompt 措辞不到位。

常见做法

对多个互相独立的子任务,逐一 spawn 子代理、等上一个返回再发起下一个

正确做法

把这些独立子任务的 Agent 调用放进同一轮响应里一次性发出,让子代理并发执行;任务规模上到几十上百个时改用 Workflow 这类脚本化编排

并行与否只看是否在同一轮 assistant 响应里发出多个工具调用,逐个等待即使任务独立也是串行,总耗时是各任务之和。

常见做法

认为下游子代理可以读取或恢复其他子代理的 session 来获取前序发现

正确做法

协调者把完整发现连同来源、置信度等元数据,结构化地写进下游子代理的 prompt

子代理的 context 从空白开始,协调者传给它的唯一内容就是那次调用的 prompt 文本,任何跨子代理的信息传递都必须由协调者显式搬运。

常见做法

子代理反复报告“结果不足”时,给它更精确的过程指令(逐步查询词、顺序、来源优先级)去纠正

正确做法

把过程指令换成目标和质量标准,让子代理自己决定如何达成

过程式指令锁死了子代理的探索路径,遇到没预设过的情形只会卡住;目标导向的指令才能保留它自主调整的空间。

练这个考点