一个 skill 就是一个带说明书的文件夹:说明书(description)常驻上下文供 Claude「认路」,正文只在被调用时加载。本课基于 2026-08-05 抓取的官方文档(Claude Code 2.1.222 水位)。
为什么这天学这个
D4 讲过 CLAUDE.md:全文常驻,每一轮请求都占着上下文,所以只放「永远要遵守」的短规则。但真实工作里还有另一类内容——部署清单、API 风格指南、调试手册——不是每轮都要,但要用时得完整。这正是 Skills 的位置:官方官方文档明确建议,当你反复往对话里粘贴同一套指令,或 CLAUDE.md 里某一节已经从「事实」长成「流程」,就该把它抽成 skill;skill 的正文只在用到时加载,长篇参考资料闲置时几乎不占上下文。
本课也是后半程的地基:D8 Hooks 会和 skill 对比「确定性执行 vs 模型理解执行」;D10 Subagents 会用到本课提到的 context: fork;D14 Plugins 讲的打包分发,装的主要货物就是 skill。跳过这课,后面三课都会缺一块拼图。
概念讲清楚
SKILL.md:frontmatter + 正文
官方每个 skill 是一个目录,入口文件固定叫 SKILL.md,分两部分:--- 包起来的 YAML frontmatter 告诉 Claude「什么时候用我」,后面的 markdown 正文是被调用时 Claude 执行的指令。目录名就是你输入的命令名(.claude/skills/deploy-staging/ → /deploy-staging)。所有 frontmatter 字段都可选,官方只推荐必写 description:
| 常用字段 | 作用 |
|---|---|
description | skill 做什么、什么时候用。Claude 靠它决定是否自动加载;省略时用正文第一段代替 |
when_to_use | 补充触发场景(触发短语、示例请求),追加在 description 之后;两者合计在列表里截断于 1,536 字符 |
disable-model-invocation | 设 true 后只有你能触发(适合 deploy、commit 这类有副作用的动作) |
user-invocable | 设 false 后从 / 菜单隐藏,只有 Claude 能用(适合纯背景知识) |
allowed-tools | 调用该 skill 的那一轮里免审批可用的工具;你发下一条消息时授权即清除 |
context: fork + agent | 在隔离的 subagent 里跑这个 skill(详见 D10) |
paths | glob 模式,只在操作匹配文件时才自动激活 |
官方正文里可以用 $ARGUMENTS / $0 接收参数,用 ${CLAUDE_SKILL_DIR} 引用 skill 自带的脚本;还有一种「动态上下文注入」:!`命令` 会在 Claude 看到内容之前先执行,输出直接替换占位符——所以 Claude 拿到的是真实数据,不是命令本身。官方建议 SKILL.md 保持在 500 行以内,细节资料拆成目录里的附属文件,让 Claude 需要时再读。
触发:description 是唯一的「广告位」
官方会话开始时,所有可被模型调用的 skill 只有名字和 description 进入上下文;Claude 拿你的请求和这些描述做匹配,命中才加载全文。你也可以直接输入 /skill-name 跳过匹配强制加载。这意味着:描述写得好不好,直接决定 skill 会不会被想起来。
描述怎么写才容易命中?官方排错指南给的方向:官方描述里要包含用户会自然说出的关键词,把最核心的使用场景写在最前面(超长会被截断);skill 不触发时,先确认它出现在「What skills are available?」的回答里,再换个更贴近描述的问法,或直接 /skill-name 调用。本站观点如果你平时用中文和 Claude Code 对话,就把中文触发词也写进 description——匹配是按语义做的,但把你真实会说的话原样放进去,命中率最稳。
一个隐蔽的坑:官方frontmatter 的 YAML 写坏时,Claude Code 会带着「空元数据」加载正文——/skill-name 手动调用照常能用,但 Claude 没有 description 可匹配,自动触发彻底失效。用 --debug 启动能看到解析报错。所以「手动能跑、从不自动触发」十有八九是描述缺关键词或 YAML 坏了。
放哪:三级位置与重名规则
官方skill 放在哪,决定谁能用:
| 级别 | 路径 | 生效范围 |
|---|---|---|
| 个人级 | ~/.claude/skills/<skill-name>/SKILL.md | 你的所有项目 |
| 项目级 | .claude/skills/<skill-name>/SKILL.md | 仅当前项目(可随仓库提交共享给团队) |
| 插件级 | <plugin>/skills/<skill-name>/SKILL.md | 启用了该插件的地方 |
| 企业级 | managed settings 下发 | 组织内所有用户 |
官方重名时按级别覆盖:企业级 > 个人级 > 项目级,且任何一级的同名 skill 会覆盖内置的 bundled skill(比如项目里放一个 code-review 就替换掉自带的 /code-review)。插件 skill 用 plugin-name:skill-name 命名空间,天然不冲突。skill 目录是被实时监听的:改动 SKILL.md 当场生效,不用重启会话(新建顶层 skills 目录除外)。这套「项目级随仓库提交、插件级打包分发」的路径,就是 D14 Plugins 的入口。
对比 CLAUDE.md:常驻 vs 按需(承接 D4)
官方官方给的分工原则:「每个会话都必须知道」的短规则(构建命令、编码约定、never-do 规则)放 CLAUDE.md,并保持在 200 行以内;偶尔才需要的参考材料和可触发的工作流做成 skill。两个细节值得记住:一是 skill 全文一旦被调用,会作为一条消息留在会话里直到结束,所以正文也要克制,别当垃圾场;二是给 skill 设 disable-model-invocation: true 后连描述都不进上下文,对只手动触发的 skill 是零闲置成本。本站观点判断口诀:内容是「事实和规矩」进 CLAUDE.md,是「流程和资料」进 skill;CLAUDE.md 里哪一节开始出现第 1、2、3 步了,就是它该搬家的信号。
slash command 就是 skill
官方自定义命令已并入 skill 体系:.claude/commands/deploy.md 和 .claude/skills/deploy/SKILL.md 都会创建 /deploy,行为一致;旧的 commands 文件继续有效。skill 是超集,多出三样:整目录附属文件、控制「谁能调用」的 frontmatter、以及被 Claude 按描述自动加载的能力。内置的 bundled skills(/code-review、/debug、/loop 等)也是同一套机制。默认你和 Claude 都能调用任何 skill;两个开关把它变成「只许你」(disable-model-invocation: true,适合 deploy 这种你要掌握时机的动作)或「只许 Claude」(user-invocable: false,适合没有动作意义的背景知识)。
当天能做完的实操
目标:做一个「总结未提交改动」的最小 skill,验证自动触发、手动调用、按需加载三件事。全程约 20 分钟,需要一个 git 项目。
- 创建 skill 目录(个人级,所有项目可用):
预期:目录创建成功。注意:如果mkdir -p ~/.claude/skills/summarize-changes~/.claude/skills/是本次新建的,而 Claude Code 会话已开着,先重启会话让它监听新目录;之后再改文件就都是当场生效了。 - 把下面内容存为
~/.claude/skills/summarize-changes/SKILL.md(结构来自官方入门示例,描述做了中文化以贴合你的真实问法):
其中--- name: summarize-changes description: 总结当前未提交的 git 改动并标出风险点。当用户问「我改了什么」「帮我看看这次改动」、想要 commit message 或让你 review diff 时使用。Summarizes uncommitted changes and flags anything risky. --- ## 当前改动 !`git diff HEAD` ## 指令 用两三条要点总结上面的改动,然后列出你注意到的风险, 例如缺少错误处理、写死的值、需要同步更新的测试。 如果 diff 为空,直接说没有未提交的改动。!`git diff HEAD`是动态上下文注入:skill 被调用时先跑这条命令,把真实 diff 塞进指令再交给 Claude。 - 在任一 git 项目里随手改一个文件,启动
claude,先测自动触发——用贴近描述的自然问法:
预期:界面显示 skill 被加载,Claude 给出基于真实 diff 的两三条总结加风险清单——而不是靠猜。我刚才改了哪些东西?帮我总结一下这次改动,并指出有没有风险。 - 再测手动调用:输入
/summarize-changes。预期:跳过描述匹配,直接得到同样结构的输出。 - 验证按需加载:问 Claude「What skills are available?」确认 skill 已注册;再回想 D1 学过的
/context,Skills 一行显示的只是描述列表的开销——全文是刚才被调用后才进的上下文。 - 进阶一步:在 frontmatter 里加一行
disable-model-invocation: true,保存(不用重启),再问一遍第 3 步的问题。预期:不再自动触发(描述已从上下文移除),但/summarize-changes依然能用。体会完删掉这行恢复。
最后,把这套能力用到真实工作上——让 Claude Code 帮你把一个常用流程沉淀成项目级 skill:
帮我把「发布前检查」流程沉淀成一个项目级 skill:
- 创建 .claude/skills/release-check/SKILL.md
- description 写清触发场景,包含我平时会说的关键词(发布、上线、release)
- 正文列出检查步骤,每步可验证
- 设 disable-model-invocation: true,只允许我手动 /release-check 触发
写完后展示文件内容,并逐个解释 frontmatter 字段的作用。
验收标准
- 能不看笔记说清 SKILL.md 的两段结构,以及 description 在「会话开始时」和「触发时」分别扮演什么角色。
- 能画出(或口述)本课第一张图:自动匹配和
/skill-name两条触发路径的区别。 - 能解释同一份内容放 CLAUDE.md 和做成 skill 的上下文成本差异,并说出各自适合装什么。
- 实操 6 步全部跑通:自动触发、手动调用、
disable-model-invocation的效果都亲眼见过。 - 自测题:一个 skill 用
/name能正常跑,但 Claude 从不自动使用它。说出两个最可能的原因和对应修法。(提示:一个在 description 的内容上,一个在 frontmatter 的语法或开关上。)