教程Claude Code系列第 6 / 8 篇

Claude Agent SDK 进阶:子代理、skills 和插件,把 Claude Code 里的三样东西用进你的程序

子代理、skills、插件这三样在 SDK 里都能用。本站用 Python 逐样实测:三个子代理并行时,一次 query() 先后来了四个结果消息,第一个只是“它们还在跑”;这个小任务上用子代理比主代理自己做慢一倍、贵三倍多;子代理的模型别名在中转站上不一定有。skill 默认就会被发现,插件一行配置加载。

发布 · 约 12 分钟读完 · 经人工审核()· 本站的内容怎么来的

本站已实测,测试日期:2026-10-13。

本页目录(6 节)
  1. 一、子代理
  2. 二、skills
  3. 三、插件
  4. 三样怎么选
  5. 这一趟花了多少
  6. 这次实测没有覆盖到的

这篇教程解决一个问题:前五篇做出来的智能体是“一个代理加几个自己写的工具”。Claude Code 里还有三样用来组织大一点的活的东西:子代理、skills、插件。它们在 SDK 里怎么用,各自有什么坑?

接着用前几篇的订单助手。

实测环境
日期 2026-10-13
版本 claude-agent-sdk 0.2.165
系统 Ubuntu 24.04,Python 3.12.3
模型 主代理用 Sonnet 5.5
连接方式 通过酷站AI 中转站,不是直连官方
示例 前几篇的订单库和三个工具:查订单、看详情、退款

一、子代理

定义一个

子代理是主代理可以派出去干一件具体的事的另一个代理:有自己的系统提示、自己的工具清单,可以用不同的模型。在 SDK 里直接写在选项里:

from claude_agent_sdk import query, ClaudeAgentOptions, AgentDefinition
from shop_tools import shop      # 前几篇的工具服务器

READ = ["mcp__shop__find_orders", "mcp__shop__get_order"]

ANALYST = AgentDefinition(
    # description 是给主代理看的:什么时候该派它
    description="订单分析员。给它一位客户的名字,它查出这位客户的全部订单并写一份三行以内的小结。",
    # prompt 是子代理自己的系统提示
    prompt="你是网店的订单分析员。用工具查出指定客户的全部订单,然后写一份三行以内的小结:有几张订单、合计多少钱、有没有需要跟进的。只根据查到的数据说话。",
    tools=READ,            # 只给两个只读的工具
    model="sonnet",
)

options = ClaudeAgentOptions(
    model="claude-sonnet-5-5",
    mcp_servers={"shop": shop},
    system_prompt="你是网店的客服主管。用中文回答。每次回答的最后另起一行写上【主管口径】。",
    tools=["Agent"],                       # 内置工具只留派子代理用的这一个
    allowed_tools=["Agent"] + READ,
    permission_mode="dontAsk",
    agents={"order-analyst": ANALYST},
)

和前几篇不同的地方有两处。前几篇用 tools=[] 去掉了全部内置工具;主代理是靠内置的 Agent 工具派子代理的,所以这里要把它留下,并且预先批准。

AgentDefinition 里多个单词的字段名(disallowedTools、mcpServers、maxTurns)保留了驼峰写法,没有改成 Python 的下划线风格,文档特意提了这一点。

实测一:三个子代理并行,结果消息来了好几个

任务是“给张伟、李娜、王芳各做一份订单小结,每位交给一个子代理,三个同时派出去”。按时间打印收到的消息:

 4.0s [派出子代理] order-analyst  交代的话:客户姓名:张伟。请查出这位客户的全部订单……
 4.6s [派出子代理] order-analyst  交代的话:客户姓名:李娜……
 5.6s [派出子代理] order-analyst  交代的话:客户姓名:王芳……
 8.7s   (子代理内)[调用] mcp__shop__find_orders {"customer": "张伟"}
 9.5s   (子代理内)[调用] mcp__shop__find_orders {"customer": "李娜"}
 9.9s   (子代理内)[调用] mcp__shop__find_orders {"customer": "王芳"}
11.1s [主代理] 我已经同时派出三个 order-analyst 子代理……它们现在还在运行。三份小结都回来后,我会原样列给你。
11.8s [结束] success  累计花费估算=0.0349
17.8s [主代理] 李娜的小结已经回来了。张伟和王芳的两位子代理还在运行……
18.0s [结束] success  累计花费估算=0.0453
18.0s [结束] success  轮数=0  累计花费估算=0.0453
22.3s [主代理] 三位客户的订单小结如下,都是子代理的原文。……
22.3s [结束] success  累计花费估算=0.053

一次 query(),来了四个“结束”。 第一个在 11.8 秒,那时主代理说的是“它们还在运行”。第三个是空的,轮数是 0。真正的答案在 22.3 秒的最后一个里。

原因是子代理默认在后台运行。主代理派出去之后不等,先把这一轮结束掉;子代理陆续回来时,主代理被重新叫起来,每次又产生一个结果消息。

这和第一篇里“一次运行最后来一个 ResultMessage”的印象不一样,写程序时要改两处:

  • 不要收到第一个 ResultMessage 就停。 把消息流读到自然结束,取最后一个。
  • 花费看最后一个。 每个结果上的 total_cost_usd 是到那时为止的累计,依次是 0.0349、0.0453、0.0453、0.053,不要相加。

让它等齐了再答

在提示里说清楚要前台运行:

……子代理用前台方式运行(run_in_background 设为 false),三份结果都拿到之后再回答。

 5.2s [派出子代理] order-analyst 后台=False ……
 ……
19.3s [主代理] 三位客户的小结如下,均为子代理的原文。……
19.3s [结束] success  花费估算=0.0484

这次只有一个结果消息,里面就是完整的答案。

AgentDefinition 有一个 background 字段,设成 True 是强制后台。反过来强制前台的字段没有,只能像上面这样在提示里要求,或者写进主代理的系统提示。

怎么认出子代理的消息

  • 主代理派子代理,是一次名叫 Agent 的工具调用,参数里的 subagent_type 是子代理的名字。文档提醒老版本里这个工具叫 Task,要兼容就两个名字都认。本站看到启动信息的工具清单里它仍然显示为 Task。
  • 子代理内部产生的消息,parent_tool_use_id 不为空。上面输出里标着“子代理内”的行就是靠它分出来的。

实测二:值不值

同一个任务,四种做法:

做法 总用时 花费估算 结果消息
不用子代理,主代理自己查 10.4 秒 0.0145 美元 1 个
三个子代理,前台 19.9 秒 0.0484 美元 1 个
三个子代理,后台(默认) 22.8 秒 0.053 美元 4 个
三个子代理,但并发上限设成 1 49.2 秒 0.0634 美元 4 个

在这个任务上,用子代理慢了一倍,贵了三倍多。

原因不难理解。每个子代理是一个新开的对话,有自己的系统提示和工具说明,都要从头付一遍。任务本身只是查两三次数据库,分出去省下的还不够开销。

子代理划算的情况是另一种:每个子任务要读很多东西。文档讲的第一条好处就是这个:子代理翻了几十个文件,这些内容留在它自己的对话里,回到主代理手上的只有一段结论。主对话不会被撑满。子任务又长又互不相干时,并行才真的省时间。

所以先问一句“子任务大不大”。小的,让主代理自己做。

实测三:子代理看得到什么

主代理的系统提示里有一条“每次回答末尾写上【主管口径】”。本站告诉主代理一个暗号,让它派子代理去问两件事,并且不许把暗号和标记告诉子代理。子代理的原话:

一,我不知道客户的暗号。依据:我手头只有系统提示和你的这条消息,里面都没有提到任何“暗号”。

二,不需要加固定标记。依据:系统提示只要求我写三行以内的小结……没有规定结尾要加标记。

两样都不知道。文档里的对照是这样的:

子代理拿得到 子代理拿不到
它自己的系统提示,和主代理派它时写的那段话 主代理的对话历史和工具结果
项目的 CLAUDE.md 主代理的系统提示
工具的定义(继承主代理的,或者 tools 里列的那几个) 没有在 skills 字段里列出的 skill 内容

主代理传给子代理的只有派它时写的那一段话。 子代理需要的文件路径、报错信息、已经做过的决定,都得写在那段话里。主代理自己写这段话,所以要在主代理的系统提示里交代清楚“派活时要带上什么”。

实测四:工具限制是硬的

子代理只有两个只读工具。让主代理派它去退款,它的原话:

我没能给订单 A1002 办理退款。我可用的工具只有 find_orders 和 get_order,都是只读的。我没有退款、取消订单或修改订单的工具,所以什么都没改。

tools 里没列的,子代理手上就没有。这是把“只许看、不许改”落到实处的办法,比在提示里写“请不要退款”可靠。

实测五:给它套上限

Claude 自己决定派几个子代理,子代理还能再派子代理。文档给了三道上限:

上限 怎么设 默认
嵌套几层 环境变量 CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH 3 层。设成 1 就不许子代理再派
同时几个 环境变量 CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS 20 个
花多少钱 选项 max_budget_usd 不限。子代理的花费算在里面

环境变量通过 env 选项传:

ClaudeAgentOptions(..., env={"CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS": "1"})

本站把并发设成 1 试了一下。主代理同时派了三个,后两个被拒绝。它的反应:

目前只派出了张伟的那一个……李娜和王芳的两个没派出去。系统限制同时只能跑 1 个子代理,所以这两次调用被拒绝了。

然后它自己改成了一个一个地派,最后三份都做完了,用了 49 秒。上限生效,任务也没有因此失败。

一个坑:模型别名在中转站上不一定有

本站第一次跑的时候,子代理的模型写的是 haiku,想用便宜的模型干简单的活。结果三个子代理全部失败。主代理发现了:

三个子代理都失败了。原因是 order-analyst 配置的模型 claude-haiku-5-5 不存在,和订单数据无关。我换用 sonnet 模型重新派出这三个。

它自己换了模型重派,任务完成了,但多花了一轮:36 秒,0.0732 美元。

单独验证:主模型直接写 haiku,得到的是

There's an issue with the selected model (claude-haiku-5-5). It may not exist or you may not have access to it.

haiku 这个别名被解析成了 claude-haiku-5-5,而本站这把 key 用不了这个模型。这不是 SDK 的问题,是接入方式的问题:别名指向哪个模型是 Claude Code 定的,你的 key 支不支持是中转站定的,两边对不上就会这样。

三种处理办法:

  • 子代理写一个你确认能用的完整模型 ID;
  • 写 model="inherit",跟主代理用同一个;
  • 用环境变量 ANTHROPIC_DEFAULT_HAIKU_MODEL 把别名改指到一个能用的模型。

这次主代理能自己绕过去,是运气。写程序时别指望它,先把每个要用的模型单独试一遍。

二、skills

它在 SDK 里是什么

skill 是一份 SKILL.md:一段说明,Claude 判断用得上的时候才去读。和子代理不一样,skill 只能是磁盘上的文件,SDK 没有用代码注册 skill 的办法。

本站给订单助手写了一份退款政策,放在项目的 .claude/skills/refund-policy/SKILL.md:

---
name: refund-policy
description: 网店的退款政策。用户问某张订单能不能退、怎么退时使用。
---

退款政策(内部条款):

- R-1:已付款、还没发货的订单,随时可以全额退。
- R-2:已发货、还没签收的订单,要先拦截物流,拦截成功后全额退。
- R-3:已签收的订单,签收后 7 天内可以退,运费由客户承担;超过 7 天不退。
- R-4:已经退过款的订单不能再退。

回答时先用工具查出订单的状态,再指出适用的条款编号。

实测:五种配置

问题都是“订单 A1001 现在还能退吗?”(它是一张已签收的订单)。选项里 cwd 指向项目目录,tools=["Skill"]。

配置 启动信息里有没有这个 skill Claude 调用了它吗 回答
什么都不加 有 调用了 引用了 R-3,并指出系统里没有签收日期、没法确定是否在 7 天内
skills=[] 有 没有 只说“已签收的订单系统不允许退款”,没有提政策
skills=["refund-policy"] 有 调用了 引用了 R-3
setting_sources=["user"] 没有 没有 同第二行
提示写成 /refund-policy 订单 A1004 能退吗 有 没有经过 Skill 工具,内容直接生效 引用了 R-4

读出四件事:

  • 默认就会被发现。 不设 skills、不设 setting_sources,项目目录里的 skill 就能用。文档说默认的 query() 会读用户和项目两层设置。
  • skills 选项管的是“Claude 能不能调用”。 设成空列表,它就不调用了。但启动信息的 skills 数组里照样列着这个 skill,文档说明那个数组不随名单变化。不要拿那个数组判断 skill 有没有启用。
  • 不读项目设置,skill 就不存在。 第四行只读用户设置,项目里的 skill 没被发现。部署那篇讲多用户隔离时建议 setting_sources=[],那样做的同时,文件系统里的 skill 也一并没了,要换成下面的插件来加载。
  • /名字 是直接点名。 不经过 Claude 的判断,内容直接进来。

有 skill 和没有的差别,在第一行和第二行的回答里很清楚:没有 skill 时,Claude 只知道退款工具的硬规则(“已签收不能退”);有了之后,它知道政策是“签收 7 天内可以退”,并且注意到数据里缺一个判断所需的日期。

文档里一条配置上的提醒:如果你像本站这样显式写了 tools 列表,要把 "Skill" 放进去,否则 Claude 没有调用 skill 的工具。

三、插件

插件是把 skills、子代理、hooks、MCP 服务器打成一包。在 SDK 里用一行配置加载一个本地目录:

options = ClaudeAgentOptions(
    ...,
    plugins=[{"type": "local", "path": "/home/ccdemo/demo/c7-plugin/express-kit"}],
)

type 只能是 "local"。路径指向插件的根目录,相对路径按 cwd 算。市场里的插件要先下载到本地,再把目录给它。

本站加载的是做一个插件分享给团队里那个 express-kit。启动信息里多出来的:

插件:['express-kit']
skills 里:'express-kit:commit-message'
代理里:'express-kit:test-reviewer'
命令里:'express-kit:commit-message'

插件里的东西都带上了插件名做前缀。 skill 叫 express-kit:commit-message,子代理叫 express-kit:test-reviewer。这样两个插件里有同名的 skill 也不会撞。

两种用法都试了:

提示 发生了什么 回答 花费估算
/express-kit:commit-message 我把 getUser 改名成了 fetchUser…… 直接点名,1 轮 refactor: rename getUser to fetchUser and update call sites 0.0094 美元
“帮我写一条提交说明:我把 getUser 改名成了 fetchUser……” Claude 自己选了这个 skill,调用时用的是带前缀的名字 refactor: rename getUser to fetchUser and update all call sites 0.0225 美元

两次都得到了符合团队格式的一行。直接点名更便宜,少了“判断该用哪个”的那一轮。

确认插件加载上没有,看启动信息里的 plugins 数组。

三样怎么选

你想要的 用 在 SDK 里怎么给
把一件大事分给几个互不干扰的帮手,或者让某个帮手只有一部分工具 子代理 agents={...},写在代码里
一份只在用得上时才加载的说明、政策、流程 skill 磁盘上的 SKILL.md,靠设置来源发现
把上面这些连同 hooks 打成一包,在几个程序之间复用 插件 plugins=[{"type": "local", "path": ...}]
让它能查你的数据、调你的接口 自定义工具 见第二篇

子代理和 skill 的区别可以这样记:skill 是往当前对话里加一段说明,干活的还是同一个代理;子代理是另开一个对话,有自己的上下文,只把结论带回来。

这一趟花了多少

约 0.45 美元。子代理的八次运行约 0.3 美元,最贵的是模型别名失败又重派的那一次;skills 和插件的七次运行约 0.13 美元。主代理和子代理用的都是 Sonnet 5.5。这是 SDK 按官方价格估算的数字,不是中转站实际扣的金额。

这次实测没有覆盖到的

  • 用文件定义子代理(.claude/agents/)没有在 SDK 里试,只试了写在代码里的。
  • 子代理的嵌套、恢复一个中断的子代理、max_budget_usd 对子代理的限制没有试。
  • “小任务上不划算”只比较了一个任务,每种做法只跑了一次。 读很多文件的大任务上子代理能省多少,没有数据。
  • AgentDefinition 的 skills(预先加载)、mcpServers、permissionMode 等字段没有试。
  • 插件里的 hook 和 MCP 服务器在 SDK 里有没有正常工作,没有验证。 只看了 skill 和子代理。
  • setting_sources=[] 时用 plugins 加载 skill 是否可行,没有实测,这个组合要给密钥,原因见部署那篇。
  • TypeScript 没有实测。
  • 只在酷站AI 一家中转站上测过。 别家支持哪些模型,各不相同。

来源

通过 API 中转站使用这些模型?到 Veridrop 免费检测中转站给你的是不是它声称的模型,或者看看中转站排行榜前 100 名。

相关文章