Claude Code SDK #18:Skills 全解——按需加载指令包、动态上下文注入、子 Agent 执行,让 Claude 的「技能库」可编程

Claude Code SDK #18:Skills 全解——按需加载指令包、动态上下文注入、子 Agent 执行,让 Claude 的「技能库」可编程

Skills 把 CLAUDE.md 里的步骤性程序剥出来,变成按需加载的独立指令包,大幅降低日常 token 消耗。本篇完整拆解 Skills 的文件结构与四级作用域、Frontmatter 全部字段(description/disable-model-invocation/allowed-tools/paths/context 等)、调用控制三状态、动态上下文注入(!`cmd` 预处理语法)、context: fork 子 Agent 隔离执行、auto-compaction 后的 token 预算机制,以及 skillOverrides 的四种可见性控制,附五条可落地的实践建议。

Claude Code SDK 每日技术拆解
2026/6/11 · 9:09
3 订阅 · 52 内容

研究速览

每次开 Claude Code 都要重新粘那段「请按这套规范写代码」?
用过 CLAUDE.md 的人到后来都会碰到同一个问题:CLAUDE.md 越来越长,token 越烧越快,而大半内容其实只在特定场景用得上。Skills 就是为了解这个问题而存在的——它把那些「按需才用」的指令从常驻上下文里剥出去,只在真正需要的时候加载进来。1

Skills 是什么,和 CLAUDE.md 有何不同

Skills 本质是带文件夹的、可按需触发的「指令包」。和 CLAUDE.md 的根本区别只有一条:CLAUDE.md 每次 session 都完整加载;Skills 的 body 只有被调用时才进入上下文。
这意味着长达几百行的 API 规范、代码模板、部署检查单,放在 Skills 里几乎不产生日常 token 成本,调用时才占用一次上下文窗口。
另一个值得注意的事实:原来 .claude/commands/ 下的自定义命令文件,和 Skills 现在是同一套机制。一个 .claude/commands/deploy.md 和一个 .claude/skills/deploy/SKILL.md 都创建 /deploy 命令,效果完全相同。只是 Skills 支持更多特性——目录结构、调用控制、子 Agent 执行。

文件结构:最小布局和扩展目录

每个 Skill 是一个目录,里面必须有一个 SKILL.md
~/.claude/skills/summarize-changes/
└── SKILL.md           # 必须
需要更复杂的功能时,可以按需增加支撑文件:
my-skill/
├── SKILL.md           # 主指令(必须)
├── reference.md       # 详细参数文档,按需加载
├── examples.md        # 样例输出
└── scripts/
    └── helper.py      # 可执行脚本
一个重要的成本控制建议SKILL.md 尽量控制在 500 行以内。大段参考文档放到 reference.md 里,在 SKILL.md 里说明「需要详细参数时看 reference.md」,Claude 就能在真正需要时才加载它,而不是每次调用都把全部内容一口气塞进来。

SKILL.md 的 Frontmatter 字段全解

每个 SKILL.md 顶部可以加 YAML frontmatter,控制 Skill 的名称、触发条件、工具权限等行为。所有字段都是可选的,但 description 强烈建议加上:
---
name: deploy-staging

disable-model-invocation: true
allowed-tools: Bash(git *) Bash(npm run *)
---
核心字段速查:
字段作用
descriptionClaude 据此判断何时自动调用该 Skill
when_to_use补充触发场景,和 description 合并后最多 1536 字
disable-model-invocationtrue 时 Claude 不会自动触发,只能用 /skill-name 手动调用
user-invocablefalse 时从 / 菜单隐藏,只有 Claude 能自动加载
allowed-toolsSkill 激活期间免确认的工具列表
disallowed-toolsSkill 激活期间禁用的工具(下次发消息后解除)
model覆盖当前 session 模型,本次 turn 结束后恢复
effort覆盖当前 effort 级别(low/medium/high/xhigh/max)
context设为 fork 时在隔离子 Agent 中运行
pathsglob 模式,只有在匹配路径下工作时才自动激活
description + when_to_use 的合并文本在 Skill 列表里会被截断到 1536 字。把最关键的触发条件写在 description 最前面,不然在 Skill 数量多的时候可能被截掉。1
正在加载统计卡片…

四个存放位置与优先级

Skills 放在哪里决定了谁能用它:
级别路径覆盖范围
Enterprise(企业)Managed settings组织内所有用户
Personal(个人)~/.claude/skills/<name>/SKILL.md你的所有项目
Project(项目).claude/skills/<name>/SKILL.md仅此项目
Plugin(插件)<plugin>/skills/<name>/SKILL.md插件启用范围
优先级:Enterprise > Personal > Project。当 Personal 和 Project 的 Skill 同名时,Personal 的覆盖 Project 的。
插件 Skills 使用 plugin-name:skill-name 命名空间,不会和其他级别冲突。
还有一个关于 --add-dir 的特殊规则:通过 --add-dir/add-dir 加入的目录里的 .claude/skills/ 会被自动加载,但 settings.jsonpermissions.additionalDirectories 配置的目录不加载 Skills——这个区别经常被忽视。

调用控制:谁能触发、谁不能

默认状态下,你可以手动 /skill-name 调用,Claude 也可以自动触发。两个 frontmatter 字段改变这个默认行为:
# 只有我能触发,Claude 不会自动跑
disable-model-invocation: true

# 从 / 菜单隐藏,只有 Claude 能自动加载(用于背景知识类 Skill)
user-invocable: false
三种状态的完整对比:
设置你能手动调用Claude 能自动触发Description 在上下文中
默认始终在
disable-model-invocation: true不在(也不会被搜到)
user-invocable: false始终在
deploysend-slack-messagecommit 这类有副作用的操作,都应该加 disable-model-invocation: true——你不希望 Claude 因为看到代码「看起来可以了」就自己跑部署。
正在加载统计卡片…

动态上下文注入:让 Skill 加载真实数据

这是 Skills 里最值得单独说明的机制。在 SKILL.md 里用 !`command` 语法,可以在 Skill 内容送达 Claude 之前先执行一条 shell 命令,把输出直接替换进去。1
---

---

## 当前变更 {#当前变更}
!`git diff HEAD`

## 指令 {#指令}
总结上方变更的要点,列出任何你注意到的风险…
执行时,!`git diff HEAD` 先跑,真实 diff 内容替换进来,Claude 收到的是已经填好数据的完整提示词。这不是 Claude 在执行命令——是「预处理」,Claude 只看到结果。
多行命令用 ```! 代码块:
## 当前环境 {#当前环境}
```!
node --version
npm --version
git status --short

一个重要的边界条件:`!` 必须出现在行首或空格之后。如果写成 `` KEY=!`cmd` ``,因为 `!` 跟在 `=` 后面,不会被执行,会原样保留为字面文本。

要禁用这个特性,在 `settings.json` 里设 `"disableSkillShellExecution": true`。企业环境里常用这个配置,防止项目 Skill 里的 shell 命令在不受控环境执行。

## 子 Agent 执行:`context: fork` {#子-agent-执行context-fork}

给 Skill 加 `context: fork`,它的内容会变成一个隔离子 Agent 的任务 prompt,在独立上下文里运行,结果汇报给主会话。这对需要大量文件读取、不想污染主会话上下文的探索性任务很有用:

```yaml
---
name: deep-research

context: fork
agent: Explore
---

深入研究 $ARGUMENTS:
1. 用 Glob 和 Grep 找相关文件
2. 读取并分析代码
3. 用具体文件引用总结发现
agent 字段可以指定内置类型(ExplorePlangeneral-purpose)或 .claude/agents/ 里的自定义子 Agent。省略时使用 general-purpose
加了 context: fork 的 Skill 不会被 disable-model-invocation: true 阻止自动加载——这是文档里明确说明的一个细节。

Skill 内容在 session 中的生命周期

Skill 被调用后,其内容作为一条消息进入对话,在整个 session 里持续存在——Claude 不会重新读文件,所以要把「应该贯穿整个任务」的指令写成常驻式说明,而不是「执行一次就过」的步骤。
auto-compaction(上下文压缩)触发时,被调用的 Skills 会在摘要后重新附着,但有上限:每个 Skill 保留前 5000 token,所有 Skill 共享 25000 token 的总预算。预算从最近调用的 Skill 开始填充,老的 Skill 可能被完全丢弃。
如果压缩后某个 Skill 的指令似乎不再起作用,重新 /skill-name 调用一次即可恢复完整内容。
正在加载统计卡片…

skillOverrides:不改 SKILL.md 就能控制可见性

如果不想修改某个项目里共享的 SKILL.md(比如团队仓库里的 Skill),可以在 .claude/settings.local.json 里用 skillOverrides 覆盖它的可见性:
{
  "skillOverrides": {
    "legacy-context": "name-only",
    "deploy": "off"
  }
}
四种状态:
Claude 能看到/ 菜单显示
"on"名称 + 描述
"name-only"仅名称
"user-invocable-only"隐藏
"off"隐藏
/skills 菜单里高亮一个 Skill 按 Space 可以循环切换这四个状态,Enter 保存到 .claude/settings.local.json,不需要手动编辑文件。

五条可落地的实践建议

建议一:CLAUDE.md 里的「步骤性程序」搬到 Skills CLAUDE.md 适合放「事实性规范」(命名约定、架构约束),但「多步操作流程」(如何写 commit message、如何部署)占着常驻 token 很浪费。把它们搬到 Skills,加 disable-model-invocation: true,按需 /skill-name 触发。
建议二:description 的首句决定触发质量 Claude 匹配 Skill 时用的是 description 文本。把最重要的触发场景放在第一句:「用户要求部署、commit 或上线时使用」比「这是一个部署工具」触发准确率高得多。
建议三:paths 字段做单仓库多包隔离 monorepo 里不同包有不同规范?在各包的 .claude/skills/ 下放对应 Skill,加 paths: packages/frontend/** 这样的 glob,只在操作该包文件时才激活,避免后端 Skill 干扰前端操作。
建议四:shell 注入做「数据预取」不做「逻辑执行」 !`cmd` 的最佳用途是把动态数据喂给 Claude(当前 diff、环境变量列表、配置文件内容),而不是把完整业务逻辑塞进一条命令。逻辑复杂时写成 scripts/ 下的脚本,Skill 只调用脚本。
建议五:多 Skill 压缩后注意 token 预算 session 里调用超过 5 个 Skill 后,压缩可能截断早期 Skill 内容。对「贯穿整个 session」的核心规范 Skill,可以在每次重大上下文切换后重新 /skill-name 调用一次刷新它在上下文里的位置。

围绕这条内容继续补充观点或上下文。

  • 登录后可发表评论。