Claude Code Skill 踩坑复盘:从50个到25个,如何写出真正可复用的工作流
1. 从写了50个Skill到前30个白写一个真实的踩坑曲线我大概是从去年年底开始系统性地写 Claude Code Skill 的。一开始的心态特别简单——既然 Skill 本质上就是一份带 frontmatter 的 SKILL.md那我多写几个不就完了于是前前后后攒了 50 个覆盖代码审查、文档生成、测试用例、数据库迁移、日志分析、接口联调、甚至还有几个专门用来去 AI 味的写作类 Skill。结果用了两个月之后我回头做了一次复盘发现一个很扎心的事实前 30 个 Skill 里真正被我自己反复调用的不超过 5 个剩下的要么是写完之后再也没打开过要么是每次调用都要重新解释一遍上下文体验还不如直接跟模型对话。这不是 Skill 机制的问题是我一开始对 Skill 的理解就错了。我把它当成了提示词收藏夹而它真正的定位是可复用的工作流封装。这两者的差别就像你把菜谱抄在便签上贴冰箱和把一道菜做成预制菜放进冷冻柜——前者你每次还得自己动手后者拿出来加热就能吃。这篇内容我想把这 50 个 Skill 的踩坑过程完整拆开讲重点不是告诉你Skill 怎么写而是告诉你什么样的 Skill 值得写、什么样的 Skill 写了也是白写。如果你刚开始接触 Claude Code或者已经写了一堆 Skill 但感觉没什么用这篇应该能帮你省下不少时间。关键词里提到的 SKILL.md、frontmatter、MCP 这些概念我会在过程中自然带出来不单独做名词解释因为脱离场景讲定义没什么意义。2. 前30个Skill为什么白写四类典型的无效封装2.1 把一次性对话硬塞进Skill模板我最早写的一批 Skill 里有一个叫code-review-helper内容大概是请帮我审查以下代码关注命名规范、边界条件、异常处理……。写完之后我用了两次就放弃了原因很简单每次要审查的代码不一样关注点也不一样。有时候我想看并发安全有时候我只想看 SQL 注入有时候我只想让模型帮我判断这段逻辑是不是写反了。一个固定的审查模板根本覆盖不了这些变化。这类 Skill 的本质问题是它封装的是一个意图而不是一个流程。意图是发散的每次对话都应该重新表达流程是收敛的有固定的输入、固定的步骤、固定的输出格式。Skill 应该封装后者。后来我把这个 Skill 删了换成了一个sql-injection-scan专门做一件事接收一段 SQL 拼接代码按固定的检查清单逐条过输出一个带风险等级的表。这个 Skill 我到现在还在用因为它每次的输入输出都是确定的。2.2 frontmatter 写得像散文触发条件模糊SKILL.md 顶部的 frontmatter 是 Skill 的身份证它决定了模型在什么场景下会想起调用这个 Skill。我早期写的 frontmatter 大概长这样--- name: 代码优化助手 description: 帮助优化代码质量提升可读性和性能 ---看起来没毛病但实际上这个 description 太虚了。优化代码质量这个描述模型根本判断不出来什么时候该用它——是重构的时候是性能调优的时候还是 code review 的时候结果就是要么不触发要么在不该触发的时候触发。我后来改成这样--- name: python-perf-hotspot description: 当用户提供 Python 函数并询问性能瓶颈、或代码中出现嵌套循环/重复 IO/大列表推导时使用。输出热点定位和三条以内的优化建议。 ---差别在哪后者给出了明确的触发信号语言是 Python、场景是性能、输入形态是函数、输出有格式约束。模型看到这些信号匹配精度会高很多。这里有个经验description 不是写给人看的简介是写给模型看的路由规则。你要假设模型只扫一眼这句话就得判断现在这个场景要不要调用它。所以动词、名词、场景词、输出约束能写多具体就写多具体。2.3 把MCP能做的事又用Skill包了一遍这是我踩过最冤的一个坑。我写过一个fetch-api-doc的 Skill功能是根据接口名去查文档并返回字段说明。写完之后我发现我完全可以用 MCP 挂一个文档检索服务让模型直接调用工具去查根本不需要 Skill 这一层。Skill 和 MCP 的分工我现在的理解是这样的维度SkillMCP本质提示词流程封装外部能力接入擅长固定步骤、固定输出格式、领域知识注入实时数据、外部系统调用、动态查询不擅长需要实时数据的场景需要复杂推理链的场景典型例子代码规范检查、文档模板生成查数据库、调 API、读文件系统如果你的 Skill 里出现大量请调用某工具获取……的描述那大概率这个逻辑应该放到 MCP 里Skill 只负责编排调用顺序和结果处理。反过来如果你的 MCP 工具调用完之后还需要一大段固定的分析逻辑那这段逻辑就适合抽成 Skill。2.4 没有版本管理改着改着就废了我前 30 个 Skill 里有一大半是改废的——今天加一条规则明天删一条后天换个输出格式改到最后自己都不知道这个 Skill 到底在干什么。更麻烦的是有些 Skill 之间还有依赖关系改了一个另一个就报错。后来我给自己定了个规矩每个 Skill 目录下必须有一个 CHANGELOG.md每次改动记录三件事——改了什么、为什么改、影响哪些调用场景。听起来很重但实际上每次就写两三行成本极低收益极高。尤其是当你 Skill 数量超过 20 个之后没有变更记录基本等于失忆。3. 一个Skill值不值得写我用这三个问题做筛选踩完前 30 个坑之后我总结出一个筛选机制。每次想写新 Skill 之前先问自己三个问题三个都过才动手。3.1 这个任务我过去一个月重复了几次这是最硬的指标。如果一个任务我一个月内重复做了 5 次以上那它值得封装如果只做了 1-2 次那大概率是一次性需求写完就废。我现在的 Skill 库里调用频率最高的是这几个commit-msg-gen根据 git diff 生成规范化的 commit message几乎每天用sql-explain-read读 EXPLAIN 结果并给出索引建议每周用 3-4 次api-mock-gen根据接口定义生成 mock 数据每周用 2-3 次log-triage把一堆报错日志按根因聚类每周用 2 次左右这几个的共同点是输入形态固定、处理逻辑固定、输出格式固定。反过来那些帮我看看这段代码有没有问题之类的通用需求我从来不封装因为每次的问题都不一样。3.2 这个任务的输出格式能不能固定下来Skill 最大的价值之一是输出稳定性。如果每次调用出来的东西格式都不一样那它跟直接对话没区别。举个例子我有个pr-descriptionSkill输出格式是固定的四段变更摘要、影响范围、测试建议、回滚方案。每次生成出来都是这个结构我复制到 PR 里几乎不用改。这就是 Skill 该有的样子。反过来我早期写过一个brainstormSkill让模型帮我发散思路。这个 Skill 的输出每次都不一样格式也没法固定用了两次就删了——因为发散思路这件事直接对话反而更灵活。判断标准很简单你能不能画出这个 Skill 的输出模板能画出来就值得写画不出来就别写。3.3 这个任务需不需要注入领域知识有些任务模型本身就能做但做得不够专业。这时候 Skill 的价值就是把领域知识塞进去。比如我有个k8s-yaml-reviewSkill里面内置了一份检查清单资源限制有没有设、探针有没有配、镜像 tag 是不是 latest、securityContext 有没有开。这些规则模型不一定每次都想得起来但写进 Skill 里就变成了强制检查项。再比如gis-buffer-analysis这类空间分析的 Skill里面会预置坐标系选择、容差设置、拓扑检查这些专业步骤。没有这些知识注入模型给出的方案往往看起来对但跑不通。所以第三个问题的本质是这个任务有没有外行看不出来、内行才知道的门道有就值得写没有直接对话就行。4. 后20个Skill的写法从提示词堆砌到工作流编排前 30 个 Skill 白写之后我调整了写法。后 20 个的复用率明显上来了核心变化有三个。4.1 把Skill拆成入口步骤校验三段式我现在写 Skill基本都遵循这个结构--- name: xxx description: [触发条件输入形态输出约束] --- ## 输入 [明确说明这个 Skill 期望接收什么] ## 步骤 1. [第一步做什么] 2. [第二步做什么] 3. [第三步做什么] ## 输出格式 [固定的输出模板] ## 校验清单 - [ ] [输出前必须检查的点]这个结构的好处是模型知道自己在哪一步、要产出什么、产出前要检查什么。尤其是校验清单这一段能显著降低输出跑偏的概率。我拿commit-msg-gen举例它的校验清单是这样的[ ] 类型前缀是否在允许列表内feat/fix/refactor/docs/test/chore[ ] 描述是否用了祈使句[ ] 是否超过 72 字符[ ] 是否包含 issue 编号如果有有了这个清单模型生成完之后会自己过一遍我基本不用再手动改。4.2 用frontmatter的description做路由而不是做介绍前面提过 description 要具体这里再展开讲一下怎么写。我现在的 description 模板是触发场景 输入形态 输出约束。比如description: 当用户粘贴 git diff 输出、或要求生成 commit message 时使用。输入为 unified diff 格式输出为单行 conventional commit 格式不超过 72 字符。再比如description: 当用户提供 EXPLAIN 或 EXPLAIN ANALYZE 的文本输出、并询问慢查询原因时使用。输出为索引建议列表每条包含字段、类型、预期收益。这种写法的好处是模型在扫描所有 Skill 的时候能快速判断当前场景匹配哪个。我实测下来触发准确率比早期那种帮助优化代码式的描述高出一大截。4.3 把重复出现的子逻辑抽成共享片段写到第 40 个 Skill 左右的时候我发现很多 Skill 里有重复的逻辑。比如读取文件内容、解析 JSON 输出、按严重程度排序这些步骤几乎每个 Skill 都要写一遍。后来我在 Skill 目录下建了一个_shared/文件夹把常用的片段抽出来比如_shared/read-file.md统一的文件读取说明_shared/json-output.md统一的 JSON 输出格式约定_shared/severity-sort.md统一的严重程度排序规则然后在各个 Skill 里用引用语法引入。这样改一处所有引用它的 Skill 都跟着更新维护成本大幅下降。注意共享片段这个做法不同版本的 Claude Code 支持程度可能不一样。如果你的版本不支持引用语法那就退而求其次用复制粘贴CHANGELOG 记录的方式至少保证变更可追溯。5. 那些看起来很美但实际没用的Skill类型复盘这 50 个 Skill有几类是我踩过坑之后明确不再写的列出来给你避雷。5.1 万能助手型Skill就是那种 description 写帮助用户解决各种编程问题的 Skill。这类 Skill 的问题是它什么都想做结果什么都做不好。模型看到这种 description要么不触发要么触发了之后给出的回答跟直接对话没区别。Skill 的价值在于专不在于全。一个只做 SQL 注入扫描的 Skill比一个代码安全助手有用得多。5.2 知识库搬运型Skill我早期写过一个python-best-practicesSkill里面塞了一大堆 PEP8 规则、命名规范、设计模式。写完之后发现这些东西模型本来就知道塞进去反而让 Skill 变得臃肿每次调用都要加载一大堆用不上的内容。Skill 应该注入的是模型不知道或容易忘的知识而不是模型已经知道的知识。比如你们团队内部的代码规范、你们项目的特殊约定、你们业务领域的专业术语这些才值得写进去。5.3 纯格式转换型Skill比如把 JSON 转成 YAML、把 Markdown 转成 HTML这类。这些任务模型本身就能做而且做得很好封装成 Skill 纯属多此一举。除非转换规则特别复杂比如涉及业务字段映射否则没必要。5.4 情绪陪伴型Skill我见过有人写安慰助手、鼓励师这类 Skill。这类 Skill 的问题不是没用而是它的输出完全无法标准化每次都要重新表达需求封装的意义不大。6. Skill与MCP的边界我踩过的三次误判前面提过 Skill 和 MCP 的分工这里展开讲三次具体的误判都是我真金白银踩出来的。6.1 第一次误判把查数据写成了Skill我写过一个query-user-infoSkill功能是根据用户 ID 查询用户信息。写完之后发现这个 Skill 每次都要我手动粘贴用户数据进去因为它没法自己查数据库。后来改成 MCP 挂了一个数据库查询工具Skill 只负责调用工具格式化结果效率高了一个量级。判断标准如果这个任务需要实时数据或外部系统交互那核心逻辑应该在 MCPSkill 只做编排。6.2 第二次误判把复杂推理塞进了MCP反过来我也犯过相反的错。我写过一个 MCP 工具功能是分析日志并给出根因。结果发现MCP 工具本身不擅长做多步推理它更适合做取数据这种确定性操作。分析逻辑放在 MCP 里反而变得又慢又难维护。后来我把这个拆开了MCP 负责读取日志文件Skill 负责按根因聚类输出分析报告。各司其职效果好很多。6.3 第三次误判Skill和MCP职责重叠最坑的一次是我写了一个 Skill 做接口文档生成同时又写了一个 MCP 工具做接口定义解析。结果两者职责重叠调用的时候经常互相干扰模型不知道该用哪个。后来我明确了边界MCP 只做取原始数据Skill 只做加工和输出。接口定义解析这种取数据的活全部归 MCP文档格式化和补充说明这种加工的活全部归 Skill。边界清晰之后调用就顺了。7. 让Skill真正被用起来的三个细节写完 Skill 只是第一步让它真正被用起来才是关键。我总结了三个细节都是实操中摸索出来的。7.1 给Skill起一个能被想起来的名字名字很重要。我早期起的名字是helper-1、tool-a这种结果自己都记不住哪个是哪个。后来改成commit-msg-gen、sql-explain-read这种动词名词的结构一眼就知道是干什么的。命名建议用动作对象的结构全小写用连字符分隔。比如log-triage、api-mock-gen、pr-description。避免用assistant、helper、util这种模糊词。7.2 在Skill里写清楚什么时候不要用这一点很多人会忽略。我现在的 Skill 里都会加一段不适用场景比如## 不适用场景 - 如果 diff 超过 500 行建议先拆分再生成 commit message - 如果涉及多个不相关的变更建议拆成多个 commit这段内容的作用是防止模型在不该用的时候硬用。尤其是当你有多个相似 Skill 的时候不适用场景能帮模型做更精确的路由。7.3 定期做Skill 体检我现在的习惯是每个月做一次 Skill 体检检查三件事哪些 Skill 这个月一次都没被调用超过两个月没调用的考虑删掉或合并。哪些 Skill 调用后我手动改了很多改得多的说明输出格式或逻辑有问题需要优化。哪些 Skill 之间有职责重叠重叠的合并或明确边界。这个体检听起来麻烦但实际上每个月花不了半小时能帮你把 Skill 库维持在一个精简且高效的状态。我现在的 Skill 库稳定在 25 个左右比巅峰期的 50 个少了一半但实际使用频率反而更高。8. 关于去AI味这类Skill的一点个人看法热搜词里有个去AI味的skill我正好写过这里分享一下我的体会。我写过一个de-ai-flavorSkill功能是把 AI 生成的文本改得更像人写的。写完之后我发现这个 Skill 的效果其实很有限——因为AI 味这个东西本质上是句式结构、词汇选择、逻辑连接三个层面的问题靠一个 Skill 很难全面覆盖。后来我调整了思路把这个 Skill 拆成了三个更具体的子 Skillsentence-vary专门处理句式单一的问题把连续的主谓宾结构打散connector-trim专门删减首先/其次/最后、综上所述这类连接词concrete-example专门把抽象表述替换成具体例子拆开之后每个 Skill 的效果都明显好于原来那个大而全的版本。这也印证了前面说的Skill 要专不要全。9. 写在最后Skill不是越多越好是越准越好从 50 个 Skill 砍到 25 个我最大的体会是Skill 的价值不在于数量而在于每次调用都能稳定产出我想要的东西。前 30 个白写不是因为它们写错了而是因为它们封装的是意图而不是流程是知识而不是判断是重复劳动而不是专业增值。后 20 个能用起来是因为我把每一个都当成一个小型工作流来设计——有明确的输入、固定的步骤、稳定的输出、清晰的边界。如果你现在正在写 Skill我的建议是先别急着写先观察自己过去一周重复做了哪些事哪些事的输出格式是固定的哪些事需要注入领域知识。从这三个问题里筛出来的才是真正值得封装的。至于那些看起来很美的通用助手型 Skill就让它们留在草稿箱里吧。