ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Claude Code Skill 开发实战:从50个失败案例中提炼的设计原则与避坑指南

Claude Code Skill 开发实战:从50个失败案例中提炼的设计原则与避坑指南 1. 从 50 个 Skill 里爬出来的血泪账我在过去几个月里陆陆续续写了超过 50 个 Claude Code Skill。从最开始的兴奋到中途的自我怀疑再到后面逐渐摸到门道这个过程踩的坑比我预想的多得多。最扎心的一个结论是前 30 个 Skill 基本等于白写。不是它们跑不起来而是它们没有真正解决“值得被自动化”的问题只是在用更复杂的方式做我本来手动就能做完的事。这篇文章不是教程式的“如何写第一个 Skill”而是把我从无效劳动里提炼出来的判断标准、设计思路、实操细节和排查经验完整地摊开来讲。如果你正在用 Claude Code或者准备把日常开发、文档处理、接口调试、数据整理这些重复动作交给 Skill 来做那这篇内容能帮你少走至少两三个月的弯路。它适合已经上手 Claude Code、写过至少一两个 Skill 的人也适合还在观望、想知道 Skill 到底值不值得投入的人。先说清楚一个基本认知Skill 不是“让 AI 多干一点活”的开关而是一套把领域知识、操作流程、工具调用和输出规范打包成可复用资产的机制。它的核心文件是SKILL.md配合脚本、配置和资源文件一起工作。很多人第一次写 Skill 时会把它当成一个“更长的提示词”结果写出来的东西又长又散Claude 执行时要么漏步骤要么自由发挥最后还得人工兜底。我前 30 个 Skill 里至少有一半死在这个问题上。2. 为什么前 30 个 Skill 会白写2.1 把 Skill 当提示词写是最常见的死法我最早写的几个 Skill基本就是把一段提示词塞进SKILL.md然后指望 Claude 每次都能稳定执行。比如我写过一个“整理 Spring Boot 接口文档”的 Skill内容大概就是“请读取 Controller 文件提取接口路径、请求方法、参数和返回值然后生成 Markdown 表格”。听起来没问题但实际跑起来Claude 每次生成的表格列顺序不一样参数嵌套深了会漏字段遇到泛型返回值直接摆烂。问题出在哪儿我把“判断逻辑”和“执行逻辑”混在一起了。Skill 真正应该做的是把确定性的部分固化下来把不确定性的部分交给 Claude 判断。接口路径怎么提取、注解怎么解析、泛型怎么展开这些是确定性的应该用脚本做而“这个接口是给第三方用的还是内部用的”“参数命名是否合理”这类需要语义理解的才交给 Claude。后来我重写了这个 Skill结构变成一个 Python 脚本负责扫描RestController和RequestMapping注解输出结构化 JSONSKILL.md里只写“调用脚本 → 读取 JSON → 按固定模板生成文档 → 检查缺失字段并提示”。重写之后同一个 Skill 的稳定性从“十次跑三次对”变成“十次跑十次对”。2.2 没有明确触发边界Skill 会被乱调用Claude Code 的 Skill 机制里触发条件非常关键。我早期写过一个“代码审查”Skill描述写得很宽泛“当用户需要审查代码时使用”。结果我在写业务逻辑时Claude 动不动就触发这个 Skill给我一堆无关的审查意见打断正常开发流程。后来我学乖了触发描述必须收窄。比如改成“当用户明确要求对指定 Java 文件进行安全审查且文件中包含 Spring Boot 控制器或服务层代码时使用”。同时我在SKILL.md开头加了一段前置检查如果目标文件不包含RestController、Service、Repository任一注解直接跳过并告知用户。这样误触发率大幅下降。这里有个经验Skill 的触发描述不是越短越好而是越精确越好。你要让 Claude 能判断“什么时候不该用”而不只是“什么时候该用”。2.3 输出格式不稳定等于没写我写过一個“生成数据库变更脚本”的 Skill输入是实体类输出是 SQL。前几次跑得挺好后面开始出现列名大小写不一致、索引命名随机、注释时有时无的问题。原因是我没有在 Skill 里定义严格的输出模板Claude 每次都在“自由创作”。后来我在SKILL.md里加了一个output_template.md引用明确规定表名必须小写下划线列名必须与实体字段一一对应并转为下划线每个字段必须有COMMENT索引命名必须是idx_表名_列名。同时加了一个校验脚本生成后自动检查命名规范不符合就报错重来。从那以后这个 Skill 的输出可以直接进代码仓库不需要人工再改。2.4 忽略 MCP 的配合Skill 能力被锁死Claude Code 的 Skill 本身是“流程编排”真正干活的是工具。我早期写 Skill 时只依赖 Claude 内置的文件读写和命令执行结果很多场景做不了。比如我想让 Skill 自动查数据库表结构内置工具做不到想让它调用内部 API 获取配置也做不到。后来我开始把 MCP 接进来。MCP 是模型上下文协议简单理解就是“给 Claude 装外设”。我接了一个数据库 MCPSkill 就能直接查表结构接了一个 HTTP MCPSkill 就能调内部接口。这样 Skill 的边界一下子打开了。比如我写过一个“根据数据库表自动生成 Spring Boot 实体类和 Mapper”的 Skill流程是调用数据库 MCP 获取表结构 → 解析字段类型 → 映射 Java 类型 → 生成实体类 → 生成 MyBatis Mapper XML → 校验字段一致性。整个过程不需要我手动复制表结构。这里要注意MCP 的接入不是越多越好。我一开始接了一堆 MCP结果 Skill 启动变慢Claude 在选择工具时也容易犹豫。后来我按场景分组每个 Skill 只声明它需要的 MCP启动速度和执行准确率都回来了。3. 一个合格 Skill 的骨架长什么样3.1 SKILL.md 的结构化写法我现在写SKILL.md基本遵循一个固定骨架。开头是元信息名称、版本、触发条件、依赖工具。然后是执行流程分步骤写每一步明确输入、操作、输出。最后是异常处理和输出模板。具体来说一个典型的SKILL.md大概长这样--- name: spring-boot-api-doc version: 1.2.0 trigger: 当用户要求为 Spring Boot 项目生成接口文档且项目包含 RestController 注解时使用 dependencies: - mcp: database - script: extract_api.py --- ## 执行流程 ### 步骤 1扫描控制器 调用 extract_api.py传入项目根路径输出 api_list.json。 ### 步骤 2补充数据库信息 如果接口涉及数据库操作调用 database MCP 获取相关表结构。 ### 步骤 3生成文档 读取 output_template.md按模板填充输出到 docs/api.md。 ### 步骤 4校验 检查文档中每个接口是否有路径、方法、参数、返回值、错误码。这个结构的好处是Claude 不需要“理解”整个 Skill只需要按步骤执行。每一步的输入输出都是明确的出错时也能定位到具体步骤。3.2 脚本和 Skill 的分工原则我总结了一条原则能用脚本做的不要交给 Claude。脚本负责确定性计算、格式转换、文件扫描、数据校验Claude 负责语义判断、内容生成、异常解释、用户交互。举个例子我写过一个“把需求文档转成测试用例”的 Skill。需求文档里有很多自然语言描述比如“用户登录失败时应提示具体错误原因”。这句话怎么转成测试用例脚本做不到必须 Claude 来理解。但测试用例的格式、编号规则、字段顺序这些是确定的应该由脚本或模板来保证。所以这个 Skill 的分工是Claude 读取需求文档提取每条需求并判断测试点脚本接收测试点列表按固定模板生成测试用例表格Claude 最后检查是否有遗漏或重复。这样既发挥了 Claude 的理解能力又保证了输出稳定。3.3 触发条件的精确写法触发条件写不好Skill 要么不触发要么乱触发。我现在的写法是“三要素”用户意图 输入特征 排除条件。比如一个“生成 Spring Boot 监控配置”的 Skill触发条件写成用户意图要求为 Spring Boot 项目添加监控配置输入特征项目中包含pom.xml或build.gradle且包含 Spring Boot 依赖排除条件项目已经包含spring-boot-starter-actuator且用户只是询问监控指标含义这样 Claude 在判断是否触发时有明确的依据。实测下来误触发率从原来的三成降到不足一成。4. 实操从零写一个能用的 Skill4.1 场景选择什么值得做成 Skill不是所有重复劳动都值得做成 Skill。我现在的判断标准是三条频率高、步骤固定、输出有明确验收标准。三条都满足才值得投入时间。比如“每天整理 Git 提交记录生成日报”频率高步骤固定输出格式明确值得做。“偶尔帮同事查一个接口参数”频率低步骤不固定不值得做。我前 30 个 Skill 里有很多是“我觉得这个场景很酷”就写了结果一个月用不到一次。后来我改成“先手动做三次确认每次都一样再写成 Skill”效率高了很多。4.2 编写 SKILL.md 的完整过程假设我要写一个“Spring Boot 接口对接第三方时的参数校验”Skill。这个场景很常见后端给第三方提供接口需要校验签名、时间戳、必填参数、参数格式。每次都要写一遍校验逻辑很烦。第一步我先手动做三次记录每次的步骤。发现固定流程是读取接口定义 → 提取必填参数 → 检查签名算法 → 生成校验代码 → 写单元测试。第二步我把确定性的部分抽出来。参数提取可以用脚本解析 Swagger 或 OpenAPI 文档签名算法有固定模板单元测试有固定结构。Claude 只需要判断“这个接口是否涉及敏感数据”“签名算法是否与项目现有规范一致”。第三步写SKILL.md。触发条件写成“当用户要求为 Spring Boot 接口添加第三方对接参数校验且接口定义文件存在时使用”。执行流程分五步每步明确输入输出。异常处理里写明如果接口定义文件不存在提示用户先补充如果签名算法无法识别列出支持的算法让用户选择。第四步写校验脚本。脚本负责检查生成的代码是否包含所有必填参数、签名算法是否在支持列表、单元测试是否覆盖每个校验分支。第五步实测。我拿三个真实接口跑了一遍发现两个问题一个是泛型参数解析漏了一个是时间戳格式校验没考虑时区。修完这两个问题后这个 Skill 基本可以稳定使用了。4.3 参数计算与配置细节在写 Skill 时经常需要处理参数计算。比如时间戳校验我一开始只检查“是否在有效期内”后来发现还要检查“是否在未来”防止重放攻击。具体计算是当前时间戳减去请求时间戳绝对值不能超过 300 秒同时请求时间戳不能大于当前时间戳加 60 秒。这个逻辑我写进了脚本里SKILL.md里只写“调用校验脚本检查时间戳有效性”。这样即使以后有效期从 300 秒改成 600 秒我只需要改脚本不需要改 Skill 描述。另一个细节是配置文件。我早期把配置直接写在SKILL.md里后来发现不同项目配置不一样改起来很麻烦。现在我把配置抽到config.yamlSKILL.md里引用配置项。比如签名算法、有效期、必填字段列表都放在配置里。这样同一个 Skill 可以在不同项目里复用只需要改配置。4.4 与 MCP 的配合实操MCP 的接入让 Skill 的能力边界扩大了很多。我举一个实际例子我写过一个“根据数据库表生成 Spring Boot 实体类”的 Skill流程是调用 database MCP传入表名获取表结构和字段注释。脚本解析表结构映射 Java 类型。比如varchar映射Stringint映射Integerdatetime映射LocalDateTime。Claude 根据字段注释生成 JavaDoc。脚本生成实体类文件包含TableName、TableId、TableField注解。校验脚本检查字段名与列名是否一一对应注解是否完整。这里 database MCP 是关键。没有它Skill 只能让用户手动粘贴表结构体验差很多。接入 MCP 后整个流程从“用户提供输入”变成“Skill 主动获取输入”自动化程度完全不一样。但要注意MCP 的调用需要权限和网络配置。我在本地环境跑的时候database MCP 连的是本地数据库没问题部署到服务器后需要配置数据库连接串和访问权限。这些配置我放在环境变量里SKILL.md里只写“调用 database MCP 获取表结构”不暴露具体连接信息。5. 常见问题与排查技巧实录5.1 Skill 不触发或乱触发这是最常见的问题。排查思路是先看触发描述是否精确再看前置检查是否生效最后看是否有其他 Skill 冲突。我遇到过一次两个 Skill 的触发条件都包含“生成文档”结果 Claude 每次都要问用哪个。后来我把其中一个改成“生成接口文档”另一个改成“生成数据库文档”冲突就解决了。还有一个情况是 Skill 完全不触发。我检查后发现触发描述里写的是“当用户要求生成 API 文档时使用”但用户实际说的是“帮我整理一下接口”。关键词不匹配Claude 判断不出来。后来我把描述改成“当用户要求整理、生成或导出接口文档时使用”覆盖了更多表达方式。5.2 执行到一半卡住Skill 执行卡住通常是某个步骤的输入不符合预期。比如脚本期望 JSON但上一步输出的是 Markdown 表格。排查方法是在SKILL.md里加中间输出检查每一步执行完后打印输出格式和内容摘要方便定位。我写过一个 Skill执行到第三步总是失败。后来发现是第二步的脚本输出编码有问题中文注释变成乱码第三步解析不了。修了编码后就好了。这种问题在 Windows 和 Linux 之间切换时特别常见建议脚本统一用 UTF-8 编码。5.3 输出格式不稳定输出格式不稳定根本原因是模板不够严格。我的做法是所有输出都走模板模板里用占位符脚本负责填充。Claude 只负责生成占位符对应的内容不负责排版。比如生成接口文档模板里写## {{api_name}} - 路径{{api_path}} - 方法{{api_method}} - 参数{{api_params}} - 返回值{{api_return}}脚本负责把 Claude 生成的内容填进去。这样无论 Claude 怎么发挥最终格式都是固定的。5.4 与现有项目规范冲突Skill 生成的代码或文档经常和项目现有规范不一致。比如项目用 4 空格缩进Skill 生成 2 空格项目用LocalDateTimeSkill 生成Date。解决方法是在 Skill 里加一个“规范读取”步骤。先读取项目根目录的.editorconfig、checkstyle.xml或现有代码样本提取缩进、命名、类型映射等规范再生成内容。这样 Skill 就能适配不同项目。我现在的做法是每个 Skill 都有一个project_profile配置记录项目的技术栈、代码规范、依赖版本。生成内容前先加载这个配置确保输出与项目一致。5.5 常见问题速查表问题现象可能原因排查方法解决方案Skill 不触发触发描述不精确检查关键词是否覆盖用户表达扩充触发描述增加同义词Skill 乱触发触发条件太宽泛查看是否有其他 Skill 冲突收窄触发条件加排除项执行卡住步骤输入输出不匹配加中间输出检查统一数据格式加校验输出格式乱没有严格模板检查是否有模板文件所有输出走模板与项目规范冲突没有读取项目配置检查是否有规范读取步骤加 project_profile 配置MCP 调用失败权限或网络问题检查 MCP 连接配置配置环境变量检查权限脚本报错编码或路径问题检查脚本日志统一 UTF-8用绝对路径6. 从 30 个废稿里提炼的设计原则6.1 先手动做三次再写 Skill这条原则帮我省了大量时间。以前我看到一个重复劳动就想写成 Skill结果写完后发现场景太少或者每次情况都不一样Skill 根本用不起来。现在我先手动做三次如果三次步骤基本一致才值得写成 Skill。如果三次都不一样说明这个场景不适合自动化或者需要先梳理流程。6.2 确定性交给脚本判断交给 Claude这是最核心的分工原则。脚本擅长精确计算、格式转换、批量处理Claude 擅长语义理解、内容生成、异常解释。把两者混在一起Skill 就会又慢又不稳。我现在的 Skill 里脚本代码量通常比SKILL.md还多。SKILL.md只负责编排流程具体干活的是脚本。这样 Skill 的执行速度快输出稳定也容易调试。6.3 输出必须有验收标准一个 Skill 好不好用看输出能不能直接使用。如果每次生成完还要人工改半天那这个 Skill 就是失败的。所以我在写 Skill 时会先定义验收标准输出格式是什么、必须包含哪些字段、命名规范是什么、错误处理怎么做。然后写校验脚本自动检查这些标准。不符合就报错让 Claude 重试或提示用户。6.4 配置与逻辑分离配置和逻辑混在一起Skill 就没法复用。我把所有可变的部分抽到config.yaml比如数据库连接、签名算法、有效期、必填字段。SKILL.md和脚本只引用配置项不写死具体值。这样同一个 Skill 可以在不同项目、不同环境里复用只需要改配置。6.5 版本管理和回滚Skill 也是代码需要版本管理。我每个 Skill 都有版本号改动记录在CHANGELOG.md里。如果新版本出问题可以快速回滚到旧版本。我遇到过一次更新了一个 Skill 的校验逻辑结果导致所有生成内容都被判定为不合格。回滚到旧版本后问题立刻消失。后来我改成新版本先在小范围测试确认没问题再全量使用。7. 后续可以这样扩展如果你已经写了一些 Skill想进一步提升我建议从三个方向入手。第一把 Skill 和 MCP 结合得更紧密让 Skill 能主动获取外部信息而不是等用户输入。第二给 Skill 加自检和自修复能力比如生成内容后自动校验不符合就自动重试或调整。第三把多个 Skill 组合成工作流比如“生成接口文档 → 生成测试用例 → 生成接口 Mock”一键完成整个链路。我自己现在在做的是把 Skill 和项目的 CI 流程结合。比如每次提交代码前自动触发代码规范检查 Skill 和接口文档更新 Skill确保代码和文档同步。这个方向还在摸索等跑顺了再单独写一篇分享。最后分享一个小技巧写 Skill 时先写异常处理再写正常流程。因为正常流程你脑子里很清楚但异常情况往往被忽略。先把异常处理写好Skill 的健壮性会高很多。我前 30 个 Skill 里大部分问题都出在异常处理没写好而不是正常流程有问题。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进