
內容摘要
SKILL.md 是文件夾裡的一份 Markdown:YAML frontmatter 決定技能名稱與誰能呼叫,內文放流程本身;在真正需要之前,代理只會讀到 description。
SKILL.md 是一份頂端帶 YAML frontmatter 的 Markdown 文件,也是代理工具在「不想再貼一次同樣提示」時載入的單位。本文只談格式本身:檔案怎麼放、哪些 frontmatter 欄位會改變行為、代理如何決定觸發,以及讓 skill 默默失效的常見錯誤。
Claude Code 官方文件把這個格式寫在 Extend Claude with skills,並說明它屬於 Agent Skills 開放標準,可跨多種 AI 工具使用。閱讀時可以隨手把 frontmatter 貼進 UrsaKit 的 YAML 驗證工具,它會指出第一個語法錯誤的行號與欄位——這件事在這裡特別重要,因為 YAML 壞掉的 skill 還是會載入,只是所有欄位都被忽略。
01skill 是一段被存下來的流程,不是更長系統提示
多數代理環境本來就有一份常駐事實檔:AGENTS.md、CLAUDE.md,或編輯器裡的一份規則檔。那種檔案每一輪都會完整載入,所以必須短。skill 剛好相反:平常代理只看得到 frontmatter,等任務真的需要時才把內文拉進上下文。
官方文件把這個取捨講得很直接:skill 內文只在被使用時載入,所以再長的參考資料,在不需要的時候幾乎不佔成本。這也是為什麼一份 40 步的發佈檢查清單可以安穩放在項目裡,卻不會污染每一個不相關的問題。
AGENTS.md -> always in context -> keep it to facts and constraints
SKILL.md -> loaded when relevant -> put long procedures here
各家產品會在共用格式上再加東西。Claude Code 就在標準欄位之外補上呼叫控制、子代理執行與動態上下文注入,所以為某個工具寫的 skill 通常換到另一個工具還能用,只是可能會多出一些行為。
02SKILL.md 會從哪些位置載入
位置決定範圍,範圍決定同事 clone 項目之後拿不拿得到這個 skill。
| 位置 | 路徑 | 範圍 |
|---|---|---|
| 個人 | ~/.claude/skills/<name>/SKILL.md | 你這台機器上的所有項目 |
| 項目 | .claude/skills/<name>/SKILL.md | 所有 clone 這個項目的人 |
| 插件 | 插件裡的 skills/ | 所有安裝這個插件的人 |
| 受管 | 由 managed settings 部署 | 組織內所有人 |
項目層級的 skill 才值得提交進版控。代理會從啟動目錄一路往上找 .claude/skills/,直到儲存庫根目錄,所以在 packages/frontend/ 裡開 session,還是抓得到最上層定義的 skill。反過來說,位於工作目錄底下的 .claude/skills/ 文件夾不會在啟動時載入,把 skill 放在它所描述的程式碼旁邊,就會踩到這個坑。
自訂指令和 skill 已經合併成同一個概念。.claude/commands/deploy.md 和 .claude/skills/deploy/SKILL.md 都會產生同一個 /deploy 呼叫。新東西一律用 skill 形式,因為只有它能帶附屬檔案。
有些團隊會在項目裡放一個像 .agent/skills/ 的目錄,再連結或複製到工具預期的路徑。那是慣例選擇,不是格式的一部分;真正有意義的檔案還是 SKILL.md。
03frontmatter 逐欄拆解
frontmatter 放在檔案最上方兩個 --- 之間,以 YAML 解析。欄位名稱是小寫加連字號,唯一的例外是 when_to_use。欄位名稱只要不合 schema 就會被忽略,而且不會報錯,所以打錯字和「這個欄位沒用到」看起來一模一樣。
---
name: config-guard
description: Validate JSON and YAML config files before a commit. Use when the user asks to check a config file, debug a failing lint step, or prepare a release.
allowed-tools: Read Grep Bash(python3 *)
---
| 欄位 | 必填 | 會改變什麼 |
|---|---|---|
name | 否 | / 選單裡的指令名稱,預設為文件夾名稱。 |
description | 建議填 | 技能做什麼、什麼時候用。代理就是靠這段文字路由。 |
when_to_use | 否 | 觸發詞與範例需求,會接在 description 後面。 |
allowed-tools | 否 | 呼叫的那一輪裡,代理可以不用詢問就使用的工具。 |
disallowed-tools | 否 | 技能生效期間,從可用池移除的工具。 |
disable-model-invocation | 否 | 設為 true 後只能由人類用 /name 啟動。 |
user-invocable | 否 | 設為 false 後只能由代理自己呼叫。 |
argument-hint | 否 | 自動補全提示,例如 [issue-number]。 |
arguments | 否 | 具名位置參數,供內文用 $name 代入。 |
兩個細節最常造成困惑。第一,列出可用技能時,description 與 when_to_use 會合併後截斷在 1,536 個字元,所以路由的關鍵在第一句。第二,布爾欄位接受 yes、no、on、off、1、0,大小寫不拘;這代表加了引號的 "false" 和沒加引號的 false,經過 YAML 之後可能不是同一件事。
YAML 壞掉不會大聲報錯。
frontmatter 解析失敗時,skill 還是會載入,只是所有欄位都沒設定。你得到的是一個沒有 description 的技能,代理永遠不會路由到它,而且紀錄裡幾乎看不出原因。先驗證這段 YAML,再往下查其他問題。
04代理怎麼決定要不要觸發 skill
觸發是文字比對,不是註冊表查詢。代理讀取技能名稱與 description 的清單,和當下的需求比對,再把看起來相關的內文載入。這個模型有三個可以事先設計的結果。
- description 要寫成「需求」,不是「功能」。寫「當用戶要檢查設定檔時使用」會比「設定檔驗證工具」更容易路由。
- 內文要窄。一個 skill 如果包辦五件不相干的事,description 就會太廣,反而路由不好。
- 文件夾用動詞命名。因為
name預設取自文件夾名稱,.claude/skills/check-config/SKILL.md會比一個叫utils的文件夾好用。
也可以完全繞過路由。人類永遠可以打 /skill-name;不想讓它自己跑起來的流程,就設 disable-model-invocation: true。allowed-tools 的授權是單輪的:官方文件寫明它在你送出下一則訊息時就會清除,所以 skill 沒辦法在整個 session 裡偷偷擴權。
05一份可以直接抄的 SKILL.md
這是一個完整技能,在提交前檢查設定檔。文件夾名稱是 config-guard,檔案放在 .claude/skills/config-guard/SKILL.md。
---
name: config-guard
description: Validate JSON and YAML config files before a commit. Use when the user asks to check a config file, debug a failing lint step, or prepare a release.
allowed-tools: Read Grep Bash(python3 *)
---
# Config Guard
Validate every changed config file and report failures as a table.
## Steps
1. List changed files with git diff --name-only -- '*.json' '*.yaml' '*.yml'
2. Parse each file with Python and capture the first error.
3. Print a table with file, line and message. Do not fix anything silently.
兩個習慣能讓它維持得久。提交前先驗證 frontmatter,內文一律用祈使句。內文是代理要照著執行的流程,不是給人讀的文件,所以「驗證每個檔案」會比「驗證步驟會被執行」更耐改。
06skill 為什麼不會被觸發
| 症狀 | 可能原因 | 修正 |
|---|---|---|
打 /name 沒反應 | 開頭的 --- 不在第一行 | 把 frontmatter 上方的空行或註釋刪掉 |
| 它永遠不會自己載入 | description 沒填或太抽象 | 改寫成用戶真的會說的需求 |
| 幾乎什麼都觸發它 | description 太廣 | 縮小範圍,拆成多個 skill |
| 某個欄位完全沒作用 | 欄位名稱打錯或大小寫不對 | 完全照 schema 寫,連連字號都不能少 |
| 自己能用、同事不能用 | skill 放在個人目錄 | 搬到 .claude/skills/ 並提交 |
有兩個保留名稱值得先知道。Claude Code 會略過不在自家同步目錄裡、名字叫 synced 的 skill;也不接受叫 anthropic-skills 或以 anthropic-skills: 開頭的文件夾,除非它在某個插件裡。
07skill、MCP 與 AGENTS.md 的分工
這三者解決不同問題,選錯就是設定起來感覺很不穩定的原因。
| 機制 | 載入時機 | 最適合 |
|---|---|---|
AGENTS.md / CLAUDE.md | 永遠 | 不會變的事實、限制與慣例 |
SKILL.md | 相關時 | 多步驟流程與檢查清單 |
| MCP server | 被呼叫時 | 模型本來沒有的能力,例如資料庫或內部 API |
一句話原則:skill 改變代理怎麼做事,MCP server 給它新的東西可用。如果一份 skill 大半篇幅都在描述怎麼呼叫某個 API,那個呼叫屬於 MCP,周邊流程才屬於 skill。
08把每個 skill 都當成不受信任的程式碼
skill 內文就是指令,而代理會照著指令做。這讓第三方 skill 成為一個不需要執行檔的注入面:惡意或粗心的 skill 可以叫代理去讀它不該讀的檔案,或用誤導的方式描述自己做過什麼。
從市集或陌生人的儲存庫安裝 skill 之前,把內文整份讀完、檢查 allowed-tools,並且留意任何指向項目外檔案的指示。你對依賴套件 postinstall 腳本的那套警覺,在這裡一樣適用,只是少了那個腳本。
想把這套流程裡的驗證部分集中在一個地方:YAML 驗證工具 抓 frontmatter 錯誤、YAML 格式化工具 重新縮排且不動註釋、JSON 驗證工具 顧到 skill 旁邊附的設定檔;等你想要的是團隊級契約而不是慣例時,再用 JSON Schema 產生器。
09資料來源
- Claude Code 官方文件 — Extend Claude with skills(取用日期:2026-10-04)
把 SKILL.md 的 frontmatter 貼進來,直接指出第一個語法錯誤的行號與欄位。
YAML 驗證工具常見問題
SKILL.md 是什麼?
它是一份頂端帶 YAML frontmatter 的 Markdown。frontmatter 定義技能名稱與呼叫方式,內文則是技能載入後代理要照著做的指示。
frontmatter 每個欄位都要填吗?
不用,所有欄位都是選填,官方只建議填 description。這個欄位最值得花時間寫,因為代理就是靠它判斷這個技能符不符合當下需求。
為什麼我的 skill 都不會被觸發?
先檢查三件事:兩個 --- 之間的 YAML 要能解析、開頭的 --- 必須是檔案第一行、description 要描述你真的會提出的需求。
skill 的 description 可以寫多長?
在技能清單裡,description 與 when_to_use 會合併後截斷在 1,536 個字元,所以主要用途要放在第一句。
SKILL.md 應該放在哪裡?
單一項目放在 .claude/skills/<name>/SKILL.md 並提交進版控;自己要跨項目使用就放 ~/.claude/skills/<name>/SKILL.md;插件則自帶 skills 目錄。
skill 和 MCP 差在哪裡?
skill 是模型讀的文字,MCP server 是模型呼叫的程式。想改變代理做事的方式就用 skill,需要它本來沒有的能力就用 MCP。
安裝第三方的 skill 安全嗎?
把它當成腳本看待。skill 內文是代理會照著執行的文字,就算沒有執行檔,惡意的 skill 一樣能把你代理帶去別的地方。
相關文章
- 如何挑選並自架 coding agent harness
技能要由哪個執行環境載入,自架之後又有哪些差別。
- 提示注入與 SQL 注入的差別
第三方 skill 檔案就是一條指令通道,這裡說明它的威脅模型。
相關工具
- YAML 驗證工具
指出 frontmatter 第一個錯誤的行號與欄位。
- YAML 格式化工具
重新縮排 frontmatter,註釋與錨點原封不動。
- JSON 驗證工具
檢查 skill 或插件旁邊附的 JSON 設定檔。
- JSON Schema 產生器
把範例 manifest 變成可在 CI 強制執行的 schema。