Files
2025-11-30 08:40:34 +08:00

16 KiB
Raw Permalink Blame History

Skills 設計最佳實踐

本文件整理 skills 設計和開發中的最佳實踐和常見錯誤,幫助你避免陷阱,寫出高品質的 skills。

常見錯誤

1. 過早抽象Premature Abstraction

錯誤示範

場景:正在開發第一個使用某個邏輯的 command

開發者:「這個檔案分析邏輯未來可能會在其他地方用到,
        我先把它抽成一個 skill 好了。」

結果:
- 創建了一個只有一個使用者的 skill
- 增加了不必要的複雜度
- 後來發現其他地方根本不需要這個邏輯

為什麼會發生

  • 過度優化:想要提前做好「未來的準備」
  • 誤解 DRY 原則:認為「可能重複」就要抽象
  • 缺乏判斷標準:沒有明確的判斷流程

正確做法

✅ 第一次使用:直接寫在 command 中,保持簡單

✅ 第二次使用:這時候才考慮是否要抽取成 skill
   - 檢查兩個使用場景的相似度
   - 評估未來的擴展可能性
   - 如果高度相似且可能繼續擴展,才抽取

✅ 第三次使用:必須抽取成 skill
   - 此時已經有明確的重複使用模式
   - DRY 原則明確適用

記住

YAGNIYou Aren't Gonna Need It:不要為「可能的」未來需求設計,等需求真正出現再重構。


2. 過度細分Over-Fragmentation

錯誤示範

場景:將 3 行簡單的邏輯抽成一個 skill

# skills/format-string/SKILL.md
---
name: Format-String
description: 格式化字串,移除首尾空白
---

## 執行步驟
1. 使用 trim() 移除首尾空白
2. 轉換為小寫
3. 返回結果

結果:
- 檔案過多,難以維護
- 增加理解成本
- 這種簡單邏輯應該直接內嵌在 command 中

為什麼會發生

  • 誤解模組化:認為「什麼都要獨立」
  • 忽略複雜度:簡單邏輯也獨立成 skill
  • 缺乏成本意識:沒有考慮維護成本

正確做法

❌ 不獨立3 行以下的簡單邏輯
   - 直接寫在 command 中
   - 保持程式碼的連貫性

✅ 考慮獨立5+ 行且有重複使用的可能
   - 邏輯稍微複雜
   - 有明確的第二個使用場景

✅ 必須獨立10+ 行或有複雜分支邏輯
   - 即使只有一個使用場景
   - 也值得為了可讀性獨立出來

判斷原則

詢問自己

  • 這個邏輯獨立出來後,是否更容易理解?
  • 維護兩個地方skill 定義 + command 引用)是否比直接寫在 command 中更簡單?
  • 未來真的會在其他地方使用嗎?

如果答案都是「否」,那就不要獨立。


3. 職責不清Unclear Responsibility

錯誤示範

# skills/commit-helper/SKILL.md
---
name: Commit-Helper
description: 協助提交相關的各種操作
---

## 核心功能
- 分析檔案變更
- 判斷原子性
- 產生提交訊息
- 執行 git add
- 執行 git commit
- 推送到遠端

問題:
- 一個 skill 包含太多不相關的功能
- 難以重複使用(因為太「大包」了)
- 難以維護(修改一個功能可能影響其他功能)

為什麼會發生

  • 缺乏單一職責意識:想把相關的東西都放在一起
  • 過度整合:認為「都是提交相關的,放一起比較方便」
  • 沒有考慮重複使用性:只想到自己目前的需求

正確做法

✅ 拆分成多個職責清晰的 skills

1. `analyze-changes` skill
   - 只負責分析檔案變更
   - 輸入:檔案列表
   - 輸出:變更分類結果

2. `generate-commit-message` skill
   - 只負責產生提交訊息
   - 輸入:變更分類結果
   - 輸出:格式化的提交訊息

3. `validate-atomicity` skill
   - 只負責判斷原子性
   - 輸入:變更分類結果
   - 輸出:原子性判斷結果

然後在 command 中組合這些 skills
commands/commit-push.md 可以選擇性地使用需要的 skills

單一職責原則

每個 skill 應該只有一個明確的職責,只因為一個理由而需要修改。


4. 缺乏文件Lack of Documentation

錯誤示範

---
name: Process-Data
description: 處理資料
---

# 處理資料

執行資料處理。

問題:
- 沒有說明輸入是什麼
- 沒有說明輸出是什麼
- 沒有執行步驟
- 其他人(或未來的自己)不知道如何使用

為什麼會發生

  • 趕時間:想快速完成功能
  • 覺得簡單:「程式碼就是最好的文件」
  • 缺乏同理心:沒有考慮其他人使用的情況

正確做法

---
name: Process-Data
description: 處理和轉換輸入資料,將 JSON 格式轉換為標準化的物件結構
---

# 處理資料

將輸入的 JSON 資料轉換為標準化的物件結構,並進行驗證和清理。

## 核心功能
- 解析 JSON 資料
- 驗證資料格式
- 清理無效欄位
- 標準化欄位名稱
- 返回處理後的物件

## 輸入條件
- JSON 格式的資料字串或檔案路徑
- 必須包含 `id``name` 欄位
- 可選:`metadata` 欄位

## 執行步驟
1. 讀取輸入資料(字串或檔案)
2. 解析 JSON
3. 驗證必要欄位
4. 清理和標準化
5. 返回處理後的物件

## 輸出結果
- 標準化的物件,包含:
  - `id`: string
  - `name`: string
  - `metadata`: object可選

## 使用範例
\`\`\`
輸入:{"id": "123", "Name": "Test", "extra": "data"}
輸出:{"id": "123", "name": "Test"}  (欄位名稱標準化,移除 extra
\`\`\`

文件最低要求

  • 清楚的 description50-100 字)
  • 核心功能列表
  • 詳細的執行步驟
  • 使用範例(如果邏輯不是顯而易見)

最佳實踐

1. 等待第二次使用Rule of Three

原則

第 1 次使用 → 直接寫在 command/agent 中
第 2 次使用 → 認真考慮是否要抽取成 skill
第 3 次使用 → 必須抽取成 skill如果還沒抽的話

為什麼這樣做

避免過早抽象

  • 第 1 次:還不知道是否真的需要重複使用
  • 第 2 次:開始看到重複模式,可以評估
  • 第 3 次明確的重複DRY 原則適用

實際重構時機更明確

  • 有真實的使用案例可以參考
  • 知道不同使用場景的差異
  • 可以設計更通用的介面

範例

場景:檔案變更分析邏輯

第 1 次:在 git:commit-push 中直接實作
決策:保持簡單,觀察是否有其他需求

第 2 次git:commit-review 也需要類似邏輯
決策:評估兩個使用場景的相似度
      - 如果 80% 以上相似 → 考慮抽取
      - 如果差異很大 → 各自保持獨立

第 3 次git:pre-commit-check 也需要
決策:明確的重複模式,必須抽取成 analyze-changes skill

2. 明確的命名Clear Naming

好的命名特徵

描述性:一看就知道 skill 做什麼

✅ analyze-atomicity      # 清楚:分析原子性
✅ generate-commit-message # 清楚:產生提交訊息
✅ resolve-conflict       # 清楚:解決衝突
✅ validate-config        # 清楚:驗證配置

動詞開頭:強調 skill 的動作

✅ analyze-*    # 分析
✅ generate-*   # 產生
✅ validate-*   # 驗證
✅ resolve-*    # 解決
✅ transform-*  # 轉換

簡潔但不失清晰

✅ parse-json              # 簡潔清楚
❌ parse-json-data-format  # 太冗長
❌ parse                   # 太簡短,不知道解析什麼

壞的命名範例

太抽象

❌ helper       # 幫什麼忙?
❌ utils        # 什麼工具?
❌ processor    # 處理什麼?
❌ manager      # 管理什麼?

太具體

❌ analyze-git-commit-atomicity-for-conventional-commits
   # 太長,應該簡化為 analyze-atomicity

❌ validate-json-config-file-format-version-2
   # 太長,應該簡化為 validate-config

不清楚的縮寫

❌ val-cfg      # 應該寫完整validate-config
❌ gen-msg      # 應該寫完整generate-message
❌ proc-data    # 應該寫完整process-data

命名檢查清單

詢問自己:

  • 其他人看到名稱,是否能猜到 70% 的功能?
  • 名稱是否使用了動詞開頭?
  • 名稱是否簡潔2-4 個單字)?
  • 名稱是否避免了不清楚的縮寫?
  • 名稱是否符合專案的命名規範kebab-case

3. 完整的文件Complete Documentation

必須包含的元素

  1. YAML Front Matter

    ---
    name: Skill-Name
    description: 清楚的一句話說明50-100 字)
    ---
    
  2. 核心功能

    ## 核心功能
    - 列出 3-5 個主要功能
    - 使用簡潔的條列式
    
  3. 執行步驟

    ## 執行步驟
    
    ### 1. 準備階段
    詳細說明...
    
    ### 2. 執行階段
    詳細說明...
    
    ### 3. 完成階段
    詳細說明...
    
  4. 使用範例

    ## 工作流程範例
    
    提供實際的使用案例,包含:
    - 使用者的輸入
    - Skill 的執行過程
    - 預期的輸出
    

建議包含的元素

  1. 輸入條件(如果有特定要求)

    ## 輸入條件
    - 需要的工具或環境
    - 前置條件
    - 必要的參數
    
  2. 輸出結果(如果有明確產出)

    ## 輸出結果
    - 返回什麼資訊
    - 如何被其他 commands/skills 使用
    
  3. 注意事項

    ## 注意事項
    
    ### 安全檢查
    - 需要注意的安全問題
    
    ### 常見錯誤
    - 避免什麼
    - 推薦什麼
    
  4. 整合其他工具(如果相關)

    ## 整合其他工具
    - 可以配合使用的 commands
    - 相關的 skills
    - 建議的工作流程
    

文件品質檢查

  • 其他人閱讀文件後,能否獨立使用這個 skill
  • 文件是否說明了「為什麼」而不只是「怎麼做」?
  • 是否提供了實際的使用範例?
  • 是否說明了邊界情況和錯誤處理?

4. 測試和驗證Testing and Validation

基本測試

在發布 skill 前,至少要測試:

  1. 獨立執行

    測試skill 可以被單獨呼叫並正常運作
    方法:創建一個簡單的 command 來測試這個 skill
    
  2. 整合測試

    測試:所有引用這個 skill 的 commands/agents 都能正常運作
    方法:逐一測試每個引用者
    
  3. 邊界情況

    測試:極端輸入、錯誤輸入的處理
    方法:故意提供不正常的輸入,確認錯誤處理
    

驗證清單

  • 在至少 2 個使用場景中測試過
  • 測試過正常輸入
  • 測試過異常輸入
  • 確認錯誤訊息清楚易懂
  • 確認文件與實際行為一致

回歸測試

當修改 skill 時:

  • 測試所有引用這個 skill 的地方
  • 確認向後相容性
  • 更新 CHANGELOG 記錄變更
  • 如果有 breaking changes考慮增加 MAJOR 版本號

特殊處理

1. YAML Front Matter 修改

修改 YAML Front Matter 時要特別小心:

---
name: Skill-Name           # 修改這個會影響引用
description: 描述          # 修改這個是安全的
---

修改 name 的影響

如果修改 name 欄位:

  • 所有引用這個 skill 的地方都需要更新
  • 考慮使用「搜尋並替換」確保不遺漏
  • 在 CHANGELOG 中明確記錄這個變更

正確的修改流程

  1. 搜尋所有引用舊名稱的地方
  2. 逐一更新引用
  3. 更新 skill 的 name 欄位
  4. 測試所有引用者
  5. 更新 CHANGELOG

2. 大幅度重構

當 skill 需要大幅度重構時:

評估影響範圍

  1. 列出所有引用者

    • Commands
    • Agents
    • 其他 Skills
  2. 評估變更影響

    • 是否會破壞現有功能?
    • 是否需要更新介面?
    • 是否需要遷移指南?

重構策略

選項 1原地重構(適用於小影響)

1. 備份當前版本
2. 修改 skill
3. 測試所有引用者
4. 更新 CHANGELOG

選項 2創建新版本(適用於大變更)

1. 創建新的 skill如 skill-v2
2. 保留舊版本一段時間
3. 逐步遷移引用者
4. 在 CHANGELOG 中標記舊版本為 deprecated
5. 一段時間後移除舊版本

溝通變更

  • 在 CHANGELOG 中詳細說明變更
  • 如果是 breaking change增加 MAJOR 版本號
  • 提供遷移指南(如果需要)

3. 同時修改多個檔案

當修改涉及多個檔案時:

使用 TodoWrite 規劃

✅ 使用 TodoWrite 追蹤任務:

Todos:
1. 修改 skill: analyze-changes
2. 更新 command: git:commit-push
3. 更新 command: git:commit-review
4. 更新 README.md
5. 更新 CHANGELOG.md
6. 測試所有變更

修改順序

建議的修改順序:

  1. 核心檔案skills、agents
  2. 引用檔案commands
  3. 文件檔案README、CHANGELOG
  4. 測試
  5. 提交

整體確認

修改完成後:

  • 所有檔案的變更是一致的
  • 文件反映了實際行為
  • 測試通過
  • CHANGELOG 記錄完整

避免這些反模式Anti-Patterns

「未來可能用到」症候群

錯誤思維:
「這個功能現在只有一個地方用到,但未來可能會有其他地方需要,
 所以我先抽成 skill 好了。」

正確做法:
等到真的有第二個使用場景時再抽取。

「什麼都要獨立」症候群

錯誤做法:
把每個小功能都抽成獨立的 skill
結果檔案數量爆炸,反而更難維護。

正確做法:
只有真正需要重複使用或邏輯複雜時才獨立。

「複製貼上」症候群

錯誤做法:
發現兩個地方有相同的邏輯,
直接複製貼上程式碼。

正確做法:
第二次使用時就應該考慮抽取成 skill。

「文件晚點再寫」症候群

錯誤想法:
「先把功能寫完,文件晚點有時間再補。」

結果:
文件永遠不會補,因為過一段時間就忘記細節了。

正確做法:
邊寫程式碼邊寫文件,趁記憶猶新時記錄下來。

成功的 Skills 設計模式

模式 1轉換器Transformer

特徵

  • 輸入一種格式
  • 輸出另一種格式
  • 職責清晰

範例

skills/generate-commit-message/
- 輸入git diff 結果
- 處理:分析變更、套用格式
- 輸出Conventional Commits 格式的訊息

模式 2驗證器Validator

特徵

  • 檢查輸入是否符合規範
  • 返回驗證結果
  • 提供清楚的錯誤訊息

範例

skills/validate-atomicity/
- 輸入:變更列表
- 處理:檢查是否符合原子性
- 輸出:通過/不通過 + 原因

模式 3工作流程Workflow

特徵

  • 完整的多步驟流程
  • 包含決策點
  • 可能與使用者互動

範例

skills/resolving-conflict/
- 偵測狀態
- 列出衝突
- 詢問使用者
- 解決衝突
- 完成流程

與其他文件的關聯


總結

核心原則

  1. YAGNI:不要過度設計,等需求明確時再抽取
  2. DRY:出現重複時才抽取,而不是提前
  3. 單一職責:一個 skill 只做一件事
  4. 清晰文件:完整的說明和範例

記住

  • 等待第二次使用再抽取
  • 使用清楚的命名
  • 撰寫完整的文件
  • 充分測試和驗證
  • 不要過早抽象
  • 不要過度細分
  • 不要職責不清
  • 不要缺乏文件

保持簡單永遠是最好的設計。


最後更新2025-10-19