Notes
← All notes
·ai·10 min

為什麼你的 Skill 會在錯誤的時機啟動:用 TRIGGER/SKIP 讓 Claude 看懂邊界

把 Skill description 從自然語言升級成結構化 TRIGGER/SKIP 信號之後,跨 Skill 互搶的問題從「偶爾踩雷」變成「機器可讀邊界」。記錄 8 支 Skill 的改寫過程與 Anthropic 官方範式的差距。

#claude-code#skill#trigger#auto-invoke#ai-engineering

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-learnkb-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`.

三個特點:

  1. 明確的 TRIGGER 區塊:不是「use when」,而是列出機器可讀的觸發條件——import 名稱、文字模式、命令前綴。
  2. 明確的 SKIP 區塊:直接告訴 Claude「這些情況你不要來」,連候選都不要成為。
  3. 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-chapter215151
leetcode196113
audit-personas190168

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)