為什麼你的 Skill 會在錯誤的時機啟動:用 TRIGGER/SKIP 讓 Claude 看懂邊界
把 Skill description 從自然語言升級成結構化 TRIGGER/SKIP 信號之後,跨 Skill 互搶的問題從「偶爾踩雷」變成「機器可讀邊界」。記錄 8 支 Skill 的改寫過程與 Anthropic 官方範式的差距。
Claude Code 的 Skill 靠 description 決定要不要自動啟動。寫得模糊,AI 靠猜;寫得有結構,AI 靠規則。8 支 Skill 改寫之後,KB 三件套(kb-fetch / kb-learn / kb-compile)的互搶問題消失了。
問題從哪裡來
Claude Code 在決定要不要自動叫用某個 Skill 的時候,讀的是 SKILL.md frontmatter 裡的 description 欄位。這個欄位不是給人類看的說明文字——它是 Claude 的觸發器。
我的 kb-fetch description 原本長這樣:
Use when the user provides a URL to fetch and save into the knowledge base.
Supports Threads discussions and general web pages.
Trigger phrases: /kb-fetch, "fetch this", "存入 KB".
看起來很清楚。問題是,同一個 KB 系統還有 kb-learn 和 kb-compile。三支 Skill 各自的 description 都在講「KB 相關工作」,沒有任何一支說清楚「什麼情況下你不該啟動、應該讓另一支來」。
結果是:使用者說「幫我整理一下 KB」,Claude 有三個候選 Skill,全部描述都部分吻合,隨機選一個。如果選錯,輕則要重新說明,重則觸發錯誤的流程。
這不是 Claude 的問題,是 description 的問題。
Anthropic 怎麼寫
在整理 anthropics/skills 官方倉庫(github.com/anthropics/skills)的 17 支示範 Skill 時,最值得研究的是 claude-api 的 description:
TRIGGER when: code imports `anthropic`/`@anthropic-ai/sdk`;
user asks for the Claude API, Anthropic SDK, prompt caching,
or cross-model migration (4.5 → 4.6 → 4.7).
SKIP: file imports `openai`/other-provider SDK,
filename like `*-openai.py`/`*-generic.py`.
三個特點:
- 明確的 TRIGGER 區塊:不是「use when」,而是列出機器可讀的觸發條件——import 名稱、文字模式、命令前綴。
- 明確的 SKIP 區塊:直接告訴 Claude「這些情況你不要來」,連候選都不要成為。
- glob 與程式符號:
`anthropic`、*-openai.py——不是自然語言描述,是可以做字串比對的信號。
自然語言觸發器和機器可讀信號的差距,不是風格問題,是精度問題。
改寫的核心原則
把 8 支 Skill 的 description 翻新之後,歸納出三條原則:
一、TRIGGER 要給條件,不給意圖
「use when user wants to fetch」是意圖描述。「user provides a URL (http:// or https://)」是條件。條件可以用規則判斷,意圖要靠推理猜。
二、SKIP 是邊界聲明,不是例外說明
SKIP 的讀者是 Claude 的 Skill 選擇邏輯。每個 SKIP 條件後面要指出替代 Skill,讓 Claude 在排除自己的同時知道該送給誰:
SKIP: no URL given and user names a topic only → use kb-learn instead.
SKIP: files already exist in raw/articles/fetched/ and no new URL → use kb-compile instead.
這讓三支 Skill 的邊界從「各自描述自己」變成「互相聲明不做什麼」。
三、URL 模式、file glob、slash 指令優先於同義詞清單
舊 description 常見寫法是列出一排同義詞:"fetch this", "存入 KB", "收進 KB"。這些仍然有用,但優先順序應該低於模式信號。http:// 或 https:// 開頭是明確的——比「fetch this」可靠得多。
KB 三件套改寫前後
以下是 kb-fetch、kb-learn、kb-compile 三支 Skill 改寫後的 description,從 PR #331 取出:
kb-fetch(改寫後):
Fetch a URL and save it into the knowledge base.
TRIGGER when: user provides a URL (http:// or https://);
user says /kb-fetch, "fetch this", "存入 KB", "收進 KB".
SKIP: no URL given and user names a topic only → use kb-learn instead.
SKIP: files already exist in raw/articles/fetched/ and no new URL → use kb-compile instead.
kb-learn(改寫後):
Find authoritative sources on a topic, then fetch + compile + explain in one flow.
TRIGGER when: user names a topic without providing a URL;
/kb-learn <topic>, "想了解 X", "幫我學 X", "快速掌握 X", "查一下 X".
SKIP: user provides a specific URL → use kb-fetch instead.
SKIP: user says /kb-compile or mentions raw/articles/fetched/ → use kb-compile instead.
kb-compile(改寫後):
Compile already-fetched articles in raw/articles/fetched/ into the wiki.
TRIGGER when: user says /kb-compile, "compile wiki", "整理 wiki";
raw/articles/fetched/ has .md files not yet in wiki.
SKIP: no files in raw/articles/fetched/ and user provides a URL → use kb-fetch.
SKIP: user wants to learn a new topic with no existing fetched file → use kb-learn.
三支 Skill 現在形成一個閉合系統:每一支都知道自己不做什麼、以及應該轉交給誰。
同場改寫:ceo-propose 與 go
KB 三件套之外,這次 PR #331 同時翻新了另外幾支:
ceo-propose 的舊 description 只說「strategic direction scan」,沒有說什麼時候不該啟動。現在:
TRIGGER when: user asks "接下來做什麼", "what should we build next",
"現在做什麼", or invokes /ceo-propose.
SKIP: user asks for a specific task (not direction-setting) → proceed directly.
SKIP: direction already confirmed this session and PM pipeline is in flight.
第二條 SKIP 特別重要。PM pipeline 跑到一半時,如果使用者說「現在怎麼辦」,不應該重新啟動 CEO 戰略掃描——那會打斷已經在執行的工作。明確告訴 Claude「這個情況不要觸發我」,比靠 Claude 自己判斷「應該繼續現有工作」可靠。
go 的改寫也加入了邊界聲明:
TRIGGER when: /go, "ship this", "ready for review", "open PR"
and the feature or fix is fully implemented.
SKIP: "先 commit" or mid-session checkpoint → use raw git commit, not this skill.
SKIP: implementation is still in progress — complete it first.
後面兩條 SKIP 解決了一個常見的誤啟動情境:使用者想要一個輕量的中間點 commit,卻觸發了完整的 E2E + 簡化 + PR 流程。
大檔案拆分:progressive disclosure
這次 PR 同時做了另一件事:把三支大 SKILL.md 拆分成主檔 + references/ 子目錄。
| Skill | 改寫前行數 | 改寫後主檔行數 |
|---|---|---|
| run-chapter | 215 | 151 |
| leetcode | 196 | 113 |
| audit-personas | 190 | 168 |
SKILL.md 是 Claude Code session 啟動時就會載入的——所有 Skill 的主檔都在系統提示的 context 裡。如果每支 Skill 都把所有細節塞進主檔,context token 會被大量佔用。把不需要每次都讀的細節(如 leetcode 的 GraphQL query 格式、run-chapter 的章節輸出範本)移到 references/ 子目錄,主檔保留觸發條件和步驟骨架,細節在執行時按需載入。
這和 Anthropic 官方 Skill 的設計思路一致:skills/ 目錄裡大部分 Skill 的主 SKILL.md 都很短,複雜邏輯分在 scripts/ 或 references/。
還沒解決的問題
兩個遺留問題值得記下來:
觸發率沒有量化。 改寫後主觀感受上互搶減少了,但沒有系統性的 eval 數據。Anthropic 官方的 skill-creator Skill 含有 benchmark variance 工具,可以用來跑觸發準確率測試,目前還沒有接入這個流程。
SKIP 條件靠 Claude 的理解,不是硬規則。 description 裡的 SKIP 是給 Claude 讀的提示,不是 hook 那種「exit 2 直接拒絕」的強制機制。如果 Claude 誤讀了 SKIP 條件,仍然會錯誤啟動。更嚴格的方案是在 Skill 的 Step 1 加入前置條件檢查,判斷失敗就立即 abort——但這樣會讓每支 Skill 的實作更複雜。目前選擇先靠 description 改善,等真的看到殘留誤啟動再考慮升級。
Full template + reproducible config in paid version → (link TBD)