第 7 章:撰寫高效 Skill 的最佳實務
如果你已經讀過前面幾章,你應該已經知道 SKILL.md 的基本格式、三層漸進式揭露的架構、以及 description 的重要性。但知道「格式」跟寫出「好用的 Skill」之間,還有一段不小的距離。
這一章要回答的問題很簡單:怎麼寫一份 Agent 第一次就做對、而且不會搞砸的 Skill?
答案不是「寫更詳細」,也不是「用更多關鍵字」。答案是遵循五個核心原則,加上四個經過實戰驗證的模式。這五個原則和四個模式,是我從數十份開源 SKILL.md 和內部專案的技能檔案中歸納出來的。每一條都有真實的失敗案例支撐——沒有理論,只有教訓。
學習目標
完成本章後,你將能夠:
- 萃取:將真實操作經驗轉化為結構化的 Skill 指令,而非憑空撰寫理論步驟
- 篩選:判斷哪些資訊該寫入 Skill(Agent 不知道的)、哪些不該寫(通用常識)
- 程序化:將抽象目標(「分析日誌」)拆解為可執行步驟(「Step 1 讀取檔案 → Step 2 解析格式 → …」)
- 預設化:設計 Skill 時提供明確預設值而非開放選單,降低 Agent 的決策錯誤率
- 歸因化:為每一條指令提供理由(為什麼这样做),而非僅列出規則(必須这样做)
- 避坑化:撰寫高價值的 Gotchas 清單,預防 Agent 踩入環境特定的陷阱
- 模板化:提供具體輸出格式範例,終結「欄位名稱不一致」的混亂
- 循環化:實作 Do → Check → Fix → Proceed 的驗證循環,防止 Agent 盲目樂觀
- 計畫化:對破壞性操作套用 Plan-Validate-Execute 模式,加上安全緩衝
7.1 從真實經驗萃取:先做過,再 Skillify
核心問題:為什麼同樣一份 SKILL.md,有些人寫出來 Agent 一次就做對,有些人寫出來 Agent 老是出錯?答案往往不在「文筆」,而在「這份 Skill 是從真實操作長出來的,還是憑空想像寫出來的」。
最常見的錯誤,是「憑想像寫 Skill」。你坐下來想「Agent 應該會需要做 A、B、C」,寫完才發現步驟 2 漏了一個關鍵設定、步驟 4 的指令在真實環境根本行不通。
核心原則:Skill 是經驗的結晶,不是推論的產物。
正確的工作流程
真實操作(手動或半自動)→ 記錄每一步 → 萃取模式 → 撰寫 Skill → 用 Skill 重做一次 → 修正這個流程不需要你從零開始。下次你做一件重複性任務時,打開編輯器同時記錄:
- 你下了什麼指令?
- 遇到了什麼錯誤?
- 你怎麼排除的?
- 哪個環節最容易出錯?
然後把這些記錄整理成 Skill。
為什麼「先做過」這麼重要?
因為你不知道你不知道什麼。當你憑想像寫 Skill 時,你只會涵蓋你「意識到」的步驟。但真實操作中,有大量的隱性知識——那些你做得太熟、以至於不覺得需要寫下來的動作——恰恰是 Agent 最需要知道的。
舉例:如果你手動做過十次資料庫備份,你會「不知不覺」地在執行 mysqldump 之前先檢查磁碟空間。因為你吃過一次 /tmp 滿了的虧。但如果你憑想像寫,你根本不會想到要寫「檢查磁碟空間」——因為對你來說那是「常識」。
而 Agent 沒有那個常識。
⚠️ 關於「萃取」的頻率:不是每一個任務都值得寫成 Skill。一個好的判斷標準是——如果你做同一個任務超過三次,而且每次都要想一下「下一步是什麼」,那就是該 Skillify 的時候了。如果是一次性的任務,直接手動做完就好,不需要寫 Skill。
從錯誤中學習:Failure Story 的價值
萃取經驗不只看「怎麼做」,更要看「做錯了怎麼救」。一個好的 Skill 應該包含 past failure story——不是為了嚇人,而是為了讓 Agent 知道「這條路走不通」。
容我在 7.1 就插播一個真實案例:我曾經寫過一個 AWS 資源清理的 Skill。步驟很簡單——列出未使用的 EBS 卷 → 確認 → 刪除。我以為寫得很完整了。結果 Agent 在「列出未使用卷」這一步,沒有過濾掉「掛載到已終止 EC2 的卷」,一口氣刪掉了 12 個有資料的 EBS 卷。原因是 AWS API 回傳的 State: available 其實包含了「曾被使用但 detachment 後沒清理」的卷——這是 AWS console 上不會顯示的細節。
從那之後,我所有的 AWS 操作 Skill 都會加上一條 Gotchas:「Volume State=available 不代表可以刪。你必須額外檢查 CreateTime 和 Attachments 歷史。」
案例:撰寫一個資料庫備份 Skill
錯誤做法(憑想像):
## Steps
1. 連接到資料庫
2. 執行 mysqldump
3. 壓縮備份檔
4. 上傳到 S32
3
4
5
正確做法(從經驗萃取):
## Steps(經實測驗證)
> 以下步驟來自實際操作記錄。如果你在執行中遇到任何差異,請先中斷並回報。
1. **連接到資料庫**
- 使用 `mysql -h $DB_HOST -u $DB_USER -p`(密碼從 1Password 取得,不是 `.env`)
- ⚠️ 測試環境的 $DB_HOST 是 `db-test.internal`,不是 `db-prod.internal`
2. **執行 mysqldump**
- `mysqldump --single-transaction --quick --no-autocommit $DB_NAME > dump.sql`
- ⚠️ **一定要加 `--single-transaction`**,否則會鎖表。我第一次沒加,production 直接卡了 3 分鐘。
3. **壓縮**
- `gzip dump.sql`(不要用 `zip`,伺服器沒裝)
- ⚠️ 確認 `dump.sql.gz` 大於 100KB,否則代表 dump 失敗
4. **上傳到 S3**
- `aws s3 cp dump.sql.gz s3://backups/$DB_NAME/$(date +%Y%m%d)/`
- ⚠️ 如果上傳失敗(例如網路不穩),保留本地檔案不要刪,等人類處理2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意到差別了嗎?第二份 Skill 的每一行背後都有真實經驗支撐——「我第一次沒加 --single-transaction,production 直接卡了 3 分鐘」這種細節,是坐在書桌前想像不出來的。
7.2 只加 Agent 不知道的:別教它寫 Python
這是最違反直覺、但也最重要的原則。
Agent 的訓練資料涵蓋了大量通用知識。你不需要告訴它:
- ❌ 「Python 的 list index 從 0 開始」
- ❌ 「用
try/except處理例外」 - ❌ 「HTTP 200 代表成功」
這些它已經知道。你寫進去只會浪費 token 空間,還會稀釋真正重要的資訊。
你要寫的是:
- ✅ 「這個專案的
user_id欄位在資料庫叫emp_code,不是user_id」 - ✅ 「
/health只檢查 web server 是否活著,不檢查資料庫。真正的健康檢查要看/readyz」 - ✅ 「刪除使用者後,必須手動清除 Redis cache,API 不會自動做」
- ✅ 「測試環境的 rate limit 是 10 req/min,不是文件上寫的 1000」
這些資訊有一個共同點:它們是環境特定的例外。你查不到公開文件,Stack Overflow 上也問不到。Agent 不問就不會知道。
⚠️ 判斷標準:如果這個資訊可以在官方文件、維基百科、或訓練資料中找到,就不要寫。如果這個資訊來自「公司內部某個工程師踩過的坑」,就一定要寫。
如何判斷「Agent 知不知道」?
不確定的時候,用這個測試:「隨便找一個 junior 工程師,他知道這件事嗎?」
- 如果 junior 知道 → 不寫
- 如果 junior 可能會踩坑 → 寫
- 如果 junior 一定會踩坑而且沒人講會卡三天 → 大寫特寫,放進 Gotchas
實際案例分析
來做一個練習判斷。以下是三條 Instruction,你覺得哪些該寫進 Skill?
A. 「使用 try/except 來處理 API 請求的網路錯誤」
- 判斷:不該寫。這是通用 Python 知識,任何 junior 開發者都知道。
B. 「呼叫 /api/orders 時,如果回傳 429 Too Many Requests,要等待 Retry-After header 指定的秒數再重試,最多重試 3 次」
- 判斷:可以寫,但如果你的 API 文件已經有 rate limiting 說明,就不需要重複。⚠️ 但如果你的 rate limiting 行為跟文件寫的不一樣(例如文件說 1000 req/min 但實際是 10 req/min),那就一定要寫進 Gotchas。
C. 「在 staging 環境中,/api/orders 實際上指向的是 orders-v2 service,不是 orders service。因為 staging 的 ingress 配置跟 production 不同。」
- 判斷:一定要寫。這完全是環境特定的事實,任何文件上都查不到,只有維運過 staging 的人才知道。
Token 預算的取捨
你可能會問:「把這些常識排除掉,真的省多少 token?」
一個具體的數字:SKILL.md 的理想長度是 < 5000 tokens。假設你寫了 200 tokens 的「通用 Python 知識」,那就佔了 4% 的預算——不算多。但問題是邊際效益遞減:Agent 已經知道的資訊,寫了也不會讓它表現更好。真正會讓 Agent 表現從 60 分進步到 90 分的,是那些環境特定的例外和陷阱。
所以與其問「這條資訊有沒有用」,不如問「這條資訊能預防什麼樣的錯誤」。如果答案是「沒有這條資訊 Agent 也不會炸」,那就不要寫。
7.3 程序 > 宣告:教 HOW,不是 WHAT
Agent 不是人類主管。你跟人類說「分析一下系統日誌」,他會自己知道去哪裡找日誌、用什麼工具、看哪些指標。但 Agent 需要的是:
Step 1: 到 /var/log/app/ 底下找出今天的 access.log
Step 2: 用這個 regex 擷取狀態碼和回應時間...
Step 3: 把回應時間超過 500ms 的端點列出來...2
3
宣告式(Declarative) 描述目標,程序式(Procedural) 描述路徑。Agent 需要的是後者。
對照範例
❌ 宣告式(沒用):
分析系統日誌並產生報表。Agent 收到這個指令後,會開始「推論」你要什麼——然後大概率推論錯。它可能用 grep 分析、可能用 Python 寫腳本、可能調用 ELK——每一種做法的輸出格式都不一樣。
✅ 程序式(有用):
## 日誌分析步驟(嚴格依序執行)
1. 讀取 `/var/log/app/` 底下當日的 `access.log`
- 如果當日檔案不存在,檢查是否有 `access.log.1`(昨日輪替檔)
2. 對每一行套用以下 regex 擷取欄位:
```
(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) \S+" (?P<status>\d{3}) (?P<size>\d+) (?P<rt>\d+)
```
⚠️ 第 7 個欄位 `rt` 是回應時間(毫秒),不是 `response_time`
3. 分類統計(用 Python 或 awk,不要用 Excel):
- 5xx 錯誤:總數 + 各 endpoint 明細
- Slow endpoints:平均 rt > 500ms,按 rt 降冪排序
- Top 10 熱門 endpoints:按請求量降冪排序
4. 輸出為 `report_{YYYYMMDD}.md`,格式見下方 §7.82
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
程序式的關鍵:把「隱含知識」變成「明確步驟」。你在心裡覺得「這不是廢話嗎」的步驟,對 Agent 來說往往是救命索。
宣告式 vs 程序式:更多的對照
| 面向 | ❌ 宣告式 | ✅ 程序式 |
|---|---|---|
| 檔案處理 | 「處理下載的 CSV 檔案」 | 「1. 找到 downloads/ 中最新日期的 .csv 2. 用 pd.read_csv(..., encoding='utf-8') 讀取 3. 移除全為空值的欄位 4. 輸出為清洗後的 clean_data.csv」 |
| API 呼叫 | 「呼叫使用者 API 取得資料」 | 「1. GET /api/v2/users?page=1&per_page=100 2. 從 response header X-Total-Pages 得知總頁數 3. 逐頁請求直到擷取完所有資料 4. 合併為一個 list」 |
| 錯誤處理 | 「妥善處理錯誤」 | 「1. 如果 HTTP 429,讀取 Retry-After header 等待後重試,最多 3 次 2. 如果 HTTP 5xx,等待 5 秒後重試,最多 3 次 3. 如果 3 次都失敗,記錄錯誤並跳過該項目(不要中斷整個流程)」 |
如何知道「程序寫夠了」?
一個實用的判斷標準:當你寫完 Skill 後,找一個不熟悉這個領域的人照著做一遍。如果他卡住了,就是程序不夠細。
不需要真的找人。你可以在腦中模擬:「一個只會寫 Python 但完全不知道我專案背景的人,照著這些步驟做,會不會在某一步停下來想『接下來呢』?」
如果會,那就是程序還不夠細。
⚠️ 例外情況:如果你的任務非常簡單且標準化(例如「用 pip install 安裝套件」),宣告式就夠了。程序式是用在 Agent 容易出錯的複雜流程。判別標準:如果這個任務你閉著眼睛都能完成,宣告式就夠。如果這個任務你上個月才踩過坑,請用程序式。
7.4 提供預設值,不是選單
最懶惰的 Instruction 長這樣:
你可以用 requests 或 httpx 來發 API 請求,看情況決定。Agent 看到這句話,會花 30 秒「思考」選哪個,然後選錯的機率約 50%。
直接告訴它用哪個,並簡短說明理由。
對照範例
❌ 選單式:
你可以用 JSON 或 YAML 來寫設定檔。看你喜好。✅ 預設值 + 理由:
## 設定檔格式
**使用 YAML(預設)**。理由:
- YAML 支援註解(JSON 不支援),方便其他工程師閱讀
- 我們的 CI/CD pipeline 已經有 YAML 驗證工具
- 專案中的 `docker-compose.yml` 和 `.github/workflows/*.yml` 都是 YAML,保持一致
如果你有具體原因需要用 JSON,在執行前先說明理由。2
3
4
5
6
7
8
心理學上這叫預設效應——人類(和 Agent)都會傾向走默認路徑。你不給預設值,Agent 就會自己「創造」一個,然後那個創造出來的選擇往往不對。
為什麼 Agent 面對選單會選錯?
這不是隨機的。Agent 的選擇傾向來自訓練資料的統計分布——它傾向於選擇在訓練資料中出現頻率更高的選項。
這就是問題所在:訓練資料中的「常見做法」不一定是你專案的正確做法。
舉例:
- Agent 選
requests而非httpx,因為requests在 GitHub 上有 50K+ stars,訓練資料中出現 10 萬次,而httpx只出現 1 萬次 - Agent 選 JSON 而非 YAML,因為 JSON 是網路傳輸的標準格式
- Agent 選
pandas而非polars,因為pandas的生態系和教學資源遠多於polars
但在你的專案中,httpx 的 async 支援才是關鍵、YAML 的註解功能才是團隊需要的、polars 的記憶體效率才是處理大資料的必備條件。Agent 不知道這些——所以你必須告訴它。
常見陷阱一覽
| 陷阱類型 | ❌ 錯誤寫法 | ✅ 正確寫法 |
|---|---|---|
| 工具選擇 | 「用 requests 或 httpx 都可以」 | 「用 httpx(預設)。理由:支援 async」 |
| 格式選擇 | 「JSON 或 YAML 看你習慣」 | 「用 YAML(預設)。理由:支援註解」 |
| 演算法選擇 | 「排序可以用 quick sort 或 merge sort」 | 「用 merge sort(預設)。理由:資料量大,quick sort 最差情況 O(n²)」 |
| 部署策略 | 「藍綠部署或滾動更新都可以」 | 「用藍綠部署(預設)。理由:我們的 Kubernetes 版本不支援滾動更新的健康檢查」 |
何時可以給選單?
只有一種情況可以給選單:當兩個選項在當前情境下真的等價,而且選錯的代價極低。
例如:
## 暫存目錄
使用 `tempfile.gettempdir()`(預設)。如果你需要自訂路徑,設定 `TEMP_DIR` 環境變數即可。
兩種方式等價,選哪個不影響結果。2
3
4
5
注意到了嗎?即使是「等價」的選擇,我還是給了一個預設值。因為「讓 Agent 少做一個決定」本身就是價值。
7.5 解釋原因:理由 > 規則
Agent 有個有趣的特性:如果你只給規則不給理由,它會遵守,但當規則不適用的時候它不會變通。反過來說,如果你給了理由,它就能在新的情境中做出合理的判斷。
為什麼理由這麼重要?
Agent 的本質是語言模型——它不是真的在「遵守規則」,而是在「模擬理解規則背後意圖的人」。沒有意圖的規則,在邊界情況下一定會出錯。
❌ 只有規則:
- 不要用 DELETE 語句刪除資料
- 使用 mysqldump 前要先檢查磁碟空間2
✅ 規則 + 理由:
- 使用軟刪除(`UPDATE SET deleted_at = NOW()`)而非 `DELETE`
- 理由:我們有 30 天的資料復原 SLA。硬刪除後無法還原。
- ⚠️ 例外:`temp_logs` 表(超過 90 天的資料)可以直接 `TRUNCATE`,因為它有獨立的備份機制
- 執行 `mysqldump` 前先檢查磁碟空間
- 理由:上次生產環境因為 `/tmp` 空間不足導致 dump 失敗,連帶影響正在進行的交易
- 檢查方式:`df -h /tmp | awk 'NR==2 {print $4}'`,剩餘空間需大於資料庫大小的 1.5 倍2
3
4
5
6
7
理由的結構
一個好的理由包含三層:
| 層級 | 內容 | 範例 |
|---|---|---|
| 1. 規則 | 做什麼 / 不做什麼 | 使用軟刪除 |
| 2. 原因 | 為什麼有這條規則 | 因為有 30 天復原 SLA |
| 3. 後果 | 不遵守會怎樣 | 上次因直接 DELETE 導致客戶資料無法還原,花了 6 小時從備份重建 |
第三層「後果」是最容易被忽略但最有說服力的。Agent 看到「上次這樣做炸了生產環境」,會比看到「請遵守最佳實務」認真十倍。
從「規則清單」升級到「理由驅動」的步驟
如果你手上已經有一個只有規則的 Skill,可以這樣逐步升級:
第一步:為每一條規則加上「因為」
- 使用 --single-transaction
+ 使用 --single-transaction(因為會鎖表)2
第二步:把「因為」擴充成具體事件
- 使用 --single-transaction(因為會鎖表)
+ 使用 --single-transaction。
+ 理由:不用的話,mysqldump 會在執行期間對所有表加上 READ LOCK。
+ 2025/11 生產環境因此導致查詢停滯 3 分鐘,P1 事故。2
3
4
第三步:加上例外情況
- 使用 --single-transaction。
+ 使用 --single-transaction。
+ 理由:...
+ ⚠️ 例外:如果資料表全部是 InnoDB,--single-transaction 不會鎖表。
+ 但為了保險起見,不管什麼引擎都加上。2
3
4
5
這個逐步升級的過程,本身就是對你專案知識的深度梳理。很多時候你會發現「我其實不知道為什麼要這樣做」——那就是你該去問資深工程師的時候了。
理由的顆粒度
不是每一行都需要理由。一條好的原則:如果這條規則被違反了,後果是「資料遺失」或「服務中斷」,就一定要附理由。如果後果只是「格式不對」或「效率稍差」,可以只給規則。
## 高風險規則(必須附理由)
- 使用 `--single-transaction` → 理由:避免鎖表,否則 P1 事故
- 先備份再 migration → 理由:硬碟故障時 Flyway 無法 rollback
## 低風險規則(可以只給規則)
- 縮圖輸出為 JPEG 格式(不用 WebP)
- 報表檔案命名為 `report_{date}.md`2
3
4
5
6
7
⚠️ 注意:給理由 ≠ 寫論文。每條理由控制在 1-2 句話內。如果理由太長,Agent 反而抓不到重點。如果你發現需要超過 3 句話才能解釋一條規則,代表那條規則本身太複雜,應該拆成多條規則。
7.6 Gotchas(避坑清單):最高價值的技能內容
在所有 Skill 撰寫技巧中,Gotchas 是單位 token 價值最高的內容。沒有之一。
為什麼 Gotchas 這麼重要?
因為 Agent 的推理路徑是基於機率的——它傾向於選擇訓練資料中最常見的做法。當你專案的實際做法與「常見做法」不同時,Agent 就會選錯。
Gotchas 的本質是:環境特定的事實,Agent 不問就不會知道。
好的 Gotchas 長怎樣
好的 Gotchas 有四個特徵:
- 具體:指名道姓,不說模糊話
- 可驗證:寫出判斷條件
- 有時效性:如果可能過期,附上檢查方式
- 有後果:不遵守會發生什麼事
❌ 模糊的 Gotchas:
- 注意 API 的認證方式
- 資料庫可能不太一樣
- 部署前最好確認一下2
3
✅ 具體的 Gotchas:
### Gotchas 的常見反模式
在告訴你「怎麼寫好 Gotchas」之前,先告訴你「不要怎麼寫」。以下三種反模式,幾乎所有初學者都會踩到:
**反模式 1:把 Gotchas 寫成注意事項清單**
```markdown
## ⚠️ Gotchas
- 注意 API 的 rate limit
- 小心資料庫連線
- 部署前要確認2
3
4
5
6
7
8
9
10
11
這些不是 Gotchas。這些是「說了等於沒說」的廢話。Gotchas 必須指名道姓——哪個 API?rate limit 是多少?確認什麼?
反模式 2:把 Gotchas 寫在步驟裡面
## Steps
1. 連線到資料庫
⚠️ 注意不要連錯
2. 執行查詢
⚠️ 注意查詢不要太慢2
3
4
5
把 Gotchas 分散在步驟中,會讓 Agent 的注意力被稀釋。集中放在一起,Agent 在執行步驟前可以先「讀一遍地雷在哪」。
反模式 3:把「通用常識」當成 Gotchas
## ⚠️ Gotchas
- ⚠️ 不要用 DELETE 刪除所有資料,用 TRUNCATE 比較快2
這不是 Gotchas,這是 SQL 101。通用常識不需要寫,環境特定的例外才需要。例如:「我們的 MySQL 不支援 TRUNCATE ... CASCADE,必須手動刪除關聯資料。」
⚠️ Gotchas(必讀,否則會出錯)
認證
/api/v2/*使用 Bearer token(從$AUTH_TOKEN環境變數讀取)/api/v1/*使用 Basic Auth(帳號密碼在 1Password 的「API v1 共用」条目中)- ⚠️ 不要混用:用 v2 token 打 v1 endpoint 會回傳 401
資料庫
- 所有表格使用
utf8mb4編碼,不是utf8。utf8在 MySQL 上不支援 4-byte emoji user_id欄位在資料庫叫emp_code(歷史遺留問題,不要改)- 測試資料庫每天凌晨 3:00 會重置,不要在測試庫放重要資料
部署
- staging 環境的
docker-compose.yml在infra/staging/下,不是在根目錄 - 部署前要先通知 #infra channel,否則 CI/CD 的 auto-scale 會把你的 build 擠掉
- ⚠️ 星期五下午不要部署。不是技術問題,是「炸了沒人要修」的文化問題
**注意到最後一條了嗎?** 這不是技術問題,是文化問題——但 Agent 不會知道,因為訓練資料裡沒有你們公司的文化。
### Gotchas 的分類框架
為了幫助你系統性地思考 Gotchas,這裡提供一個分類框架。每當你寫一個新的 Skill,就用這四類來檢查有沒有遺漏:
| 分類 | 典型問題 | 範例 |
|------|---------|------|
| **環境差異** | 不同環境(dev/staging/prod)有不同的設定、網域、權限 | 「staging 的 DB host 是 `db-stage.internal`,不是 `db.internal`」 |
| **歷史遺留** | 系統演進過程中留下的不一致命名或行為 | 「`user_id` 欄位在資料庫叫 `emp_code`,不要改」 |
| **隱含依賴** | 文件上沒寫但實際存在的依賴關係 | 「刪除使用者後必須手動清除 Redis cache,API 不會自動做」 |
| **文件與現實不符** | 官方文件的說法與實際行為不同 | 「文件說 rate limit 1000 req/min,實際是 10 req/min」 |
### Gotchas 的擺放位置
Gotchas 應該放在 SKILL.md 的**最前面**,僅次於 metadata 區塊。原因很簡單:
1. Agent 讀取 SKILL.md 的順序是從上到下。如果 Gotchas 放在最後,Agent 可能已經在前面步驟執行到一半了才讀到「等等,這個 API 其實傳回的是不同的欄位名」
2. Gotchas 是「如果先知道這些就不會出錯」的資訊——它本質上是前置知識
```markdown
---
name: my-skill
description: ...
---
## ⚠️ Gotchas(先讀這個,再讀步驟)
<!-- 放這裡 -->
## Steps
<!-- 放這裡 -->2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
⚠️ 一個實務建議:Gotchas 的標題最好加上 emoji(⚠️)或明確警告字眼(「必讀,否則會炸」)。這不是裝飾——這是視覺提示,幫助 Agent 辨識「這一段的權重跟其他段落不同」。雖然 Agent 不吃視覺,但 markdown header 的語意層級會影響 Agent 的注意力分配。
如何累積 Gotchas?
不可能一次寫完所有的 Gotchas。比較實際的做法是迭代累積:
- 第一版:寫你 currently 記得的坑(大概 3-5 條)
- 第一個月:每次 Agent 執行這個 Skill 出錯,就把錯誤原因加進 Gotchas
- 持續迭代:每當有新人加入團隊踩了新的坑,就加進 Gotchas
六個月後,你的 Gotchas 就會從 3 條變成 20+ 條——而那 20 條,就是這個專案最寶貴的知識資產。
7.7 輸出格式模板:給範例,不要給形容詞
「請輸出 JSON 格式」——Agent 會給你 JSON,但欄位名可能叫 result、data、content、responseData,隨它心情。
一個具體的模板,勝過一千字的格式描述。
模板的兩個功能
- 格式約束:告訴 Agent 欄位名稱、型別、允許值
- 語言協定:統一你和 Agent 之間的術語(
rolevsuser_rolevspermission_level)
模板撰寫指南
❌ 只有描述:
輸出一個包含使用者資訊的 JSON 物件,要有姓名、Email、角色和狀態。✅ 具體模板:
## Output Format
你必須嚴格按照以下格式回傳,不得增減欄位:
```json
{
"name": "string",
"email": "string",
"role": "admin | editor | viewer",
"status": "active | suspended | pending",
"last_login": "ISO 8601 datetime or null"
}
```
### 欄位規則
- `role` 只能是 `admin`、`editor`、`viewer` 三者之一,不要自己發明
- `status` 的 `pending` 代表 Email 尚未驗證,`suspended` 代表管理員停權
- `last_login` 如果是 `null` 代表該使用者從未登入過(不是資料遺失)2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
模板的進階技巧:列舉 vs 自由格式
不是所有輸出都適合用 JSON 模板。根據輸出的性質,選擇不同的模板形式:
1. 列舉型(Enumeration):當輸出只能是有限幾種值時
## 回傳值
你只能回傳以下三者之一:
- `approved` — 所有檢查通過
- `rejected` — 任一檢查未通過
- `needs_review` — 無法確定,需要人類判斷2
3
4
5
6
2. 結構化型(Structured):當輸出包含多個固定欄位時(使用 JSON/YAML 模板)
3. 自由格式型(Template):當輸出是文字但需要固定章節結構時
## 報表格式
你的報表必須包含以下章節,順序不可調換:
# 錯誤摘要
(2-3 句話總結本次發現的問題)
# 詳細發現
(每個問題一個小節,包含:影響範圍、嚴重程度、建議修復方式)
# 附錄
(完整的錯誤 log,供工程師參考)2
3
4
5
6
7
8
9
10
11
12
原則:輸出越結構化,模板就要越嚴格。輸出越自由,模板就要越「有彈性的骨架」。
何時該用模板?
任何 Agent 需要「產出結構化內容」的時候都該用:
| 情境 | 給模板的好處 |
|---|---|
| JSON / YAML 輸出 | 欄位名稱和型別完全一致 |
| Markdown 報表 | 章節結構固定、標題層級一致 |
| 程式碼產生 | import 語句、函數簽名、回傳型別都正確 |
| 錯誤訊息 | 訊息格式統一,方便後續解析 |
| Git commit message | 格式一致,方便 CI 解析 |
⚠️ 一個常見的糾結:模板要精準到什麼程度?答案是——精準到你不會收到「格式不對」的修改請求為止。如果你發現 Agent 一直產出
userRole而你想要role,那就是模板不夠精準。
7.8 驗證循環模式:Do → Check → Fix → Proceed
Agent 最大的缺點不是能力不足,而是盲目樂觀——它做完一件事後很少回頭檢查,然後帶著錯誤自信滿滿地繼續下一步。
基本模式
┌─────────────────────────────────────────┐
│ Validation Loop │
│ │
│ ┌─────────┐ │
│ │ Do │ 執行該步驟 │
│ └────┬────┘ │
│ ↓ │
│ ┌─────────┐ ┌─────────┐ │
│ │ Check │ ──→ │ Fix │ │
│ │ 驗證結果 │ │ 修正問題 │ │
│ └────┬────┘ └─────────┘ │
│ ↓ (通過) │
│ ┌─────────┐ │
│ │ Proceed │ 進到下一步 │
│ └─────────┘ │
│ │
│ 只有在 Check 通過時才能 Proceed │
└─────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
7.9 Plan-Validate-Execute 模式:破壞性操作的保險
刪除資料、清空資源、覆蓋檔案——這些操作,你不會想讓 Agent 直接動手。PVE 模式加了一層安全緩衝:強制 Agent 先提交計畫,驗證通過後才能執行。
為什麼 PVE 比「直接下指令」安全?
直接給 Agent 一個破壞性指令(例如「刪除所有 inactive user」),相當於給實習生一把刀然後說「去把該切的切掉」。Agent 會去做,但它不會停下來問:「你確定這些使用者全部都該刪?有沒有依賴的訂單記錄?」
PVE 強制 Agent 在動手之前先「說出來」。而「說出來」這個動作本身,就是一種安全機制——因為:
- 人類有機會攔截:如果 Plan 看起來不對,人類可以在 Execute 之前喊停
- Agent 自己也有機會發現問題:在寫 Plan 的過程中,Agent 可能會自己意識到「等等,這個操作會影響到 O 喔」
- 留下了審計軌跡:Plan 的內容可以記錄到 log 中,方便事後追責
三階段流程
┌─────────────────────────────────────────────┐
│ Plan-Validate-Execute │
│ │
│ ┌─────────┐ ┌──────────┐ ┌─────────┐ │
│ │ Plan │ → │ Validate │ → │ Execute │ │
│ │ 提計畫 │ │ 驗證計畫 │ │ 執行操作 │ │
│ └─────────┘ └──────────┘ └─────────┘ │
│ ↑ ↑ ↑ │
│ 不做任何修改 確認安全才繼續 執行後再驗證 │
└─────────────────────────────────────────────┘2
3
4
5
6
7
8
9
10
在 Skill 中實作
PVE 的實作需要三個關鍵設計決策:
- 誰負責 Validate? 可以讓 Agent 自己驗證(自主模式),也可以要求 Agent 提交 Plan 給人類驗證(人工模式)。前者快但風險較高,後者安全但慢。建議:低風險操作用自主模式,高風險操作用人工模式。
- Plan 的格式要多嚴格? 越嚴格的格式越容易讓 Agent 完整思考所有面向。建議至少包含「目標、影響範圍、風險評估、回滾方案」四個欄位。
- Validate 失敗後可以修改幾次? 建議跟 Validation Loop 一樣:3 次上限。超過 3 次代表 Agent 對這個操作的理解有根本性的偏差,需要人類介入。
## Plan-Validate-Execute(所有破壞性操作必須使用)
### 適用範圍
- 資料庫 DDL / DML(尤其是 DELETE、DROP、ALTER)
- 檔案系統寫入/覆蓋/刪除
- 生產環境部署與設定變更
- 雲端資源建立/刪除
### Step 1: Plan(計畫)
先列出你打算做的事。**不要執行任何修改。**
計畫格式:2
3
4
5
6
7
8
9
10
11
12
13
Plan
- 操作目標:(例如「刪除 users 表中 status=inactive 且 last_login > 365 天的帳號」)
- 影響範圍:(例如「約 1,200 筆資料」)
- 風險評估:(低/中/高 + 理由)
- 回滾方案:(例如「備份表已建立在 users_backup_20260101」)
- 預計執行時間:(例如「離峰時段 03:00 AM」)
### Step 2: Validate(驗證)
驗證計畫的安全性。以下檢查**全部必須通過**:
- [ ] 影響範圍合理嗎?(不是意外的全表操作)
- [ ] 有完整的回滾/備份機制嗎?
- [ ] 有考慮相依資料嗎?(例如刪除使用者前先確認沒有進行中的訂單)
- [ ] 執行時機恰當嗎?(避開尖峰時段?)
- [ ] 有通知相關團隊嗎?
如果任一項為「否」,**退回修改計畫,不得執行**。
### Step 3: Execute(執行)
只有 Validate 全部通過,才能執行。執行後必須再次驗證結果。2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PVE vs Validation Loop
兩者很容易混淆,但它們解決不同的問題:
| 面向 | Validation Loop | Plan-Validate-Execute |
|---|---|---|
| 時機 | 每個步驟之後 | 所有操作之前 |
| 目的 | 確保步驟正確 | 確保操作安全 |
| 應用場景 | 所有操作 | 僅破壞性操作 |
| 檢查內容 | 輸出品質 | 計畫安全性 |
| 驗證失敗處理 | 修正後重試 | 退回修改計畫 |
簡單判斷:如果你刪錯東西會炸,用 PVE。如果東西沒做好會炸,用 Validation Loop。
PVE 的實戰案例
以下是一個真實的「清理 EBS 未使用磁碟」Skill 的 PVE 實作:
Plan:
操作目標:刪除 7 天前建立、且狀態為 available 的 EBS 磁碟
影響範圍:目前列出 23 個磁碟,總計約 4.5TB
風險評估:中 — 需要確認這些磁碟真的沒有包含重要資料
回滾方案:無法回滾。刪除後資料永久遺失。
預計執行時間:離峰時段 02:00 AM(已通知 #infra)2
3
4
5
Validate:
- [ ] 影響範圍檢查:23 個磁碟,4.5TB — ✅ 數量合理(上週也大概這個數字)
- [ ] 回滾方案:❌ 無法回滾 — 這是一個 red flag。應該先建立 snapshot 再刪除。
- [ ] 相依資料:已確認 detach 狀態 — ✅
- [ ] 通知:已通知 #infra — ✅
Validate 結果: 發現「無法回滾」的問題。修改 Plan:先為每個磁碟建立 snapshot,再刪除。
## Plan(修正版)
- 回滾方案:無法回滾
+ 回滾方案:刪除前先建立 snapshot,snapshot 命名為 ebs-cleanup-{volume-id}-{date}2
3
這個案例展示了 PVE 的核心價值——Agent 在 Plan 階段並沒有錯(它誠實地寫了「無法回滾」),但 Validate 階段攔截了一個不該發生的風險。如果直接讓 Agent 執行,23 個 EBS 磁碟就再見了。
7.10 綜合案例:一個完整的高效 Skill
以下是一個整合了本章所有原則的 Skill 片段:
---
name: db-migration
description: 執行資料庫 schema migration。使用 Flyway,支援 MySQL 8.0+。使用前請確認已閱讀 migration 檔案。不要用於 production 資料變更。
---
## ⚠️ Gotchas(必讀,否則會炸)
- `user_id` 在資料庫叫 `emp_code`,migration 檔案中請用 `emp_code`
- staging 資料庫的 `MIGRATION_URL` 在 1Password 的「Staging DB」條目,不在 `.env`
- ⚠️ 不要對 `audit_log` 表執行 `ALTER TABLE`——它使用觸發器自動更新 schema,手動修改會破壞觸發器
- ⚠️ Flyway 的 baseline 版本是 `1.0.0`,不是 `1`。寫錯會跳過所有 migration
## 執行步驟(程序式)
### Step 1: 確認環境
1. 檢查 `MIGRATION_URL` 環境變數是否存在(用 `echo $MIGRATION_URL`)
2. 檢查目標資料庫版本:`flyway info`
3. 比對 migration 檔案列表與 `flyway info` 的 pending 清單是否一致
### Step 2: 備份(PVE 模式)
使用 Plan-Validate-Execute:
1. **Plan**:列出要備份的表格(至少包含所有受 migration 影響的 table)
2. **Validate**:確認備份檔案大小合理、寫入權限正常
3. **Execute**:執行 `mysqldump --single-transaction ...`(理由見下方)
### Step 3: 執行 Migration
1. 執行 `flyway migrate`
2. **Validation Loop**:執行後檢查 `flyway info` 確認所有 migration 都成功
## 預設值
- **資料庫用戶端**:使用 `flyway` CLI(預設),理由:
- 支援版本控制與 rollback
- 團隊統一使用 flyway,避免手動 SQL 造成的 drift
- ⚠️ 不要用 `mysql` CLI 直接執行 migration SQL(容易漏執行或執行順序錯誤)
## 輸出格式
Migration 完成後,回傳以下格式:
```json
{
"status": "success | failed | rolled_back",
"version": "2.3.1",
"files_applied": ["V2.3.1__add_index.sql"],
"files_pending": [],
"backup_file": "s3://backups/db/20260101_prod.sql.gz"
}
```
## 為什麼這麼做?(理由)
- `--single-transaction`:避免鎖表影響線上服務。2025/11 曾因為沒加導致 production 查詢停滯 3 分鐘
- 執行前備份:即使 Flyway 有版本控制,實務上硬碟故障或網路中斷會讓 rollback 不可靠
- 使用 Flyway 而非手動 SQL:團隊有 6 個開發者,手動執行會導致環境 drift。Flyway 確保所有人執行相同的 migration2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
⚠️ 以上範例為了展示所有原則而濃縮了實際內容。真實的 db-migration Skill 會更長,包含更多 error handling 和邊界情況。
7.11 常見反模式總整理
為了幫你避開最常見的坑,這裡整理了一份「反模式快速參考表」。當你 review 自己的或同事的 SKILL.md 時,用這張表來找問題:
| 反模式 | 症狀 | 解法 | 對應原則 |
|---|---|---|---|
| 百科全書式 | 花了一半篇幅解釋通用概念(「什麼是 SQL injection」) | 全部刪掉,只留 Agent 不知道的 | §7.2 |
| 直銷式 | 只說「做一個報表」,沒有步驟 | 拆成 Step 1/2/3,每一步可驗證 | §7.3 |
| ** buffet 式** | 「你可以用 A 或 B 或 C,看情況」 | 選一個預設值,說明理由 | §7.4 |
| 命令式 | 「不要用 DELETE」「記得備份」沒有解釋 | 加上「理由:」和「後果:」 | §7.5 |
| 樂觀式 | 只有步驟,沒有「如果錯了怎麼辦」 | 加入 Validation Loop + 校驗失敗處理 | §7.8 |
| 蠻幹式 | 直接叫 Agent 刪東西,沒有 Plan 階段 | 加上 PVE 模式 | §7.9 |
| 模糊式 | 「輸出 JSON」沒有模板 | 給完整的 JSON 範例 | §7.7 |
| 後置式 | Gotchas 寫在步驟後面 | 移到 SKILL.md 最前面 | §7.6 |
如何判斷一個 Skill 寫得好不好?
把 Skill 交給 Agent 執行一次。如果發生以下情況,就知道哪裡出問題了:
- Agent 一直問你問題 → 代表 Instruction 寫得不夠程序化(§7.3),Agent 不知道下一步
- Agent 產出結果但不對 → 缺少輸出格式模板(§7.7)
- Agent 鏟掉了不該鏟的東西 → 缺少 Gotchas(§7.6)或 PVE(§7.9)
- Agent 做錯但自己不知道 → 缺少 Validation Loop(§7.8)
- Agent 選了錯的工具 → 缺少預設值(§7.4)
- Agent 在邊界情況做出奇怪決定 → 缺少理由(§7.5)
- Agent 做了你沒預期到的事 → 這份 Skill 可能不是從真實經驗萃取的(§7.1)
把這些「症狀 → 原因」的對應關係記下來。未來你 review Skill 時,不需要從頭讀到尾——直接看 Agent 的反應就能 pinpoint 問題。
[DIAGRAM: 驗證循環流程圖]
┌──────────────┐
│ 開始任務 │
└──────┬───────┘
│
▼
┌──────────────┐
│ Step N: │
│ Do (執行) │
└──────┬───────┘
│
▼
┌──────────────┐
│ Check (驗證) │
│ │
│ • 檔案存在? │
│ • 格式正確? │
│ • 數值合理? │
│ • API 正常? │
└──────┬───────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 全部通過 │ │ 部分失敗 │ │ 全部失敗 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Proceed │ │ Fix │ │ STOP │
│ 進下一步 │ │ 修正問題 │ │ 通知人類 │
└──────────┘ └────┬─────┘ └──────────┘
│
▼
┌──────────────┐
│ 回 Do 重新 │
│ 執行修正版 │
└──────────────┘
失敗次數累計 ≥ 3 → 強制 STOP2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
圖 7.1: 驗證循環流程。每一次 Do 之後必須執行 Check,只有 Check 通過才能 Proceed。Check 失敗時進入 Fix → 回 Do。連續失敗 3 次則強制 STOP 通知人類。
7.12 Skill 的生命週期:寫完只是開始
一個常被忽略的事實:SKILL.md 不是一次寫完就結束的文件,它是一個需要持續維護的活文件。
寫完之後的三個階段
第一階段:驗證期(第 1-3 次執行)
剛寫好的 Skill 一定有不完美的地方。在頭三次執行中,你可能會發現:
- 步驟遺漏(Agent 在某個環節卡住)
- 假設錯誤(你以為某個工具一定裝了,但環境沒有)
- 邊界情況沒處理(檔案為空、API 離線、權限不足)
修正方式:每次 Agent 執行完後,review 它的執行過程。把 Agent 卡住的地方加進 Instructions,把 Agent 誤解的地方改寫清楚。
第二階段:穩定期(第 4-20 次執行)
Skill 開始穩定產出預期結果。這時的重點轉為:
- 增加 Gotchas(新人踩的坑)
- 補強 Validation Loop(曾經出錯的環節)
- 精簡冗餘(有些步驟 Agent 已經很熟了,可以濃縮)
第三階段:維護期(20+ 次執行後)
Skill 已經很成熟了。這時要關注的是:
- 環境變更(API 升級了?路徑改了?工具換了?)
- Description 優化(是否需要調整觸發條件?)
- 淘汰(如果這個任務不再需要,就歸檔 Skill,不要留著佔用 token)
維護頻率建議
| 階段 | 多久 review 一次 | 誰負責 |
|---|---|---|
| 驗證期 | 每次執行後 | Skill 作者 |
| 穩定期 | 每週一次 | Skill 作者 / 團隊輪值 |
| 維護期 | 每月一次 | 團隊指定負責人 |
一個實用的習慣:在你的 SKILL.md 最後加上
## Changelog區塊,記錄每一次的重大修改。這樣當 Agent 執行出錯時,可以快速比對是不是最近修改造成的。
## Changelog
- 2026-06-01: 增加 Step 2 的 Validation Check(因為 Agent 在 migration 後沒檢查 flyway info)
- 2026-05-15: 增加 Gotchas 第 4 條(staging DB URL 在 1Password 不在 .env)
- 2026-05-01: 初版建立2
3
4
5
7.13 總結:Skill 品質檢查清單
寫完一個 Skill 後,用以下清單逐項檢查:
一趟回顧:從原則到模式
在開始檢查清單之前,快速回顧一下本章的結構。我們從五個核心原則出發:
| 原則 | 一句話總結 | 對應的常見錯誤 |
|---|---|---|
| 從真實經驗萃取 | 先做過再寫,不要憑空想像 | 漏掉隱性知識,步驟不完整 |
| 只加 Agent 不知道的 | 環境特定事實 > 通用常識 | 浪費 token 在 Agent 已知的事 |
| 程序 > 宣告 | 教 HOW 不是 WHAT | 只說「做報表」不給步驟 |
| 預設值 > 選單 | 直接告訴 Agent 用哪個 | 給選擇權 = 給犯錯空間 |
| 理由 > 規則 | 解釋為什麼,不只要做什麼 | Agent 在邊界情況不會變通 |
然後把這五個原則實作為四個具體模式:Gotchas(避坑清單)、Output Format Templates(輸出模板)、Validation Loop(驗證循環)、Plan-Validate-Execute(計畫-驗證-執行)。
每一個模式都是在解決一個真實的問題——不是理論上的「最佳實務」,而是「不這樣寫就會炸」的 survival guide。
基本結構
- [ ] name 符合格式(小寫+數字+連字號,≤ 64 字元)
- [ ] description 符合語意(包含了觸發條件和適用場景,≤ 1024 字元)
- [ ] 目錄結構 完整(SKILL.md + 必要的 scripts/、references/ 或 assets/)
內容品質
- [ ] 從真實經驗萃取:每一條指令背後都有實際操作經驗支持
- [ ] 只寫 Agent 不知道的:沒有通用知識(「Python 的 list 從 0 開始」)、只有環境特定事實
- [ ] 程序式 > 宣告式:每個任務都有明確步驟,不是只有目標描述
- [ ] 預設值 > 選單:每個選擇點都有明確的預設選項和理由
- [ ] 理由 > 規則:每條規則都附了原因(最好包含實際發生的後果)
- [ ] 有 Gotchas 區塊:放在 Steps 前面,包含環境特定陷阱和邊界情況
- [ ] 有輸出格式模板:任何 Agent 需要產出的內容都有具體範例
- [ ] 有驗證循環:至少關鍵步驟有 Do → Check → Fix → Proceed 模式
安全性
- [ ] 破壞性操作使用 PVE:DELETE、DROP、覆蓋檔案等操作有 Plan-Validate-Execute
- [ ] 校驗失敗有中止機制:連續失敗會停下來通知人類,不會無限重試
- [ ] 沒有模糊的「根據情況判斷」:所有決策點都有明確條件
可測試性
- [ ] 每一條 Gotchas 都可驗證:不是「注意安全」,而是「檢查
/readyz回傳 200」 - [ ] Checklist 的每個項目都可量化:不是「確保足夠大」,而是「確認 > 100KB」
- [ ] 模板不包含歧義:不是「輸出 JSON」,而是「輸出以下格式的 JSON」
7.14 練習題
本章的練習題設計原則是:不做虛擬題,每一個練習都來自在真實專案中發生過的失誤。我們希望你寫出來的答案可以直接貼到下週的 Skill PR 裡。
每個練習大約需要 20-30 分鐘完成。如果你時間有限,優先做練習 1——因為 Gotchas 是單位時間投資報酬率最高的技能。
練習 1:為資料庫操作 Skill 撰寫 Gotchas
情境:你正在撰寫一個「資料庫查詢與報表產生」的 Skill。這個 Skill 需要連接到公司的 MySQL 資料庫、執行 SELECT 查詢、並產生 CSV 報表。
請根據以下「你可能踩過的坑」撰寫至少 5 條 Gotchas:
- 公司有兩個資料庫:
app_prod(正式)和app_dev(開發),但很多人連錯 - 正式資料庫的
orders表格有 2,000 萬筆資料,SELECT *會炸 - 查詢時間超過 30 秒會被 kill,但
EXPLAIN不會 - 報表中的日期預設是 UTC,但台灣團隊需要 UTC+8
customer.name欄位可能包含空值,CONCAT會因此回傳 NULL- 公司規定 CSV 的欄位分隔符號是
|(pipe),不是逗號
提示:參考 §7.6 的 Gotchas 格式——具體、可驗證、有後果。
練習 2:為現有 Skill 加入驗證循環
情境:以下是一個「圖片批次處理」的 Skill(目前沒有驗證循環):
# bulk-image-resize
## Steps
1. 讀取 input/ 資料夾中的所有 .jpg 檔案
2. 將每張圖片縮放到 800x600
3. 在圖片右下角疊加浮水印(使用 logo.png)
4. 輸出到 output/ 資料夾2
3
4
5
6
7
任務:
- 為每一個步驟加上 Validation Check(檢查什麼?如何檢查?請寫出具體的檢查指令)
- 設計 Fail 次數超過多少次後要 STOP
- 加上「校驗失敗處理規則」區塊
提示:參考 §7.8 的 Validation Loop 範例。不同步驟的檢查方式不同:
- Step 1 Check:
input/資料夾存在嗎?.jpg檔案至少有一個嗎?檔案大小都大於 0 嗎? - Step 2 Check:縮放後的圖片真的是 800x600 嗎?用
PIL.Image.open().size確認。所有圖片都成功處理了嗎?(數量跟輸入一致?) - Step 3 Check:浮水印存在嗎(
logo.png)?疊加後的位置是右下角嗎?可以用像素採樣確認角落是否有浮水印的顏色分布 - Step 4 Check:
output/資料夾存在嗎?輸出檔案數量等於輸入數量嗎?每個輸出檔案都可以正常開啟嗎?
練習 2 的進階挑戰
完成基本驗證循環後,試著加入以下機制:
- 部分失敗處理:如果 100 張圖片中有 3 張縮放失敗,是要全部重來,還是只重做那 3 張?
- 中斷續傳:如果在 Step 3 進行到一半時中斷,重新執行時如何避免重複處理已完成的圖片?
- 品質 Threshold:如果 Step 2 縮放後的圖片品質低於某個標準(例如 JPEG compression artifacts 過多),是否要警示?
練習 3(進階):將宣告式改為程序式
情境:以下是一個部署 Skill 的描述:
# deploy-to-staging
部署最新版本到 staging 環境。2
3
任務: 將這個宣告式描述改為完整的程序式指令,包含:
- 至少 4 個具體步驟
- 每個步驟包含檢查點
- 至少 2 條 Gotchas(例如:staging 環境的 Docker registry 不同、deploy 前需要先通知團隊)
- 明確的預設值選擇(例如:使用藍綠部署還是滾動更新?)
- 每個規則附上理由
- 包含輸出格式模板(部署完成後回傳什麼格式的結果?)
提示:回想你實際做過的部署流程。你在哪一步最常出錯?那一步就需要最詳細的 instruction。
練習 3 的引導問題
如果你不知道從何下手,先回答以下問題:
- 部署前需要做哪些檢查?(程式碼已 merge?CI 通過?權限足夠?)
- 部署的實際操作是什麼?(
git pull?docker compose up?kubectl apply?) - 如何驗證部署成功?(HTTP 200?資料庫 migration 完成了?背景 worker 启动了?)
- 部署失敗怎麼辦?(rollback?保留舊版本?通知誰?)
- 團隊有什麼部署慣例?(不能星期五部署?部署前要在 Slack 喊一聲?要排隊?)
把這些答案轉換成程序式步驟和 Gotchas,就是一個完整的 Skill 了。
練習解答方向與參考答案
以下不是「標準答案」(因為沒有標準答案),而是合理的解答方向。你可以拿來對照自己的答案。
練習 1 參考方向
不要這樣寫(❌):
- 注意不要連錯資料庫
- 查詢不要太慢
- 日期要注意時區2
3
要這樣寫(✅):
## ⚠️ Gotchas
### 資料庫連線
- 連線字串中的 `app_dev` 和 `app_prod` 只差 `_dev` vs `_prod` 三個字元。
連線後**必須先執行 `SELECT DATABASE();` 確認**當前所在的資料庫。
⚠️ 2025/08 曾有人在 dev 主機上因為連到 production 跑了 DELETE 查詢。
### 查詢效能
- `orders` 表有 ~2,000 萬筆資料。**禁止 `SELECT *`**(會全表掃描,耗時 > 5 分鐘)。
查詢前先用 `EXPLAIN` 確認使用到索引。
⚠️ 任何查詢執行超過 30 秒會被資料庫 kill,但 `EXPLAIN` 不受限。
### 時區
- 資料庫的日期欄位預設存的是 UTC。
報表需要台灣時間(UTC+8),請在 SELECT 時轉換:`CONVERT_TZ(created_at, '+00:00', '+08:00')`。
⚠️ 不要信任應用層的轉換——之前有工程師在 Python 端轉時區,漏掉了凌晨時段的資料。
### 空值處理
- `customer.name` 可能為 `NULL`。使用 `CONCAT(name, ' ', email)` 時,任一個欄位為 NULL 就會回傳 NULL。
請改用 `CONCAT(COALESCE(name, ''), ' ', COALESCE(email, ''))`。
### CSV 輸出格式
- 公司規定的 CSV 分隔符號是 `|`(pipe)**不是逗號**。
原因是部分客戶的資料中包含了逗號,導致過去常有解析錯誤。
請使用 `SELECT ... INTO OUTFILE ... FIELDS TERMINATED BY '|'`。2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
練習 2 參考方向
驗證循環的關鍵在於「把檢查寫成可執行的指令」,而不是模糊的描述:
✅ 好的 Step 1 Check:
### Step 1 Check
- [ ] `input/` 資料夾存在:`Test-Path -LiteralPath "input"` 回傳 `True`
- [ ] 至少有一個 `.jpg` 檔案:`Get-ChildItem -LiteralPath "input" -Filter "*.jpg" | Measure-Object` 的 Count > 0
- [ ] 每個檔案大小 > 0:`Get-ChildItem ... | Where-Object { $_.Length -eq 0 }` 的結果為空2
3
4
關於失敗次數:2 次失敗就 STOP 適用於「成本高」的操作(如資料庫寫入),5 次則適用於「容易受環境波動影響」的操作(如下載檔案)。圖片批次處理建議設為 3 次——因為縮放失敗可能是單一圖片損毀,不應該為了一張壞圖卡住整個流程。
練習 3 參考方向
程序式的 deploy Skill 應該包含至少以下層面:
- 部署前檢查:CI 狀態、程式碼版本、權限、#deploy channel 確認
- 實際部署步驟:pull → build → migrate → deploy → health check
- 部署後驗證:HTTP 200、資料庫 migration 狀態、background worker 狀態
- 回滾方案:如果 health check 失敗,自動回滾到上一個版本
- Gotchas:staging 的 registry 不同、部署時間限制、部署鎖機制
範例 Gotchas:
- ⚠️ staging 的 Docker registry 是 `registry-stage.internal`,不是 `registry.internal`
- ⚠️ 部署前必須在 #deploy channel 發送「Deploying to staging...」訊息
如果 5 分鐘內沒有人回應「hold」,才能開始部署
- ⚠️ 星期五下午 4:00 後禁止部署。不是技術限制——是「炸了沒人修」2
3
4
本章是「Agent Skills 設計實務」課程的第 7 章。下一章將深入探討 Description 優化——如何寫出讓 Agent 在正確時機觸發的 Description,包含觸發率測試方法與 train/validation 分割策略。