前面幾篇文章講過 SummarizationMiddleware(幫 AI 助理整理筆記)、FilesystemMiddleware(幫 AI 助理開一間房子讓它能動手做事)。這篇要講的 Agent Skills,解決的是另一個問題:怎麼讓 AI 助理擁有一套「專業 SOP」,但又不會把 system prompt 塞爆。
先用一個比喻:Skills 就是公司圖書館裡的 SOP 手冊
想像你剛到一間新公司上班,公司不會叫你把所有 SOP 手冊整本背起來才能開始工作。正常的做法是:圖書館的書架上貼好每本手冊的書名跟一句話簡介——「客訴處理手冊:客戶生氣的時候照這個流程走」、「請假流程手冊:怎麼跟主管請假」。你平常不用管這些手冊寫了什麼,等到真的遇到客訴了,才走去書架把那本手冊抽出來翻開細讀。
Agent Skills 做的就是這件事:先讓 AI 助理知道「有哪些手冊、每本大概在幹嘛」,等到真的遇到對應的任務,才去把完整內容讀進來。這個「先看目錄、需要才展開細節」的做法,術語上叫 progressive disclosure(漸進式揭露)——不這樣做的話,把所有 SOP 全文都塞進 system prompt,不但塞爆,AI 助理也很難每次都精準記得該用哪一本。
一份 Skill 長什麼樣子
一個 skill 就是一個資料夾,裡面至少要有一個 SKILL.md:
skills/
└── greeting-skill/
└── SKILL.md
---
name: greeting-skill
description: 示範用的打招呼技能,教 agent 用特定風格跟使用者打招呼
---
# Greeting Skill
跟使用者打招呼時,請用「嗨,我是你的助理!」開頭。
最上面那段 YAML 就是「貼在書架上的書名跟簡介」:name 是手冊的名字,description 是一句話說明「這本手冊在幹嘛、什麼時候該翻它」。AI 助理平常只看得到這兩行,只有真的判斷用得上,才會去讀 SKILL.md 裡面完整的內容。
接下來用兩種方式,把這套「圖書館」接到 AI 助理身上,再來比較差在哪裡。
接法一:用 create_agent 自己動手組裝
langchain.agents 的 create_agent 是一張白紙——工具、middleware 都要你自己一個一個掛上去。Skills 的能力本身放在 deepagents 這個套件裡的 SkillsMiddleware,但它可以單獨拿出來,插進 create_agent:
import os
from deepagents.backends.filesystem import FilesystemBackend
from deepagents.middleware.filesystem import FilesystemMiddleware
from deepagents.middleware.skills import SkillsMiddleware
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
load_dotenv()
model = ChatOpenAI(
model=os.environ["MODEL_NAME"],
base_url=os.environ["MODEL_URL"],
api_key=os.environ["MODEL_KEY"],
)
# 圖書館蓋在本機的 ./skills 資料夾裡
backend = FilesystemBackend(root_dir="./skills")
agent = create_agent(
model,
tools=[],
system_prompt="你是一個友善的助理。",
middleware=[
# 負責「書架目錄」:把 name/description 唸給 AI 助理聽
SkillsMiddleware(backend=backend, sources=["/"]),
# 負責「把書架上的手冊實際抽出來翻開」:提供 read_file 等工具
FilesystemMiddleware(backend=backend),
],
)
result = agent.invoke({"messages": [{"role": "user", "content": "跟我打個招呼"}]})
print(result["messages"][-1].content)
這裡故意掛了兩個 middleware,不是筆誤。SkillsMiddleware 只做「把書架目錄唸出來」這件事,它自己不附帶任何讀檔工具;真的要讓 AI 助理把 greeting-skill/SKILL.md 打開來讀,必須另外靠 FilesystemMiddleware 提供的 read_file 工具。少裝這一個,AI 助理會停在「知道有這本手冊」,但沒有手可以把它從書架上抽下來。
接法二:用 deepagents 的 create_deep_agent,一個參數帶過
deepagents 提供的 create_deep_agent 是一個已經蓋好完整骨架的 agent 建構函式——檔案操作工具、execute 執行程式碼、task 呼叫 subagent 這些都內建好了。Skills 只是它其中一個參數:
import os
from deepagents import create_deep_agent
from deepagents.backends.filesystem import FilesystemBackend
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
load_dotenv()
model = ChatOpenAI(
model=os.environ["MODEL_NAME"],
base_url=os.environ["MODEL_URL"],
api_key=os.environ["MODEL_KEY"],
)
agent = create_deep_agent(
model=model,
system_prompt="你是一個友善的助理。",
backend=FilesystemBackend(root_dir="./skills"),
skills=["/"], # 只要指到圖書館的路徑
)
result = agent.invoke({"messages": [{"role": "user", "content": "跟我打個招呼"}]})
print(result["messages"][-1].content)
沒有另外掛 FilesystemMiddleware,是因為 create_deep_agent 本來就內建了 read_file、write_file、ls、glob、grep 這些檔案工具——只要傳了 skills=[...],SkillsMiddleware 跟能讀檔的工具就一起到位了,不用自己再補一次。
兩種接法差在哪裡
create_agent + SkillsMiddleware | create_deep_agent(skills=[...]) | |
|---|---|---|
讀檔工具(read_file 等) | 沒有內建,要自己另外掛 FilesystemMiddleware | 內建好了,skills 一填就能讀 |
| 除了 skills 以外的工具 | 完全空白,一個都沒有,要自己一個個加 | 內建 execute(執行程式碼)、task(呼叫 subagent)等一整套 |
| 摘要、prompt caching 這類機制 | 不會自動有,要自己掛對應 middleware | 自動組進骨架裡(視使用的模型而定) |
| 適合場景 | 想要精準控制「這個 agent 到底有什麼能力」,不想要用不到的東西也一起打包進來 | 想要一個「開箱即用、什麼都有」的完整 agent,不想從零組裝 |
用蓋房子來比喻的話:create_agent 給你的是一塊空地跟建材,圖書館(skills)、水電(工具)要你自己一樣一樣接上去;create_deep_agent 則是直接把圖書館蓋在一棟已經裝好水電的房子裡,你只要決定圖書館放哪個書架(skills 參數指到哪個路徑)就好。想要細粒度控制 agent 到底有哪些能力,選前者;想要快速拿到一個功能齊全的 agent,選後者。
常見的坑
只掛了 SkillsMiddleware,卻忘了配一個能讀檔的工具(create_agent 路線最容易犯這個錯)——結果就是 AI 助理在系統提示裡看得到「有 greeting-skill 這個技能,說明是打招呼用的」,但一旦真的需要它,呼叫 read_file 的時候才發現這個工具根本沒接上去,等於書架上只貼了書名,書永遠抽不出來。
一句話總結
Agent Skills 讓 AI 助理擁有一套「先看目錄、需要才展開細節」的 SOP 圖書館;create_agent 要你自己把書架(SkillsMiddleware)跟能把書抽出來的手(FilesystemMiddleware)分開裝上去,create_deep_agent 則是把整套圖書館連同手一起蓋好,skills 參數指個路徑就能用——選哪一種,取決於你是想要精準控制 agent 的每一份能力,還是想要一個開箱即用的完整骨架。