vatt'ghern jaskier's ballads

你替自己家的技術做了一個 skill,把 API reference、authentication flow、SDK pattern、error handling、version info 全部塞進同一份檔案。看起來很完整。問題是——model 早就讀過你的文件、你的 Stack Overflow 答案、你的 GitHub repo、你的 blog,這份「完整」裡有一大半是你花 token 重講它已經會的東西。

別把 skill 塞太多東西——agent skill 粒度,從零講起

完這篇你會知道一件事:一個好的 agent skill 不是「我把所有相關知識打包進去」,而是「我只補 model 缺的那一塊」。Microsoft 的 Waldek Mastykarz 在一篇叫做 Stop Overloading Your Skills 的文章裡,把這件事講得很白——他的核心句是「In many cases, models don't need a textbook. They needed a cheat sheet.」這篇會從 skill 是什麼開始,講到 overloading 在 token 與選擇準度上各付了什麼代價,怎麼用「先量再建」的方法把 skill 的 scope 框出來,最後落到一個你設計 skill 時可以直接拿來用的判準:哪些 context 該進 skill、哪些該延後到真的需要時再載入。

那份塞滿的 skill——一個你大概寫過的東西

先把場景攤開。你維護一套 SDK,希望 agent 用你的庫時別寫出過時或錯誤的程式碼,於是你做了一個 skill。原文開頭描述的就是這個東西:「You built a skill for your technology. API references, authentication flows, SDK patterns, error handling, version info, all packed into one skill.」一份檔案,把跟你技術有關的所有知識都收進去,理由很直覺——資訊愈多,agent 愈不會出錯。

這個直覺有一個沒被檢查的前提:agent 對你的技術是一張白紙,所以你給的每一段 context 都是淨增益。但這個前提幾乎一定是錯的。今天的 model 在 pre-training 階段早就把公開的技術文件吃進去了。原文點得很具體:「Models have ingested your documentation, your Stack Overflow answers, your GitHub repos, your blog posts.」也就是說,你的 skill 裡那些「標準用法」,model 大概率已經內建。

哪些東西內建?原文給的清單很日常:「The default imports, the standard auth flow, the common CRUD operations: the model already has all of that baked in.」default import、標準 auth flow、常見的 CRUD——這些都是 model 不需要你教就會的。當你的 skill 把這些重新講一遍,你不是在幫忙,你是在加重量。原文這句是整個論點的軸心:「When your skill repeats what the model already knows, you're not helping, you're adding weight.」

「weight」這個詞要認真看。它不是修辭。skill 的內容最終會以 token 的形式進到 model 的 context window,而 context window 是有限的。重講已知的東西,等於拿一個稀缺資源去存放零資訊量的內容。下一節把這個「重量」拆成兩種具體成本。

overloading 的兩種成本——吃掉 context,鈍化選擇

第一種成本是 token 預算。原文的說法是:「Every token your skill returns occupies space in a finite context window, and those tokens aren't neutral.」每一個 skill 回傳的 token 都佔掉 context window 裡的一格,而且這些 token「不是中性的」。中性的意思是「放著也不礙事」;非中性的意思是「它會擠掉別的東西」。擠掉什麼?原文接著講:「They push out the stuff the model doesn't know: workspace files, conversation history, or output from other tools.」被擠出去的,正好是 model 真正不知道、必須靠當下 context 才拿得到的東西——使用者的 workspace 檔案、對話歷史、其他 tool 的輸出。

這是一個很尖銳的對調:你塞進去的是 model 已經會的(低價值),被你擠出去的是 model 不會的(高價值)。一份重講基礎用法的 skill,等於主動用低價值內容換掉高價值內容。token 預算是零和的,這筆交換永遠虧。

把這個機制再拆細一點。原文那句「Every token your skill returns occupies space」裡的「returns」要當真——skill 不是貼進去就算了的靜態字串,它是在 model 需要時被回傳進 context 的內容。也就是說,你在 skill 裡塞的每一行,在每一個觸發它的場景上都會再被算進視窗一次。一份重講 default import 與標準 auth flow 的 skill,等於你在每一次用到它的時候都重新付一次「教 model 它早就會的東西」的帳。原文用「baked in」形容 model 對這些標準寫法的掌握——既然已經烤進權重裡,你在 runtime 再餵一份,就是拿稀缺的視窗空間去複製一份權重裡已有的副本。這不是省一次的問題,是每次都在漏。

而漏掉的那一塊,原文點名得很具體:「workspace files, conversation history, or output from other tools.」這三樣的共同點是它們都是「此時此地才存在」的資訊——使用者這個 repo 的實際檔案、這段對話累積下來的脈絡、剛剛某個 tool 跑出來的結果。這些東西 model 的權重裡不可能有,只能靠當下的 context 帶進來。當你的 skill 把視窗佔掉一大塊,model 能看到的「現場」就變窄,它對你真正要解的任務反而更不了解。你以為自己在補資訊,實際上是在拿走現場。這就是為什麼原文要強調這些 token「不是中性的」——一段中性的內容是放著不礙事,但 skill 的內容會主動把高價值的現場資訊推出視窗外。

下面這個 widget 把「零和」這件事畫出來。拖動 skill 回傳的 token 量,看一個固定大小的 context window 裡,留給 workspace、對話、其他 tool 輸出的空間怎麼被壓縮:

drag the skill size handle to watch the rest of the window shrink · 1 fixed budget

3000 token
FIXED CONTEXT WINDOW(此圖示意為 8000 token) skill workspace · 對話 · tool 輸出 橙色=你的 skill 佔走的;綠色=留給 model 真正不知道的東西的
視窗總量固定。skill 多吃一格,留給 workspace 與對話的就少一格——這正是原文所謂 token「不是中性的」的字面意思。

第二種成本更隱蔽:選擇準度。當你的 skill 很大,它不只拖累自己負責的場景,還會吃掉別的 skill 需要的預算。原文這句講的就是這件事:「Your oversized skill isn't just dragging its own scenarios, it's eating into the budget other skills need.」一個 agent 通常掛了不只一個 skill、不只一組 tool。當其中一個 skill 把 context 佔滿,其他 skill 與 tool 的描述就被擠到邊緣,model 在「該叫哪個 skill、該用哪個 tool」這個決策上能看到的資訊就變少、變糊。skill 的肥大會外溢成整個系統的選擇變鈍。

把這兩種成本連起來看:overloading 不是「多一點沒差」的問題,它是一個有複利的負債。你多塞的每一段已知內容,先擠掉高價值的當下 context(成本一),再排擠其他 skill 與 tool 的能見度(成本二)。skill 愈大,這兩條曲線疊加得愈快。

對設計 MCP server 或 tool 系統的人,第二種成本特別值得放在心上。原文用的詞是「the budget other skills need」——這裡的 budget 是共享的,不是某個 skill 私有的。當你的 skill 把它能塞的都塞了,受害的不只是你自己這個 skill 的表現,而是同一個 agent 上所有 skill 與 tool 的選擇品質。model 做 tool selection 時,本質是在一堆候選的描述之間比對當前任務該叫誰;候選描述被擠到視窗邊緣、或乾脆被截斷,這個比對就會出錯。換句話說,一個過肥的 skill 是會傷害鄰居的——它不是孤立地浪費自己的 token,而是讓整個工具櫃變得難以挑選。

大不等於準——把 token 量畫成一條會回頭的曲線

很容易把 skill 想成「資訊愈多愈安全」的線性關係:多給一點 context,輸出就好一點。但前一節的兩種成本告訴我們,這條關係會在某個點之後反轉。token 成本是隨 skill 大小單調上升的——你塞多少就花多少,沒有上限。而輸出品質(這裡用「選擇/生成的準度」當代理)一開始會隨著你補上 model 缺的知識而上升,但一旦你開始重講 model 已知的東西、開始排擠當下 context 與其他 skill,準度反而會掉下來。

下面這個 hero widget 讓你拖 skill 的 token 量,同時看兩條曲線:紅線是 token 成本(單調上升),綠線是準度(先升後降)。注意綠線的峰值——那個峰值對應的不是「最大的 skill」,是「剛好補完 model 缺口、還沒開始重講已知」的那個 skill。這張圖是定性示意,不是量測數值,曲線形狀來自原文的因果論述(重講已知會 adding weight、會 push out 高價值 context),不是某個 benchmark。

drag skill size and watch cost rise while accuracy peaks then falls · 2 curves

22 / 100
skill 塞入的 context 量 → 相對量(已正規化) 準度峰值 token 成本(單調上升) 輸出準度(先升後降)
定性示意。準度的峰值落在「補完缺口、尚未重講已知」之處;過了峰值,多塞的 context 同時抬高成本、壓低準度。最大的 skill 從來不是最好的 skill。

把這條綠線記住,後面所有設計判準都是在回答同一個問題:怎麼把 skill 停在綠線峰值,而不是一路衝到右邊那塊「成本高、準度低」的區域。原文給的答案不是憑感覺抓,是量出來的。

先量再建——讓 baseline 替你框出 skill 的 scope

怎麼知道 model 缺什麼、不缺什麼?原文的回答很乾脆:「You don't, unless you measure. And most folks skip this step entirely.」你不知道,除非你量;而大多數人直接跳過這一步。跳過量測,你就只能憑「這看起來相關」來決定塞什麼進 skill——而「相關」與「model 缺這個」是兩回事。

量測的方法原文寫得很具體:「Start by running your scenarios without the skill. Same model, same harness, same prompts.」先在「沒有 skill」的條件下跑一遍你的場景——同一個 model、同一個 harness、同一組 prompt。這一輪的輸出就是 baseline:model 靠內建知識能做到什麼程度。

有了 baseline,scope 就自動浮現。原文這句直接給出定義:「What's left after you subtract the baseline? The patterns the model gets wrong or doesn't know about at all. That's your skill's scope. Nothing more.」減掉 baseline 之後剩下的——model 答錯的、或根本不知道的 pattern——那才是你 skill 該管的範圍,僅此而已。換句話說,skill 的內容=(你希望 model 做到的)-(model 不靠你就做得到的)。這個減法就是把 skill 停在綠線峰值的操作型定義。

減法落到具體規則,原文給了兩個例子:「If the model handles CRUD correctly, don't put CRUD examples in your skill. If auth flows work out of the box, don't include your auth guide.」如果 model 本來就把 CRUD 寫對了,別把 CRUD 範例放進 skill;如果 auth flow 開箱即用,別放你的 auth 指南。判準不是「這個主題重不重要」,是「model 在這個主題上現在錯不錯」。重要但 model 已經會的,留在 skill 裡只是重量。

實際操作時這個減法長什麼樣子?假設你跑了十個場景的 baseline,發現 model 在其中八個都寫出正確、慣用的程式碼,只有兩個出錯——一個是你的庫在某個版本把 API 簽名改了,model 還在用舊的;另一個是你內部有個非標準的初始化順序,公開文件沒寫過,model 沒看過。那麼你的 skill 就只該包含這兩件事,剩下八個場景對應的內容一行都不要寫。這跟「把整套 SDK 文件貼進 skill」是兩種完全不同尺寸的產物——前者可能只有十幾行,後者動輒上千 token,而它們在那八個 model 本來就會的場景上,輸出品質沒有差別。

下面這張表把這個減法整理成可以直接套用的清單。左欄是 model 通常已內建、該從 skill 砍掉的;右欄是 model 容易出錯、值得進 skill 的。表格可排序,按「進 skill?」排可以一眼看出留下來的是哪幾類。

click column header to sort · 3 columns × 7 rows

內容類型 為什麼 進 skill?
標準 CRUD 操作原文:handles CRUD correctly 就別放砍掉
開箱即用的 auth flow原文:works out of the box 就別放 auth guide砍掉
default import 與標準寫法原文:already baked in砍掉
公開文件能查到的 API referencemodel 已 ingested 你的 documentation砍掉
model 量測下會寫錯的 API pattern原文:gets wrong → 這才是 scope留下
model 根本不知道的私有約定原文:doesn't know about at all留下
版本差異造成的 breaking change 陷阱baseline 上 model 用了過時寫法留下
判準是「model 現在錯不錯」,不是「主題重不重要」。auth 很重要,但若 model 已寫對,它在 skill 裡只是重量。

下面這張圖把這個減法畫成一條視覺等式:你要 model 做到的整個範圍,減掉 baseline 已經涵蓋的,剩下的窄帶才是 skill。

你要 model 做到的全部 baseline 已涵蓋 = skill model 已會的那塊愈大,skill 該留的窄帶就愈窄——這正是「Nothing more」的幾何意思。 隨著 model 變強,中間那塊會往右長,skill 會被進一步壓窄。
skill scope 是一個差集,不是一個聯集。把它想成「目標減 baseline」而非「相關知識的總和」,是整篇方法的幾何核心。

這裡有一個容易被忽略的副作用:baseline 不是只跑一次的事。model 會更新,今天 model 還寫錯的 pattern,下一代 model 可能就內建了。所以「skill 的 scope」是一個會隨 model 能力縮小的東西——你 skill 裡的某幾條,可能在下次 model 升級後就該被砍掉。把 baseline 量測寫進你維護 skill 的流程,而不是一次性的開發步驟。

小而可組合——progressive disclosure 與多 skill 的預算共享

把單一 skill 砍到只剩 model 缺的部分,還只解決了一半。另一半是「組合」。一個真實的 agent 通常掛多個 skill、多組 tool,它們共用同一個 context window。前面那句「it's eating into the budget other skills need」講的就是這個共享預算——當每個 skill 都瘦下來,整個系統能同時掛載的能力就變多,model 在 skill/tool 之間做選擇時看到的也更清楚。

從這裡自然導出一個設計原則:與其做一個無所不包的大 skill,不如做幾個各管一塊、可以按需組合的小 skill。把「什麼時候載入哪段 context」延後到真的需要的那一刻——這就是 progressive disclosure 的精神。skill 的定義裡只放最精簡的指引(哪個情況下做什麼、哪幾個非標準的陷阱要避開),把厚重的細節留在「真的走到那一步」時才取用,而不是開場就把整本手冊倒進 context window。

怎麼把一個胖 skill 拆開?回到原文的減法。原文開頭列的那一串「API references, authentication flows, SDK patterns, error handling, version info」其實是好幾個彼此獨立的主題被綁在同一份檔案裡——拆的第一刀就是沿著這些主題的接縫切。對每個主題各跑一次 baseline:CRUD 那塊 model 寫對了,整塊砍掉,連 skill 都不用建;auth 開箱即用,同樣砍掉;只有「version info 裡那個改過簽名的 API」和「error handling 裡某個非標準的重試約定」是 baseline 上 model 真的會錯的,那就讓它們各自成為一個瘦 skill。拆完之後,原本一份混著大量已知內容的胖檔案,變成兩三個只裝缺口的窄檔案——每一個都通過了「subtract the baseline」這道測試,每一個都還是「Nothing more」。

拆小帶來的不只是省 token,還有一個運作上的好處:載入時機可以各自獨立。當三個瘦 skill 各管一塊,agent 可以只在碰到 version 問題時才把「version 陷阱」那個 skill 的細節拉進來,碰到 error handling 才拉另一個——這就是 progressive disclosure 落到多 skill 上的樣子。常駐在 context 裡的,只需要每個 skill 一兩句「我負責什麼、什麼時候找我」的指引;厚重的內容延後到觸發那一刻。對照原文「occupies space in a finite context window」這句,延後載入的意義就是讓任何一個 turn 真正佔住視窗的,只有當下這個任務用得到的那一塊,而不是所有 skill 的全文。下面這張對照圖把「一個過載大 skill」與「幾個瘦身可組合 skill」放在同一個固定預算下並排:

OVERLOADED:一個大 skill 吃滿預算 SKILL A(API + auth + CRUD + 版本 + error,多半 model 已會) 剩餘預算 其他 skill 與 tool 描述被擠到邊緣,選擇變鈍 COMPOSABLE:幾個瘦 skill 各管一塊,按需載入 skill:版本陷阱 skill:私有約定 skill:易錯 API 充裕的剩餘預算 PROGRESSIVE DISCLOSURE:細節延後到走到那一步才載入 精簡指引(常駐) → 觸發時才取用 → 厚重細節(按需) 同一個固定預算下,瘦身+組合+延後載入,能同時掛更多能力且選擇更清楚
上排是一個過載 skill 吃滿視窗;下排是幾個瘦 skill 共享預算,常駐只放精簡指引,厚重細節延後到觸發時才取用。

需要強調的是:progressive disclosure 與「按需組合」是設計原則層面的引申,原文的軸心論點集中在「量出 model 缺口、把 skill 砍到只剩缺口」。但這個引申是直接從原文「oversized skill eating other skills' budget」這句長出來的——既然肥大的 skill 會排擠別人,最小化每個 skill、把載入時機延後,就是讓多個能力在同一預算裡共存的自然解。合理的推測是:你愈是把 context 延後到真正需要的時刻載入,model 在「現在該選哪個 skill」這個決策上看到的雜訊就愈少。

把單一 skill 的減法與多 skill 的組合合起來,整套方法可以濃縮成原文的收尾:「Do this and you'll end up with skills a fraction of their original size that produce measurably better results.」做到這件事,你會得到只有原本一小部分大小、卻產出可量測地更好結果的 skill。注意「a fraction of their original size」與「measurably better results」這兩個詞同時出現——更小且更好,不是取捨,是同一個動作的兩面。

什麼時候該載入什麼——一張可以貼在設計文件旁的判準

回到你要設計 skill 的當下。把前面所有東西收斂成幾條可操作的判準。第一條:不要從「我有什麼知識」出發,要從「model 缺什麼」出發——先跑沒有 skill 的 baseline,量出缺口。第二條:skill 的 scope 就是缺口,原文的話是「Nothing more.」凡是 baseline 上 model 已做對的,不論主題多重要,都不進 skill。第三條:能拆就拆,把一個無所不包的 skill 拆成幾個各管一塊的小 skill,讓它們在同一預算裡共存。第四條:能延後就延後,常駐 context 只放最精簡的觸發指引,厚重細節在走到那一步時才載入。

那有沒有「skill 大一點才對」的時候?有,但條件很窄。原文的判準從頭到尾是「model 在這件事上現在錯不錯」,所以一個 skill 該大,唯一正當的理由是 baseline 量出來的缺口本身就大——你的技術夠新、夠私有,公開資料裡沒有,model 的權重裡自然也沒有。原文那句「The patterns the model gets wrong or doesn't know about at all」裡的「doesn't know about at all」就是這種情況:model 不是寫錯,是根本沒看過。這時候 skill 大是因為缺口大,不是因為你想保險。反過來,凡是你拆不出獨立載入時機、或拆開後每一塊都太碎而失去脈絡的主題,硬拆只是徒增管理成本——判斷拆不拆的標準同樣是 baseline,不是「越小越好」這種教條。真正要避免的不是「大 skill」這個外形,而是「重講已知」這個內容;一個全是缺口的大 skill,仍然通過了原文的測試。

這四條背後其實是同一個世界觀:context window 是一個你要替 model 精打細算的稀缺資源,而不是一個你盡量塞滿的容器。每一段你考慮放進 skill 的 context,都要先問一句原文式的問題——這段 model 已經會了嗎?如果會,它就不是幫助,是重量。原文最後把整件事壓成一個你可以記一輩子的對比:「In many cases, models don't need a textbook. They needed a cheat sheet.」很多時候,model 要的不是一本教科書,是一張小抄。

下面這張小圖把「教科書 vs 小抄」這個對比量化成「同一份缺口資訊,兩種包裝的 token 佔用」。它不是 benchmark,是把原文那句對比畫成可視的比例——教科書版把已知內容也一起塞,小抄版只留缺口:

同一份缺口資訊,兩種包裝 textbook 缺口 model 已知(重講) ≈ 大 cheat sheet 缺口 ≈ 一小部分(a fraction) 綠=model 缺的(兩者相同);橙=model 已會的(只有 textbook 多花這段 token) 兩者教給 model 的有效資訊相同,差別全在那段被重講的已知內容
小抄與教科書在「補缺口」上等效;教科書多出來的長度,全是 model 已知、被重講的內容——也就是原文說的 weight。

Plain words:skill 的內容=(你要 model 做到的)-(model 不靠你就做得到的)。先量出那個減法,把 skill 砍到只剩缺口,拆小、延後載入——你會得到更小、卻可量測地更好的 skill。model 要的不是教科書,是一張小抄。