:用agent-skills與Claude Code實現(xiàn)TDD自動化)
1. 從“agent-skills”說起為什么它值得單獨(dú)拿出來聊第一次看到agent-skills這個標(biāo)題我腦子里蹦出來的不是某個具體工具而是一類正在快速成型的東西——給 AI coding agent 用的“技能包”。你可以把它理解成一套可插拔的能力模塊agent 本身負(fù)責(zé)推理、規(guī)劃、調(diào)用工具而 skills 負(fù)責(zé)告訴它“遇到這類任務(wù)時具體該怎么做、按什么順序做、做到什么程度算合格”。這個思路解決了一個很現(xiàn)實的問題?,F(xiàn)在用 Claude Code、Cursor、Windsurf 這類 AI coding agent 的人越來越多但大家普遍會遇到同一個尷尬agent 很聰明可它不知道你團(tuán)隊的規(guī)范。比如你要求所有新功能必須先寫測試再寫實現(xiàn)它可能上來就給你把業(yè)務(wù)代碼寫完了你要求提交前必須跑 lint 和類型檢查它可能改完文件就直接說“完成了”。每次都要在 prompt 里重復(fù)交代效率極低還容易漏。agent-skills這類項目的核心價值就在這兒把重復(fù)的、有固定套路的工程實踐沉淀成 agent 可以直接加載和執(zhí)行的技能定義。它不是一個孤立的工具而是一種組織方式。配合skills CLI、Claude Code這類運(yùn)行環(huán)境你可以把 TDD 流程、代碼審查清單、重構(gòu)規(guī)范、文檔生成模板全部打包成 skill讓 agent 在合適的時機(jī)自動調(diào)用。這篇文章適合三類人看一是已經(jīng)在用 Claude Code 或類似 agent 工具、想進(jìn)一步提升自動化程度的開發(fā)者二是團(tuán)隊里負(fù)責(zé)制定工程規(guī)范、想讓 AI 真正落地到生產(chǎn)流程的技術(shù)負(fù)責(zé)人三是對 AI coding agent 生態(tài)感興趣、想搞清楚“skills 到底是怎么回事”的同行。我會從設(shè)計思路、核心機(jī)制、實操落地、常見坑幾個角度把這件事講透。2. agent-skills 的整體設(shè)計與核心思路拆解2.1 為什么是“技能”而不是“提示詞”很多人第一反應(yīng)是這不就是高級一點(diǎn)的 prompt 嗎我直接把規(guī)范寫進(jìn) system prompt 不就行了。剛開始我也這么想但實際用下來會發(fā)現(xiàn)兩者有本質(zhì)區(qū)別。Prompt 是一次性、上下文相關(guān)的。你在一個會話里寫了“先寫測試”換個會話就沒了而且隨著對話變長早期 prompt 的權(quán)重會被稀釋。Skill 是持久化、可復(fù)用、可組合的。它是一份獨(dú)立的定義文件有明確的觸發(fā)條件、執(zhí)行步驟和驗收標(biāo)準(zhǔn)。agent 可以在需要的時候主動加載它不需要你每次重復(fù)。更關(guān)鍵的是skill 把“知識”和“執(zhí)行”分開了。知識部分是你團(tuán)隊積累的最佳實踐執(zhí)行部分是 agent 的推理和工具調(diào)用能力。這種分離帶來的好處是規(guī)范可以獨(dú)立迭代不用動 agent 本身同一個 skill 可以被不同 agent、不同項目復(fù)用skill 之間還能互相引用形成能力網(wǎng)絡(luò)。從工程角度看這其實是在給 agent 建立一套可測試、可版本控制的行為契約。你可以像 review 代碼一樣 review skill 的定義可以給 skill 寫測試用例可以在 CI 里驗證 agent 是否真的按 skill 執(zhí)行了。這一點(diǎn)對于想把 AI 引入生產(chǎn)流程的團(tuán)隊來說是決定性的。2.2 目錄結(jié)構(gòu)與加載機(jī)制的設(shè)計考量一個典型的agent-skills項目目錄結(jié)構(gòu)通常長這樣agent-skills/ ├── skills/ │ ├── test-driven-development/ │ │ ├── SKILL.md │ │ ├── examples/ │ │ └── scripts/ │ ├── code-review/ │ │ ├── SKILL.md │ │ └── checklist.md │ └── refactoring/ │ ├── SKILL.md │ └── patterns/ ├── cli/ │ └── index.ts └── package.json每個 skill 一個目錄核心是SKILL.md。這個文件不是隨便寫的文檔它有約定俗成的結(jié)構(gòu)元信息名稱、描述、觸發(fā)條件 執(zhí)行步驟 示例 驗收標(biāo)準(zhǔn)。agent 讀取這個文件后就能理解“什么時候該用這個技能”以及“用了之后要產(chǎn)出什么”。為什么用 Markdown 而不是 JSON 或 YAML因為 agent 本身就是靠自然語言推理的Markdown 對模型最友好可讀性也最好。你寫 JSON 描述步驟模型還得先解析結(jié)構(gòu)再理解語義多一層損耗。Markdown 直接就是模型訓(xùn)練時見過無數(shù)次的格式理解成本最低。加載機(jī)制上skills CLI通常提供幾個命令list列出所有可用技能show name查看某個技能的詳情install name把技能注冊到當(dāng)前 agent 環(huán)境。安裝的本質(zhì)是把 skill 目錄鏈接或復(fù)制到 agent 的配置路徑下讓 agent 在啟動時能掃描到。這里有個設(shè)計細(xì)節(jié)值得注意技能是按需加載還是全量加載。全量加載簡單但會占用上下文窗口按需加載需要 agent 有判斷能力實現(xiàn)復(fù)雜但更高效。目前主流做法是啟動時只加載技能的元信息名稱和描述真正執(zhí)行時才讀取完整內(nèi)容。2.3 與 Claude Code 等 agent 的協(xié)作方式agent-skills不是要替代 Claude Code而是增強(qiáng)它。Claude Code 本身有很強(qiáng)的代碼理解和工具調(diào)用能力但它默認(rèn)不知道你的工程規(guī)范。Skill 就是補(bǔ)上這一環(huán)。協(xié)作流程大致是這樣你在 Claude Code 里提出一個任務(wù)比如“給用戶模塊加一個手機(jī)號驗證功能”。Claude Code 分析任務(wù)后發(fā)現(xiàn)這屬于“新功能開發(fā)”于是查找已安裝的 skills找到test-driven-development加載它的定義。定義里寫著第一步先寫一個會失敗的測試第二步運(yùn)行測試確認(rèn)失敗第三步寫最小實現(xiàn)讓測試通過第四步重構(gòu)。Claude Code 就按這個流程執(zhí)行每一步都調(diào)用相應(yīng)的工具寫文件、跑命令、讀輸出。這個過程中skill 扮演的是流程控制器的角色。它不關(guān)心具體代碼怎么寫那是 agent 的能力它關(guān)心的是“先做什么、后做什么、什么算做完”。這種分工讓 skill 可以跨語言、跨框架復(fù)用。同一個 TDD skill用在 Python 項目和 TypeScript 項目上流程完全一樣只是 agent 生成的代碼不同。3. 核心細(xì)節(jié)解析與實操要點(diǎn)3.1 SKILL.md 到底該怎么寫這是整個項目里最需要花心思的地方。寫得好agent 執(zhí)行順暢寫得爛agent 要么不觸發(fā)要么執(zhí)行到一半跑偏。我踩過幾次坑之后總結(jié)出一個比較穩(wěn)的模板--- name: test-driven-development description: 當(dāng)需要開發(fā)新功能或修復(fù) bug 時使用確保先寫測試再寫實現(xiàn) trigger: 新功能開發(fā)、bug 修復(fù)、代碼修改 --- ## 執(zhí)行步驟 1. 理解需求明確輸入輸出 2. 編寫一個會失敗的測試用例 3. 運(yùn)行測試確認(rèn)它確實失敗 4. 編寫最小實現(xiàn)讓測試通過 5. 運(yùn)行全部測試確認(rèn)沒有破壞其他功能 6. 重構(gòu)代碼保持測試通過 ## 驗收標(biāo)準(zhǔn) - 每個新功能都有對應(yīng)的測試 - 測試在實現(xiàn)之前編寫 - 所有測試通過 - 沒有跳過或注釋掉的測試 ## 示例 附上一個完整的 TDD 循環(huán)示例幾個關(guān)鍵點(diǎn)。第一description要寫清楚什么時候用而不是這是什么。模型是根據(jù)場景匹配技能的你寫“測試驅(qū)動開發(fā)技能”它可能不知道啥時候該調(diào)用你寫“當(dāng)需要開發(fā)新功能或修復(fù) bug 時使用”匹配就準(zhǔn)確多了。第二步驟要可執(zhí)行、可驗證。不要寫“編寫高質(zhì)量代碼”這種沒法驗證的話要寫“運(yùn)行npm test并確認(rèn)輸出中沒有 failing”。agent 需要明確的信號來判斷自己是否做對了。第三驗收標(biāo)準(zhǔn)要獨(dú)立于實現(xiàn)。不要寫“使用了 Jest 框架”要寫“所有測試通過”。這樣 skill 才能跨技術(shù)棧復(fù)用。注意SKILL.md 不要寫太長。我見過有人寫了三千字結(jié)果 agent 加載后反而抓不住重點(diǎn)。核心步驟控制在 10 條以內(nèi)細(xì)節(jié)放到 examples 目錄里需要時再讀。3.2 skills CLI 的安裝與基本操作skills CLI是管理技能的命令行工具通常通過 npm 全局安裝npm install -g agent-skills/cli安裝后驗證skills --version skills listlist會掃描當(dāng)前項目或全局配置下的所有 skill輸出名稱和描述。如果什么都沒顯示說明還沒安裝任何技能。安裝一個技能skills install test-driven-development這個命令做的事情是從注冊表或本地路徑找到 skill 目錄復(fù)制到~/.agent-skills/下并在 agent 的配置文件中注冊。不同 agent 的配置路徑不一樣Claude Code 通常讀~/.claude/skills/CLI 會自動處理路徑映射。查看某個技能的詳情skills show test-driven-development這會打印 SKILL.md 的完整內(nèi)容方便你在安裝前確認(rèn)它是否符合預(yù)期。移除技能skills remove test-driven-development這里有個實操心得安裝前先 show 一下。有些社區(qū)貢獻(xiàn)的 skill 寫得比較粗糙觸發(fā)條件太寬泛裝上去之后 agent 動不動就調(diào)用反而干擾正常流程。先看內(nèi)容再決定裝不裝能省很多事。3.3 觸發(fā)條件的設(shè)計讓 agent 在對的時候做對的事觸發(fā)條件是 skill 設(shè)計里最微妙的部分。寫得太窄agent 該用的時候想不起來寫得太寬不該用的時候亂用。我的經(jīng)驗是觸發(fā)條件應(yīng)該描述任務(wù)特征而不是技術(shù)關(guān)鍵詞。比如差的寫法trigger: 當(dāng)用戶提到 test、jest、pytest 時好的寫法trigger: 當(dāng)需要新增功能、修改現(xiàn)有行為、或修復(fù)缺陷時前者依賴關(guān)鍵詞匹配用戶換個說法就失效了后者描述的是任務(wù)本質(zhì)不管用什么詞表達(dá)只要任務(wù)性質(zhì)對上了就能觸發(fā)。另外觸發(fā)條件之間要有互斥性。如果你裝了test-driven-development和quick-prototyping兩個技能前者要求先寫測試后者要求先跑通再說它們的觸發(fā)條件如果都覆蓋“新功能開發(fā)”agent 就會糾結(jié)用哪個。解決辦法是在描述里加限定比如 TDD 用于“生產(chǎn)代碼”quick-prototyping 用于“探索性原型”。3.4 技能之間的組合與依賴單個技能能解決的問題有限真正強(qiáng)大的是技能組合。比如一個完整的“功能開發(fā)”流程可能涉及requirement-analysis把模糊需求拆成明確任務(wù)test-driven-development按 TDD 流程實現(xiàn)code-review自查代碼質(zhì)量documentation更新相關(guān)文檔這些技能可以串成一條流水線。實現(xiàn)方式有兩種一種是在 skill 里顯式引用其他 skill比如在feature-development的步驟里寫“調(diào)用 test-driven-development 技能”另一種是讓 agent 自己根據(jù)當(dāng)前階段判斷該加載哪個。顯式引用的好處是流程可控壞處是靈活性差。隱式判斷的好處是靈活壞處是可能漏掉步驟。我的建議是核心流程用顯式引用輔助技能用隱式判斷。TDD 這種必須嚴(yán)格執(zhí)行的就寫死在流程里文檔更新這種可以視情況而定的就讓 agent 自己決定。4. 實操過程與核心環(huán)節(jié)實現(xiàn)4.1 從零搭建一個 agent-skills 項目假設(shè)你要在團(tuán)隊里落地這套東西第一步是建倉庫。我建議直接用 monorepo 結(jié)構(gòu)skills 和 CLI 放一起方便版本同步。mkdir agent-skills cd agent-skills npm init -y mkdir -p skills cli然后創(chuàng)建第一個技能mkdir -p skills/test-driven-development/examples touch skills/test-driven-development/SKILL.md把前面模板里的內(nèi)容填進(jìn)去。接著寫一個最簡單的 CLI核心功能就是掃描skills/目錄、解析 SKILL.md 的 frontmatter、提供 list 和 show 命令。// cli/index.js const fs require(fs); const path require(path); const SKILLS_DIR path.join(__dirname, .., skills); function listSkills() { const dirs fs.readdirSync(SKILLS_DIR); return dirs.map(dir { const skillPath path.join(SKILLS_DIR, dir, SKILL.md); if (!fs.existsSync(skillPath)) return null; const content fs.readFileSync(skillPath, utf-8); const match content.match(/name:\s*(.)/); const descMatch content.match(/description:\s*(.)/); return { name: match ? match[1].trim() : dir, description: descMatch ? descMatch[1].trim() : }; }).filter(Boolean); } const command process.argv[2]; if (command list) { console.table(listSkills()); }這個最小實現(xiàn)跑通后再逐步加 install、remove、sync 等命令。不要一上來就追求功能完整先把核心鏈路走通。4.2 在 Claude Code 中加載并驗證技能Claude Code 的技能加載路徑通常是~/.claude/skills/。你可以手動把 skill 目錄復(fù)制過去也可以用 CLI 的 install 命令。cp -r skills/test-driven-development ~/.claude/skills/然后在 Claude Code 里發(fā)起一個任務(wù)比如“給 utils 模塊加一個日期格式化函數(shù)”。觀察它的行為如果它先創(chuàng)建了測試文件運(yùn)行測試看到失敗再寫實現(xiàn)說明 skill 生效了。如果它直接寫實現(xiàn)說明觸發(fā)條件沒匹配上需要調(diào)整 SKILL.md 里的 description。驗證的時候有個技巧故意給一個模糊的任務(wù)。比如“優(yōu)化一下用戶模塊”。好的 skill 應(yīng)該能讓 agent 先追問清楚要優(yōu)化什么而不是直接動手。如果 agent 上來就改代碼說明 skill 里缺少“需求澄清”這一步。4.3 參數(shù)計算與選擇以測試覆蓋率為例Skill 里經(jīng)常需要設(shè)定量化標(biāo)準(zhǔn)比如測試覆蓋率。寫多少合適我見過團(tuán)隊要求 100%結(jié)果 agent 為了湊覆蓋率寫了一堆無意義的斷言反而降低了測試質(zhì)量。我的建議是分層次設(shè)定代碼類型建議覆蓋率理由核心業(yè)務(wù)邏輯90% 以上出錯代價高必須充分覆蓋工具函數(shù)80% 左右邏輯相對簡單重點(diǎn)覆蓋邊界UI 組件60% 左右交互邏輯多快照測試為主配置文件不強(qiáng)制通常由集成測試覆蓋這個標(biāo)準(zhǔn)寫進(jìn) skill 的驗收條件里agent 就會按這個目標(biāo)執(zhí)行而不是盲目追求 100%。計算方式上用jest --coverage或pytest --cov輸出的行覆蓋率作為依據(jù)但要注意行覆蓋不等于邏輯覆蓋。一個 if-else 只測了 if 分支行覆蓋率可能顯示 100%但 else 分支根本沒測。所以 skill 里最好加一條“關(guān)鍵分支必須有對應(yīng)用例”。4.4 實操現(xiàn)場一次完整的 TDD 技能執(zhí)行記錄我拿一個真實任務(wù)跑了一遍記錄如下。任務(wù)給一個 Express 應(yīng)用加/health接口返回{ status: ok }。Agent 加載 TDD skill 后的執(zhí)行序列讀取SKILL.md確認(rèn)流程創(chuàng)建tests/health.test.js寫入測試請求/health期望狀態(tài)碼 200body 為{ status: ok }運(yùn)行npm test輸出顯示Cannot GET /health測試失敗符合預(yù)期創(chuàng)建routes/health.js實現(xiàn)路由在app.js中注冊路由再次運(yùn)行npm test測試通過運(yùn)行npm run lint無報錯輸出總結(jié)完成了什么、測試結(jié)果如何、下一步建議整個過程沒有人工干預(yù)耗時約 40 秒。對比不用 skill 的情況agent 通常會直接寫路由和實現(xiàn)測試要么不寫要么事后補(bǔ)一個走過場的。TDD skill 的價值就在于把順序鎖死了agent 沒有偷懶的空間。提示第一次跑的時候agent 可能會在“運(yùn)行測試確認(rèn)失敗”這一步卡住因為它不確定失敗是不是預(yù)期的。解決辦法是在 skill 里寫清楚“確認(rèn)失敗信息與預(yù)期一致比如 404 而非語法錯誤”。這樣 agent 就有了判斷依據(jù)。5. 常見問題與排查技巧實錄5.1 技能不觸發(fā)怎么辦這是最高頻的問題。表現(xiàn)是明明裝了 skillagent 卻按自己的方式執(zhí)行。排查順序如下。先檢查 skill 是否真的被加載了。在 Claude Code 里輸入/skills或類似命令看列表里有沒有。如果沒有檢查文件路徑對不對SKILL.md 的 frontmatter 格式是否正確。YAML frontmatter 對縮進(jìn)敏感name:前面不能有空格冒號后面要有一個空格。如果加載了但不觸發(fā)問題多半在 description。把 description 改得更貼近任務(wù)描述。比如原來寫“測試驅(qū)動開發(fā)”改成“當(dāng)需要編寫新功能、修改現(xiàn)有邏輯或修復(fù)缺陷時按測試先行的方式執(zhí)行”。改完后重啟 agent 會話讓它重新讀取。還有一個隱蔽原因上下文里已經(jīng)有其他指令覆蓋了。比如你在 prompt 里寫了“快速實現(xiàn)一個 demo”agent 可能判斷這屬于原型開發(fā)主動跳過了 TDD skill。這時候要么調(diào)整 prompt要么給 skill 加一個更高優(yōu)先級的觸發(fā)條件。5.2 技能執(zhí)行到一半跑偏有時候 agent 開始按 skill 執(zhí)行了但中途偏離。比如 TDD 流程走到“寫最小實現(xiàn)”它卻順手把重構(gòu)也做了還改了不相關(guān)的文件。原因通常是 skill 的步驟描述不夠原子化。每一步應(yīng)該是一個獨(dú)立、可驗證的動作。不要寫“實現(xiàn)功能并重構(gòu)”要拆成“實現(xiàn)功能運(yùn)行測試通過”和“重構(gòu)再次運(yùn)行測試通過”兩步。agent 在每一步結(jié)束時都有明確的完成信號就不容易越界。另外可以在 skill 里加一條約束“除非當(dāng)前步驟明確要求否則不要修改任務(wù)范圍之外的文件”。這句話能擋掉大部分跑偏行為。5.3 多個技能沖突的解決裝了多個 skill 后agent 可能同時匹配到兩個。比如code-review和refactoring都可能在“代碼修改”后觸發(fā)。解決辦法是在 skill 里聲明優(yōu)先級和互斥關(guān)系??梢栽?frontmatter 里加priority: 10 conflicts: [quick-prototyping]CLI 在加載時檢查沖突如果兩個互斥的 skill 同時匹配按優(yōu)先級高的執(zhí)行并提示用戶。這個機(jī)制需要 CLI 支持實現(xiàn)起來不復(fù)雜但能省很多調(diào)試時間。5.4 常見問題速查表現(xiàn)象可能原因解決方法skill 不加載路徑錯誤或 frontmatter 格式錯檢查~/.claude/skills/下是否有對應(yīng)目錄驗證 YAML 縮進(jìn)加載了不觸發(fā)description 太窄或太泛改成描述任務(wù)特征而非技術(shù)關(guān)鍵詞執(zhí)行中途跑偏步驟不夠原子化拆分步驟每步加驗證條件多個 skill 沖突觸發(fā)條件重疊加 priority 和 conflicts 聲明執(zhí)行結(jié)果不穩(wěn)定驗收標(biāo)準(zhǔn)模糊把“高質(zhì)量”換成可量化的檢查項上下文占用過大skill 內(nèi)容太長核心步驟精簡細(xì)節(jié)移到 examples5.5 幾個我踩過的坑第一個坑在 skill 里寫死了具體命令。比如寫“運(yùn)行npm test”結(jié)果換到 Python 項目就失效了。后來改成“運(yùn)行項目對應(yīng)的測試命令”讓 agent 自己判斷通用性好了很多。第二個坑驗收標(biāo)準(zhǔn)寫得太主觀。寫“代碼整潔”agent 覺得挺整潔我覺得不行。改成“函數(shù)不超過 50 行、沒有重復(fù)代碼塊、命名符合項目現(xiàn)有風(fēng)格”可操作性就強(qiáng)了。第三個坑忽略了 skill 的版本管理。團(tuán)隊里幾個人各自改 skill沒有版本控制導(dǎo)致行為不一致。后來把 skills 倉庫納入 Git 管理每次修改走 PR問題就解決了。6. 技能生態(tài)的擴(kuò)展與個人實踐體會agent-skills這套東西真正有意思的地方是它打開了一個可積累的工程知識庫的可能性。你每解決一類問題就可以把解法沉淀成一個 skill。時間長了這個倉庫就是你團(tuán)隊工程實踐的完整映射。新人入職裝上這套 skillsagent 就能按團(tuán)隊規(guī)范干活比看文檔快得多。我現(xiàn)在維護(hù)的 skills 倉庫里除了 TDD還有幾個用得比較順的api-design負(fù)責(zé)按 RESTful 規(guī)范生成接口error-handling統(tǒng)一異常處理模式commit-message按約定式提交格式生成提交信息。每個都不復(fù)雜但組合起來agent 的產(chǎn)出質(zhì)量明顯上了一個臺階。擴(kuò)展方向上我覺得有兩個值得嘗試。一是技能的市場化社區(qū)貢獻(xiàn)、評分、按需安裝類似 npm 的生態(tài)。二是技能的自動化測試給每個 skill 寫測試用例在 CI 里跑確保 agent 按預(yù)期執(zhí)行。后者對生產(chǎn)環(huán)境尤其重要畢竟你不能指望每次都人工檢查 agent 有沒有偷懶。最后分享一個小技巧寫 skill 的時候先手動跑一遍流程把每一步的實際操作和輸出記下來再整理成 skill 定義。這樣寫出來的步驟最貼近真實執(zhí)行agent 理解起來也最順。憑空想象的流程往往會在某個環(huán)節(jié)卡住因為你自己都沒實際走過。