Claude Skills 详解-从工作原理到手写第一个SKILL
Claude Skills 详解-从工作原理到手写第一个SKILL
如果你曾经向Claude反复解释同一套工作流程-"先读这个文件,再按照这个格式输出并加上页码"。那么SKILL正是为你准备的。它把一次性的口头叮嘱,变成一份可复用、可迭代、能被自动调用的"专业手册"。
什么是Skill(Agent Skills)
Skill是一个装着SKILL.md文件的文件夹,用来给Claude这类智能体补上某项"专业能力"。
这个文件夹里组织着指令、脚本和资源,赋予智能体额外的能力。官方的定义是:Agent Skills是扩展Claude功能的模块化能力,每个Skill把指令、元数据,以及可选的资源(脚本、模版)打包在一起,Claude在相关场景下会调用他们。
Skill与Prompt的区别
- Prompt:是对话层面的一次性指令。相当于"这次帮我这样做"。
- Skill:是按需加载、可复用的资源,省去了在多个对话里反复粘贴同一套说明的麻烦。相当于"以后遇到这类任务,都照这个手册来"。
SKILL文件的存放位置
SKILL文件的存放位置决定了SKILL的作用范围和优先级
| 存放位置 | 路径 | 作用范围 | 适用场景 | 优先级 |
|---|---|---|---|---|
| 用户全局级 | ~/.claude/skills/ | 所有项目 | 个人通用习惯、常用工具 | 中 |
| 项目本地级 | 项目根目录/.claude/skills | 当前项目 | 项目特定规范、团队协作 | 高 |
注:当你有多个同名SKILL时,项目级的SKILL会覆盖用户全局级的同名SKILL,从而让你能够为特定项目定制行为,而不影响个人其他项目的设置。
Skill的核心结构
最小骨架
每个Skill都必须有一个带YAML Front Matter的SKILL.md文件。最小的Skill就是一个文件夹加一个SKILL.md文件,文件模版如下
---
name: skill的名字
description: 简要说明这个Skill做什么、什么时候该调用它
---
# skill的名字
## 指示
[给Claude的清晰、分步骤的指引]
## 示例
[使用这个skill的具体示例]
可以看出SKILL.md文件分两部分:YAML头(frontmatter-前言)配置元数据,Markdown正文写真正的指令。
frontmatter:两个必填字段及其约束
必填字段只有两个:name和description。但它们有各自有硬性要求,写错了Skill可能无法加载或无法被正确触发
- name字段: 最长64个字符;只能包含小写字母、数字和连字符;不能含XML标签;不能使用保留词"anthropic"和"claude"。
- description字段: 必须非空;最长1024个字符;不能含XML标签。
注:description字段应该同时写清楚"这个Skill做什么"和"Claude什么时候该用它"。因为description是Claude唯一始终加载、用来判断要不要触发这个Skill的信息。
对比官方PDF Skill的写法就很清楚,它不只写"提取PDF文本表格、填表单、合并文档",还补充"当处理PDF文件或用户提到PDF、表单、文档提取时使用"。前半句是"做什么",后半句是"何时用"。
正文: Skill的"操作手册"
Markdown正文是Skill被触发后真正进入上下文的内容。它承载的是流程性知识:工作流、最佳实践和操作指引。常见会分成Instructions(怎么做)和Examples(具体示例)等小节。
三类内容类型
Skill的结构本质上是围绕三种内容类型展开,这里以anthropic官方的skill-pdf为例进行一下说明
pdf(skill名字)/
|-- SKILL.md (主指令)
|-- FORMS.md (额外指令)
|-- REFERENCE.md (资源)
|__ scripts/ 可执行脚本,处理确定性的重复任务
三类内容各有分工:
指令(instructions):是SKILL.md正文及额外的markdown文件,提供灵活的指引;
代码(Code):是Claude通过bash运行的脚本,提供确定性操作且不占用上下文;
资源(Resources):是数据库schema,API文档,模版,示例等参考材料;
Skill的核心机制:渐进式披露(Progressive Disclosure)
渐进式披露机制解决的问题
上下文窗口又贵又有限,但你希望Claude能掌握的专业知识可能很庞大。如果每装一个Skill就把它的全部内容塞进上下文,装几个就把窗口占满了。渐进式披露就是为这个矛盾设计的:Claude分阶段按需加载信息,而不是一上来就全部占用上下文。
三层加载,逐级加载

- L1元数据--始终加载。这一层frontmatter里的name和description。Claude在启动时就把它加载进系统提示,因此你可以装很多Skill而几乎不增加上下文负担--Claude只是知道每个Skill存在,以及什么时候该用它。
- L2指令--被触发时才加载。这层是SKILL.md的Markdown正文,承载具体工作流和最佳实践。当你的请求匹配上某个Skill的描述,Claude才通过bash把SKILL.md读进来,这部分内容到这时才进入上下文窗口。
- L3+资源与代码--用到时才读。这层时Skill打包的额外文件:更细的指令文档、可执行脚本、参考资料等。Claude只在指令引用到它们时才访问;没用到的文件一直留在文件系统上,消耗零token。
机制成立的底层:文件系统+代码执行
渐进式披露不是凭空设计出来的,它依赖一个关键前提--Skill跑在一个Claude拥有文件系统和命令行能力的环境里。可以这样理解:Skill就是虚拟机上的一个个目录,Claude用你在自己电脑上导航文件时一样的bash命令去访问它们。
具体访问链路是:Skill被触发时,Claude用bash读取SKILL.md把指令载入上下文;如果指令里引用了别的文件,Claude再用额外的bash命令去读那些文件;如果指令提到了可执行脚本,Claude通过bash运行它,只拿回输出结果。
脚本被执行时,脚本的代码本身从不进入上下窗口,只有它的输出才占用token。所以让Skill调用脚本,比让Claude现生成等价代码要省得多,也更可靠。
编写SKILL.md的技巧
1.description是重中之重,决定Skill会不会用上
description会被注入系统提示,Claude靠它从可能上百个Skill里挑出该用哪一个,编写时有以下要点
- 用第三人称写。要写"处理excel并生成报告"而不是"你可以使用..."
- 同时写清"做什么"和"何时用"。description要具体,带上关键触发词,既能说明功能也能说明使用场景。比如"使用git-stuff提升代码追踪效率"太含糊,相对的"审查代码变更、生成提交信息、提交前校验diff;当用户要求审查代码、准备提交、写commit message、检查diff或发起PR请求时使用"就能让Claude精准命中
- 塞进领域里真实会出现的词。用户怎么说,你就把那些词放进去,确保稳定触发
2.正文要克制:简洁不是风格,是性能
上下文窗口是一种公共资源,你的Skill要和系统提示、对话历史、其他Skill的元数据、以及用户的实际请求一起分享它。虽然渐进式披机制让未触发的内容不占上下文,但是SKILL.md被加载,它的每个token都在和对话历史内容竞争。因此:
- 只写Claude还不知道的东西--逐条审视每段信息,通用知识它本来就有,删掉
- 正文控制在500行以内。SKILL.md正文建议保持在500行以下以获得最佳表现,接近上限时就把内容拆到单独的文件里
- 术语前后统一。同一个概念只用一个词来指代,并尽量使用该领域最地道的术语。比如Thymeleaf下的文件称为template,别一会又叫html或视图
3.把渐进式披露落到结构上
让SKILL.md当"目录",细节外置。SKILL.md起的是概览作用,按需把Claude指引到详细材料。一个被反复推荐的目录约定是:
skill-name/
|-- SKILL.md # 元数据+核心指令(<500行)
|-- scripts/ # 可执行脚本,写成小型CLI
|-- references/ # 补充资料:schema,字典表
|__ assets/ # 输出用的模版、静态文件
注:引用的文件直接从SKILL.md提供的链接找到,且只保持一层深,不要做深层引用链。确保Claude不会迷路。
4.善用脚本,而不是让Claude每次现写
这是Skill比纯Prompt强的地方之一。即使Claude能自己写脚本,预置脚本仍有优势:更可靠、省token(代码不进上下文)、省时间、保证多次调用结果一致。
脚本要返回描述性强,人类可读的报错信息,这样Claude看到报错信息就知道哪里的问题进行自我纠正不必回头找用户。
5.常见的注意事项
- 别写会过期的信息
- 别给太多选项
- 别假设环境里装了什么
- 示例要具体,不要抽象
6.开发方法:让Claude帮你写,再用另一个Claude测
官方推荐了一套闭环流程:用一个Claude实例(A)帮你设计和打磨指令,再让另一个加载了该Skill的实例(B)在真实任务里测试。具体做法是先不带Skill完整做一遍任务,留意你反复提供了哪些上下文,把这些可复用的模式沉淀成Skill的内容。
其次,在写大量文档之前先建评估:找出Claude没有Skil时会失败的地方,构建几个测试场景,量出基线,再写最少够用的指令去补差距,然后迭代。观察实例B的真实行为--它是不是按你没预期的顺序读文件、有没有漏掉重要文件的引用、有没有从不读取某个文件,据此回去让实例A调整。比如规则不被遵守时,可以把always xxx 改成强硬的 MUST xxx,或者重排让规则更显眼。
构建一个git-commit-helper(git提交助手)Skill示例
用于规范化git提交信息,符合约定式提交(Conventional Commits)规范的skill
git-commit-helper skill的目录结构
git-commit-helper/
|-- SKILL.md #L2指令:大脑/目录,被触发时加载
|-- references/ #L3 资源: 按需才读
|__ conventisons.md
|__ scripts/
|__ summarize_diff.sh # L3 代码: 代码不进上下文
SKILL.md文件
---
name: git-commit-helper
description: 分析暂存(staged)的 git 改动,生成符合 Conventional Commits 规范的提交信息。每当用户要求撰写、起草或改进提交信息、准备一次提交、总结暂存改动,或在提交前审查 diff 时,都应使用本技能——即便用户没有明确说出 "Conventional Commits"。
---
# Git 提交信息助手
根据暂存的改动生成清晰、统一的提交信息,遵循 Conventional Commits 格式。
## 工作流程
1. 检查暂存的改动。运行辅助脚本获取结构化摘要:
bash scripts/summarize_diff.sh
脚本会按状态分组列出改动文件,并给出净增删行数。如果它输出“没有暂存的改动”,
请提示用户先用 `git add` 暂存文件,然后停止。
2. 根据摘要选择提交类型。挑选最能描述本次主要改动的那**一个**类型。常见类型见下表;
对于不常见的类型、scope 以及 breaking change 规则,请阅读 `references/conventions.md`。
3. 按以下格式撰写信息:
<type>(<scope>): <subject>
<body>
<footer>
- subject 行控制在 50 个字符以内,使用祈使语气,结尾不加句号。
- 仅当改动需要解释“做了什么”和“为什么”(而非“怎么做”)时才写 body,每行折行在 72 字符。
- 仅在涉及破坏性变更(`BREAKING CHANGE: ...`)或需要关联 issue(`Closes #123`)时才写 footer。
4. 把生成的信息展示给用户,并询问是否要在提交前调整类型、scope 或措辞。
## 提交类型
| 类型 | 适用场景 |
|----------|--------------------------------------------|
| feat | 面向用户的新功能 |
| fix | 修复 bug |
| docs | 仅文档改动 |
| refactor | 既不修 bug 也不加功能的代码重构 |
| test | 新增或修正测试 |
| chore | 构建、工具链或依赖改动;不涉及生产代码 |
本表覆盖常见情况。`perf`、`ci`、`style`、`revert` 以及 scope 和破坏性变更的规则,
请阅读 `references/conventions.md`。
## 示例
**示例 1**
输入:新增了签发 JWT 令牌的登录接口
输出:feat(auth): add JWT login endpoint
**示例 2**
输入:修复了购物车为空时结算崩溃的问题
输出:fix(checkout): prevent crash on empty cart
**示例 3**(带 body 和 footer)
输入:重构了定价模块;旧的折扣 API 不再支持
输出:
refactor(pricing): restructure discount calculation
Consolidate the three discount paths into a single resolver so rates
stay consistent across regions.
BREAKING CHANGE: the legacy applyDiscount() API is removed.
Closes #482
## 为什么用这套格式
统一的 type/scope 前缀能让人和工具都快速扫读历史。changelog 生成器和 semantic-release
依赖它来自动提升版本号,所以类型写错或缺失会带来真实的下游代价。subject 保持简短、
祈使语气,能让 `git log --oneline` 易读。遇到上面没覆盖的情况,请查阅
references/conventions.md,不要凭空猜测。
参考资源-references/conventions.md
# Conventional Commits —— 参考
针对主 SKILL.md 表格未覆盖的情况,这里给出更完整的参考。仅当某次改动无法干净地
归入 feat / fix / docs / refactor / test / chore 时,才需要阅读本文件。
## 目录
- 完整类型列表
- scope 规则
- 破坏性变更
- subject、body、footer 规则
- 棘手情况
## 完整类型列表
| 类型 | 适用场景 |
|----------|----------------------------------------------|
| feat | 面向用户的新功能 |
| fix | 面向用户的 bug 修复 |
| docs | 仅文档(README、注释、docstring) |
| style | 仅格式:空白、分号;不改逻辑 |
| refactor | 既不修 bug 也不加功能的重构 |
| perf | 提升性能的改动 |
| test | 新增或修正测试 |
| build | 构建系统或外部依赖改动 |
| ci | CI 配置与脚本 |
| chore | 不涉及 src 或 tests 的其他改动 |
| revert | 回滚此前的某次提交 |
## scope 规则
- scope 可选,用于指明受影响的代码区域,例如 `feat(parser):`、`fix(api):`。
- 使用团队已有的名词(模块、包或功能名)。保持一致:模块若叫 `auth`,就始终写
`auth`,不要一次写 authentication、另一次写 login。
- 改动确属全局时省略 scope;不要硬造一个。
## 破坏性变更
用以下任一方式标记(两者同时用也可以):
1. 在 type/scope 后加 `!`:`feat(api)!: drop support for Node 16`。
2. 添加以 `BREAKING CHANGE:` 开头的 footer,说明破坏了什么以及迁移办法。
破坏性变更会让 semantic-release 提升主版本号,所以绝不要把它用在非破坏性的改动上。
## subject、body、footer 规则
- **subject**:50 字符以内,祈使语气(用 "add",而非 "added" 或 "adds"),
结尾不加句号,冒号后首字母小写。
- **body**:可选。解释“做了什么”和“为什么”,而非“怎么做”(diff 已经显示了怎么做)。
每行折行在 72 字符,与 subject 间隔一个空行。
- **footer**:可选。用于 `BREAKING CHANGE:` 说明和 issue 关联(`Closes #123`、
`Refs #456`),与 body 间隔一个空行。
## 棘手情况
- **混合改动(功能 + 不相关的修复)**:优先拆成两次提交。若用户坚持合并,选主要
改动的类型,并在 body 中提及次要改动。
- **回滚**:使用 `revert:`,并在 body 中引用被回滚的 commit 哈希:
`This reverts commit <hash>.`
- **纯格式化(linter 产生)**:用 `style`,不要用 `refactor`。
- **依赖升级**:常规升级用 `chore(deps):`;仅当升级修复了用户可见的 bug 时才用
`fix(deps):`。
- **仅改代码内文档(仅 docstring)**:可用 `docs`;但若同一次提交也改了行为,
则用该行为对应的类型。
脚本代码-scripts/summarize_diff.sh
#!/usr/bin/env bash
#
# summarize_diff.sh —— 汇总已暂存的 git 改动,用于生成提交信息。
#
# 以 agent 可读的稳定格式输出:
# 1. 按状态(A/M/D/R)分组的已暂存文件
# 2. 净增删行数(shortstat)
# 3. 已暂存文件按扩展名的计数
#
# 设计说明:
# - 当没有已暂存改动时,返回清晰、人类可读的提示(而非仅以非零状态退出),
# 这样 agent 能准确地告诉用户下一步该做什么。
# - 硬性错误写入 stderr,并附带可操作的提示。
set -euo pipefail
# 必须在 git 仓库内运行。
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "错误:不在 git 仓库内。请在项目根目录下运行。" >&2
exit 1
fi
staged_status="$(git diff --staged --name-status)"
# 没有已暂存改动:以成功状态(exit 0)退出,并给出可操作的提示。
if [ -z "$staged_status" ]; then
echo "没有暂存的改动。请提示用户先用 git add <文件> 暂存文件。"
exit 0
fi
echo "=== 已暂存文件(状态 路径) ==="
echo "$staged_status"
echo
echo "=== 净增删行数 ==="
git diff --staged --shortstat
echo
echo "=== 已暂存文件按扩展名统计 ==="
git diff --staged --name-only \
| sed -n 's/.*\.\([^.\/]*\)$/\1/p' \
| sort \
| uniq -c \
| sort -rn
git-commit-helper Skill解释说明
frontmatter(前言)--对应"L1元数据+触发机制"
name: git-commit-helper
description: 分析暂存(staged)的 git 改动,生成符合 Conventional Commits 规范的提交信息。每当用户要求撰写、起草或改进提交信息、准备一次提交、总结暂存改动,或在提交前审查 diff 时,都应使用本技能——即便用户没有明确说出 "Conventional Commits"。
这就是始终加载的L1层也是Skill唯一靠它决定要不要触发的信息。description内容先说"做什么",之后则一连串的"何时用"的触发词,最后备注则是强调其触发条件,避免Claude容易"该用不用"的毛病。
SKILL.md正文--对应"L2指令"与简洁原则
正文是被触发后才进上下文的L2层。需要写得克制,一个workflow、一张常见类型表、几个示例、一句为什么
- 当目录/大脑,不堆细节:正文只放最常用的6个类型,其余(perf、cl、revert等规则)一句话指向
references/conventions.md。这就是“SKILL.md是概念,按需指引到详细材料” - 祈使句+具体示例:用Input/Output形式给了三个例子(含一个带body和footer),比讲抽象规则管用
- 解释为什么:结尾用"为什么用这套格式"说清这套格式对changlog、semantic-release的实际价值,而不是堆一串生硬的MUST--让模型理解动机,泛化得更好
references/conventisons.md--对应""渐进式披露""的精髓
这个文件就是渐进式披露活生生的演示,平时它不进上下文(零token),只有当某次提交是revert或涉及breaking这类正文没覆盖的情况,Claude才按SKILL.md里的指引去读它。两个细节呼应了技巧:文件只有一层深(直接从SKILL.md连接,不做深层引用链)
scripts/summarize_diff.sh--对应"脚本省token且可靠"
这是最能体现Skill比纯Prompt强的一块。运行bash scripts/summarize_diff.sh时,脚本的代码本身从不进上下文,只有它打印的摘要进上下文--省token、结果稳定、不用每次现写解析逻辑。
代码中还专门示范了那条"脚本要返回人类可读信息让Agent自我纠正的实践":没有暂存改动时,它不是默默报错而是正常退出(exit 0)并打印一句"没有暂存的改动,请提示用于先用git add <文件>暂存文件",Claude读到就知道下一步该提示用户git add,不必回头问用户。
参考资料
Equipping agents for the real world with Agent Skills \ Anthropic
Agent Skills - Claude Platform Docs
GitHub - anthropics/skills: Public repository for Agent Skills · GitHub
claude-cookbooks/skills at main · anthropics/claude-cookbooks · GitHub

浙公网安备 33010602011771号