實戰(zhàn):從50個踩坑到可復(fù)用工程體系)
1. 從 50 個 Skill 里爬出來的血淚賬先交代背景。過去大半年我陸陸續(xù)續(xù)寫了 50 個 Claude Code Skill覆蓋代碼生成、接口文檔、數(shù)據(jù)庫遷移、前端組件、測試用例、部署腳本這些日?;顑骸懙阶詈笪一仡^一盤點發(fā)現(xiàn)前 30 個基本等于白寫——不是不能用而是用起來別扭、維護成本高、復(fù)用率低最后大部分都被我自己棄用了。這篇文章就是把這 50 個 Skill 的踩坑過程攤開講。核心關(guān)鍵詞是Claude Code、Skill、SKILL.md、MCP順帶會聊到Spring Boot場景下的落地案例。適合誰看三類人一是剛開始接觸 Claude Code Skill、還在糾結(jié)怎么寫第一個 SKILL.md 的人二是已經(jīng)寫了一堆 Skill 但發(fā)現(xiàn)復(fù)用率上不去、維護越來越累的人三是想把 Skill 和 MCP 結(jié)合起來做工程化落地的人。不管你是剛上手還是已經(jīng)踩過幾腳泥這篇應(yīng)該都能幫你少走點彎路。我先把結(jié)論擺前面省得你看到一半才反應(yīng)過來Skill 的價值不在于能跑而在于穩(wěn)定復(fù)用。前 30 個 Skill 之所以白寫根本原因是我把它們當(dāng)成了一次性腳本來寫而不是當(dāng)成可維護的工程資產(chǎn)來設(shè)計。這個認(rèn)知轉(zhuǎn)變是我寫完第 31 個 Skill 之后才真正想明白的。下面我按四個部分展開先講整體設(shè)計思路的轉(zhuǎn)變再拆核心細(xì)節(jié)和實操要點然后是完整的實操流程最后是我整理的常見問題排查表。每一部分都是真金白銀換來的不是從文檔里抄的。2. 整體設(shè)計思路為什么前 30 個 Skill 會白寫2.1 把 Skill 當(dāng)腳本寫是最大的坑我最早寫 Skill 的思路特別樸素有個重復(fù)性任務(wù)就寫個 Skill 把它固化下來。比如生成 Spring Boot Controller 模板、把 MyBatis 的 XML 轉(zhuǎn)成注解、根據(jù)實體類生成建表 SQL。每個 Skill 單獨看都能用但問題在于——它們之間沒有共享上下文沒有統(tǒng)一的輸入輸出約定沒有版本管理。結(jié)果就是寫第 5 個 Skill 的時候我復(fù)制了第 3 個的 SKILL.md 結(jié)構(gòu)寫第 12 個的時候又復(fù)制了第 8 個的。到第 20 個的時候我發(fā)現(xiàn)有 6 個 Skill 的 prompt 里都重復(fù)寫了同一段項目使用 Spring Boot 3.x MyBatis-Plus包名統(tǒng)一為 com.xxx的上下文。這段上下文一旦要改比如項目升級到 Spring Boot 3.2我得挨個改 6 個文件。這就是典型的腳本思維每個 Skill 自包含不考慮復(fù)用和抽象。而正確的做法應(yīng)該是工程思維把公共上下文抽出來把輸入輸出標(biāo)準(zhǔn)化把 Skill 當(dāng)成有生命周期的資產(chǎn)來管理。2.2 SKILL.md 的結(jié)構(gòu)決定了 Skill 的上限很多人寫 SKILL.md 就是隨手寫一段 prompt前面加個標(biāo)題就完事了。我前 30 個 Skill 基本都是這個路子。后來我才意識到SKILL.md 的結(jié)構(gòu)直接決定了這個 Skill 能不能被穩(wěn)定觸發(fā)、能不能被復(fù)用、能不能被維護。一個合格的 SKILL.md 至少應(yīng)該包含這幾塊觸發(fā)條件什么時候用這個 Skill、輸入約定用戶需要提供什么、執(zhí)行步驟Skill 內(nèi)部怎么處理、輸出格式產(chǎn)出什么、邊界說明什么情況下不該用。我前 30 個 Skill 里有超過一半只寫了執(zhí)行步驟觸發(fā)條件和邊界說明基本空白。結(jié)果就是 Claude Code 經(jīng)常在不該觸發(fā)的時候觸發(fā)或者觸發(fā)了但輸入不完整導(dǎo)致輸出亂七八糟。2.3 為什么是 30 這個數(shù)字你可能會問為什么偏偏是前 30 個白寫不是前 20 或前 40說實話這個數(shù)字不是精確的是我復(fù)盤時的一個大致分界。前 30 個 Skill 我基本是想到就寫沒有統(tǒng)一規(guī)劃從第 31 個開始我做了三件事建立公共上下文庫、統(tǒng)一 SKILL.md 模板、引入 MCP 做外部能力補充。這三件事做完之后后面 20 個 Skill 的復(fù)用率和穩(wěn)定性明顯上了一個臺階。所以30更像是一個認(rèn)知拐點的標(biāo)記而不是一個精確的統(tǒng)計數(shù)字。如果你現(xiàn)在正處在想到就寫的階段那這篇文章就是寫給你的。2.4 Skill 和 MCP 的分工要提前想清楚這是我在第 35 個 Skill 左右才想明白的事。Skill 負(fù)責(zé)流程編排和上下文注入MCP 負(fù)責(zé)外部能力調(diào)用。兩者分工不清就會導(dǎo)致 Skill 里塞了一堆本該由 MCP 做的事或者 MCP 配置了一堆本該由 Skill 處理的邏輯。舉個 Spring Boot 場景的例子我要做一個根據(jù)需求描述生成完整 CRUD 模塊的 Skill。這個 Skill 需要讀項目現(xiàn)有的實體類、需要查數(shù)據(jù)庫表結(jié)構(gòu)、需要寫文件。讀實體類和寫文件是本地能力Skill 自己就能做但查數(shù)據(jù)庫表結(jié)構(gòu)這件事如果項目用的是遠(yuǎn)程數(shù)據(jù)庫就需要通過 MCP 去調(diào)用數(shù)據(jù)庫工具。如果我把查數(shù)據(jù)庫這件事也硬塞進 Skill 的 prompt 里那這個 Skill 就綁死了特定的數(shù)據(jù)庫環(huán)境換個項目就廢了。正確的做法是Skill 負(fù)責(zé)什么時候查、查什么、拿到結(jié)果怎么用MCP 負(fù)責(zé)實際去查。這樣 Skill 是可移植的MCP 是可替換的。3. 核心細(xì)節(jié)解析SKILL.md 到底該怎么寫3.1 觸發(fā)條件要寫得像路由規(guī)則觸發(fā)條件是 SKILL.md 里最容易被忽視、但最重要的一塊。我前 30 個 Skill 里觸發(fā)條件基本就是一句話當(dāng)用戶需要生成 Controller 時使用。這種寫法太模糊了Claude Code 根本判斷不準(zhǔn)。好的觸發(fā)條件應(yīng)該像路由規(guī)則一樣精確。比如## 觸發(fā)條件 當(dāng)滿足以下全部條件時觸發(fā) 1. 用戶明確提到生成 Controller、創(chuàng)建接口、新增 REST 接口之一 2. 當(dāng)前工作目錄下存在 pom.xml 或 build.gradle 3. 用戶提供了實體類名或表名 不滿足任一條件時先向用戶確認(rèn)不要直接執(zhí)行。這樣寫的好處是Claude Code 在判斷是否觸發(fā)時有明確的依據(jù)不會因為用戶隨口提了一句接口就貿(mào)然觸發(fā)。我實測下來加了精確觸發(fā)條件之后誤觸發(fā)率從大概三成降到了不到一成。3.2 輸入約定要明確缺什么就問輸入約定這塊我踩過的坑是Skill 假設(shè)用戶會提供完整信息但實際用戶往往只給一半。比如我寫過一個根據(jù)表名生成 MyBatis Mapper的 Skill假設(shè)用戶會提供表名和字段列表。結(jié)果用戶經(jīng)常只給表名Skill 就開始瞎編字段生成的 Mapper 完全不能用。后來我改成這樣## 輸入約定 必需輸入 - 表名必填 - 字段列表必填格式字段名 類型 注釋 可選輸入 - 包名默認(rèn) com.example.mapper - 是否生成 XML默認(rèn)否 如果用戶未提供必需輸入逐項詢問不要自行假設(shè)。關(guān)鍵就是那句不要自行假設(shè)。Claude Code 很聰明聰明到會幫你腦補缺失信息但腦補出來的東西往往不對。明確告訴它缺什么就問比讓它自由發(fā)揮靠譜得多。3.3 執(zhí)行步驟要拆到可驗證的粒度執(zhí)行步驟是 SKILL.md 的主體也是最容易寫得太粗或太細(xì)的地方。寫太粗Claude Code 不知道具體怎么做寫太細(xì)又變成了死板的腳本失去靈活性。我的經(jīng)驗是拆到每一步都有明確產(chǎn)出、且產(chǎn)出可驗證的粒度。比如生成 CRUD 模塊這個 Skill我拆成這幾步讀取實體類提取字段列表產(chǎn)出字段清單可驗證字段數(shù)和實體類一致根據(jù)字段清單生成建表 SQL產(chǎn)出SQL 文件可驗證能執(zhí)行不報錯生成 Mapper 接口產(chǎn)出Java 文件可驗證編譯通過生成 Service 層產(chǎn)出Java 文件可驗證編譯通過生成 Controller 層產(chǎn)出Java 文件可驗證編譯通過每一步都有產(chǎn)出每一步都能驗證。這樣即使中間某一步出錯也能快速定位是哪一步的問題而不是面對一個整體跑不通的黑盒。3.4 輸出格式要固定方便下游消費輸出格式這塊我前 30 個 Skill 基本沒管導(dǎo)致每個 Skill 的輸出風(fēng)格都不一樣。有的輸出 Markdown有的輸出純文本有的直接輸出代碼塊。結(jié)果就是這些 Skill 之間沒法串聯(lián)——A Skill 的輸出沒法直接喂給 B Skill。后來我統(tǒng)一了輸出格式所有 Skill 的輸出都遵循摘要 詳情 下一步建議三段式。摘要用一兩句話說明做了什么詳情用代碼塊或表格展示具體產(chǎn)出下一步建議告訴用戶接下來可以做什么。這樣不僅人看著舒服Skill 之間也能互相消費輸出。3.5 邊界說明是防呆設(shè)計邊界說明是我最后才補上的一塊但補上之后效果立竿見影。所謂邊界說明就是明確告訴 Claude Code什么情況下不該用這個 Skill。比如生成 CRUD 模塊這個 Skill邊界說明寫的是## 邊界說明 以下情況不要使用本 Skill - 項目不是 Spring Boot 項目 - 實體類使用了 JPA 注解而非 MyBatis-Plus 注解 - 用戶要求生成的是 GraphQL 接口而非 REST 接口 遇到以上情況向用戶說明原因并建議替代方案。這段說明的價值在于它防止了 Skill 在不適用的場景下被強行觸發(fā)避免了用錯工具導(dǎo)致的返工。我實測下來加了邊界說明之后因為Skill 用錯場景導(dǎo)致的返工減少了大概一半。4. 實操過程從零搭一個可復(fù)用的 Skill 體系4.1 第一步建立公共上下文庫這是我從第 31 個 Skill 開始做的第一件事。具體做法是在項目根目錄建一個.claude/context/目錄里面放幾個公共上下文文件比如project.md項目技術(shù)棧、包名約定、代碼風(fēng)格、database.md數(shù)據(jù)庫連接信息、表命名規(guī)范、api.md接口規(guī)范、返回格式約定。然后在每個 SKILL.md 里通過引用這些文件來注入上下文而不是把上下文硬編碼在 Skill 里。比如## 上下文 執(zhí)行前先讀取 .claude/context/project.md 和 .claude/context/api.md 按照其中的約定執(zhí)行。如果文件不存在向用戶確認(rèn)項目約定。這樣做的好處是項目約定變了只需要改一處所有 Skill 自動生效。我實測下來項目從 Spring Boot 2.7 升級到 3.2 的時候只改了project.md一個文件所有 Skill 就都適配了省了大量重復(fù)勞動。4.2 第二步統(tǒng)一 SKILL.md 模板第二件事是統(tǒng)一模板。我定了一個標(biāo)準(zhǔn)模板所有新 Skill 都按這個模板寫# Skill 名稱 ## 觸發(fā)條件 精確的路由規(guī)則 ## 輸入約定 必需輸入 可選輸入 缺失處理 ## 上下文 需要讀取的公共上下文文件 ## 執(zhí)行步驟 拆到可驗證粒度 ## 輸出格式 摘要 詳情 下一步建議 ## 邊界說明 什么情況下不該用這個模板看起來簡單但它強制我在寫每個 Skill 的時候都思考這七個問題。前 30 個 Skill 之所以白寫很大程度上就是因為沒有這個模板寫的時候想到哪寫到哪漏掉了很多關(guān)鍵信息。4.3 第三步引入 MCP 做能力補充第三件事是引入 MCP。MCP 的全稱是 Model Context Protocol簡單說就是一套讓 AI 調(diào)用外部工具的協(xié)議。在 Claude Code 里MCP 可以讓 Skill 具備訪問外部系統(tǒng)的能力比如查數(shù)據(jù)庫、調(diào) API、讀遠(yuǎn)程文件。我在 Spring Boot 項目里最常用的 MCP 配置是數(shù)據(jù)庫查詢。配置好之后Skill 就可以通過 MCP 去查真實的表結(jié)構(gòu)而不是靠用戶描述或者靠猜。具體配置大概長這樣以常見的數(shù)據(jù)庫 MCP 為例{ mcpServers: { database: { command: npx, args: [-y, modelcontextprotocol/server-postgres], env: { DATABASE_URL: postgresql://user:passlocalhost:5432/mydb } } } }配置好之后Skill 里就可以這樣用## 執(zhí)行步驟 1. 通過 MCP 的 database 工具查詢目標(biāo)表結(jié)構(gòu) 2. 根據(jù)表結(jié)構(gòu)生成實體類 3. 根據(jù)實體類生成 Mapper、Service、Controller這里的關(guān)鍵是Skill 只負(fù)責(zé)調(diào)用 MCP不負(fù)責(zé)實現(xiàn) MCP。這樣 Skill 是可移植的換個數(shù)據(jù)庫只需要換 MCP 配置Skill 本身不用改。4.4 第四步建立 Skill 版本管理第四件事是版本管理。我前 30 個 Skill 基本沒有版本概念改了就直接覆蓋出了問題想回滾都回不去。后來我給每個 Skill 加了版本號放在 SKILL.md 的頭部--- name: generate-crud version: 1.2.0 last_updated: 2024-05-20 ---版本號遵循語義化版本規(guī)范大版本號變了說明有不兼容改動小版本號變了說明加了功能補丁號變了說明只是修了 bug。這樣我在用 Skill 的時候能一眼看出這個 Skill 是不是最新版改動大不大。4.5 第五步寫測試用例驗證 Skill第五件事是寫測試用例。這是我從第 40 個 Skill 開始才做的但做了之后發(fā)現(xiàn)太值了。具體做法是給每個 Skill 準(zhǔn)備 2-3 個測試用例覆蓋正常場景、邊界場景、異常場景。比如生成 CRUD 模塊這個 Skill我準(zhǔn)備了三個測試用例用例編號場景輸入預(yù)期輸出TC-01正常場景完整實體類 表名生成 4 個文件編譯通過TC-02邊界場景實體類只有 1 個字段生成 4 個文件編譯通過TC-03異常場景實體類不存在提示錯誤不生成文件每次改完 Skill我都跑一遍這三個用例確保沒有引入回歸問題。這個習(xí)慣幫我避免了好幾次改了一個地方壞了另一個地方的事故。4.6 第六步Skill 之間的串聯(lián)第六件事是讓 Skill 之間能串聯(lián)。單個 Skill 再強能力也有限但多個 Skill 串起來就能完成復(fù)雜任務(wù)。串聯(lián)的關(guān)鍵是統(tǒng)一輸出格式讓 A Skill 的輸出能直接作為 B Skill 的輸入。比如我有一個需求分析Skill輸出是結(jié)構(gòu)化的需求清單還有一個代碼生成Skill輸入是需求清單。這兩個 Skill 串起來就能實現(xiàn)從需求描述到代碼生成的端到端流程。我實測下來這個串聯(lián)流程能把一個中等復(fù)雜度的 CRUD 模塊開發(fā)時間從半天壓縮到半小時左右。5. 常見問題與排查技巧實錄5.1 Skill 不觸發(fā)怎么辦這是最常見的問題。Skill 寫好了但 Claude Code 就是不觸發(fā)。排查思路按順序來第一檢查觸發(fā)條件是不是寫得太窄。比如你寫當(dāng)用戶說生成 Controller時觸發(fā)但用戶實際說的是幫我寫個接口那就觸發(fā)不了。解決辦法是把觸發(fā)條件寫寬一點覆蓋同義詞。第二檢查 SKILL.md 的位置對不對。Claude Code 對 Skill 文件的位置有要求放錯地方就加載不到。一般是放在.claude/skills/目錄下每個 Skill 一個子目錄子目錄里放 SKILL.md。第三檢查文件編碼和格式。SKILL.md 必須是 UTF-8 編碼Markdown 格式要正確。我有一次因為文件里有個不可見字符導(dǎo)致整個 Skill 加載失敗排查了半天才發(fā)現(xiàn)。5.2 Skill 觸發(fā)了但輸出不對這個問題比不觸發(fā)更隱蔽。Skill 觸發(fā)了但輸出質(zhì)量差、格式亂、內(nèi)容不對。排查思路第一檢查輸入是不是完整。很多時候輸出不對是因為輸入不全Claude Code 自己腦補了。解決辦法是在 SKILL.md 里明確寫缺什么就問。第二檢查上下文是不是注入成功。如果 Skill 依賴公共上下文文件但文件沒讀到輸出就會跑偏??梢栽?SKILL.md 里加一步確認(rèn)上下文已讀取讀不到就報錯。第三檢查執(zhí)行步驟是不是太粗。步驟太粗Claude Code 就會自由發(fā)揮發(fā)揮出來的東西往往不符合預(yù)期。解決辦法是把步驟拆細(xì)每一步都有明確產(chǎn)出。5.3 Skill 之間沖突怎么辦當(dāng)你寫的 Skill 多了難免會遇到兩個 Skill 都想觸發(fā)的情況。比如生成 Controller和生成 CRUD 模塊這兩個 Skill用戶說生成用戶模塊的接口時兩個都可能觸發(fā)。解決辦法是在觸發(fā)條件里加優(yōu)先級和互斥規(guī)則。比如## 觸發(fā)條件 優(yōu)先級高 互斥當(dāng) generate-crud 已觸發(fā)時本 Skill 不觸發(fā)或者在更上層的 Skill 里做路由根據(jù)用戶意圖分發(fā)給不同的子 Skill。我現(xiàn)在的做法是建一個總?cè)肟赟kill所有請求先經(jīng)過它由它判斷該走哪個子 Skill。5.4 MCP 調(diào)用失敗怎么排查MCP 調(diào)用失敗的原因比較多我整理了一個排查表現(xiàn)象可能原因排查方法MCP 工具列表為空MCP 服務(wù)沒啟動檢查 MCP 配置文件的 command 和 args調(diào)用超時網(wǎng)絡(luò)問題或服務(wù)響應(yīng)慢檢查網(wǎng)絡(luò)連接看 MCP 服務(wù)日志返回權(quán)限錯誤認(rèn)證信息不對檢查 MCP 配置里的 env 變量返回數(shù)據(jù)格式不對MCP 服務(wù)版本不匹配檢查 MCP 服務(wù)版本和協(xié)議版本我踩過最坑的一次是 MCP 配置文件里路徑寫錯了導(dǎo)致服務(wù)啟動失敗但 Claude Code 沒有任何提示只是默默不加載。后來我養(yǎng)成了習(xí)慣每次改完 MCP 配置先手動跑一遍 MCP 服務(wù)確認(rèn)能啟動再集成到 Skill 里。5.5 Skill 維護成本怎么降Skill 寫多了維護成本會指數(shù)級上升。降低維護成本的核心是抽象和分層第一層是公共上下文所有 Skill 共享改一處生效全部。第二層是基礎(chǔ) Skill只做單一職責(zé)的事比如讀實體類、寫文件。第三層是組合 Skill通過串聯(lián)基礎(chǔ) Skill 完成復(fù)雜任務(wù)。這樣改基礎(chǔ) Skill 的時候組合 Skill 自動受益不用挨個改。我實測下來做了分層之后維護 50 個 Skill 的成本大概相當(dāng)于之前維護 15 個的成本。這個投入產(chǎn)出比還是很劃算的。5.6 幾個獨家避坑技巧最后分享幾個我踩坑踩出來的獨家技巧技巧一Skill 名稱用動詞開頭。比如generate-crud、parse-entity、validate-api這樣一看就知道這個 Skill 是干什么的。我早期用名詞命名比如crud-helper、entity-tool結(jié)果自己都記不清哪個是哪個。技巧二SKILL.md 里加示例。給每個 Skill 加一兩個輸入輸出示例Claude Code 看了示例之后輸出質(zhì)量明顯提升。示例比描述管用這是我在寫了 40 多個 Skill 之后才總結(jié)出來的。技巧三定期清理廢棄 Skill。我每兩個月會盤一次 Skill 列表把三個月沒用過的 Skill 歸檔。Skill 不是越多越好多了反而會互相干擾。我現(xiàn)在保留的活躍 Skill 大概 20 個比 50 個的時候好用多了。技巧四用 Git 管理 Skill。Skill 也是代碼也該進版本控制。我用 Git 管理所有 SKILL.md每次改動都有記錄出問題能回滾還能看到演進歷史。技巧五Skill 的輸出盡量結(jié)構(gòu)化。能用 JSON 就用 JSON能用表格就用表格避免大段自然語言。結(jié)構(gòu)化輸出方便下游消費也方便你自己檢查。6. 我在實際項目里的落地體會聊了這么多方法論最后說點實際的。我在一個 Spring Boot MyBatis 的多商戶項目里用這套 Skill 體系做了完整的落地。項目大概有 30 多個實體類每個實體類都要生成 CRUD 模塊。用傳統(tǒng)方式一個模塊大概要半天用 Skill 串聯(lián)的方式一個模塊壓縮到 20 分鐘左右而且生成的代碼風(fēng)格統(tǒng)一review 起來省心很多。但我也得說實話Skill 不是銀彈。它擅長的是重復(fù)性高、規(guī)則明確的任務(wù)對于需要創(chuàng)造性思考的任務(wù)Skill 反而會限制發(fā)揮。我現(xiàn)在的做法是重復(fù)性任務(wù)用 Skill 固化創(chuàng)造性任務(wù)還是手動做兩者結(jié)合效率最高。另外一點體會是寫 Skill 的過程本身就是梳理業(yè)務(wù)邏輯的過程。很多平時沒想清楚的細(xì)節(jié)在寫 SKILL.md 的時候被迫想清楚了。所以哪怕你最后不用這個 Skill寫的過程也是有價值的。如果你現(xiàn)在正準(zhǔn)備寫第一個 Skill我的建議是別急著寫先花半小時想清楚觸發(fā)條件、輸入約定、邊界說明這三塊。這三塊想清楚了Skill 就成功了一半。至于執(zhí)行步驟反而是最容易補的。