工坊裡有三樣東西:一個工匠、一張工作檯、一本學徒筆記。
工匠就是模型。反應快、用途廣,能把原始文字變成計劃、程式碼和文章——但憑他自己,他的水平止步於訓練資料。給他一個從未見過的問題,他只能即興發揮——說得客氣點就是靠猜。
工作檯就是 MCP。固定在檯面上的電動工具——電鑽、帶鋸、計算器、一根連到 GitHub 的活線、一條通往你資料庫的連線。每個工具只做一件事。每個工具都能按名字被發現,有類型化的輸入,產出類型化的輸出。工匠在需要對外部世界採取行動時才伸手去拿:查詢資料庫、抓取檔案、發送郵件、執行運算。工作檯是 外部——活的、有狀態的、可替換的。換一張檯面,工匠就能做另一類工作,而無需更換他自己。
學徒筆記就是 Agent Skills。由每一位曾在這張檯面上工作過的大師留下的筆記——公司內部的風格指南、填寫 PDF 表單的配方、發佈程式碼審查的多步驟流程、公司特有的提交訊息寫法。這本筆記按需被翻閱。工匠不會背下每一頁;他只在任務匹配時才翻開對應章節。筆記是 內部——編碼化、版本化、可在工匠之間共享。
現在想像三種失敗模式。
失敗模式 1:有滿檯工具但沒筆記的工匠。 他手上有電鑽,但不知道該用哪個鑽頭,也不知道量兩次再鑽一次。該用螺絲的地方他掄鎚。他一連調了十七次 gh list_prs,因為他腦子裡沒有「我們公司的程式碼審查就長這樣」這種流程。強大、危險、昂貴。
失敗模式 2:有厚厚筆記但沒檯面的工匠。 他知道筆記裡的每一道工序——填表、發審查、寫發佈說明——但每道工序的最後一句都是 「然後呼叫 gh API」。而他手上沒有 gh API。他寫下漂亮的小作文,描述「假如我有工具會怎麼做」。理論上有用,實踐上無力。
失敗模式 3:既沒檯面也沒筆記的工匠。 一個裸的 LLM。他能做模式匹配、能即興發揮。有些時候這就夠了。大多數時候,即興發揮會以你無法察覺的方式出錯——因為正如上個月那篇關於諂媚性的文章花三千字解釋的那樣,模型會贊同你問它的任何形狀的問題,而問題的形狀就是它能拿到的全部訊號。
有意思的設計空間,是兩者都有的工匠。這就是現代智能體棧:一個模型、一張裝滿類型化工具的 MCP 工作檯、一本寫滿編碼化流程的 Skill 筆記,即時組合。本文要講的就是為什麼 MCP 和 Skills 這兩套體系不是對手、它們在底層究竟有何不同、以及決定伸手拿哪一個、或者兩個都拿的判定流程。
30 秒預覽,兩張截圖
MCP 是一種 執行時通訊協定。模型透過 JSON-RPC 發送 tools/list 來發現工具。它透過發送符合工具 schema 的 JSON 負載、呼叫 tools/call 來呼叫一個工具。伺服端執行工具並返回類型化結果。一次來回大約幾百毫秒。工具是 可執行的。
Skills 是一種 資料夾格式。一個目錄,裡面有一個 SKILL.md,其 YAML frontmatter 包含 name 和 description。智能體啟動時,每個 skill 的 name 和 description 會被載入到系統提示裡。當任務匹配某個 description 時,智能體把完整的 SKILL.md 讀入上下文,也可以執行附帶在目錄裡的腳本。沒有網路來回——skill 在磁碟上。Skills 是 編碼化的。
如果說 MCP 是工作檯,Skills 就是筆記本——而現在的筆記本也透過 scripts/ 自帶工具了,連同 SKILL.md 下的配方一起。不同形態的資訊,不同的延遲特徵,不同的失敗模式。同一隻智能體。
困惑的根源
兩套標準的名字裡都有一個做了大量工作的單字母:S。MCP 裡的 Server,Agent Skills 裡的 Skills。兩者都被描述為「擴展 LLM 能力」的方式。Claude、VS Code、Cursor 以及越來越多的 AI 客戶端都支援兩者。一個理性的工程師,在一個星期二的早晨,瀏覽發佈說明時,會讀到「MCP server」和「Agent Skill」,然後得出結論:它們是解決同一問題的兩種競爭方案。
它們不是。它們解決的是不同的問題。
這是我見過最簡潔的一句話區分:
MCP 回答:「模型能不能到達外部世界?」 Skills 回答:「模型知道怎麼工作嗎?」
寫成矩陣就是:
第一個問題的 是 給模型一雙新的手搭上網路。第二個問題的 是 給模型一份新的訓練躺在磁碟上——流程,加一些可選的、繞過網路直接觸及世界的腳本。兩者能組合,但不是同一種擴展。本文剩下的部分,就是拆解這種區分,再展示它的實際含義。
MCP 是什麼(與不是什麼)
MCP(Model Context Protocol,模型上下文通訊協定) 是一種開放標準——最初由 Anthropic 在 2024 年 11 月發佈,現在由一個社群工作組維護——用於把 AI 應用連接到外部系統。Anthropic 自己給出的描述最為貼切:“MCP 是 AI 應用的 USB-C 介面。” 一個標準化插座,許多可能的外部裝置。
其架構是一種用戶端-伺服端通訊協定,分層如下:
通訊協定分兩層。傳輸層 要麼是 STDIO(Host 啟動的一個本地子處理程序,透過 stdin/stdout 通訊——開銷最小、單用戶端、信任邊界由 OS 處理程序模型劃定),要麼是 streamable HTTP(一個透過網路可達的遠端伺服端,使用 bearer-token 或 OAuth 認證——多用戶端、有真實延遲、有通訊協定層的安全邊界)。資料層 是 JSON-RPC 2.0——一種在 LLM 出現之前就已經久經考驗的線纜格式,帶有方法名、ID 和用於單向推送的通知通道。
在遠端 HTTP 傳輸上,認證模型就是讓 MCP 能用於 敏感 操作的關鍵。Host 與認證伺服器完成一次 token 交換——使用者委託流用 Authorization Code + PKCE,服務到服務用 client credentials——並在每次請求時出示一個受限 bearer token。MCP 伺服端在 受限 token 下執行每一次工具呼叫,而不是用使用者的原始憑證,也不是用某個全 Host 通用的服務帳號。Token 可以被輪換、撤銷、稽核。一個 scope 是 repo:read 的 token,構造上就無法 push 到 repo。憑證從來不進入模型的上下文視窗,也不需要被貼在設定檔裡,也不放在 Skill 資料夾裡。這就是 Skills 沒有的安全邊界。
線纜上傳輸的是 原語(primitives)。MCP 伺服端 可以向用戶端提供三種東西:
tools——模型可以呼叫的 可執行 函式。一個工具有名字、描述和一個 JSON Schema 描述其輸入。呼叫工具返回一個類型化結果。這是最常用的原語。例如:gh_create_pr、sql_query、send_email、get_weather。resources——唯讀 資料來源,模型可以拉到上下文裡。與 tool 的區別在於:讀 resource 沒有副作用,概念上是「抓取檔案」而不是「執行函式」。例如:一個專案的README.md、資料庫的 schema、今天的日曆。prompts——可複用 模板,模型或使用者可以帶參數呼叫。例如:接受 diff 然後輸出 conventional-commit 格式的commit_messageprompt;summarise_meetingprompt。
MCP 用戶端 可以向伺服端提供三種東西(反方向):
sampling——伺服端可以要求 用戶端呼叫 Host 自帶的 LLM 做一次補全。在伺服端想要 LLM 幫助、但不想再捆綁一個模型 SDK 時很有用。elicitation——伺服端可以向 使用者 提一個結構化問題。在伺服端需要在副作用發生前做確認(「你確定要刪除正式環境資料庫嗎?」)時很有用。logging——伺服端可以向用戶端推送結構化的記錄訊息,便於除錯。
通訊協定是 有狀態 的——雙方在啟動時協商能力(initialize / notifications/initialized),伺服端在工具清單變化時可以即時推送通知(notifications/tools/list_changed)。一個活的、能自我更新的伺服端——比如一個檔案系統 MCP 伺服端,在使用者 cd 到新專案時新增工具——這是一等公民特性,不是臨時加的。
認證與人在回路
有兩類原語讓 MCP 成為敏感操作的正確形狀:受限認證 和 結構化的人工確認。Skills 這兩樣都沒有。
認證。 在 streamable-HTTP 傳輸上,Host 從認證伺服器那裡拿到一個受限 token,並在每次請求時出示。伺服端在那個 scope 下執行每一次工具呼叫。一個 scope 是 repo:read 的 token 不能 push;一個 scope 是 db:SELECT users 的 token 不能 DROP TABLE users。Token 可撤銷、可輪換、可稽核。整套機制不需要憑證進入模型上下文,也不需要放在 Skill 資料夾裡。Stdio MCP 繼承 OS 處理程序模型——沒有通訊協定層認證,但子處理程序擁有啟動使用者所擁有的一切,這也是另一種信任邊界。
透過 elicitation 實現的人在回路。 MCP 伺服端可以在執行副作用之前暫停一次工具呼叫,向使用者提一個結構化問題。伺服端發送一個 schema(問題、允許的答案、驗證規則);Host 渲染它;使用者回答;結果作為一個類型化的 JSON 值返回。這是通訊協定層對「你確定嗎?」的回答——不是一個模型可以被說服放棄的系統提示指令,而是一個伺服端強制執行的線纜通訊協定步驟。
// 伺服端 → 用戶端:在合併前問使用者一個結構化問題
{
"jsonrpc": "2.0",
"id": 7,
"method": "elicitation/create",
"params": {
"message": "把 PR #482 合併到 main 嗎?",
"requestedSchema": {
"type": "object",
"properties": {
"confirm": { "type": "boolean", "title": "是的,合併" },
"reason": { "type": "string", "title": "原因(可選)" }
},
"required": ["confirm"]
}
}
}
伺服端的 merge_pr 工具要等使用者回答 confirm: true 後才會執行。一段 Skill 文字說 「不要在沒確認的情況下合併」 是模型可以被說服放棄的一句話;elicitation/create 則是伺服端強制執行的一個線纜通訊協定步驟。這就是「問」與「查」的區別。
預先批准、受限憑證,以及逆向問題。 同樣的邏輯反過來也成立:當 Skill 提示詞 預先批准 某些操作(「你可以直接跑 gh pr list 和 gh pr diff 而不必問」)時,是模型在做分類決策——而那個分類決策和其他任何下一詞預測一樣,會遇到同樣的諂媚性和提示詞注入失敗模式。Skill 可以在文件裡寫明哪些操作安全到可以預先批准;只有 通訊協定 能讓預先批准成為線纜上的屬性。其機制就是 受限憑證:一個 scope 是 repo:read 的 token 在構造上就無法 push;一個 MCP 工具的實作拒絕接受白名單以外的任何東西。Skill 說「這一步可以跳過檢查」;MCP 伺服端說「這一步從這裡根本到不了」。兩層、兩種保證,同一種模式。
合起來,受限憑證和 elicitation 是本文反覆回到的那個問題的兩種通訊協定層回答:怎麼給模型一個不去贊同使用者的理由?
一次工具呼叫在 wire 上到底長什麼樣
一次簡化的握手,直接取自 MCP 架構文件。兩條 JSON-RPC 2.0 訊息,先請求後回應:
請求——用戶端到伺服端:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "weather_current",
"arguments": {
"location": "舊金山",
"units": "imperial"
}
}
}回應——伺服端到用戶端:
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "舊金山當前天氣:68°F,多雲……"
}
]
}
}這就是 使用 一個工具的完整通訊協定表面。所有有趣的設計選擇——類型化 schema、content 陣列、能力協商——都在為這一件事服務:模型呼叫一個類型化的函式,拿回一個類型化的答案,線纜格式是可移植的。
MCP 不是什麼:
- 它不是一個 prompt。你不能寫
system: 你是一個禮貌的助手然後叫它 MCP。MCP 是一個針對 動作和資料 的執行時通訊協定,不是用來引導生成的。 - 它不是一次微調。MCP 伺服端不會改變模型的權重。
- 它不是一段系統提示覆寫。Host 的系統提示是 Host 自己的事;MCP 是模型決定行動時 Host 能觸及的東西。
- 它不是一個 Agent Skills。這部分下一節再說。
Skills 是什麼(與不是什麼)
Agent Skills 是 Anthropic 在 2025 年 10 月 16 日 作為 Claude 平台特性發佈、又在 2025 年 12 月 18 日 作為跨平台開放標準在 agentskills.io 發佈的開放標準。它的格式令人耳目一新地樸素:一個目錄,裡面有一個必需的檔案。
my-skill/
├── SKILL.md # 必需:YAML frontmatter + 指令
├── scripts/ # 可選:智能體可以執行的可執行程式碼
├── references/ # 可選:智能體可按需載入的文件
└── assets/ # 可選:模板、schema、圖片
SKILL.md 必須以 YAML frontmatter 開頭,至少包含 name 和 description。檔案正文是純 markdown——指令、範例、配方、該做什麼、不該做什麼。一個完整可發佈的 Skill,可以只是一個不到 50 行的單檔案。一個複雜的 Skill 可能捆綁 200 行 Python 和三個 reference 檔案;通訊協定不挑。
互動模型是分三階段的 漸進式披露(progressive disclosure):
階段 1,發現。 智能體啟動時,把每個已安裝 skill 的 name 和 description 載入進系統提示。就這樣。一些幾百位元組的小行。智能體現在 知道 這個 skill 存在以及它的用途,但還沒有讀它的正文。
階段 2,啟動。 當使用者訊息進來時,智能體的推理判斷任務是否匹配某個 skill 的 description。如果匹配,智能體就把完整的 SKILL.md 讀入上下文。流程性知識就是在這裡落地的——配方、流程、內部風格指南。
階段 3,執行。 如果 SKILL.md 引用了附帶的腳本或 reference 檔案,智能體可以呼叫它們。Skill 可以攜帶由智能體 確定性 執行的程式碼——同樣的輸入、同樣的輸出、位元組級一致,在每一台機器和每一個模型上都一樣。這是讓人驚訝的那一點:Skill 不只是指令。它是 指令加程式碼即工具。指令告訴智能體 何時 與 為何;程式碼告訴智能體 怎麼做,可靠地、每次都一樣。在智能體其餘部分是可被誘導性提問影響的下一詞取樣器的地方,一個附帶的腳本就是一個函式呼叫:它返回程式碼說它返回的東西,模型的諂媚性先驗無法與之爭辯。這 才是那個「確定性」的論斷,也是把程式碼而不是 markdown 描述的流程放進 scripts/ 的真正承重理由。
三個階段組合在一起。智能體啟動時按名字知道每個 skill。只在相關的時候載入某個 skill 的指令。只在需要的時候執行 skill 的程式碼。裝一百個 skill 的上下文視窗成本,在閒置時——是一百對 name + description 的成本,不是一百本完整手冊的成本。
採用情況。 Skills 從一項 Claude 特性出發,長得很快。寫作本文時,agentskills.io 目錄列出了大約四十個採用它的用戶端,包括 Claude Code、Cursor、VS Code、GitHub Copilot、OpenAI Codex、Gemini CLI、OpenHands、Junie、JetBrains 的 Junie、Spring AI、Laravel Boost、Snowflake Cortex Code、Databricks Genie Code 等等。當四十個工具都同意一個帶 SKILL.md 檔案的目錄格式,這個格式就贏了——你可以寫一次 Skill,然後在你整個工具棧裡複用。
附帶腳本:設定與確定性
這是我在寫第一個那麼平凡的 Skill 之前本該讀到的一節,因為它把「Skill = markdown」這種簡單框架和真實 Skill 實際做的事情之間的差距給補上了。Skill 目錄允許在 scripts/ 下攜帶可執行程式碼,一旦這樣做,就有了 markdown 單獨給不了你的兩樣東西:與活系統的連線 和 確定性答案。
連線。 scripts/ 裡的腳本就是一個程式。它能 import 宿主執行階段提供的任何函式庫——Postgres 驅動、HTTP 用戶端、SMTP 函式庫、SSH shell、Redis 用戶端——然後用它們觸及世界。markdown 不會連線任何東西;腳本會。所以當有人問「Skill 能跟我的資料庫對話嗎?」,答案是 「Skill 的 markdown 不行,但一個攜帶腳本、其程式碼負責建立連線的 Skill 完全可以。」 markdown 告訴智能體 何時 與 為何;腳本告訴智能體 怎麼做,並且自己連到資料庫。
設定。 一個連資料庫、發郵件、調內部 API 的腳本需要設定——連線字串、憑證、租戶 ID、特性開關。Skill 格式刻意不發明設定通訊協定;腳本用任何其他程式都會用的方式讀取設定:
- 執行階段讀取的環境變數(
os.environ["PII_DB_URL"])。智能體執行階段注入它們;密鑰留在 Skill 資料夾之外。這是最常見的路徑。 - Skill 資料夾裡的設定檔(
scripts/db.yaml、.env.example、references/connection.md)。智能體讀一次,然後把路徑作為參數傳給腳本。當設定是 per-Skill 而不是 per-host 時用這個。 - 智能體傳入的參數(
python scripts/query_db.py --tenant=acme --sql="SELECT 1")。當流程的參數由使用者驅動、每次呼叫都不一樣時用這個。
三種路徑都只是 plumbing。它們的共同點是讓腳本拿到確定性所需的輸入,連到正確的系統。
一個真實例子。 想像一個 pii-audit Skill,它的工作是標記一篇文章裡未知的 email 地址。Skill 攜帶 scripts/check_pii.py,腳本連到正式環境資料庫的 canonical-emails 表,對輸入跑一個確定性的正則,然後輸出計數和未知 email 清單。設定是 PII_DB_URL 環境變數。Skill 的 SKILL.md 指示智能體每當使用者問「這篇文章裡有沒有未知 email?」時透過 Bash 工具呼叫腳本。
pii-audit/
├── SKILL.md
└── scripts/
└── check_pii.py
# scripts/check_pii.py — 與 Skill 資料夾一起打包
import os, re, sys, sqlite3
EMAIL_RE = re.compile(r"[\w.+-]+@[\w-]+\.[\w.-]+")
def main() -> int:
text = sys.stdin.read()
db_url = os.environ.get("PII_DB_URL")
if not db_url:
print("error: PII_DB_URL not set", file=sys.stderr)
return 2
conn = sqlite3.connect(db_url)
canonical = {row[0] for row in conn.execute(
"SELECT email FROM canonical_emails"
)}
conn.close()
found = set(EMAIL_RE.findall(text))
unknown = sorted(found - canonical)
if unknown:
for e in unknown:
print(f"unknown_email: {e}")
print(f"summary: {len(unknown)}/{len(found)} emails not in canonical list",
file=sys.stderr)
return 1
print(f"ok: all {len(found)} emails canonical", file=sys.stderr)
return 0
if __name__ == "__main__":
sys.exit(main())
把腳本讀兩遍。注意三件事:
- 它連資料庫。 Skill 的 markdown 什麼都沒有連線;腳本連線了。Skill 資料夾,端到端,可以跟 Postgres、MySQL、Redis、任何宿主執行階段能 import 的東西對話。
- 它的輸出是確定性的。 給同樣的輸入文字和同樣的 canonical 集合,腳本的 stdout 在每次執行、每台機器、每個模型上都位元組一致。正則是確定性的。SQL 是確定性的。退出碼是確定性的。模型的諂媚性先驗沒什麼可抓的,因為根本沒有取樣。
- SKILL.md 只管「何時」與「為何」。 它沒有發明流程;沒有即興發揮 SQL;沒有判斷某個 email 是否 canonical。這些決定都在程式碼裡,而程式碼才讓答案可以重現。
這就是模式。Markdown 是配方,腳本是烹飪。 一個只攜帶 markdown 的 Skill 能教會智能體怎麼思考,但不能保證一個確定性的答案。一個攜帶腳本的 Skill 能教會智能體怎麼思考 並且 保證答案——前提是設定接好了。兩者合一——磁碟上的流程性知識,加上磁碟上的確定性執行——就是 Skill 資料夾不只是花俏系統提示的原因。
信任邊界
Skills 沒有認證模型。它們就是磁碟上的檔案。智能體讀它們;這就是全部介面。沒有「Skill OAuth 流程」、沒有受限憑證、沒有 token 刷新、沒有稽核記錄、沒有人在回路的檢查點。格式刻意沒有發明這些。
一個跑起來的 Skill 腳本就是一個程式。它以智能體宿主處理程序所屬的使用者身份執行,繼承那個使用者擁有的環境變數、檔案系統存取權限和網路存取權限。爆炸半徑與在智能體 shell 裡跑 bash 完全相同。如果智能體宿主處理程序的環境裡有 AWS_SECRET_ACCESS_KEY,Skill 的 scripts/*.py 裡就有 AWS_SECRET_ACCESS_KEY。沒有 scope,沒有過期,沒有撤銷。
由此推出幾條尖銳的結論:
- 設定放在環境變數裡(第 211 行)或設定檔裡(第 212 行)或參數裡(第 213 行)。把這些密鑰當成
.bash_history一樣對待——因為從 OS 的角度來看,它們就是。 - 如果某個動作需要 受限 憑證——唯讀這張表、只寫那個桶、有時限、可撤銷——那不是 Skill 的活,是 MCP 的活。MCP 伺服端做認證;Skill 負責協調流程;讓 MCP 工具帶著一個有 scope 的 token 去幹那件髒活。
- 如果某個動作需要 人在回路——刪之前要確認、發之前要批准、部署之前要雙檢——Skill 的
SKILL.md可以 指示 智能體去問,但通訊協定不會 強制 它被問。MCP 的elicitation/create會強制這一步。Skill 說「合併前不要跳過確認」;MCP 的elicitation/create讓 merge 工具在使用者答完之前拒絕執行。
這就是和 MCP 之間最乾脆的一句話對比,也是兩者要組合起來的最乾脆的一句話理由:
Skill 教會智能體何時與為何;MCP 認證執行並強制檢查。
把流程留給 Skill;把觸敏系統的部分留給 MCP。兩者組合;信任等級不組合。
每當答案必須每次都一樣時,就伸手拿 Skill 腳本
一次正則檢查、一次雜湊計算、一次 SQL 計數、一次檔案的內容雜湊、一次 JSON-schema 驗證、一次 CSV diff——任何「正確答案可重現」的步驟——都屬於 Skill 腳本,不屬於 markdown。markdown 可以描述 何時 跑它;腳本每次被呼叫都 一樣地 跑。回顧你自己寫的或繼承的 Skill 時用這條:如果你能把這一步寫成純函式,就把它打成腳本;如果它需要判斷力,就打成 markdown。
Skills 不是什麼:
- 它不是一個執行時通訊協定。沒有基於 JSON-RPC 的
tools/list。Skill 在磁碟上;智能體讀它。沒有伺服端要起。 - 它不是一個 MCP 伺服端——Skill 的 markdown 自身並不能連資料庫、抓網頁或發郵件。但一個 Skill——這個資料夾——經常可以,靠捆綁一段負責連線的腳本(見上一節)。Skill 也可以 指示 智能體呼叫某個 MCP 工具去做這些事——而這種組合是本文最強大的模式,下面會講。
- 它不是一次微調。Skill 改變的是模型的條件上下文,不是它的權重。
- 它不是一個喬裝打扮的系統提示片段。Skill 有自己的發現模型、自己的啟動生命週期、自己的打包程式碼。一個 500 行的
system:區塊不是同一回事,即便它覆蓋了相似的領域。
機制:為什麼兩者都存在
到這裡,上個月那篇文章就成了承重的那一篇。
在《總是說「是」的魔鏡》一文裡,論點是 LLM 是一個下一詞預測器:它計算 ——給定前文,下一個 token 的機率。諂媚性從這個式子裡以一個退化均衡的形式掉出來:模型透過 RLHF 被獎勵去贊同使用者的先驗。那篇文章的結尾是:
“如果你想讓它告訴你真相,就給它一條不經過你就能找到真相的路。”
這句話是理解為什麼 MCP 和 Skills 作為兩套獨立標準存在的關鍵。
MCP 是一條把真相錨定在 活的外部資料 上的路。 當模型呼叫 sql_query 而資料庫返回了一行計數,這個行計數就處在模型的條件上下文裡。使用者的先驗還在那裡,但工具輸出也在那裡,而工具輸出不以使用者的信念為條件。下一詞的分布,以工具結果為條件,向工具的答案方向偏移,這種偏移是使用者的先驗不容易覆蓋掉的。模型仍然可能誤讀工具——但它不能假裝工具沒說它說的東西。真相從一條不經過使用者的線纜上傳回來。
Skills 是一條把真相錨定在 編碼化的流程性知識 上的路。 當智能體啟動一個 Skill,Skill 的指令進入條件上下文。使用者的先驗還在那裡,但現在有了一個競爭的先驗——Skill 描述的流程——它是由那些已經把這件事做過很多次、並把正確方法蒸餾成配方的人寫下的。下一詞的分布,以 Skill 正文為條件,向 Skill 的流程方向偏移,這種偏移是一個沒見過該流程的使用者不容易覆蓋掉的。真相來自對話開始之前就已經寫好的一本手冊。
兩套標準以各自的方式都是 反諂媚性基礎設施。它們是同一種防禦的不同層次:
| 真相錨定機制 | 層次 | 方向 | 延遲 |
|---|---|---|---|
| MCP 工具呼叫 | 活的外部系統 | 線纜 → 上下文 | 幾百毫秒 |
| MCP 資源讀取 | 唯讀外部資料 | 線纜 → 上下文 | 幾十毫秒 |
| MCP 提示 | 可複用模板 | 磁碟 → 上下文 | 幾乎為零 |
| Skill 啟動 | 編碼化流程 | 磁碟 → 上下文 | 幾乎為零 |
| Skill 腳本執行 | 確定性的程式碼即工具 | 磁碟 → 處理程序 → 上下文 | 幾十毫秒 |
| Skill 腳本 + 設定 | 帶參數的確定性程式碼即工具 | 環境/檔案/參數 → 處理程序 → 上下文 | 幾十毫秒 |
五月那篇文章的論點是,沒有任何單一的提示模式能修好諂媚性先驗。六月這篇文章從另一面給同一個論點:「真相錨定」也沒有任何單一層次能修好那個先驗。現代智能體棧——既有工作檯又有筆記本的工匠——是 MCP 和 Skills 的組合,加上五月那篇文章裡的推理時模式(自洽、辯論、先批判後修訂),加上系統級的緩解(Constitutional AI、結構化輸出),加上一個校準過的人在回路。
模式背後的模式
每一項反諂媚性干預在底層做的都是同一件事:給模型的輸入上下文加一個 顯式先驗,讓它和使用者隱含的信念競爭。MCP 工具 加一個 工具輸出 先驗。MCP 資源 加一個 檢索 先驗。Skill 啟動 加一個 流程 先驗。Skill 腳本 加一個 確定性計算 先驗。Constitutional AI 加一個 原則 先驗。使用者信念先驗是隱含的,但它在那裡。這些干預都是用模型自己的語言告訴它:「不要以那個為條件;以這個為條件。」
並排看:同一個任務,兩種實作
感受差異最乾淨的方式是把同一個玩具任務用兩種方式都搭一遍。我們要搭一個 當前時間 工具——世界上最沒用的特性——一次做成 MCP 伺服端,一次做成 Skill。玩具很 trivial;實作各自的形狀 才是重點。
Side A —— MCP 伺服端
一個 Python 檔案,用官方的 mcp SDK 暴露一個 current_time 工具。模型在啟動時透過 tools/list 發現這個工具。當使用者問「現在幾點了?」,模型透過 tools/call 呼叫這個工具。伺服端跑 datetime.now() 然後返回一個類型化結果。
# time_server.py — 執行方式:python time_server.py
from datetime import datetime
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("time-server")
@mcp.tool()
def current_time(timezone: str = "UTC") -> str:
"""Return the current time in the given IANA timezone.
Use this whenever the user asks for the current time or date.
"""
return datetime.now().isoformat()
if __name__ == "__main__":
mcp.run()
包括 docstring 在內共 33 行。要使用它,把它註冊到你的 MCP Host(Claude Code、Cursor、VS Code,任何講 MCP 的都行),Host 把它當作子處理程序啟動。這個工具立刻可用,類型化 schema 驗證、按名字發現。線纜格式:JSON-RPC over stdio。
Side B —— Skill 資料夾
一個目錄,裡面有 SKILL.md 和一個打包的腳本。當使用者任務匹配 description 時,智能體讀 markdown;當流程需要時,執行腳本。沒有伺服端。沒有 JSON-RPC。流程性知識——包括「用 datetime.now().isoformat()」這個訣竅——和確定性執行一起躺在磁碟上。
time-skill/
├── SKILL.md
└── scripts/
└── now_utc.py
---
name: current-time
description: 返回當前的日期與時間。當使用者問「現在幾點了?」「今天是哪一天?」或要求時間戳時使用。不要猜——呼叫工具或執行腳本。
---
# 當前時間
當使用者問當前時間或日期時,**不要猜**。可以這樣做:
1. **首選:** 呼叫一個 `current_time` MCP 工具(如果有的話)。該工具有類型化,
返回 ISO-8601 字串。
2. **回退(打包腳本):** 透過 Bash 工具從 Skill 資料夾執行 `scripts/now_utc.py`。
該腳本不讀環境、不讀 stdin、不存在時鐘漂移——它在同一秒內,
在每一台機器上確定性地返回當前的 UTC 時間(ISO-8601 格式)。
```python
# scripts/now_utc.py — 與 Skill 資料夾一起打包
from datetime import datetime, timezone
if __name__ == "__main__":
print(datetime.now(timezone.utc).isoformat())
```
3. **最後手段:** 如果工具和 bash 都沒有,告訴使用者你拿不到時鐘,
請他們確認時間。
永遠不要編造時間戳。如果你不確定使用者說的是本地時間還是 UTC,
問一下。這個 skill 的預設是 UTC。
總共 55 行,其中一半在打包的腳本裡。markdown 把智能體偏向正確的流程;腳本在 MCP 工具不可用時提供確定性執行。
改了什麼——以及為什麼
跟 MCP 版本同樣的回退形狀,但有一個關鍵區別:現在的回退是和流程一起被版本化的程式碼,不是塞進 SKILL.md 裡的一行程式碼。MCP 伺服端是工作檯;Skill 是筆記本——按需翻開,有時還附帶一把鑿子。
同一個任務。兩種形狀。不同的延遲,不同的失敗模式,不同的擴展故事。MCP 伺服端能同時服務多個智能體,且在 Host 重啟後存活。Skill 是 per-host、per-folder 的,但它跟著程式碼庫走,能扛住任何工具鏈的變動。
實作本身並不告訴你該用哪個
你絕對可以把同一個 正式環境 特性用任意一種方式實作,權衡各有不同。「時間」這個例子是玩具;在真實系統裡,選哪個並不顯然,取決於:
- 這個特性需要 跨多個 Host 存在嗎? → MCP。
- 這個特性需要 活在儲存庫裡,跟程式碼一起版本化 嗎? → Skills。
- 這個特性需要 執行不受信任的程式碼 嗎? → 把 MCP 伺服端放在沙箱裡。
- 這個特性需要 教會智能體何時去做 X 嗎? → Skills。
下一節就是決定這件事的判定流程。
自己動手:更大規模的並排實作
兩套標準都獎勵親手實驗。留出二十分鐘。
同時搭建一個「問候」工具的兩個版本
第 1 部分 —— MCP 版本(約 5 分鐘):
- 安裝官方 Python SDK:
pip install mcp - 把上面的
time_server.py程式碼片段儲存為time_server.py。 - 跑一次:
python time_server.py。它會在 stdio 上閒置。按Ctrl+C。 - 註冊到 Claude Code:把路徑加到
.mcp.json。 - 問 Claude Code 「現在幾點了?」,觀察
tools/call來回。
第 2 部分 —— Skill 版本(約 5 分鐘):
- 建立
~/.claude/skills/current-time/(或你用戶端對應的位置)。 - 把上面的
SKILL.md放進那個目錄。 - 重啟用戶端,讓 skill 的中繼資料在啟動時被載入。
- 問 「現在幾點了?」 —— 智能體啟動 Skill,執行回退那一行程式碼,返回 ISO-8601 時間戳。
- 驗證:編輯
SKILL.md把預設時區改成Asia/Tokyo。再問一次。下一次答案反映新的流程,而且不需要重啟任何伺服端,因為 Skills 是按需從磁碟拉的。
第 3 部分 —— 組合 版本(約 10 分鐘):
- 保持 MCP 伺服端在跑。
- 編輯 Skill 的正文,把回退路徑刪掉:它現在應該 只 指示智能體使用 MCP 工具。
- 再問一次。Skill 的流程說「用 MCP 工具」;MCP 工具可用;智能體呼叫它。
- 現在停掉 MCP 伺服端(Ctrl+C)。再問一次。Skill 的流程降級到 bash 那一行。
- 這就是組合。這就是現代智能體棧。
對比這三次的體感。MCP 版本 快且類型化,但感覺 遠——模型和工作檯之間一根線。Skill 版本 柔和、流程化,但在該硬的時候是硬的——指令在智能體腦子裡,當流程需要觸及真實世界時,磁碟上有一段確定性的腳本。Skill 的回退不是一個想法;它是程式碼,程式碼每次都長一樣。組合版本是 兩者同時——Skill 知道做什麼,MCP 知道怎麼快快地做,而 Skill 的回退讓系統在 MCP 離線時優雅降級。
那個三位一體——類型化工具 + 流程性知識 + 優雅降級——是真正能上正式環境的智能體團隊在做的事。其他都是裝飾。
決策矩陣
這是流程。五條規則,每條一句話,對應一類特定形狀的問題。
決策矩陣——五條規則,每條 ≤ 2 句話
規則 1 —— 當智能體需要 活的 外部資料或副作用時,伸手拿 MCP。 如果答案必須反映世界的當前狀態(資料庫的一行、磁碟上的一個檔案、一個 GitHub PR、一次 API 回應、使用者的日曆),MCP 就是正確形狀。工作檯是用來對外部世界採取行動的。
規則 2 —— 當智能體需要 編碼化的專業能力(基礎模型缺乏的那種)時,伸手拿 Skills。 如果答案需要一道流程、一份內部風格、一個領域特定的配方、或一份被人類打磨過的清單,Skills 是正確形狀。筆記本是用來教模型怎麼工作的。
規則 3 —— 當多步驟流程要呼叫外部工具時,伸手拿 兩者。
如果一道流程不接觸活的系統就完不成——透過讀資料庫填表、呼叫 gh 發程式碼審查、透過查詢分析 API 生成報表——就把兩者組合起來。Skill 協調流程;MCP 工具執行步驟。
規則 4 —— 當智能體需要唯讀上下文資料時,伸手拿 MCP resource(不是 tool)。
如果資料不需要被操作,只需要被讀入模型的條件上下文,把它暴露成 resource。resource 沒有副作用,概念上是「抓檔案」,在模型只需要位元組內容時省掉 tool-call 的開銷。
規則 5 —— 當一個普通系統提示或微調就夠用時,伸手拿 都不。 如果問題是 風格——更簡潔一些、用英式拼寫、絕不使用被動語態——把它寫在系統提示裡。如果問題是模型已經掌握的 知識——一般常識、常見的程式設計模式——別為 Skill 付費。把這兩套標準留給那些「確實能值回票價」的場景。
規則 6 —— 當流程需要確定性步驟時,伸手拿 Skill 腳本。
如果一道流程裡有一步的 正確答案 在每次執行時必須一樣——一次雜湊、一次正則匹配、一次 SQL 計數、一次 JSON-schema 驗證、一次內容指紋——把它打成 scripts/ 下的打包腳本,讓 SKILL.md 告訴智能體何時呼叫它。同樣的輸入,同樣的輸出,位元組級一致。模型的諂媚性先驗無法重寫一個確定性的返回值。每當你意識到自己在 markdown 裡描述了一步本可以寫成純函式的操作,就用這條。
規則 7 —— 當動作需要 受限憑證 或 人工確認 時,伸手拿 MCP。
PII 讀取、正式環境寫入、金額操作、任何破壞性的事——MCP 的認證邊界和 elicitation 通道是正確形狀。一個 scope 是 repo:read 的 token 不能 push;一個在 merge_pr 之前呼叫 elicitation/create 的工具在沒有使用者的 confirm: true 時不能合併。一個從 os.environ["PROD_API_KEY"] 讀密鑰的 Skill 腳本繼承了宿主的爆炸半徑;沒有 scope、沒有過期、沒有稽核軌跡、沒有強制檢查。讓 MCP 伺服端做認證;讓 Skill 協調;讓 MCP 去碰那個觸敏的系統。
規則 8 —— Skill 腳本的安全邊界就是宿主處理程序。 把帶憑證的執行留給 MCP。把 Skill 留給流程;把 MCP 留給認證和檢查的那部分。兩者組合;信任等級不組合。如果你能把這個動作描述成「模型決定做這件危險的事」,它就該在 MCP 後面。如果你能把它描述成「模型知道正確的流程」,它就該在 Skill 裡。危險的事需要安全邊界。流程需要被編碼。把標準挑對。
規則 0(承重的那條)—— 拿不定主意時,聽模型的先驗。 兩套標準都聽命於宿主的推理。一個沒被啟動的 Skill 就是一個沒發生的 Skill。一個沒被呼叫的 MCP 工具就是一個沒發生的工具。沒有任何系統能覆蓋模型 使用它 的決定。如果「要不要啟動這個 skill」「要不要呼叫這個工具」的先驗錯了,那是系統提示的問題,不是 Skill 或 MCP 的問題。
讀兩遍。存到你團隊的 wiki 裡。你接下來要交付的五個智能體特性,會各自乾淨地落到其中一條規則裡。
組合模式:Skill 編排的 MCP
這是我一直寫到這裡的部分。在現代智能體棧裡你能做的最強大的事,是把這兩套標準組合起來,讓 一個 Skill 編排一個用 MCP 工具作為步驟的流程。
一個具體的例子。想像一個團隊同時發佈一個 code-review Skill 和一個 github MCP 伺服端。Skill 是一個目錄,裡面的 SKILL.md 描述團隊的內部審查流程——查什麼、按什麼順序、嚴重程度怎麼分。MCP 伺服端把 list_prs、get_pr_diff、post_review_comment、merge_pr 暴露為工具。
注意 Skill 沒有 做什麼。它沒有調 gh。它沒有解析 diff。它沒有發審查。所有這些都是 MCP 的活。Skill 做的是 告訴智能體何時與為何——流程、內部風格、嚴重度量表、「不要合併」這道閘。智能體按流程以正確的順序呼叫正確的 MCP 工具。
也注意 Skill 和智能體都不能覆蓋什麼:merge_pr 工具要等使用者回答 MCP 伺服端的 elicitation/create 請求之後才會執行。Skill 的流程說「合併前不要跳過確認」。模型可能被說服放棄那句話。MCP 伺服端不是一句話。它是一個線纜通訊協定步驟。流程引導 意圖;通訊協定強制 檢查。這就是「指示」與「檢查」之間的承重差別,也是為什麼兩套標準是組合而不是替代。
這就是模式。Skills 把流程放到磁碟上。MCP 把工作檯掛到線纜上。 任何一個單獨都不夠。一個只攜帶 markdown 的 Skill 能教會智能體怎麼工作;一個同時攜帶 scripts/ 下腳本的 Skill 也能不繞線纜地觸及世界。一個 MCP 伺服端能給智能體跨 Host 共享的類型化活的工具。流程被編碼化、可分享;執行要不是磁碟上確定性的(Skill 腳本),要不是線上類型化的(MCP 工具)。當任何一邊缺席,系統作為整體仍能優雅降級。
看到一個新智能體特性時,先畫那四張圖
寫任何程式碼之前,畫這四個框:使用者、智能體、Skill、MCP。問四個問題:
- 使用者 說什麼?
- 智能體 決定什麼?
- Skill 教什麼?
- MCP 伺服端 執行什麼?
如果你的草圖裡有空格,那這個特性還沒設計。如果草圖把所有東西都放到 Skill 裡,那流程和執行沒有分開。如果草圖把所有東西都放到 MCP 伺服端裡,那有一道流程正住在該住在筆記本裡的程式碼裡。四框草圖是把兩層不互相吞掉的最簡單辦法。
更深一層
這一節是任何決策矩陣都替代不了的。
MCP 和 Skills 不是對手,因為它們不在同一個軸上。它們不是解決同一問題的兩種方式;它們是同一套智能體棧的兩層,就像 HTTP 和 DNS 是網際網路的兩層。HTTP 原則上可以做自己的名字解析。DNS 原則上可以攜帶負載。它們都不做,因為每一層都有自己的形狀、自己的失敗模式、自己的生命週期。兩層組合出一個任何單獨一層都搭不出來的系統。
智能體棧有同樣的結構。模型層預測下一個 token。Skill 層把預測條件化在編碼化流程上。MCP 層把預測條件化在活的工具輸出上。使用者層把一切條件化在使用者的問題上。每一層都有自己的形狀、自己的延遲、自己的失敗模式。智能體是把它們整合在一起的那個東西。
這也是為什麼「我該用哪個?」是錯的問題。對的問題是「我的智能體需要的擴展,形狀是什麼?」如果擴展是 可執行的、觸及外部世界的基礎設施,用 MCP。如果擴展是 教智能體怎麼工作的編碼化流程,用 Skills。如果擴展是 一道流程同時要觸及外部世界,兩個都用。問題不是 哪個。問題是 什麼形狀。
最後——把上一篇文章那一節閉環——這兩種形狀都是反諂媚性基礎設施。MCP 反諂媚,是因為工具的答案處在條件上下文裡,一個誘導性提問無法說服它假裝沒說。Skills 反諂媚,是因為流程處在條件上下文裡,把下一詞的分布偏向「以前這麼做能成」的方向。沒有一種形狀能消除模型贊同使用者的傾向。兩種形狀都 給它一個不去贊同的理由。這才是關鍵。
一幀看完的決策手冊
給團隊白板用的。兩個軸、四個象限、一層覆蓋。把你的任務標上去;讀象限;檢查覆蓋。決定是幾何的,不是流程的。
敏感性 & HITL 覆蓋層。 如果你的任務落在象限 1 或象限 4 並且 動作是敏感的(PII、正式環境、金額),或者在副作用之前需要人工確認,決定就升級為帶 受限憑證 和 elicitation/create 的 MCP。Skill 說「這一步可以跳過檢查」;MCP 伺服端說「這一步從這裡根本到不了」。Skill 在文件裡寫明哪些操作安全到可以預先批准是一句話;一個 scope 是 repo:read 的受限 token 不能 push。
兩個軸,一層覆蓋。原本花 250 字線性描述的同一個形狀校驗,現在白板上就一幀。在團隊討論時用這張圖;當答案是敏感動作時翻到上面的 認證與人在回路 小節;當流程需要編排時翻到 組合模式 這一節。
推薦資料
兩套標準都是活躍的、仍在演進的、文件完善的。下面是我在設計一個智能體特性時一直打開的幾個來源:
modelcontextprotocol.io——MCP 的官方規範、架構概覽、SDK 清單。從 Architecture 這一頁開始;JSON-RPC 那段走讀是關於線纜格式的最乾淨的入門。agentskills.io——Skills 的跨平台開放標準,含完整的 SKILL.md 格式規範和採用用戶端目錄(寫作本文時約 40 個)。
工坊自測:你更缺哪一邊?
失敗模式是鏡像的。有的團隊過度依賴工具,發佈的智能體在工作檯上鎚來鎚去,卻不知道自己在建什麼。有的團隊過度依賴流程,發佈的智能體寫出漂亮的回答卻連不到資料庫。有的團隊完全忘了安全層。一次六題的自測。
打開這 6 題自測
打開這 6 題自測(不依賴 JavaScript:自己數勾,然後看下面的解讀)
解讀(不依賴 JavaScript——數你的勾):
- 0–2 分: 平衡型。你在合適的時候用上了兩層,並且把敏感操作放到安全邊界後面。繼續保持。
- 3–4 分: 偏一邊,或者漏掉了安全層。「有工具無流程」失敗模式(Q1、Q3)說明你過度依賴 MCP;「有流程無工具」失敗模式(Q2、Q4)說明你過度依賴 Skills。Q6 打了勾,說明安全層整個漏掉了——你的「先問一下」活在模型的好意裡,不活在通訊協定裡。
- 5–6 分: 單臂工匠。你要不是漏掉了另一層,要不然在沒有安全邊界的情況下裸跑。最快的修法:拿你下一個智能體特性,強迫自己同時搭兩邊——一個 Skill 編排至少一個 MCP 工具,並在任何破壞性步驟之前使用
elicitation,端到端。
結語
工匠從來不是瓶頸。光有工作檯不夠。光有筆記本也不夠。2025 年浮出、在 2026 年穩定下來的那個有意思的設計,是工匠在工作檯前坐著、筆記本翻開在他手邊、執行他沒即興發揮過的流程、讀他沒編造過的資料。MCP 是工作檯。Skills 是筆記本——而筆記本現在也透過 scripts/ 自帶手工具了。工匠伸手去拿離他最近的那一件:一條通往工作檯的線,或者從筆記本裡翻出來的一把鑿子。智能體就是工匠。工作的形狀決定你伸手去拿哪一套標準,以及怎麼組合它們。模型仍然一如既往是一個下一詞預測器——但現在它是一個被給了兩種「不經過使用者就能找到真相」的方式的下一詞預測器。這就是全部訣竅。
試一下工坊自測。然後帶著兩種形狀的視角去搭下一個智能體特性,看看對話會多麼乾淨地坍縮到「這一層歸誰?」
留言
請接受「功能性」Cookie 類別以查看和發表留言。
留言載入失敗。您可以重試,或前往 GitHub 查看討論。
在 GitHub 上查看