从一个矛盾说起
当你反复使用 Claude Code 处理工作,很快会遇到一个矛盾:
一方面,你希望 Claude 掌握越来越多的专业流程和领域知识------你们团队的 commit 规范、部署 checklist、某类文档的处理套路、某个数据库的表结构......知道得越多,它越好用。
另一方面,上下文窗口是有限且昂贵的 。如果把这些知识全塞进 CLAUDE.md(每次会话开始就整体加载的项目说明文件),那么无论这次对话用不用得上,你每一轮都在为这些内容付费。知识越多,固定开销越大,最终把上下文挤爆。
Skill 机制就是对这个矛盾的回答。 它的核心思想可以用一句话概括:
让 Claude "知道"自己拥有大量专业能力,但只在真正需要时才把对应的详细内容加载进来。
理解了这个核心矛盾,Skill 的所有设计------目录结构、description 的写法、脚本的组织方式------都会变得顺理成章。本文就从这个角度,把 Skill 讲透。
一、Skill 是什么
Skill 本质上是一个"按需加载的、可复用的指令包"------一个目录,里面装着 Claude 完成某类任务时要遵循的操作说明,以及可能用到的脚本、模板、参考文档。
它的最小形态只需要一个文件:
objectivec
skill-name/
└── SKILL.md (唯一必需的文件)
完整形态则可以打包各种辅助资源:
objectivec
skill-name/
├── SKILL.md (必需)
│ ├── YAML frontmatter ------ 元数据,name、description 必填
│ └── Markdown 正文 ------ 具体操作指令
├── scripts/ (可选)------ 可执行代码
├── references/ (可选)------ 按需加载的参考文档
└── assets/ (可选)------ 直接用于产出物的文件
可以把一个 Skill 理解成给某个专业领域写的**"入职手册"**:它把"遇到这类任务该怎么一步步做"的流程性知识固化下来,让一个通用的 Claude 瞬间变成这个领域的专家。
二、核心设计原则:渐进式加载(Progressive Disclosure)
这是 Skill 的灵魂,必须先讲清楚,后面的结构设计才有意义。
Skill 采用三层加载模型,每一层的加载时机和成本完全不同:
| 层级 | 内容 | 加载时机 | 成本 |
|---|---|---|---|
| 第 1 层 | name + description(元数据) |
会话全程常驻 | 极低(约 100 词) |
| 第 2 层 | SKILL.md 正文 |
仅当 Skill 被触发时 | 中(建议 < 500 行) |
| 第 3 层 | references / scripts / assets |
Claude 判断需要时才按需读取 | 趋近于零(脚本甚至不用读进上下文就能执行) |
这三层的分工是整个机制的精髓:
- 第 1 层始终在线,是一份"能力目录"。它让 Claude 时刻知道"我有哪些技能可用",但只花极小的常驻成本。
- 第 2 层只在触发时加载。用户的请求匹配到某个 Skill,它的正文才会被读进上下文。没触发的 Skill,正文一直躺在磁盘上,不花一分钱。
- 第 3 层最省 。庞大的参考资料、几千行的工具脚本都放在这里,Claude 只在具体需要时才去读某一个文件;脚本更是可以直接执行而不必把源码读进上下文(后文详述)。
一个直观的成本对比
假设你有一份 50KB 的领域参考手册:
- 写进
CLAUDE.md:每一轮对话都要多花约几万 token,哪怕这次根本没用到。 - 做成 Skill :平时只有一句话的
description常驻(几十 token 量级),只有当用户请求真正相关时,正文(可能仅 1000 token)才被加载,参考手册按需读取。
这就是 Skill 相比"把知识堆进 CLAUDE.md"的根本优势:把"固定开销"变成了"按需开销"。
三、结构解剖:三个可选目录各司其职
scripts/、references/、assets/ 这三个目录最容易被混为一谈,但它们的职责界限其实非常清晰:
| 目录 | 存什么 | 判断标准 | 典型例子 |
|---|---|---|---|
scripts/ |
可执行代码(Python / Bash 等) | 同样的代码要被反复重写 ,或需要确定性可靠(不能靠模型每次现场推理) | rotate_pdf.py(PDF 旋转,逻辑固定,没必要每次重写) |
references/ |
给 Claude 阅读的文档,按需加载进上下文 | 详细的领域知识、schema、API 文档、规范------内容多,但不是每次都用得上 | api_docs.md(接口规格)、schema.md(表结构说明) |
assets/ |
直接用在产出物里的文件,不会被读进上下文 | 模板、图标、字体、样板代码------是被复制/填充,而不是被阅读理解的 | template.pptx(PPT 模板)、logo.png、前端脚手架目录 |
一句话记住它们的区别:
scripts/是"帮你干活的手",references/是"给你看的书",assets/是"要塞进成品里的原料"。
这个区分背后,正是第二部分讲的渐进式加载:references 会被读进上下文(消耗 token),而 scripts 通常是被执行、assets 是被搬运------两者几乎不占上下文。
按领域组织的进阶模式
当一个 Skill 需要覆盖多个变体时,推荐让 SKILL.md 只做"路由",把细节拆进 references/:
markdown
cloud-deploy/
├── SKILL.md (通用流程 + 判断该用哪个 reference)
└── references/
├── aws.md
├── gcp.md
└── azure.md
这样 Claude 只会读到当前场景对应的那一个 reference 文件,而不会把三个云平台的内容全部塞进上下文。
四、SKILL.md 内部:Frontmatter 与正文的写法
4.1 Frontmatter:为什么 description 要用第三人称
SKILL.md 开头是 YAML frontmatter,严格必填的只有 name 和 description 两个字段。其中 description 是整个机制里最关键的一行,它的写法有明确规范。
先看对比:
yaml
# ❌ 不推荐
description: Use this skill when you want to create X...
# ✅ 推荐
description: This skill should be used when the user asks to "create X", "configure Y"...
为什么必须用第三人称? 这不是文风洁癖,背后有实际的工程理由。
关键在于:description 的作用不是"下达指令",而是"提供一个可比对的触发条件"。
所有 Skill 的 description 会汇成一份"能力目录"常驻在上下文里。当用户说话时,Claude 要做的是拿用户的原话,去和目录里每一条的触发条件做语义比对,看哪个最匹配。
在这个语境下:
- 写成 "Use this skill when you want to..."(第二人称祈使句)------这个 "you" 指向模糊(是在命令 Claude,还是在转述用户?),而且很容易滑向"描述功能"而非"描述触发条件"。
- 写成 "This skill should be used when the user asks to '...'" (第三人称客观陈述)------它强迫作者写出"用户会说什么",也就是列举具体的触发短语。而这恰恰是匹配机制最需要的信号。
换句话说,第三人称是一种"倒逼":它逼你把触发条件写成客观事实陈述,自然而然就会去列举用户的具体原话。对比一下就很清楚:
yaml
# ❌ 差:模糊、无触发短语
description: Provides guidance for working with hooks.
# ✅ 好:第三人称、列出具体触发短语
description: This skill should be used when the user asks to "create a hook",
"add a PreToolUse hook", "validate tool use", or mentions hook events
(PreToolUse, PostToolUse, Stop).
还有一个实用技巧:Claude 有时会"欠触发"(明明该用某个 Skill 却没用)。为了对冲这一点,可以把 description 写得主动一些------不只描述功能,还要点明"即使用户没明说某个关键词,只要涉及某类场景就应该触发"。
4.2 正文:为什么要用祈使句而非第二人称
frontmatter 之后的 Markdown 正文,规范是用祈使句 / 动词开头的指令语气:
csharp
❌ 第二人称:You should start by reading the configuration file.
✅ 祈使语气:Start by reading the configuration file.
原因和 4.1 一脉相承,但侧重点不同:正文是写给"另一个 Claude 实例去执行的操作手册",不是给人看的说明书。祈使句 "Validate the input" 直接下达动作,比 "You should validate..." 更干脆、更省 token、也不会有代词指向的歧义。
小结这两条规则的共性:无论是
description还是正文,都要避免用 "you" 制造指向不清的歧义 ------description要客观陈述触发条件,正文要直接下达操作指令。
正文通常回答三个问题:这个 Skill 干什么 、什么时候用 (主要放在 description)、具体怎么用 。其中"怎么用"必须明确引用 所有的 scripts / references / assets,否则 Claude 根本不知道这些资源存在。常见的写法是在正文末尾放一段资源索引:
markdown
## Additional Resources
### Reference Files
- `references/patterns.md` ------ 常见模式
- `references/advanced.md` ------ 高级用法
### Scripts
- `scripts/validate.sh` ------ 校验工具
五、Skill 是怎么被触发的
触发方式有三种,但机制的核心是同一个:
- 自动触发(Claude 判断) :这不是有独立的分类器或规则引擎,而是 Claude(LLM 本身)在做语义匹配 ------它像决定"要不要调用某个工具"一样,拿用户的话去和"能力目录"里每条
description比对,匹配上就发起一次 Skill 调用,这时对应的正文才被加载进来。这正是description质量如此重要的原因。 - 用户手动触发 :直接输入
/skill-name。 - Claude 主动触发:在工具调用循环里,Claude 判断相关就调用(可通过权限规则精细控制哪些 Skill 允许/禁止)。
有一个值得知道的细节:Claude 只会为"它自己不容易直接搞定"的任务去查阅 Skill。 像"读一下这个文件"这种一步到位的简单请求,即使某个 Skill 的描述完全匹配,也可能不会触发------因为用基础工具直接做更快。只有复杂的、多步骤的、专业化的任务,才会稳定地触发匹配的 Skill。
六、Skill 在生态中的位置:与 MCP / Subagent / Workflow 的关系
这四个概念常被放在一起比较,但它们其实不是"竞品",而是四个不同的层,可以叠加使用。用一个团队协作的比喻:
| 概念 | 类比 | 解决什么 |
|---|---|---|
| MCP | 给电脑装新硬件 / 新 App | 让 Claude 拥有新能力------连上外部系统(代码托管平台、数据库、设计工具等) |
| Skill | 一本操作手册 / SOP | 把"遇到某类任务该怎么做"的流程与经验固化下来,复用已有能力 |
| Subagent | 一个独立员工,有自己的工位和记忆 | 把一件事隔离出去做,避免过程中的海量中间信息(搜索结果、文件内容)塞满主对话 |
| Workflow | 项目经理的调度脚本 | 按固定流程(并行 / 流水线 / 分阶段)指挥一群独立员工协作完成大规模任务 |
它们如何协同?以"审查一个大型改动里的所有安全问题"为例:
- MCP 提供"连上代码托管平台、拿到改动内容"这个底层能力------没有它,Claude 根本碰不到数据;
- Skill (如一个
security-review技能)里写着"审查安全问题时,按 OWASP Top 10 逐项检查、用什么模式去搜索危险代码"------这是把最佳实践固化成可复用的步骤,而非每次现场发明; - 如果这个 Skill 配置了在独立 Subagent 中运行,审查过程中翻阅的几十个文件、几百行搜索结果都留在 Subagent 自己的上下文里,不会拖垮主对话;
- 如果改动涉及大量文件,还可以用 Workflow 并行派发多个 Subagent 各查一部分,最后统一汇总------这就是"项目经理指挥一群员工"。
一句话记忆:
MCP = 能力从哪来;Skill = 做这件事的经验/流程;Subagent = 在哪做(隔离上下文);Workflow = 一群 Subagent 怎么协作。
七、辨析:Skill 里的 script 和 tool 有什么区别?
这是一个特别容易混淆的点,也是理解 Skill 执行机制的关键。先说结论:
script 不是 tool 的"同类替代品",而是"通过某个 tool 来运行的东西"。二者根本不在同一层。
7.1 它们分别是什么
-
tool(工具) 是注册给模型的一个函数接口:有名字、有参数 schema,模型可以直接发出结构化调用,由执行层去跑并返回结果。读文件、执行命令、抓取网页等都是 tool;MCP server 提供的也是 tool。
-
script(脚本) 只是磁盘上的一个代码文件 。它没有被注册到任何地方,模型不能"直接调用"它,只能借助执行命令的那个 tool 去运行它:
bash
模型 → 发起"执行命令"的工具调用 → 运行 python scripts/xxx.py → 拿到 stdout
也就是说,script 是嵌套在一次 tool 调用内部的,它跑在 tool 之上,而非与 tool 平级。
一个类比:tool 是模型的"手"(能直接使的原语),script 是工具箱里的一个"零件/刀头"------模型得先用手把它拿起来用。
7.2 逐维度对比
| 维度 | script(脚本) | tool(含 MCP tool) |
|---|---|---|
| 本质 | 磁盘上的代码文件 | 注册给模型的函数接口,有名字 + 参数 schema |
| 模型怎么用 | 通过"执行命令"的工具去运行 | 直接发结构化调用,是"一等公民"动作 |
| 上下文成本 | 平时零成本,用到时才在 SKILL.md 里被提及 | 定义常驻上下文,每轮对话都占 token |
| 模型如何知道它存在 | 靠 SKILL.md 正文明确指出来 | 天生就在工具清单里,随时知道能调 |
| 输入/输出 | 命令行参数进、stdout 出------非结构化文本,需自行解析 | schema 校验的结构化输入输出,有类型保障 |
| 分发/安装 | 跟着 Skill 目录走,自包含,无需安装、无需起服务 | MCP 需要配置、运行服务器进程,可能要鉴权 |
| 状态/生命周期 | 一次性进程,跑完即退,无状态 | MCP 是长驻进程,可保持连接、鉴权会话、状态 |
一个关键机制值得强调:script 通常不会被"读"进上下文,而是被"执行"。 references 里的文件要先读进上下文 Claude 才能看懂(实打实占 token),而 scripts 里的文件是被直接调用,进入上下文的只有那条执行命令和它的输出结果------脚本内部几百行代码完全不占 token。这就是为什么第三层加载被称为"理论上无限"。(例外:当脚本报错、需要打补丁或做环境适配时,Claude 才会退回去用读取/编辑工具打开源码,这时它才真正进入上下文。)
7.3 那能不能用 tool 替代 script?
可以,同一段逻辑完全能包装成一个 MCP tool。但要不要这么做,取决于场景:
-
用 script 更合适 ------当逻辑是"某个 Skill 专属、偶发使用、自包含、确定性"的。 例:旋转 PDF 。就是个一次性的确定性转换,打包成脚本放进 Skill 里最合适。硬做成 MCP tool 反而要跑一个服务器进程、注册一个永久占用上下文 token 的工具定义,只为一个偶尔用一次的功能,性价比极低。
-
用 tool / MCP 更合适 ------当它是"跨场景通用、需常驻可用、需保持连接/状态、需结构化接口"的能力。 例:查询数据库。需要长期保持连接和鉴权、被很多不同任务反复用到、还希望有类型化的参数和返回------这就该做成 MCP tool。
决策口诀:
偶发的、Skill 内部的、"跑一下算个结果"的活 → script (成本几乎为零,还能随 Skill 一起分发); 常驻的、跨场景的、需要状态/连接/结构化接口的能力 → tool / MCP(值得付"常驻上下文"这个固定税)。
7.4 一个把两者连起来的细节
Skill 的 frontmatter 里有个 allowed-tools 字段,正好体现了这层依赖关系。如果一个 Skill 要靠脚本干活,通常会预授权执行命令的工具,免得每次跑脚本都弹权限确认:
yaml
---
name: pdf-tools
description: This skill should be used when the user asks to rotate, merge, or split PDF files...
allowed-tools: Bash(python *)
---
这句话的意思是:"这个 Skill 激活时,允许 Claude 无需再确认就用命令行去跑 python 脚本"。script 要跑,得先放行它所依赖的那个 tool ------这再次印证了核心关系:script 永远站在某个 tool 的肩膀上运行,不是 tool 的替代。
八、最佳实践与避坑清单
综合前面的原理,一个高质量 Skill 的经验可以归纳为:
✅ 应该做:
description用第三人称,并列出具体触发短语("create X"、"configure Y"),这是自动匹配的唯一依据;写得笼统就永远不会被触发。SKILL.md正文保持精简 (目标 1500--2000 词,硬上限约 500 行),把详细内容拆到references/。- 正文用祈使句,把它当作"写给另一个 Claude 的操作手册"。
- 复杂、重复、需确定性的逻辑打包成脚本 放进
scripts/,而不是写成大段 prompt 让模型每次现场推理。 - 在正文里明确引用所有资源,否则 Claude 不知道它们存在。
- 超过 300 行的 reference 文件加个目录,方便快速定位。
❌ 应该避免:
description模糊、无触发短语、人称混乱。- 把所有内容塞进一个巨大的
SKILL.md(几千词全在一个文件),导致每次触发都加载一大堆平时用不到的内容。 - 正文里用第二人称 "You should..."。
references / scripts里放了文件,正文却只字未提。
⚠️ 几个已知局限:
- Skill 内容一旦加载进上下文就固定了 ,会话中途修改
SKILL.md不会实时生效,需重新触发或开新会话。 - Skill 太多时,
description可能被自动截断以适配上下文预算。 - 上下文自动压缩时,较旧的、不常用的 Skill 内容可能被丢弃,需要时得重新触发。
结语:Skill 的哲学
回到开头那个矛盾------能力广度 vs 上下文成本。
Skill 给出的答案,本质上是一次巧妙的"分层":把一个能力拆成"什么该常驻 (一句话的触发描述)、什么该按需加载 (详细的操作正文和参考资料)、什么根本不该进上下文 (只用来执行或产出的脚本与素材)"三层,分别对应 description、SKILL.md 正文、scripts / references / assets。
理解了这一点,你就能超越"照着模板填空",真正判断出:一段知识该放哪一层?一个功能该做成 Skill 里的脚本,还是独立的 MCP 工具?一个流程该内联执行,还是隔离到 Subagent? 这些判断力,才是用好 Skill 乃至整个 Claude Code 能力体系的关键。
Skill 不是最重的抽象(那是 Subagent 和 Workflow),也不是最轻的(那是一次性的 prompt),它是中间那一层------比一次性提示更结构化、可复用,又比独立 Agent 更轻量。用对了地方,它能把你和团队反复打磨的专业经验,变成 Claude 随取随用、却几乎不占成本的"肌肉记忆"。