iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
Claude AI

從 LLM 到 Agent:用 Claude 拆解現代 AI 工程的每一層系列 第 19 篇

技能:把作法打包成一個資料夾,用得到才讀進來

  • 分享至 

  • xImage
  •  

我把「發版前要檢查什麼」寫成一個資料夾,放進專案的 .claude/skills/,然後問 Claude Code 兩個問題:一個跟發版無關,一個是「我要發版了」。兩次的請求我都用 proxy 攔下來,看那份作法到底什麼時候才進到窗口。第 18 篇給 Claude 的是一個工具,這一篇給的是一整套作法。

先講結論:技能(skill)就是一個資料夾。平常窗口裡只有它的一行描述,等 Claude 判斷用得到,才把整份作法讀進來。


30 秒實驗

資料夾長這樣:

.claude/skills/release-check/
├── SKILL.md
└── scripts/
    └── check_version.py

SKILL.md 開頭是一段 YAML,只有名字和描述,後面才是作法:

---
name: release-check
description: 檢查這個專案現在能不能發版:版本號、CHANGELOG 是否對得上。使用者說要發版、release、打 tag 之前使用。
---

# 發版前檢查

照順序做,任何一步不過就停下來,告訴使用者哪裡不過、怎麼修。

1. 讀 `VERSION`,記下要發的版本號。
2. 確認 `CHANGELOG.md` 裡有這個版本號的段落。
3. 執行 `python3 .claude/skills/release-check/scripts/check_version.py`。
4. 全部通過才回答「可以發版」,並列出要打的 tag:`v<版本號>`。

我故意讓專案過不了:VERSION 寫 1.4.0,CHANGELOG.md 只有 1.3.0。跑了一次(2026-10-03,Claude Code 2.1.287,claude-opus-5-5),用 token-inspectour 攔下每一個請求,逐一比對檔案的哪一部分、在哪一個請求進來:

由左到右的時序圖,三條泳道。中間是 Claude Code 送出的請求 1、2、3,每個請求是一條堆疊橫條。請求 1 只有 SKILL.md 開頭的一行描述。模型開了 Skill 的單之後,請求 2 多了 SKILL.md 本文。模型再開 Bash cat 三個檔案的單之後,請求 3 多了腳本原始碼、VERSION 與 CHANGELOG。最後模型回答現在還不能發版。下方的檔案用箭頭標出各自進到哪一個請求

由左往右讀:一行描述從請求 1 就在。Claude 開了 Skill 的單,本文才在請求 2 進來。它再用 Bash 把三個檔案 cat 出來,腳本原始碼、VERSION、CHANGELOG 才在請求 3 進來。問「VERSION 檔裡寫的是什麼」那一題,兩個請求都只有那一行描述,本文從沒進來。

那一行描述在請求裡長這樣,混在一份技能清單裡:

- release-check: 檢查這個專案現在能不能發版:版本號、CHANGELOG 是否對得上。使用者說要發版、release、打 tag 之前使用。

發版那一題,Claude 照「任何一步不過就停」在第 2 步停下,回答「現在還不能發版」,附上要補的 CHANGELOG 段落,連要打的 tag v1.4.0 都寫了。(只跑一次,是例子不是證據。)


三層載入

Claude 的技能文件把這個叫漸進式揭露(progressive disclosure),分三層(2026-10-03 查):

層 什麼時候載入 大小 內容
1. 中繼資料 一直都在 每個技能約 100 詞元 name 與 description
2. 指令 技能被觸發時 5k 詞元以內 SKILL.md 本文
3. 資源與程式碼 用到才載入 沒讀之前 0 附帶的檔案與腳本,腳本執行時只有輸出進窗口

實驗大致照這張表走,例外是腳本的原始碼還是進了窗口。它這次沒有執行腳本,卻自己用 cat 讀了一次。文件說的「只有輸出進窗口」,前提是它直接執行。它要讀檔,你攔不住。想省這一筆,就在 SKILL.md 寫明「直接執行,不用讀內容」。

攔到的請求還顯示,Claude Code 把技能清單放在對話訊息裡,不在系統提示。官方的提示詞快取文件寫明,載入技能是把指令附加成對話訊息,所以前面已經快取的那一段不會失效(2026-10-03 查)。


底下發生了什麼

把攔到的請求跟 Claude Code 的技能文件對起來,從資料夾到 Claude 照著做,中間是五步(2026-10-03 查):

一,掃描。 Claude Code 會找幾個固定的位置:企業管理的、你個人的 ~/.claude/skills/、專案的 .claude/skills/,以及子目錄裡的 .claude/skills/。同名的技能,企業的蓋過個人的,個人的蓋過專案的。SKILL.md 改了它會即時偵測,不用重開。

二,列清單。 每個技能變成一行「名稱:描述」,整份清單放在對話裡的一段系統提醒中。這份清單有預算:模型窗口的 1%。技能太多、放不下時,它從你最少用的技能開始拿掉描述,名字永遠留著。/context 裡 Skills 那一列,就是清單實際占掉的大小。

三,Skill 是一個普通的工具。 我攔到的定義只有兩個參數:

{ "name": "Skill",
  "input_schema": { "properties": { "skill": { "type": "string" },
                                    "args":  { "type": "string" } },
                    "required": ["skill"] } }

它的描述寫明,可用的技能會出現在系統提醒的清單裡,任務符合時先叫這個工具。所以「Claude 決定用技能」,就是第 16 篇講的開一張單。

四,叫了之後,本文用一段新的訊息送進來。 實驗裡第二個請求長這樣:

tool_use     {"name": "Skill", "input": {"skill": "release-check"}}
tool_result  "Launching skill: release-check"
text         Base directory for this skill: <repo>/.claude/skills/release-check
             # 發版前檢查
             照順序做,任何一步不過就停下來……

工具結果只有一句「Launching skill」,作法是另外一段文字,開頭那段 YAML 已經拿掉,換成這個技能資料夾的路徑。有了這個路徑,本文裡寫的 scripts/check_version.py 才能用一般的 Bash 工具去跑。

五,送進來之前還會先加工。 本文裡的 $ARGUMENTS、$0、或你在 arguments 欄位宣告的 $name,會換成叫用時帶的參數。寫成 !`git diff HEAD` 的那一行,Claude Code 會先執行、把那一行換成輸出,Claude 看到的就是目前的 diff。

最後,這份本文會一直留在對話裡。第 14 篇講過,壓縮之後技能本文會被重新放回來,每個上限 5,000 詞元、合計 25,000 詞元,超過的從最舊的開始丟。


描述決定它會不會被叫到

第一層只有描述,所以 Claude 決定要不要用這個技能,靠的就是那一行。官方文件說描述必須同時寫它做什麼、什麼時候用。我實際試了一次這句話的分量。

同一個專案裝兩個技能:release-check,以及一個會跟它搶的 git-tag,描述是「幫使用者打 git tag。使用者說要打 tag 時使用」。release-check 準備兩種描述,一種只寫「發版用的檢查。」,另一種是上面那句,多寫了「打 tag 之前使用」。然後問一句沒提到檢查的話:「幫我打 v1.4.0 的 tag」,各跑三次(2026-10-03):

兩組、共六次執行的流程圖。描述只寫「發版用的檢查。」的那一組,三次都叫了 git-tag,最後都只列出打 tag 的指令,沒提檢查。描述多寫了「打 tag 之前使用」的那一組,第一、二次叫了 git-tag 但建議先跑 /release-check,第三次先叫了 release-check,在第 2 步停下,回答還不能打 tag

只寫「發版用的檢查」時,三次都直接列出打 tag 的指令,沒有人提到這個版本其實過不了檢查。多寫一句「打 tag 之前使用」,三次裡有一次先跑了檢查,另外兩次也會提醒你先跑。(各跑三次,是例子不是證據。)

所以描述會改變它被叫到的機會,但不保證一定會叫。「打 tag 之前一定要檢查」這種非做不可的規矩,不能只寫在描述裡,要寫進 git-tag 技能的步驟本身。

Claude Code 的技能文件還給了幾個欄位(2026-10-03 查):

  • when_to_use:補充觸發的說法,接在描述後面,兩者合計上限 1,536 個字元。
  • disable-model-invocation: true:不讓 Claude 自己叫,只能由你打 /release-check。發版這種有副作用的流程,很適合這樣設。
  • allowed-tools:叫用這個技能的那一輪,哪些工具不用再問你。

為什麼是「作法」這種形狀

把做過的事存成可以重用的步驟,研究上有脈絡。Voyager(Wang et al., 2023)讓代理維護一個持續長大的技能庫,每個技能是一段可執行的程式碼,需要時取回來組合。Agent Workflow Memory(Wang et al., 2024)則從過去的任務裡歸納出常用的工作流程(workflow),選擇性地提供給代理,在 Mind2Web 與 WebArena 兩個網頁導航基準上,成功率相對提升 24.6% 與 51.1%。

技能是同一個想法的手寫版:流程是你寫的,Claude 只負責在對的時候讀進來照做。它也接得回第四層:記憶存的是事實,技能存的是作法。


技能跟 MCP 怎麼分工

面向 MCP 伺服器 技能
給的是什麼 一個動作,一組參數 一套作法:步驟、檢查清單、腳本
誰執行 另一個行程裡的伺服器 Claude 照著做,用它原本就有的工具
平常在窗口裡的 工具名稱,定義用到才載入 一行描述
存的是什麼 一筆 JSON 設定:怎麼啟動或連上伺服器 一個資料夾:作法本身

兩邊存在磁碟上的資料,長得完全不一樣。第 18 篇的退款伺服器,在 .mcp.json 裡只有這一筆:

{ "mcpServers": { "refund": {
    "type": "stdio", "command": "python3", "args": ["refund_raw.py"], "env": {} } } }

這一筆裡沒有任何一個工具的名稱或描述。它只告訴 Claude Code 怎麼把伺服器跑起來,工具有哪些、參數長什麼樣,要等伺服器啟動、回應 tools/list 才知道。這一篇的技能則是反過來,作法本身就是檔案:

.claude/skills/release-check/
├── SKILL.md            ← YAML 開頭(name、description)+ Markdown 本文
└── scripts/
    └── check_version.py

這帶出三個實際的影響:

  • 誰定義內容。 MCP 的工具定義在伺服器的程式碼裡,執行期才交出來。換一個工具,要改伺服器、重新啟動。技能的內容就在 SKILL.md,改完存檔,Claude Code 當下就偵測得到。
  • 怎麼審。 .mcp.json 在 code review 裡只看得到一行指令,真正會交給模型的描述藏在伺服器裡,要攔下來才看得到。技能的描述與每一個步驟都是純文字,跟程式碼放在同一個 PR 裡讀得到、diff 得出來。
  • 誰執行。 MCP 的設定指向一個獨立的行程,執行的是那個行程。技能沒有自己的行程,裡面的腳本是 Claude 用它原本就有的 Bash 去跑的。

Anthropic 在介紹技能的工程文章裡說,技能可以補 MCP 伺服器的不足,教代理怎麼完成用到外部工具的複雜流程,並在 2025 年 12 月把技能發布成開放標準。MCP 規格那一側,也列了一個「Skills over MCP」的擴充,讓技能可以透過 MCP 被找到與使用。

我的分法:一個動作用 MCP,一套作法用技能,作法裡要用的動作交給 MCP 的工具。 發版檢查就是例子:要跑的檢查是作法,真的去打 tag 的那個動作,可以是一個 MCP 工具。


去哪裡找別人寫好的技能

技能是開放標準(agentskills.io),所以已經有跨工具的目錄。skills.sh 是 Vercel 做的技能目錄,自稱「開放的代理技能生態系」,配一個命令列工具:

npx skills add vercel-labs/agent-skills          # 裝進這個專案
npx skills add vercel-labs/agent-skills -g       # 裝到使用者層,所有專案都看得到

它的說明寫支援 Claude Code、Codex、Cursor 等將近八十種代理(2026-10-03 查),網站上也有一欄安全稽核。不過裝別人的技能,等於把別人的指令和腳本放進你的專案,裝之前,自己把 SKILL.md 和腳本讀一遍。


這麼做的代價

第一,描述要跟其他技能搶位置。 每多一個技能,清單就多一行,Claude 要從裡面挑。描述寫得含糊,該用的時候就不會被叫到,上面那三次「發版用的檢查」就是。

第二,技能是別人的指令加別人的腳本。 官方文件說得很直接:只用可信來源的技能,也就是你自己寫的、或 Anthropic 提供的。來路不明的,要把 SKILL.md、腳本和所有附檔都審過一次,因為惡意的技能可以叫 Claude 執行跟描述不符的動作。

第三,載入不完全由你控制。 它可能讀你只想讓它執行的檔案,本文一旦載入也會留在這段對話裡。


這一篇多了什麼,又多付了什麼

多了什麼能力:你能把一套作法寫成技能資料夾,知道它的三層載入在請求裡實際長什麼樣、描述怎麼決定它會不會被叫到,以及技能跟 MCP 各自該放什麼。

多付了什麼代價:一份要跟其他技能搶位置的描述、一個需要審過才能信的資料夾,以及一個不完全由你決定讀什麼的載入過程。


下一篇

這一層給了 Claude 工具、伺服器和技能,它終於能動手了。那如果那段工具描述、那份 SKILL.md,是別人寫來騙它的呢?

第 18 篇引過的研究說,5.5% 的開源 MCP 伺服器有工具投毒的問題。下一篇把這一層的帳算完:副作用、權限的邊界,以及藏在描述與資料裡的指令。


延伸閱讀


上一篇
做一個 MCP 伺服器:交給 Claude 寫,看它踩了哪些坑
系列文
從 LLM 到 Agent:用 Claude 拆解現代 AI 工程的每一層 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言