我见过不少刚上手 Claude Code 的人,第一件事就是把所有规矩塞进 CLAUDE.md,指望模型每次对话都背着几十条“军规”跑。上下文一长就开始丢三落四,回头还怪模型不听话。直到把官方那套 Skills 机制真正用起来,才意识到问题不在于提示词写得不够狠,而在于你把这些专项能力和日常规矩混在了同一个平面上。这篇文章不聊玄学,只说我基于官方 Skill 结构落地几个技能之后的经验,适合正想给 Claude Code 加自定义能力、又不想把配置弄得一团糟的人。
官方把 Skill 定义成一种把专属能力打包的机制,说白了就是给模型一个“带说明书的工具”,而不是往上下文里再塞一段提示词。它解决的是差异化问题:日常规范适合常驻记忆,而 PDF 解析、日志归档、发布检查这种事,没必要让模型每次都从零理解,更没必要让它们在每轮对话里占用上下文。Skill 的价值就是按需出现、用完即走。
下面这些内容有一部分来自官方文档的推荐做法,有一部分是我在模拟项目里反复调出来的实际经验。我会尽量把为什么这么做讲清楚,而不是只丢给你一堆目录结构。
1. Skills 是能力包不是提示词:先分清该放哪里
Skill 这个概念刚出的时候,不少人误以为它就是“更高级的 system prompt”。我当时也这么想过,直到我对比了触发机制才发现完全不是一回事。官方设计的核心是:Skill 是一组文件、一份说明、可选的一组资源,在模型判定“当前任务匹配”时才被加载。而 CLAUDE.md 是每轮对话都会进入上下文的基础设定。这两种东西的读取频率和侧重完全不同。
1.1 日常规矩、快捷命令与 Skill 的分工
Claude Code 里至少有三种写“规则”的途径,很多人一上来就混淆:
- CLAUDE.md(或团队级 CLAUDE.md):每次会话都会被读取,适合放代码风格、架构约定、常用命令、项目背景这类“不变应万变”的信息。
- 斜杠命令(Slash Commands):用户主动输入
/xxx来触发一段固定 prompt,适合“一键执行某个固定流程”。 - Skill:模型根据任务意图自动判断是否加载,适合“遇到这类任务时才有用的完整操作指南”。
这三者的分界线其实很清楚。你在 CLAUDE.md 里写“代码风格遵循 xxx”,这是模型每次写代码都要遵守的,放 Skill 里反而会因为没触发而失效。你写一个/review命令,只有你主动敲它才运行,这不需要模型判断。而“当用户提到要压缩日志、按日期归档时,自动调用一组脚本并按固定流程执行”,这才是 Skill 的典型场景。
我见过一个反面案例:有人把“项目技术栈说明”做成了 Skill,导致模型经常不去读它,因为普通对话场景里模型不认为需要触发“技术栈说明技能”。最后那个人又老老实实把它搬回 CLAUDE.md。CLAUDE.md 管“常态”,Skill 管“任务态”,这个认知是第一步。
1.2 什么任务适合做成 Skill:四个判断条件
不是所有东西都值得做成 Skill。按照官方文档推荐的思路,我一般用四个问题来筛选:
- 这个任务是否有清晰的输入和输出?
- 处理步骤是否相对固定,且步骤不止一两句?
- 是否依赖脚本、外部工具、特定文件结构?
- 触发条件是不是“用户正在做某一类事”,而不是“某一句固定指令”?
如果四个问题都答“是”,那 Skill 就是合适的载体。如果只有前两个成立,可能写进 CLAUDE.md 或者做个斜杠命令就够了。我自己做过一次错误判断:当时想把“写周报”做成 Skill,想得很好,让模型自动汇总 Git 提交记录,再生成周报格式。结果发现这个任务主观性太强,每次需求的差异远大于共性,写出来的 Skill 触发率极低,最后删了。所以千万不要为了“有 Skill”而造 Skill。
1.3 官方内置技能的典型形态
官方给的标准技能模板,基本上都遵循“一个技能只解决一类问题”的原则。比如处理 PDF、处理文档、处理表格这类技能,每个目录里都有一个 SKILL.md 负责说明,配套的脚本负责转换,再附上几个测试样例。这个结构本身就说明了一件事:官方希望你用工程的方式管理这些能力,而不是堆提示词。后面几节我会按这个理念展开。
2. 从目录到 frontmatter:一个 Skill 的物理结构
建 Skill 的门槛其实很低:一个目录,一个 SKILL.md 文件,就构成了最简技能。但越简单的东西越容易做歪。我见过无数人把 SKILL.md 写成一篇两千字的论文,最后模型读得慢、还找不着重点。这一节先说清楚物理结构和元数据。
2.1 放对位置:用户级与项目级目录
Claude Code 会从两个位置读取自定义 Skill:
- 用户级:放在用户主目录下的
.claude/skills/目录里,所有项目可用。 - 项目级:放在项目根目录的
.claude/skills/目录里,只对当前项目生效。
这个选择直接影响维护成本。我个人的习惯是:跟项目强相关的放项目级,比如“这个仓库特有的发布检查流程”;通用的放用户级,比如“PDF 提取”、“图片压缩”。如果你把一个通用技能放进了项目目录,换个项目又要复制一份,后期修改就得各处同步,很容易出现“这个项目是旧版”的混乱。
一个推荐的目录结构长这样:
~/.claude/skills/ └── log-archiver/ ├── SKILL.md ├── assets/ │ └── troubleshooting.md └── scripts/ └── archive_logs.py如果内容简单,甚至可以连 assets 和 scripts 都不建,一个 SKILL.md 就完事。但我建议凡是步骤超过三步、或者有可能会踩坑的技能,都单独开一个 assets 目录放详细说明,这一点在第四节会展开讲。
2.2 frontmatter 里容易被忽略的字段
SKILL.md 的开头是 YAML frontmatter,官方会读取里面的字段来决定“这个技能是什么、什么时候用、能用什么工具”。最核心的字段如下:
| 字段 | 是否必填 | 作用 | 注意事项 |
|---|---|---|---|
name | 必填 | 技能的唯一标识 | 尽量短,用连字符分隔,不要带空格 |
description | 必填 | 模型判断是否触发的依据 | 最关键字段,第三节专门讲 |
allowed-tools | 选填 | 限定该技能可用的工具范围 | 不填则继承当前会话已有的工具权限 |
license | 选填 | 声明技能使用协议 | 团队共享时建议标明来源 |
我见过不少人漏掉allowed-tools。这个字段在本地单机使用时可能感觉不到差别,但一旦技能涉及网络请求或文件写入,它的意义就很大:你可以约束一个技能只能读文件和编辑文件,不允许调用其他网络操作。这等于给技能划了一条安全边界。后面第六节我会专门展开说权限控制。
2.3 SKILL.md 正文结构:先判断,再执行,再检查
官方文档里给过一个很重要的理念:SKILL.md 不是给人看的,是给模型看的手册。所以它的结构应该围绕“模型如何执行”来设计,而不是“这个功能多牛”。我推荐的正文结构分为三块:
- 触发条件与前置检查:明确什么情况下使用本技能,进入技能后先检查哪些前置条件。
- 执行步骤:按顺序写步骤,每一步要告诉模型做什么、产出什么。
- 输出要求与检查清单:最后验收,避免模型跑完就收工,结果文件放错位置。
举个例子,如果是一个代码审查技能,执行步骤里应该写“先读取变更文件列表,再逐个读取 diff,最后输出按严重级别分类的问题清单”。当你把“做什么、怎么做、做完怎么验收”写清楚,模型的表现会稳定很多。我一开始写 SKILL.md 时喜欢堆“注意事项”,后来发现那是最没用的部分。不如把注意事项融进执行步骤里,让模型按步骤走,自然不会踩坑。
3. SKILL.md 的灵魂在 description:触发时机是设计出来的
如果说 SKILL.md 正文是技能的执行逻辑,那 description 就是技能的“入口判断逻辑”。模型每轮都可能面对一堆可用 Skill,它必须在极短时间内判断“当前用户请求到底匹配哪个技能”。description 写不好,要么该触发时不触发,要么不该触发时乱触发。我花过很长时间调这段文字,下面全是实战中总结出来的东西。
3.1 反例:一个过于宽泛的 description 长什么样
很多人写 description 时会不自觉写得非常宽:“处理日志文件时使用”。听起来没毛病对吧?但模型在遇到“帮我看看这个 log 为什么报错”时,它也会觉得“处理日志文件”应该是匹配的,于是把这个技能加载进来。结果你的技能教它的是“按日期归档日志”,它压根帮不上“看报错”这件事。这就是典型的误触发:description 的语义范围大于技能实际能处理的范围。
更离谱的反例是只写关键词:“日志”、“归档”。模型对关键词的匹配容错很低,它可能在任何提到日志的地方都触发,也可能在你的描述缺少“归档”二字时完全不触发。description 必须是一个完整的句子,包含动作、对象、场景,而不是几个干巴巴的名词。
3.2 按用户意图写,而不是按工具名写
这是官方文档里我觉得最有价值的一句话:description 应该描述“用户在做什么”,而不是“这个技能叫什么”。举个对比:
- 差:
处理 PDF 文件的技能 - 好:
当用户要求提取 PDF 中的文本、合并多个 PDF、或将 PDF 转为其他格式时使用
差别在哪?后者用了“当用户要求……时”的句式,它模拟的是模型看到用户消息时的判断路径。用户说“帮我把这两个 PDF 合并一下”,模型在扫描技能描述时,会发现“合并 PDF”这个词跟描述里的“合并多个 PDF”高度重合,触发率一下子高很多。而前一种写法,模型需要自己推理“处理 PDF 文件”是否包含“合并”,这可能就错过了。
3.3 触发成本:description 越长,模型越不容易触发
还有一个反直觉的点:description 不是写得越详细越好。模型在选择技能时,会把所有候选技能的 description 都过一遍。如果每个 description 都很长,整体扫描成本上升,模型反而可能因为“选择困难”而漏掉真正的技能。
我建议 description 控制在两三句话以内,把核心意图讲清楚即可。更详细的触发条件、前置检查,应该放到 SKILL.md 正文里。官方给的 description 字符上限虽然很大,但那是边界,不是推荐值。我在本地测试时发现,当技能数量超过 5 个之后,description 的长短会显著影响模型的准确率。短而精准的 description,比长而全面的描述更容易被命中。
3.4 在 description 里主动声明“不做什么”
这一条是所有人都容易忽略的。如果你在 description 里写“适用于处理超大日志文件”,模型还是会遇到“看日志报错”的请求时犹豫。我后来学会在 description 末尾加排除条件:“不适合排查日志报错原因”。这个负向排除看着简单,但能把误触发率降一大半。
用一句话总结 description 的写法:说清用户正在做什么、技能能产出什么、以及什么时候不应该用它。这三件事都讲明白了,触发逻辑基本就稳了。
4. 渐进式披露:别把 SKILL.md 写成使用手册
官方文档里反复强调渐进式披露(Progressive Disclosure)原则。这个概念听起来高级,解释起来很朴素:SKILL.md 只放必要的信息,把细节拆到子文件中,等模型真正需要时再去读。它的好处是双重的:加载轻、上下文省,同时不会让模型被信息淹没。
我第一次没按这个原则来,把 SKILL.md 写成了完整手册,包含所有参数、异常处理、示例脚本,结果一次技能加载就把上下文撑大了不少。后来我改成“总览 + 索引”结构,模型执行得更干脆了。
4.1 三层结构:总览层、细节层、脚本层
我推荐把技能内容拆成三层:
- 总览层(SKILL.md):只写触发条件、执行主流程、关键输出要求。目标是让模型读完这个文件就知道“接下来按步骤走就行”。
- 细节层(assets/ 目录):放参数说明、排错指南、边界情况。比如
assets/troubleshooting.md专门写“如果归档后磁盘空间没释放怎么办”。 - 脚本层(scripts/ 目录):放可执行脚本。SKILL.md 里通过代码引用把脚本内容带进上下文,而不是让模型自己去翻目录。
这套结构和写技术文档一样:README 永远只讲怎么跑起来,详细 API 文档放 docs。SKILL.md 就是技能的 README,资产目录就是技能的附件。渐进式披露的最终效果是:模型加载一个技能,默认只读 SKILL.md;只有执行到某个步骤发现存疑,才会主动去 assets 目录翻排错指南。
4.2 SKILL.md 里的文件引用要写清楚相对路径
官方在资产组织上其实没有强制限制,但推荐用相对路径。你可以在 SKILL.md 里写:
读取 assets/troubleshooting.md 获取常见错误的处理方式。这样模型在执行时,如果遇到问题,就会自己去读那个文件。我见过有人把绝对路径~/Documents/xxx写进 SKILL.md,结果换一台机器就全部失效。用相对路径是唯一不容易出事的做法。
4.3 把脚本直接“拉”进上下文:减少模型乱猜代码
第三种分层是直接在 SKILL.md 里嵌入代码引用,让模型在读取技能时就把脚本内容加载进来,而不是让它自己去打开scripts/archive_logs.py。比如:
<<< scripts/archive_logs.py你在 SKILL.md 里写这行标记,模型会把对应脚本内容拉进当前上下文,然后基于真实代码来执行修改或调用。这个做法比写“请参考 scripts/archive_logs.py 文件”要可靠得多,因为模型不需要先去猜文件路径,也不需要额外一步读取操作。说白了,你给它的上下文里已经带了它要用的工具源码,它当然更清楚该怎么调。
我个人的经验是:脚本超过二十行就建议用这种引用方式,而不是把完整脚本复制到 SKILL.md 里。复制带来的问题是维护时脚本和文档两处要同步,很容易出现改了脚本忘了改 SKILL.md 的情况。引用则天然只维护一份代码。
5. 实操复盘:我写了一个日志归档 Skill
前面几节都是在讲原则,这节我用一个完整的例子走一遍流程:做一个日志归档技能。这个技能不算复杂,但正好覆盖了 description、渐进式披露、允许工具、执行脚本这几个关键点。你也跟着走一遍,能更清楚一个 Skill 是怎么从想法变成可用的。
5.1 需求定义与文件结构
需求来自模拟项目 X:这个项目的服务会产生大量日志文件,需要按日期归档并压缩,同时保留最近七天的原始日志。这个任务有个特点:步骤固定,但涉及查找大文件、按日期创建目录、压缩移动文件三个环节,靠对话临时指导模型做很容易出错,适合固化成一个 Skill。
我在.claude/skills/log-archiver/下建了三个文件:
log-archiver/ ├── SKILL.md ├── assets/ │ └── troubleshooting.md └── scripts/ └── archive_logs.pyassets 文件用来存放“归档后磁盘空间没变”这类问题的排查方法,scripts 文件放真正的归档脚本。SKILL.md 只负责告诉模型什么时候用、大概怎么执行、该去引用什么脚本。
5.2 SKILL.md 与脚本的具体写法
SKILL.md 的 frontmatter 长这样:
--- name: log-archiver description: 当用户要求归档或压缩项目日志、按日期整理日志目录、或者清理过期日志时使用。不适合排查具体日志报错原因。 allowed-tools: - Read - Edit - Bash ---正文部分我刻意做短,只写三步主流程和目标:
# 日志归档与压缩 ## 使用步骤 1. 调用 scripts/archive_logs.py --help 查看当前参数。 2. 根据日志目录大小,决定是否按日期子目录归档。 3. 运行脚本完成归档,并验证磁盘空间变化。 ## 排错 - 如果归档后空间未释放,读取 assets/troubleshooting.md。然后我写了scripts/archive_logs.py,整体逻辑大概是先扫描指定目录下的日志文件,按日期分组,再把超过保留天数的文件压缩归档。这里有个细节:脚本不负责“思考”,只负责执行;怎么调用它、什么时候调用,由模型根据 SKILL.md 的指引决定。
5.3 验证与调试:用日志说话
写完不是就结束了。我建议每次创建新技能后,都用真实的场景去验证两个东西:触发条件和执行结果。
我第一次写好这个技能时,description 用的是“处理日志时使用”,结果我在项目里问模型“帮我看看这个日志为什么报错”,它居然触发了几分钟前的归档技能,跑到一半发现根本不是在归档。这个坑我在第三节已经说过了,后来把 description 改成“归档或压缩日志、按日期整理、清理过期日志”,再补了一句“不适合排查具体日志报错原因”,误触发问题立刻消失。
执行层面的调试也很有必要。我让模型跑了一次归档流程,结果发现脚本在压缩阶段没有跳过正在被写入的日志文件,导致部分日志归完档还在原目录。这个也不是模型的问题,是我脚本本身的缺陷。你看到没有:把 Skill 当作产品来做,调试就得覆盖“触发、执行、脚本健壮性”三个层面,而不是能跑通一次就万事大吉。
6. 团队共享、版本管理与 allowed-tools 边界
Skill 一旦在本地稳定,接下来就会遇到一个更现实的问题:怎么让团队其他人也用上,怎么维护它。官方后来引入了插件机制的升级版,Skill 可以作为插件的一部分分发,通过 marketplace 或者私有仓库同步。但这套东西对于大部分小团队来说可能有点重,这里我聊一下更轻量的实践。
6.1 用 Git 仓库统一维护技能库
最简单可靠的做法是把技能目录放进同一个 Git 仓库,团队拉下来后在本地指向这个目录,或者直接 clone 到各自的~/.claude/skills/下。这个方式不需要额外发布工具,成本最低。
我习惯把技能库单独开一个仓库,命名为 skills-collection。里面分目录放用户级通用技能,README 里写清楚什么时候该往里面加新 Skill。团队里的其他人通过git pull就能同步最新版本。如果项目里用到项目级技能,我建议直接在业务仓库里管,跟着项目分支走,避免项目之间互相污染。
6.2 改 description 必须重新验证触发
版本管理不只是把文件推上去就完事。技能最容易出问题的点,恰恰是更新的时候。我给自己定了一条规矩:凡是改了 description 或 SKILL.md 主流程,必须重新跑一遍触发测试。
因为 description 一变,模型命中这个技能的概率就变了。你可能只是加了一句话,结果它开始在所有相关任务里高频触发;或者你改动了一个动词,导致原来的触发路径断掉。这种回归测试成本不高,但它能拦住绝大多数“上次还能用,这次怎么就疯了”的问题。
6.3 allowed-tools:让技能只拿到它需要的工具
官方对allowed-tools的语义是:如果定义了该字段,技能只能使用列表内声明的工具;如果没定义,则继承会话已有的工具权限。这个机制在团队场景下特别有价值,因为你不能保证每个团队成员在对话中都会严格限制自己的工具授权。
以日志归档这个技能为例,它只需要Read、Edit、Bash。如果我在 frontmatter 里写了这三个工具,那模型在这个技能内就不会发起网络请求,也不会有其他越权操作。哪怕某个人在会话里打开了 WebFetch,这个技能内部也会被限制住。这是一种“最小权限”思路。我给所有自定义技能都加上这个字段,宁可少一点都不放多。
6.4 团队协作的小技巧:技能里留维护者注释
团队场景下,SKILL.md 还可以在末尾加一段“维护记录”,写清楚这个技能的最后更新人、更新时间和改动内容。这个不是给模型看的,是给队友看的。因为模型会忽略那些被<!-- -->注释掉的 Markdown 内容,但人读文件时能看到。这个习惯帮我省掉了非常多沟通成本。
7. 三个最容易翻车的地方:误触发、臃肿和维护断裂
说再多原则,踩过的坑才是最有说服力的。最后总结三个我反复见到的高频事故,你可以对照自己的 Skill 看看有没有中招。
7.1 误触发:description 写太宽容
事故表现:模型在“不该用技能”的地方调用了技能,跑一半发现任务不匹配,重新读上下文,浪费时间和 token。
根源只有一个:description 的范围大于技能实际能力。修复也很直白:在 description 里把“什么时候不该用”写出来,同时在正文前置检查里再加一道防线,比如“进入技能后,先检查日志目录是否存在,不存在则中止并向用户说明”。这两道防线一加,误触发的概率会大幅下降。
7.2 臃肿:SKILL.md 被写得又长又全
事故表现:技能能跑,但每次加载都很重,上下文被撑大,模型读到后面反而忽略关键步骤。
根源是没做渐进式披露。修复方式不是删内容,而是把内容拆走:详细参数表移到 assets、脚本代码移到 scripts、排错指南独立成文件。SKILL.md 只留执行主流程,保证模型读一遍就能开始干活。我一直跟别人强调一句话:SKILL.md 的目标不是“讲清楚”,而是“让模型能立即开始做”。完整性和冗余度之间,必须优先选择后者。
7.3 维护断裂:建完就没再更新
事故表现:某个 Skill 用了几次,后来项目结构变了、脚本依赖变了,技能再也没被触发过,静默腐烂在技能目录里。
这个问题没有银弹,只有习惯。我自己的做法是:每个技能都在 SKILL.md 末尾写下“适用版本”或“依赖条件”,同时在项目大版本升级时,把技能目录整个过一遍,跑一次冒烟测试。另外,技能一旦连续一个月没被触发,我会主动考虑是不是 description 失效了。一个不能被触发的技能,和一段没人看的注释没有区别。
我在实际维护中还有一个偏方:把“技能更新日志”写进仓库的根目录,而不是只放 README。每当有人改动技能,都要在更新日志里登记一条。这个动作本身很简单,但它能让技能库始终保持“有人负责”的状态,而不是人人建设、无人维护的垃圾堆。
总的来说,官方这套 Skill 机制并不复杂,真正复杂的反而是使用者的纪律性。你只要做好了定位判断、写好 description、做对分层、管住权限,一个技能库就能像一套好工具一样,越用越顺手。我的体会是:每当我发现自己第三次对模型重复同样一串操作指导,那就是该建 Skill 的时候了。