上一篇文章講了 Agent Skills 是什麼、怎麼接到 create_agent 跟 deepagents 身上,但沒細講那份 SKILL.md 到底該怎麼寫。這篇就專門拆解它的資料結構,規則哪裡來的呢——來自 Agent Skills specification,而 deepagents 的 SkillsMiddleware 就是照這份規格在解析每一份 SKILL.md。
資料夾結構:一個 skill 就是一個資料夾
skills/
└── weekly-report/
├── SKILL.md # 必要:YAML frontmatter + Markdown 說明
├── template.md # 選用:報告範本
└── generate_report.py # 選用:輔助腳本
規則很單純:資料夾名稱就是這個 skill 的識別碼,SKILL.md 放在資料夾最上層,其他任何檔案(範本、腳本、參考文件)都算「輔助檔案」,agent 需要的時候會自己用絕對路徑去讀取或執行。
SKILL.md 的 YAML frontmatter
檔案最上面用一對 --- 包起來的區塊是 YAML frontmatter,底下才是給 agent 看的 Markdown 說明:
---
name: weekly-report
description: 產生每週工作進度報告,彙整本週完成的任務並套用固定格式
---
# Weekly Report Skill
## 使用時機
- 使用者要求「幫我寫週報」或「整理這週的工作進度」
## 步驟
1. 讀取 `template.md` 了解報告要套用的格式
2. 詢問使用者這週完成的任務清單(如果對話裡已經有,就不用再問)
3. 依照範本填入內容,標題用「YYYY-MM-DD 週報」
frontmatter 裡每個欄位的規則整理成表:
| 欄位 | 必填 | 規則 |
|---|---|---|
name | ✅ | 1~64 字元;只能是小寫字母、數字、連字號 -;不能開頭或結尾是 -,不能有連續的 --;必須跟資料夾名稱一模一樣 |
description | ✅ | 1~1024 字元;要同時說明「做什麼」跟「什麼時候該用」,超過長度只會保留前面部分,後面直接被砍掉 |
license | 選填 | 授權名稱或授權檔案的參照,單純字串 |
compatibility | 選填 | 最多 500 字元;寫清楚這個 skill 需要什麼環境,例如需要的套件、平台 |
metadata | 選填 | 任意的 key-value 字典,給你自己或工具塞額外資訊用,agent 不會特別解讀 |
allowed-tools | 選填 | 建議這個 skill 該搭配哪些工具用,可以是用空白或逗號分隔的字串,也可以是 YAML 清單;官方標註這欄位還是實驗性質 |
name 為什麼要跟資料夾名稱一致?
因為 agent 認一個 skill,是先看到資料夾、再打開裡面的 SKILL.md 確認內容——如果 SKILL.md 裡寫的 name 跟資料夾名稱對不上,系統會當成一個警告訊號(這個 skill 的設定可能有問題,不會直接不給用,但會被記錄下來)。所以資料夾叫 weekly-report,frontmatter 裡就要老老實實填 name: weekly-report。
合法跟不合法的名字對照:
✅ weekly-report
✅ pdf-summarizer
✅ web-research-v2
❌ Weekly_Report # 大寫、底線都不行
❌ -weekly-report # 不能用 - 開頭
❌ weekly--report # 不能有連續的 --
description 是整個系統裡最關鍵的一行
回顧一下前一篇文章提過的「圖書館比喻」:agent 平常只看得到 name 跟 description,要不要去讀完整內容,全靠這一行 description 判斷。所以寫的時候盡量包含具體的關鍵字跟使用情境,而不是寫得太抽象:
# 太抽象,agent 很難判斷什麼時候該用
description: 處理報告相關的事情
# 具體寫出「做什麼」+「什麼情境會用到」
description: 產生每週工作進度報告,彙整本週完成的任務並套用固定格式,
適合使用者要求「寫週報」、「整理這週進度」時使用
完整範例:週報產生器 skill
把前面提到的三個檔案內容整份寫出來:
skills/weekly-report/SKILL.md
---
name: weekly-report
description: 產生每週工作進度報告,彙整本週完成的任務並套用固定格式,
適合使用者要求「寫週報」、「整理這週進度」時使用
license: MIT
compatibility: 需要 Python 3.10+,且已安裝 pandas
metadata:
category: 文件產生
owner: hou
allowed-tools: read_file write_file execute
---
# Weekly Report Skill
## 使用時機
- 使用者要求「幫我寫週報」、「整理這週的工作進度」
## 步驟
1. 用 `read_file` 讀取同目錄下的 `template.md`,了解報告要套用的格式。
2. 跟使用者確認這週完成的任務清單(如果對話裡已經提過,不用重複問)。
3. 依照範本填入內容,檔名存成 `report-YYYY-MM-DD.md`。
4. 如果使用者要求連同數據圖表一起產生,執行同目錄下的 `generate_report.py`,
並把任務清單以 JSON 格式透過標準輸入傳進去。
skills/weekly-report/template.md
# {{日期}} 週報
## 本週完成事項
- {{任務一}}
- {{任務二}}
## 下週預計事項
- {{預計事項}}
skills/weekly-report/generate_report.py
"""讀取標準輸入的任務清單 JSON,依照 template.md 產生當週報告檔案。"""
import json
import sys
from datetime import date
from pathlib import Path
SKILL_DIR = Path(__file__).parent
def main() -> None:
tasks = json.load(sys.stdin)
template = (SKILL_DIR / "template.md").read_text(encoding="utf-8")
today = date.today().isoformat()
body = template.replace("{{日期}}", today)
body = body.replace(
"- {{任務一}}\n- {{任務二}}",
"\n".join(f"- {task}" for task in tasks["completed"]),
)
body = body.replace("- {{預計事項}}", "\n".join(f"- {task}" for task in tasks["next"]))
out_path = Path(f"report-{today}.md")
out_path.write_text(body, encoding="utf-8")
print(f"已產生 {out_path}")
if __name__ == "__main__":
main()
這份範例把前面表格裡幾乎每個欄位都用上了:license、compatibility、metadata、allowed-tools 都是選填,但填了之後,agent(或維護 skill 的人)能更清楚這個 skill 的使用邊界——例如 compatibility 講明白需要 pandas,allowed-tools 提示這個 skill 主要會用到讀檔、寫檔、跟執行程式碼這三種工具。
幾個容易寫錯、但系統不會直接報錯的地方
| 錯誤 | 系統的反應 |
|---|---|
SKILL.md 沒有 name 或 description | 整份 skill 被跳過,不會出現在 agent 看得到的清單裡,但不會讓整個程式壞掉 |
YAML frontmatter 格式寫錯(例如漏了結尾的 ---) | 一樣直接跳過這份 skill,只在系統日誌留下警告 |
name 跟資料夾名稱對不上 | 仍然會載入,但會記一筆警告,提醒你不符合規格,建議修正 |
description 超過 1024 字元 | 不會報錯,但超過的部分會被砍掉,agent 完全看不到後半段內容 |
這些情況都是「悄悄跳過或截斷」,不會讓你的程式直接炸掉——好處是不會因為一份 skill 寫錯就拖垮整個 agent,壞處是你很容易以為 skill 已經生效,但其實它根本沒被載入。寫完 SKILL.md 後,養成習慣去檢查系統日誌裡有沒有 skill 載入警告,會比單純祈禱它有生效可靠得多。
一句話總結
SKILL.md 就是那本 SOP 手冊的「封面加目錄」:name 是書名(要跟放書的資料夾同名)、description 是封底那句話(agent 靠這句話決定要不要翻開來看)、其餘的 license、compatibility、metadata、allowed-tools 都是選填的補充資訊——真正的操作細節,則寫在 frontmatter 下面的 Markdown 正文裡,以及同資料夾裡的範本、腳本這些輔助檔案中。