技能系統 Skills
職責
Skill 是比 Tool 更高階的能力單元:一段可複用的、帶 frontmatter 的流程(類似 agentskills.io 規範),按領域歸類在 skills/<category>/ 下。與工具 (tool) 的「靜態函式」不同,技能可被學習閉環建立、改寫、精煉,是 agent「越用越聰明」的載體。技能透過 skills_tool 暴露給主迴圈呼叫。
設計動機
為什麼 skill 要按 category 分目錄而不是平鋪?技能數量會隨使用增長(使用者加的、hub 同步的、學習循環生成的都在 ~/.hermes/skills/),平鋪後 skills_list 回傳的列表很快變成雜訊。category 讓 progressive disclosure 第一層只暴露「這有一組 research 技能」而不是 50 條具體技能名。每個 SKILL.md 是單檔案即定義(帶 frontmatter),搭配 skills/<category>/<skill>/references/ 的子檔案做按需載入,token 不會一次性爆。
bundle 是另一層封裝:把多個技能用一個 slash 命令載入,因為「後端開發」這種場景要同時掛上 code review、TDD、PR 三個技能,逐個 /skill 太碎。
關鍵檔案
skills/ 目錄— 按領域分組(apple、computer-use、research、software-development、yuanbao…),每個技能長這樣:
skills/
├── my-skill/
│ ├── SKILL.md # 主指令(必需)
│ ├── references/ # 支撐文件
│ │ ├── api.md
│ │ └── examples.md
│ ├── templates/ # 輸出模板
│ └── assets/ # 附件(agentskills.io 標準)
└── category/
└── another-skill/
└── SKILL.mdSKILL.md 頂部是 YAML frontmatter(name/description/version/platforms/prerequisites…),下面是正文指令。name 限 64 字元、description 限 1024,這兩個欄位是 progressive disclosure 第一層 skills_list 唯一回傳的內容,保 token。
skills_tool— 把技能作為工具暴露給主迴圈。它註冊了兩個工具:
registry.register(
name="skills_list",
toolset="skills",
schema=SKILLS_LIST_SCHEMA,
handler=lambda args, **kw: skills_list(
category=args.get("category"), task_id=kw.get("task_id")
),
check_fn=check_skills_requirements,
emoji="📚",
)
registry.register(
name="skill_view",
toolset="skills",
schema=SKILL_VIEW_SCHEMA,
handler=_skill_view_with_bump,
check_fn=check_skills_requirements,
emoji="📚",
)skills_list 只回傳 name + description + category(第一層 progressive disclosure);skill_view 載入完整 SKILL.md(第二層);reference/template 等支撐檔案按需 skill_view(name, file_path="references/api.md") 拉(第三層)。_skill_view_with_bump 在成功後呼叫 bump_view / bump_use 更新使用計數,curator 的 stale 計時器據此清理沒人用的技能。
skill_manager_tool— 技能管理(列表/查看/啟用)skills_hub— 技能中心同步(agentskills.io 相容)skills_sync— 本地與遠端技能同步skill_bundles— 技能束打包,注入到提示 (prompt)。bundle 是~/.hermes/skill-bundles/*.yaml裡的小檔案:
name: backend-dev
description: Backend feature work — code review, testing, PR workflow.
skills:
- github-code-review
- test-driven-development
- github-pr-workflow
instruction: |
Optional extra guidance to inject above the skill bodies./<bundle-name> 呼叫 build_bundle_invocation_message 把所有成員技能的 SKILL.md 拼進一則 user message。bundle 與 skill 同名時 bundle 優先——使用者顯式起了同名就是想覆蓋那條碰巧撞名的技能。缺失的成員技能不報錯,只在結果裡附「哪些被跳過」的提示。
skill_preprocessing— 技能內容預處理。負責替換${HERMES_SKILL_DIR}/${HERMES_SESSION_ID}模板變數,以及執行!date +%Y-%m-%d`` 這種內嵌 shell snippet。內嵌 shell 失敗回傳[inline-shell error: ...]標記而不是拋例外,一個壞 snippet 不會拖垮整條技能訊息。技能節點與邊:125-193—build_skill_nodes/build_edges,把技能組織成學習圖
資料流
- 啟動期
skill_preprocessing(agent/skill_preprocessing.py)掃描skills/<category>/,讀 frontmatter。 skill_bundles(agent/skill_bundles.py)把相關技能打包,經build_turn_context:268注入本輪提示。- provider 決定呼叫某技能 → tool_call
skills→tools/skills_tool.py載入並執行該技能的流程。 - 執行結果回填主迴圈。
- 學習閉環在背景審視本輪,可能:
- 建立新技能(
learning_mutations) - 把新技能登記進學習圖
agent/learning_graph.py:125 - 透過
tools/skills_sync.py同步到技能中心
- 建立新技能(
邊界與失敗
- 路徑穿越:skill name 會被拼接到
~/.hermes/skills/上找檔案,_skill_lookup_path_error拒絕..段、絕對路徑、Windows 磁碟代號。否則name="../outside"能讀到 skills 目錄之外的檔案。 - prompt 注入:SKILL.md 內容裡有「ignore previous instructions」「system prompt:」「]]>」等模式會被
_INJECTION_PATTERNS標記。第三方技能(從 hub 同步的)是不可信內容,主迴圈載入時要 sanitize。 - bundle 缺成員:
build_bundle_invocation_message對缺失/停用的成員技能只記到missing/disabled列表裡,不報錯——bundle 是寬容載入,少幾個不影響其餘技能。 - 快取陳舊:掃描
skills/是 O(#dirs) stat,簽名包含目錄 mtime + disabled 集合 + 平台。但單檔案SKILL.md的就地編輯只 bump 自身 mtime,目錄簽名看不到,所以額外加了 30s TTL 兜底。
小結
技能 = 可演化的高階流程,frontmatter 描述、檔案即定義、學習閉環可改寫。它與工具的關係:工具是技能流程裡呼叫的原子,技能是「怎麼組合工具完成一類任務」的知識。技能的演化機制見學習閉環。