Agent SDK 是”造 Agent 的底座”:把 agent 循环、工具系统、上下文管理、会话与权限、MCP 通道、模型路由、子 agent 调度等一套原语整体打包给开发者;Subagent 是”底座上跑起来的一种实例”:拥有独立上下文窗口、独立工具集和独立 prompt 的子任务执行者。两者的关系是”SDK 提供机制,Subagent 使用机制”——SDK 决定了 Subagent 能”独立到哪一步、并行到多深、权限收到多细、模型切到多灵活”,脱离 SDK 直接手搓一个 Subagent,几乎一定会重写这些底层能力。
一、重新对齐两个词
“Agent SDK”通常指一套官方/厂商发布的开发框架,把”模型 + 工具 + 循环 + 上下文管理”打包成可编程原语;”Subagent”是这套原语运行时衍生出来的子任务执行单元,它跑在 SDK 维护的 agent 循环里,但拥有自己的上下文与工具子集。
不少团队误把 Subagent 当成”自己写在另一个文件里的 prompt”,结果要么上下文互相污染,要么并行跑不起来。问题往往出在没意识到 Subagent 的”独立上下文”不是 prompt 层面的,而是 SDK 把它作为一个独立 Agent 实例拉起——背后有会话隔离、工具隔离、模型隔离、追踪隔离四件套。
二、SDK 给 Subagent 提供的能力清单
把各家主流 Agent SDK(Claude Agent SDK、Codebuddy Agent SDK 等)暴露给 Subagent 的原语对齐到一张表上,差异一目了然。
| 底层能力 | SDK 提供给 Subagent 的形态 | 没有 SDK 时要自己手搓的部分 |
|---|---|---|
| 上下文窗口隔离 | SDK 拉起新 Agent 实例,对话历史不共享 | 自己写消息队列和窗口管理 |
| 工具集 | tools / disallowedTools 字段白名单/黑名单 |
重新接工具函数、做权限拦截 |
| 模型选择 | model 字段按子代理覆盖,支持别名与完整 ID |
自己串多 provider、计费、限流 |
| 嵌套递归 | 深度限制(如 5 层)+ lineage 数组追踪 | 自己写循环检测、超时与回滚 |
| 权限模式 | permissionMode 字段细粒度授权 |
自己实现工具调用前的审核 |
| MCP 通道 | 每个子代理可独立挂载 MCP 服务器 | 自己实现 MCP client 与生命周期 |
| 内存/技能 | memory 来源、skills 预加载 |
自己写加载逻辑、缓存与失效 |
| 追踪与可观测 | 父子链路 span、parent_tool_use_id |
自己串日志、trace 与指标 |
| 后台运行 | background: true 非阻塞调度 |
自己起进程/线程、回收句柄 |
| 超时与中断 | 内建超时与 stop 语义 | 自己写定时器、取消传播 |
这张表的关键是:每一行都是”SDK 替 Subagent 抹平的复杂度”。拿掉 SDK 直接拼,每个 Subagent 都会膨胀成一个小型 agent 框架。落地时通常按四步推进:
- 在 SDK 的
agents参数里声明 Subagent,写清 description、prompt、tools、model; - 父 agent 通过 Agent 工具按 description 自动或按名称显式调用 Subagent;
- SDK 拉起独立 Agent 实例跑 Subagent,工具调用与结果留在子上下文,仅最终消息回传;
- 父 agent 拿到 Subagent 摘要后继续推进,必要时并行调用多个 Subagent 提速。
三、独立上下文窗口是 Subagent 的命门
Subagent 的核心价值是”不污染主对话”。SDK 实现这一点的方式是:把 Subagent 拉成一个独立 Agent 实例,分配独立会话与上下文窗口;Subagent 内部的所有工具调用、结果、思考都留在自己的窗口里,只把最终摘要消息返回给父 agent。父 agent 收到的不是几十次工具结果,而是 1-2 段精炼后的总结,主对话的 token 消耗被压到极低。
如果脱离 SDK 实现,开发者要自己写:消息路由、窗口大小控制、上下文压缩、父子消息回传协议、错误恢复。每一项都是”看起来简单、做起来全是边界条件”的工程活。SDK 替 Subagent 把这些抽掉,开发者只关心”我让这个 Subagent 干什么”。
四、并行与嵌套:SDK 帮 Subagent 解决的两道难题
“并行”和”嵌套”是 Subagent 的两个高频用法,也是没有 SDK 时最常翻车的两个点。
并行:多个 Subagent 跑不同子任务,需要 SDK 提供独立会话、工具互不干扰、结果异步回传、流式输出。SDK 一般通过”每个 Subagent 一个 client + 共享任务队列 + 父 agent 用 Agent 工具统一消费”实现。脱离 SDK 自己起多进程,IO 调度、错误恢复、上下文回传都要重写。
嵌套:Subagent 再生成 Subagent,理论上无限递归,SDK 强制设定最大深度(如 5 层),并在每一层用 lineage 数组记录父 ID,便于追踪与排错。脱离 SDK 自己写,极易出现”无限递归把 token 烧穿”或”父子上下文循环依赖”。
五、工具与模型:SDK 给 Subagent 的精细控制
工具白名单是 Subagent 安全的关键。SDK 通过 tools(允许列表)和 disallowedTools(禁用列表)控制 Subagent 能调的工具——例如 doc-reviewer 只给 Read 和 Grep,避免它误改文件。SDK 还支持按 MCP 服务粒度(mcp__server、mcp__*)做整批工具禁用。没有 SDK,每个 Subagent 都要自己做工具注册、鉴权、拦截。
模型覆盖是 Subagent 性价比的关键。SDK 允许每个 Subagent 用不同模型,例如重逻辑的子任务用 opus,机械抓取用 haiku,省钱与质量可以按子任务粒度调。脱离 SDK 要自己串多 provider、计费、限流、回退策略。
六、落地代码示例
下面这段伪代码展示 Agent SDK 中 Subagent 的最小声明与调用方式。Subagent 的”独立上下文 + 工具集 + 模型覆盖”都通过 SDK 的字段表达,开发者不需要自己维护消息队列与会话隔离。
# 在 Agent SDK 里定义两个 Subagent
from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
options = ClaudeAgentOptions(
allowed_tools=["Read", "Grep", "Glob", "Bash", "Agent"],
agents={
"code-reviewer": AgentDefinition(
description="只读专家,做安全与风格审查",
prompt="你是只读代码审查员,只用 Read/Grep/Glob,不修改文件。",
tools=["Read", "Grep", "Glob"], # 工具白名单
model="sonnet", # 模型覆盖
),
"test-runner": AgentDefinition(
description="跑测试并分析结果",
prompt="你是测试执行专家,负责运行和分析测试套件。",
tools=["Bash", "Read", "Grep"],
model="haiku", # 轻量任务用便宜模型
),
},
)
# 父 agent 运行时调用 Subagent(SDK 负责上下文隔离与结果回传)
async for message in query(prompt="审查 auth 模块并跑测试", options=options):
print(message)
七、容易踩的三个坑
把 Subagent 当并发装饰:Subagent 的”独立上下文”是用 token 换的,太多 Subagent 并行会让总成本失控,按”必要才并行”的原则挑选。
把 Subagent 的工具集默认放开:不写 tools 字段会继承父 agent 全部工具,等于把 Bash 之类的危险工具下放给所有子任务,权限被穿透。
忽略 background 与 maxTurns 字段:默认情况下 Subagent 同步跑、轮数不限,长任务会卡住主流程。SDK 提供 background: true 与 maxTurns 让调度可控,自己手搓几乎一定会漏。
常见问题(FAQ)
Q1:Subagent 不调用 SDK 能跑吗?
可以手写,但等于自己实现一遍 SDK 的会话隔离、工具白名单、模型切换和递归限制。
Q2:Subagent 之间能共享上下文吗?
主流 SDK 默认不共享;可通过 memory 字段或外部存储做”记忆外挂”,但需开发者显式接入。
Q3:嵌套 Subagent 的深度受什么限制?
由 SDK 强制设定最大深度(通常 5 层),并通过 lineage 数组追踪父子链路防止无限递归。