Skill 如何被发现与加载
由描述驱动的触发,以及渐进式加载机制。
一句话理解:Agent 启动时只看每个 Skill 的“名片”(name 和 description),判断相关才翻开 SKILL.md,真正需要时才读附件或运行脚本。能不能触发取决于描述,花多少上下文取决于分层。
你给 Agent 装了一个精心打磨的客服工单处理 Skill,结果用户问“T-1042 这个工单怎么挂了三天没人管”,Agent 自己开始乱查,完全没用上它。这种情况很常见,而问题几乎从来不在 Skill 的正文,而在于 Agent 根本没有打开它。要设计和排查 Skill,必须先搞清楚:Agent 什么时候知道有这个 Skill?凭什么决定用它?加载之后,内容在上下文里停留多久?
三层加载
Agent Skills 规范把加载过程称为渐进式披露,分三层:
Anthropic 文档给出了每一层的量级:
| 层级 | 何时加载 | 上下文成本 | 内容 |
|---|---|---|---|
| 元数据 | 启动时,始终存在 | 每个 Skill 约 100 Token | name 与 description |
| 指令 | Skill 被触发时 | 建议少于 5000 Token | SKILL.md 正文 |
| 资源与代码 | 实际用到时 | 未访问时为零 | 参考文件读取后才占用;脚本运行后通常只有输出进入上下文 |
这就是为什么一个 Agent 可以同时装几十个 Skill:平时每个只占一行“名片”。它也解释了为什么 Skill 能附带大量参考资料:没被读取的文件,对上下文来说等于不存在。这背后的通用原则见「渐进式披露」。
触发是怎么发生的
最关键的一点:是否使用 Skill,是模型根据描述做出的判断,而不是关键词匹配。 Agent 看到的只有名字和描述,它要回答的问题是:“眼前这个任务,翻开这本手册会不会更好?”这带来几个直接后果:
- 描述承担了全部触发责任。 正文写得再好,描述没说清“什么时候用”,Skill 就不会被打开。
- 触发不是确定性的。 同一句话,这次触发,下次可能不触发。所以要用多条提问、多次运行来测触发率,而不是试一次就下结论。
- 简单任务可能不触发。 agentskills.io 的文档提到,Agent 通常只在任务超出自身能力时才去查 Skill。“读一下这个 PDF”这样的一步操作,Agent 自己就能完成,即使描述完全匹配也可能不加载 Skill。Skill 真正发挥作用的,是需要专门知识的任务:不熟悉的内部系统、领域专属流程、少见的数据格式。
把触发过程画出来,就是模型对每个请求做的一次判断,外加一条用户手动调用的捷径:
在 Claude Code 里,Skill 有两条调用路径,可以用元数据分别控制:
| 配置 | Claude 能否自动加载 | 用户能否用 /name 调用 |
描述是否常驻上下文 |
|---|---|---|---|
| 默认 | 能 | 能 | 是 |
disable-model-invocation: true |
不能 | 能 | 否,调用前零成本 |
user-invocable: false |
能 | 不能 | 是 |
有副作用的流程(发布、提交、发通知)适合设为只能手动调用;纯背景知识(比如某个遗留系统的说明)适合对用户隐藏。
描述也有长度预算。Claude Code 会把所有 Skill 的名字和描述组成一个列表放进上下文:每个 Skill 的 description 加 when_to_use 在列表中最多显示 1536 个字符;整个列表的预算按模型上下文窗口的 1% 计算,超出时优先截短最少使用的 Skill 的描述。这意味着描述要把最关键的使用场景写在最前面,否则一旦被截断,触发词就可能丢失。
加载之后发生什么
Skill 被触发后,它的内容不是“用完即走”。以 Claude Code 为例:
- 内容会留在对话里。 渲染后的
SKILL.md作为一条消息进入对话,后续轮次一直存在,Claude Code 不会每轮重新读文件。所以正文应该写成整个任务期间都成立的规则,而不是只适用于第一步的一次性指令。 - 上下文压缩时有保留预算。 自动压缩对话时,Claude Code 会重新附上最近调用过的 Skill,每个保留前 5000 Token,所有 Skill 合计不超过 25000 Token;如果一次会话调用了很多 Skill,较早的可能被丢掉。这也是正文要精炼、关键内容要放在前面的另一个原因。
- 可以在隔离上下文中运行。 设置
context: fork后,Skill 会在一个新的子 Agent 里执行,子 Agent 看不到主对话历史,只把结果返回主对话。适合会产生大量中间输出的任务,代价是 Skill 的指令必须能独立成立。 - 可以在模型阅读前注入实时数据。 Claude Code 支持在 Skill 正文里写
!`git diff HEAD`这样的语法,命令在模型看到内容之前执行,输出直接替换到正文里。这属于 Claude Code 的扩展,在 claude.ai 或 API 里不生效。
以 Claude Code 为例,一个 Skill 被触发之后的去向如下:
不同产品里的差异
同一个 Skill 文件夹,放进不同产品,加载位置和运行环境都不一样:
| 产品 | Skill 放在哪 | 运行环境要点 |
|---|---|---|
| Claude Code | 个人级 ~/.claude/skills/,项目级 .claude/skills/,也可以通过插件或企业托管配置分发;同名时企业级优先于个人级,个人级优先于项目级 |
与本机其他程序有相同的网络访问能力;修改 Skill 文件后在当前会话内即可生效 |
| Claude API | 通过 Skills API 上传,在工作区内共享;需要配合代码执行工具使用 | 在沙箱容器中运行,无网络访问,不能在运行时安装软件包 |
| claude.ai | 在设置中以 zip 包上传,仅对上传者本人可用;需要开启代码执行 | 网络访问取决于用户和管理员设置 |
| 其他支持该标准的产品 | 各自约定目录,比如 Hermes Agent 使用 ~/.hermes/skills/ 并按分类建子目录 |
以各产品文档为准 |
Anthropic 文档特别提醒:自定义 Skill 在不同平台之间需要分别管理,在一个地方上传的 Skill,不会自动出现在所有地方。如果 Skill 依赖联网(比如 Search Router 要调用搜索 API),放到无网络的 API 容器里就无法工作。这类环境要求应该写进 compatibility 字段。
常见误区
- “Skill 装得越多,Agent 越强。” 每个可自动触发的 Skill 都在每轮对话里占用描述位置;描述互相重叠时,Agent 更容易选错。Claude Code 提供了
/skill-doctor,用来找出从未被调用、却一直占用上下文的 Skill。 - “触发一次成功,就说明描述没问题。” 触发是概率性的,而且真实用户的说法五花八门。要用一组应该触发和不应该触发的提问,多次运行来测。
- “触发了就说明 Skill 有效。” Claude Code 文档说得很直接:看到 Skill 被触发,只能说明 Claude 找到了它,不代表它按你的预期完成了任务。触发和效果要分开评估,方法见「写好一个 Skill」。
产品经理检查清单
- 这个 Skill 的描述,能否让一个只看到这句话的人判断出“什么时候该用”?
- 用户在真实场景里会怎么说?描述覆盖了那些不含专业术语的说法吗?
- 它有副作用吗?有的话,是否应该只允许手动调用?
- 它依赖联网、特定命令行工具或本地文件吗?目标产品的运行环境满足吗?
- 同一个 Agent 里,是否有另一个 Skill 的描述和它高度重叠?
练习
在 Claude Code 里观察一次完整的加载过程(约 40 分钟):
- 在一个测试项目里创建
.claude/skills/目录,放入两个描述相近的 Skill,比如“销售周报生成”和“销售数据异常排查”。 - 启动
claude,先问 “What skills are available?”,确认两个 Skill 都被识别。 - 准备 6 句提问:各有 2 句明显指向其中一个 Skill,另外 2 句是容易混淆的说法(例如“上周华东为什么掉了这么多”)。先写下你预测会触发哪个,再实际运行。
- 每句提问在新会话里各跑 2 次,记录实际触发情况,并与预测对比。触发错误的,试着只修改描述再测一轮。
延伸阅读
- Agent Skills 概览(Claude 文档):三层加载机制、各平台支持情况与运行环境限制。
- Extend Claude with skills(Claude Code 文档):调用控制、描述预算、内容生命周期与故障排查。
- Optimizing skill descriptions:触发机制说明与触发测试方法。
下一步
下一课「Skill、Tool、Workflow 与 Subagent」会把 Skill 和它最容易混淆的几种能力放在一起比较,给出一套选择方法。