1. 从50个Skill里爬出来的血泪账
先交个底。我在过去大半年里,前前后后写了差不多50个Claude Code Skill,从最开始照着文档瞎摸,到后来能稳定产出可复用的技能包,中间踩的坑能填满一个中型项目的issue区。最扎心的一个结论是:前30个基本等于白写。不是它们跑不起来,而是它们解决的都是“伪需求”,或者用了一种极其别扭的方式去解决真需求,导致维护成本高到不如手敲。
这篇文章不打算给你灌“Skill有多强大”的鸡汤。我想干的事很具体:把我在写这50个Skill过程中,关于SKILL.md结构、frontmatter设计、MCP协同、触发词编排、测试与去AI味这几个核心环节的真实经验摊开讲。如果你刚开始接触Claude Code,或者已经写了几个Skill但总觉得“哪里不对”,这篇内容应该能帮你省下至少两三个周末的无效折腾。
先对齐一下基础认知。Claude Code Skill本质上是一个可被模型按需加载的能力包,核心载体是SKILL.md文件,通过frontmatter里的元信息告诉Claude“我是谁、我什么时候该被调用、我依赖什么工具”。它和MCP的关系不是替代,而是互补:MCP负责把外部能力(数据库、设计工具、调试器接口等)接进来,Skill负责把“怎么用这些能力解决某类具体问题”的流程固化下来。一个管“手”,一个管“脑”。
适合读这篇的人:已经装好Claude Code、跑通过至少一个Skill、但产出质量不稳定的开发者;或者正准备从零开始写Skill、想少走弯路的同学。纯小白也能看,但建议先把官方文档里关于Skill目录结构和frontmatter字段的部分过一遍,不然有些细节会跟不上。
2. 前30个Skill为什么白写了:三个致命误区
2.1 误区一:把Skill当成“提示词收藏夹”
我最早写的十几个Skill,现在回头看,本质上就是把一段常用提示词塞进SKILL.md的正文里,frontmatter随便填个name和description就完事。比如我写过一个“代码审查Skill”,正文就是“请仔细审查以下代码,关注性能、安全和可读性”。这东西能用吗?能用。但它有价值吗?几乎没有。
原因很简单:Claude本身已经具备代码审查能力,你写不写这个Skill,它都能干这件事。Skill的真正价值在于注入模型默认不知道的上下文——你们团队的代码规范、特定框架的坑、某个内部工具的调用约定。如果Skill的内容是“通用常识”,那它就是在浪费加载开销。
我后来给自己定了一条硬标准:如果一个Skill的正文,删掉之后Claude靠默认能力也能完成80%的任务,那这个Skill就不该存在。这条标准直接砍掉了我早期一半的产出。
2.2 误区二:frontmatter写得像填空题
frontmatter是Skill的“身份证”,但很多人(包括早期的我)把它当填空题,name随便起,description写一句“用于处理XX任务”就交了。这是灾难性的。
Claude决定是否加载一个Skill,几乎完全依赖description的语义匹配。你写“用于处理数据处理任务”,模型根本不知道什么时候该用它;你写“当用户需要对CSV文件做列式聚合、且数据量超过10万行时使用,内部封装了分块读取和内存控制逻辑”,模型就能精准命中。
我做过一个粗糙的对比测试:同一个Skill,description分别用“模糊版”和“场景化版”,在20次随机任务里,模糊版的触发准确率大概只有三成,场景化版能到八成以上。这个差距直接决定了Skill是“资产”还是“噪音”。
2.3 误区三:忽视MCP与Skill的边界
这是最隐蔽的一个坑。我早期写过一个“数据库查询Skill”,正文里详细描述了怎么拼SQL、怎么处理连接。问题是,Claude Code本身没法直接连数据库,它需要MCP提供数据库访问能力。我的Skill写得再详细,没有对应的MCP工具,它就是一张废纸。
正确的做法是:MCP负责“能不能做”,Skill负责“怎么做才对”。比如你有一个数据库MCP,那Skill应该写的是“查询前必须先检查表的分区键,避免全表扫描”“涉及金额字段时统一用DECIMAL而不是FLOAT”这类业务规则,而不是重复MCP已经提供的能力说明。
把这三个误区理清楚之后,我后面20个Skill的产出效率和质量明显上了一个台阶。下面我把这套方法论拆成可操作的步骤。
3. SKILL.md的骨架设计:从frontmatter到正文的完整规范
3.1 frontmatter字段的取舍与写法
一个典型的frontmatter长这样:
--- name: csv-large-aggregation description: 当用户需要对超过10万行的CSV文件做分组聚合、且关注内存占用时使用。封装了分块读取、类型推断和流式聚合逻辑。 version: 1.2.0 ---字段不多,但每个都有讲究。name建议用小写连字符,语义上体现“领域+动作”,方便自己在几十个Skill里快速定位。description是重中之重,我总结了一个“三段式”写法:
- 触发条件:什么场景下该用(“当用户需要……”)
- 能力边界:这个Skill覆盖什么、不覆盖什么
- 关键约束:有没有特殊前提(数据量、依赖工具、性能要求)
version字段很多人不写,但我强烈建议加上。Skill是会迭代的,尤其是当你的团队规范变化时,没有版本号,你根本不知道线上跑的是哪一版逻辑。
注意:description不要写成“这是一个用于XX的Skill”,这种自指式描述对模型匹配毫无帮助。要站在“模型看到这句话,能不能判断当前任务该不该加载我”的角度去写。
3.2 正文结构:为什么“步骤化”比“说明化”更有效
我早期正文喜欢写成说明文,大段描述“本Skill的作用是……”。后来发现,模型对有序步骤的执行准确率明显高于对描述性文本的理解。现在我的正文基本遵循这个结构:
- 前置检查:执行前必须确认的条件(文件存在、依赖工具可用等)
- 执行步骤:编号列表,每步一个明确动作
- 输出规范:结果应该长什么样(格式、字段、单位)
- 异常处理:常见失败情况怎么应对
举个真实例子。我写过一个“日志分析Skill”,早期版本正文是一段话描述分析逻辑,模型经常漏掉时间戳解析。改成步骤化之后:
## 执行步骤 1. 读取日志文件,按行分割 2. 用正则提取时间戳字段,格式为 `YYYY-MM-DD HH:mm:ss` 3. 按小时聚合,统计每个小时的ERROR级别条目数 4. 输出为Markdown表格,列为:小时、错误数、占比同样的模型,同样的任务,准确率从六成提到了九成以上。步骤化本质上是在替模型做任务分解,减少了它自由发挥的空间,也就减少了出错的可能。
3.3 触发词编排:让Skill在该出现的时候出现
触发词不是越多越好。我试过在一个Skill里塞二十几个触发词,结果它在很多不相关场景下被误加载,反而干扰了正常任务。后来我改用“核心触发词+场景限定”的策略:
- 核心触发词控制在3到5个,必须是任务描述里高频出现的词
- 场景限定写在description里,而不是堆在触发词列表里
比如一个“GIS空间分析Skill”,核心触发词是“空间分析”“缓冲区”“叠加分析”,但description里明确写“仅当用户处理的是矢量数据且需要做几何运算时使用”。这样既保证了命中率,又避免了在纯属性查询场景下被误触发。
4. 实操:从零写一个能打的Skill
4.1 需求筛选:先问三个问题
在动手写之前,我会先问自己三个问题:
- 这个任务Claude默认能做吗?能,且做得不错,就不写。
- 这个任务有明确的“正确做法”吗?如果做法因人而异、没有标准,Skill的价值就有限。
- 这个任务会重复出现吗?一次性任务不值得固化成Skill。
三个问题都过了,才进入下一步。这个筛选过程帮我砍掉了大量“看起来有用、实际鸡肋”的想法。
4.2 目录结构与文件组织
一个规范的Skill目录大概是这样:
skills/ csv-large-aggregation/ SKILL.md examples/ sample-input.csv expected-output.md scripts/ validate.pySKILL.md是必须的,examples和scripts是可选的。但我强烈建议至少放一个examples目录,里面放输入样例和期望输出。这不仅是给模型看的,更是给你自己测试用的。没有样例,你根本没法验证Skill是否按预期工作。
scripts目录用于放辅助脚本。比如我那个CSV聚合Skill,里面放了一个validate.py用来检查输入文件的行数和编码,Skill正文里会引用它。这样把“确定性逻辑”交给脚本,把“判断性逻辑”留给模型,分工明确。
4.3 正文编写:一个完整的示例
下面是我现在常用的正文模板,以“CSV大文件聚合”为例:
## 前置检查 - 确认输入文件存在且为 `.csv` 格式 - 调用 `scripts/validate.py` 检查文件行数和编码 - 若行数超过50万,提示用户确认是否继续 ## 执行步骤 1. 使用分块读取方式加载文件,块大小设为10000行 2. 对每块数据做类型推断,数值列转为对应类型 3. 按用户指定的分组键做流式聚合 4. 合并各块的聚合结果 5. 按聚合值降序排列,取前20条 ## 输出规范 - 输出为Markdown表格 - 数值列保留两位小数 - 若存在空值,单独标注空值数量 ## 异常处理 - 编码错误:尝试 `utf-8` 和 `gbk` 两种编码 - 内存不足:将块大小降至5000行并重试 - 分组键不存在:列出所有可用列名供用户选择这个模板我用了大概十几次,每次只需要替换具体步骤,结构不用动。模板化的好处是降低认知负担,让你把精力集中在“这个任务的核心逻辑是什么”上,而不是“正文该怎么组织”。
4.4 测试:怎么判断一个Skill“能打”
写完不等于能用。我的测试流程分三层:
- 单元测试:用examples里的样例跑一遍,看输出是否符合expected-output
- 边界测试:故意给空文件、超大文件、格式错误的文件,看异常处理是否生效
- 干扰测试:在一个不相关的任务里,看Skill会不会被误触发
第三层最容易被忽略,但恰恰最重要。我有个Skill因为触发词写得太宽泛,在写文档的任务里被反复加载,导致输出里莫名其妙多了一堆数据分析的步骤。后来把触发词收窄才解决。
5. MCP协同:Skill和外部工具的配合方式
5.1 什么时候该用MCP,什么时候该用Skill
这个边界我前面提过,这里展开说。判断标准很简单:需要访问外部系统(数据库、API、设计工具、调试器)的,用MCP;需要固化业务流程和规范的,用Skill。
举个例子。你要做一个“Figma设计稿转代码”的能力。Figma的访问需要MCP(因为要调Figma的API),但“转成什么风格的代码、用什么组件库、命名规范是什么”这些属于Skill。两者配合的方式是:MCP提供原始设计数据,Skill定义转换规则。
我见过有人试图用Skill去“模拟”MCP的能力,比如在Skill正文里写“假设你可以访问数据库”,然后让模型编造查询结果。这种做法在演示里能跑通,在真实场景里毫无价值。
5.2 MCP工具流的Skill封装技巧
当你有一个MCP提供了一堆工具时,直接让模型自由调用容易乱。我的做法是写一个Skill来约束调用顺序和参数规范。比如一个数据库MCP提供了query、list_tables、describe_table三个工具,我会写一个Skill规定:
- 先调
list_tables确认表存在 - 再调
describe_table确认字段 - 最后才调
query,且query语句必须带LIMIT
这样既利用了MCP的能力,又通过Skill注入了“安全查询”的规范。实测下来,模型乱查表、查错字段的情况明显减少。
5.3 流式输出到文件的处理
有个场景我踩过坑:用MCP工具流式输出内容到文件时,Skill如果没规定好写入方式,容易出现内容截断或重复写入。后来我在Skill里明确写了:
## 流式写入规范 - 使用追加模式写入,避免覆盖已有内容 - 每写入1000字符做一次flush - 写入完成后校验文件大小是否与预期一致这些细节看起来琐碎,但正是它们决定了Skill在真实项目里能不能稳定跑。
6. 常见问题与排查速查
6.1 Skill不触发怎么办
这是最高频的问题。排查顺序:
| 排查项 | 检查方法 | 常见原因 |
|---|---|---|
| description | 读一遍,问自己“模型能判断吗” | 描述太模糊 |
| 触发词 | 看是否与任务描述用词一致 | 用词偏差 |
| 文件位置 | 确认在skills目录下 | 路径错误 |
| frontmatter | 检查YAML语法 | 缩进或冒号问题 |
我遇到最多的情况是description写得太“官方”,比如“用于优化代码性能”,模型根本不知道什么时候该用。改成“当用户反馈某个函数执行超过1秒、且需要定位性能瓶颈时使用”之后,触发率立刻上来了。
6.2 Skill触发了但输出不对
通常是正文的步骤不够明确。我的经验是:凡是模型做错的地方,都是你写得不够具体的地方。比如你写“处理数据”,模型可能按自己的理解处理;你写“按第二列分组、对第三列求和、结果保留两位小数”,模型就很难出错。
另一个原因是缺少前置检查。如果Skill假设输入是干净的,但实际输入有脏数据,输出必然出问题。加上前置检查步骤,能挡掉大部分异常。
6.3 多个Skill冲突
当你有几十个Skill时,冲突是必然的。表现是:一个任务触发了多个Skill,输出里混了不同Skill的逻辑。解决办法有两个:一是收窄description,让每个Skill的适用场景更明确;二是设置优先级,在frontmatter里加一个priority字段,冲突时高优先级的生效。
我现在的做法是按领域分目录,比如skills/data/、skills/code/、skills/doc/,不同领域的Skill几乎不会冲突,同领域内的再靠description区分。
6.4 去AI味的Skill怎么写
这是个有意思的需求。所谓“去AI味”,本质是让输出更像人写的。我写过一个这样的Skill,核心逻辑是:
- 禁止使用“首先、其次、最后”这类结构化连接词
- 禁止使用“综上所述”“总而言之”这类总结套话
- 句子长度要有变化,避免全是中长句
- 允许口语化表达和适度的不完美
把这些规则写进Skill正文,模型输出确实会自然很多。但要注意,去AI味不等于降低质量,该有的信息密度不能丢。
7. 我现在的Skill工作流
走到第50个Skill,我现在的流程已经比较固定了。有新需求时,先花五分钟判断值不值得写Skill;值得写的话,先写description和触发词,用几个测试任务验证触发准确性;触发没问题了,再补正文步骤;正文写完后,用examples跑一遍,再做边界和干扰测试。整个过程大概半小时到一个小时,比早期快了很多。
有个小技巧我一直在用:给每个Skill写一个“废弃条件”。比如“当Claude默认能力能覆盖此任务时,废弃此Skill”。这逼着我定期回顾,把过时的Skill清理掉。毕竟Skill不是越多越好,维护成本是实打实的。
最后分享一个我踩过的坑:不要试图用一个Skill解决所有问题。我早期写过一个“万能代码助手Skill”,想覆盖审查、重构、测试、文档所有场景,结果每个场景都做得不深,触发还特别乱。后来拆成四个独立Skill,每个都专注一件事,整体效果反而好了很多。Skill的粒度,宁小勿大。