Skills:程序性记忆
从 Voyager 在 Minecraft 里建立 skill library, 到 Hermes 用 SKILL.md 让 Agent 越用越聪明。这一章讲"程序性记忆"是什么、为什么这么设计。
从这一章开始我们进入 Hermes 最有意思的部分——学习闭环。一个好 Agent 不应该每次 都从零开始,它应该把过去的成功解法存下来下次用。 这就是"程序性记忆"——区别于"情景记忆"("我们上次聊过什么"),程序性记忆是 "做某类事情的方法"。
8.1认知科学的两种记忆
这个区分来自认知科学。人脑有两种长期记忆:
| 陈述性记忆 (Declarative) | 程序性记忆 (Procedural) | |
|---|---|---|
| 例子 | "巴黎是法国首都" | "骑自行车的肌肉记忆" |
| 能用语言描述? | 是 | 很难——"做了就会" |
| 提取方式 | 主动回忆 | 遇到情境自动启动 |
| 形成方式 | 看到 + 编码 | 重复练习 |
对 LLM Agent 来说,这两种都很重要,但实现机制完全不同:
- 陈述性记忆 → Memory 系统(向量库、SQLite 全文索引)。下章讲。
- 程序性记忆 → Skill 系统。本章讲。
8.2Voyager:让 Skill Library 成为研究范式
程序性记忆在 Agent 上的开山之作是 Voyager。我们之前提过一次,这里详细看:
NVIDIA 团队在 Minecraft 里跑 GPT-4 Agent。三个核心组件:
- Automatic Curriculum:根据当前进度提议下一个"刚好够难"的任务。
- Skill Library:每次完成新任务时,把解法(一段 JavaScript 代码) 存进 library。Library 里的代码索引向量化,下次任务来时按相似度检索。
- Iterative Prompting:执行报错时把错误信息送回 GPT-4 改代码,直到通过。
成绩:unique items 拿到 3.3 倍、移动距离 2.3 倍、tech tree milestone 解锁速度 15.3 倍。 更重要的是:在一个全新的 Minecraft 世界里,Voyager 能用学来的 skill解决新任务, 而对照组从零开始。
Voyager 教会业界的事
- Skill 是"可执行的",不是描述性 prompt。Voyager 的 skill 是真的 JavaScript 函数。
- Skill Library 是向量化检索的。任务描述 → 找最相似 skill → 直接调用或参考。
- 每个 skill 在使用中 iterative refine。错了不要扔,要改。
- Skill 抵抗 catastrophic forgetting:因为 skill 是 explicit storage,不是模型权重。
Hermes 的 Skill 系统继承了这些思想,但选择了一个更"人类友好"的表达:
8.3Hermes 的 Skill 是什么
Hermes 的 Skill 不是可执行代码,而是一份 Markdown 文档——
SKILL.md。它告诉 LLM "在什么情况下、按什么步骤、用什么工具"。
看一个真实的例子。skills/apple/apple-reminders/SKILL.md:
skills/apple/apple-reminders/SKILL.md
---
name: apple-reminders
description: "Apple Reminders via remindctl: add, list, complete."
version: 1.0.0
author: Hermes Agent
license: MIT
platforms: [macos]
metadata:
hermes:
tags: [Reminders, tasks, todo, macOS, Apple]
prerequisites:
commands: [remindctl]
---
# Apple Reminders
Use `remindctl` to manage Apple Reminders directly from the terminal.
Tasks sync across all Apple devices via iCloud.
## Prerequisites
- **macOS** with Reminders.app
- Install: `brew install steipete/tap/remindctl`
- Grant Reminders permission when prompted
- Check: `remindctl status` / Request: `remindctl authorize`
## When to Use
- User mentions "reminder" or "Reminders app"
- Creating personal to-dos with due dates that sync to iOS
- Managing Apple Reminders lists
- User wants tasks to appear on their iPhone/iPad
## When NOT to Use
- Scheduling agent alerts → use the cronjob tool instead
- Calendar events → use Apple Calendar or Google Calendar
- Project task management → use GitHub Issues, Notion, etc.
## Quick Reference
\`\`\`bash
remindctl # Today's reminders
remindctl today
remindctl tomorrow
remindctl week
remindctl overdue
remindctl all
remindctl 2026-01-04 # Specific date
remindctl add "Buy milk" --due tomorrow
remindctl done <id>
\`\`\`
解剖这份 skill:
| 区块 | 作用 |
|---|---|
| YAML frontmatter | 元数据:name、description、platforms 限制、依赖命令 |
| 简介段落 | 告诉模型这个 skill 大致干啥 |
| Prerequisites | 系统要求 / 安装指令 / 权限要求 |
| When to Use | 触发条件,这是最关键的一块 |
| When NOT to Use | 反触发条件,防误用 |
| Quick Reference | 具体命令例子 |
When to Use / When NOT to Use 是 Skill 的灵魂。LLM 看到这两块,就知道 在用户说"提醒我明天 9 点开会"时调用,在用户说"5 分钟后通知我"时不调用(那个应该走 cronjob)。
8.4Skill 被发现:scan_skill_commands
用户敲 /apple-reminders 启动 skill 之前,Hermes 必须先发现它。
扫描器在 agent/skill_commands.py:263-326:
agent/skill_commands.py:263-326 (节选)
def scan_skill_commands() -> Dict[str, Dict[str, Any]]:
"""Scan ~/.hermes/skills/ and return /command → skill info."""
global _skill_commands, _skill_commands_platform
_skill_commands_platform = _resolve_skill_commands_platform()
_skill_commands = {}
from tools.skills_tool import (
SKILLS_DIR, _parse_frontmatter,
skill_matches_platform, _get_disabled_skill_names,
)
from agent.skill_utils import (
get_external_skills_dirs, iter_skill_index_files,
)
disabled = _get_disabled_skill_names()
seen_names = set()
# 扫多个目录:本地 + 外部
dirs_to_scan = []
if SKILLS_DIR.exists():
dirs_to_scan.append(SKILLS_DIR)
dirs_to_scan.extend(get_external_skills_dirs())
for scan_dir in dirs_to_scan:
for skill_md in iter_skill_index_files(scan_dir, "SKILL.md"):
# 跳过 .git / .archive 等
if any(p in {'.git', '.archive'} for p in skill_md.parts):
continue
content = skill_md.read_text(encoding='utf-8')
frontmatter, body = _parse_frontmatter(content)
# 平台不匹配跳过(macOS-only skill 在 Linux 上不显示)
if not skill_matches_platform(frontmatter):
continue
name = frontmatter.get('name', skill_md.parent.name)
if name in seen_names or name in disabled:
continue
# 把 "Apple Reminders" → /apple-reminders
cmd_name = name.lower().replace(' ', '-').replace('_', '-')
cmd_name = _SKILL_INVALID_CHARS.sub('', cmd_name)
_skill_commands[f"/{cmd_name}"] = {
"name": name,
"description": frontmatter.get('description', ''),
"skill_md_path": str(skill_md),
"skill_dir": str(skill_md.parent),
}
seen_names.add(name)
return _skill_commands
设计要点:
- 多目录扫描:内置
skills/、用户~/.hermes/skills/、外部~/.hermes/external_skills/。 - 平台过滤:
platforms: [macos]的 skill 只在 macOS 上显示。 - 禁用列表:用户可以在 config 里
skills.disabled: [foo]显式禁用。 - 命名空间:名字 → kebab-case slash command。
"Apple Reminders"→/apple-reminders。 - 同名优先:先扫到的赢。本地覆盖外部,外部覆盖内置。
结果是个全局 dict _skill_commands,TUI 的 tab 补全直接读它。
8.5Skill 被加载:作为 User Message
这是 Hermes Skill 系统最聪明的设计。回顾第 5 章: system prompt 不能改,会破 cache。但我们想动态启用 skill 怎么办?
答案:把 skill 作为 user message 追加,不动 system prompt。
agent/skill_commands.py:160-183:
agent/skill_commands.py:160-183
def _build_skill_message(
loaded_skill, skill_dir, activation_note,
user_instruction="", runtime_note="", session_id=None,
) -> str:
"""Format a loaded skill into a user/system message payload."""
from tools.skills_tool import SKILLS_DIR
content = str(loaded_skill.get("content") or "")
# 模板变量替换({{config.foo}} 等)
skills_cfg = _load_skills_config()
if skills_cfg.get("template_vars", True):
content = _substitute_template_vars(content, skill_dir, session_id)
# Inline shell 展开 — 实验功能,默认关
if skills_cfg.get("inline_shell", False):
timeout = int(skills_cfg.get("inline_shell_timeout", 10) or 10)
content = _expand_inline_shell(content, skill_dir, timeout)
parts = [activation_note, "", content.strip()]
# 拼好后作为 user 消息追加
return "\n".join(parts)
activation_note 长这样:
[IMPORTANT: The user has invoked the "apple-reminders" skill, indicating they want
you to follow its instructions for the upcoming request. Read the skill content
below carefully and apply it to your next response.]
# Apple Reminders
Use `remindctl` to manage Apple Reminders directly from the terminal.
...
这条 user message 被追加到 messages 末尾:
messages = [
{"role": "system", "content": "...(stable + context + volatile)"},
{"role": "user", "content": "你好"},
{"role": "assistant", "content": "嗨..."},
{"role": "user", "content": "[Skill activated: apple-reminders]\n# Apple Reminders\n..."},
# ↑ 新加的 — 不动前面任何一条
]
cache 命中分析:
- System prompt 没改 → stable+context+volatile 缓存命中。
- 前面的 user/assistant 历史没改 → 前缀命中。
- 新追加这条 user message 是 cache miss,但它之前的内容全部命中。
对比:错误做法
如果你天真地用"system prompt 加 skill":
# BAD
def load_skill(skill_name):
agent.system_prompt += f"\n\n--- Skill: {skill_name} ---\n{SKILL_MD_CONTENT}"
# 立刻破 cache
下次 API call,前缀完全变了。整个 cache 报废。一个简单 turn 可能成本 ×10。 这就是为什么 Hermes 选择"以 user message 注入"——不要碰前缀。
8.6Skill 的高级特性
模板变量
SKILL.md 里可以写 {{config.something}},加载时被替换:
# Send Email via SMTP
Use SMTP server at {{config.smtp.host}}:{{config.smtp.port}}.
Auth user: {{config.smtp.user}}
变量来源是 config.yaml 的 skills.config.<key>。
Inline Shell 展开(实验功能)
SKILL.md 里的 $(command) 块在加载时被替换成 shell 输出:
# Project Context
Current branch: $(git branch --show-current)
Recent commits:
$(git log --oneline -5)
默认关(inline_shell: false),有安全顾虑。打开后注意:
这相当于让 SKILL.md 在加载时执行任意命令。只用于自己写的 skill。
Skill 附带 scripts
真正复杂的逻辑不该让 LLM 每次重新生成。把它存成脚本:
~/.hermes/skills/research/web_scrape/
├── SKILL.md # 决策手册
├── scripts/
│ ├── extract_main.py # 提取主内容的复杂 BeautifulSoup 逻辑
│ └── dedupe_urls.py
└── references/
└── selectors.json # 常见网站的 CSS 选择器
SKILL.md 里只说 "用 scripts/extract_main.py 提取主内容",LLM 会用 execute_code
或 terminal 工具执行这个脚本。Hermes 在 execute_code 工具的
working directory 里把 scripts/ 加入 PATH。
8.7Skill 的来源与生态
Hermes 的 skills 有三个来源:
① 内置 (skills/)
仓库自带的,~30 个分类:
skills/
├── apple/ apple-reminders, imessage
├── research/ web-deep-research, fact-check
├── devops/ ci-cd-status, k8s-debug
├── data-science/ pandas-eda, sklearn-baseline
├── software-development/ conventional-commit, code-review
├── mcp/ mcp-server-init
├── mlops/ mlflow-experiment, training-monitor
└── ...
② 用户自创 (~/.hermes/skills/)
你随时可以扔一个 SKILL.md 在这个目录下,Hermes 重启自动发现。
或者让 Agent 自己用 skill_manage(action="create") 工具创建——这是核心 self-improving 机制。
③ Skills Hub (远程仓库)
Hermes 维护一个公开 skill 索引在 agentskills.io。
用户用 hermes skills install <name> 装。
这个标准被多个 Agent 框架支持(Claude Desktop、Aider、Cursor)—— 写一份 SKILL.md 可以被所有兼容工具用上。这是"skill 互操作性"方向,Hermes 团队主推。
8.8Agent 自己创建 Skill
最有趣的部分:Agent 自己学新本事并把它存下来。
Hermes 提供 skill_manage 工具:
# LLM 决定创建一个新 skill
skill_manage(
action="create",
name="data-processing-pipeline",
skill_md="""---
name: data-processing-pipeline
description: 'Generic CSV-to-Parquet ETL with pandas/polars.'
version: 1.0.0
author: Hermes Agent
metadata:
hermes:
created_by: agent
tags: [data, etl, pandas]
---
# Data Processing Pipeline
When the user asks to clean a CSV and produce Parquet output:
## Steps
1. Use read_file to inspect the CSV schema.
2. Use execute_code to run polars (faster than pandas for > 1GB):
```python
import polars as pl
df = pl.read_csv(input_path, ignore_errors=True)
df.write_parquet(output_path, compression='zstd')
```
3. Verify with df.describe() ...
""",
)
触发条件由 LLM 自己判断——通常在 system prompt 里有这样一段建议:
After completing a non-trivial task that required figuring out a workflow, consider whether the pattern is general enough to be saved as a skill. Use skill_manage(action="create", ...) when you think future you would benefit from the recipe.
关键元数据:created_by: agent——标记这是 Agent 自创,
为下章要讲的 Curator 提供归属信息。Curator 只管理 agent-created skill。
8.9Hermes Skill vs Voyager Skill
| Voyager | Hermes | |
|---|---|---|
| Skill 是什么 | 可执行 JavaScript 函数 | Markdown 决策手册(可附脚本) |
| 检索方式 | 向量相似度 | 显式 /skill-name + LLM 自己 skill_view |
| iterative refine | 报错回灌 → LLM 改代码 | Curator 定期审查 + LLM skill_manage(action="patch") |
| 使用环境 | Minecraft(封闭世界) | 用户自己的电脑(开放世界) |
| 可读性 | 代码——只机器懂 | Markdown——人和机器都懂、能编辑 |
| 互操作性 | 专属 Voyager 实现 | 跨 Agent 框架(Claude/Cursor/Hermes) |
Hermes 这个设计的取舍:放弃 Voyager 的全自动化,换来用户可审计、可编辑、可分享。 对生产用户来说,"Agent 自己写了什么 skill"是必须可看的;不可读的代码会让人不敢信。
8.102025.12 — Skill 成为跨厂家开放标准
本章前面把 SKILL.md 当作"Hermes 内部约定"讲。2025 年 12 月一个事件改写了它的地位。
Anthropic 把 SKILL.md 格式作为开放标准发布并开源 reference 实现。
格式核心:YAML frontmatter + Markdown body + 可选 scripts/ 目录。
OpenAI 在两周内宣布 Codex CLI 和 ChatGPT 采用同一格式。
这是继 MCP 之后第二个跨厂家 Agent 标准。
标准化之后的生态形态
2026 年 5 月时,Agent Skills 生态已经稳定下来,主要节点:
| 角色 | 例子 | 定位 |
|---|---|---|
| 标准 owner | Anthropic anthropics/skills |
spec + reference skills(PDF / PPTX / DOCX 等) |
| 免费 marketplace | agentskills.io | Hermes 早期支持的、社区免费 skill 索引 |
| 商业 marketplace | agensi.io · skillsmp.com | 付费 skill;作者拿 80% 分成 |
| Host(支持加载) | Claude Desktop / Claude Code / Codex CLI / ChatGPT / Cursor / Zed / Hermes | 同一份 SKILL.md 在每个 host 都能用 |
| 分发协议 | Claude Plugin Marketplace(/plugin) |
三合一:MCP server + Skill + plugin |
Hermes 的 SKILL.md 是否兼容标准?
几乎完全兼容——因为 Hermes 的格式就是参考标准(之一)。 Hermes 维护者参与了 standard 早期讨论。差异点不到 5%:
- frontmatter 字段名:标准定
name, description, version, license, platforms—— Hermes 已用相同字段。 metadata.hermes.*是 Hermes 私有扩展,标准不要求;其他 host 看到就忽略。- 路径约定:标准建议
SKILL.md+scripts/+references/+templates/——Hermes 同样。 - 触发模型:标准强调"model-invoked"(让 LLM 自动决定何时用)。Hermes 同时支持 model-invoked 和 user-invoked (slash command)。
metadata.hermes.config 等)。
这样这份 SKILL.md 可以扔给 Claude Code 或 ChatGPT 直接用,不光在 Hermes 里跑。
Skill 互操作性是这个领域接下来最值钱的资产。
Agent Skills 与 MCP 的分工
同期 Anthropic 还推 MCP。两者经常被混淆,区别看一眼就清楚:
| MCP Server | Agent Skill | |
|---|---|---|
| 形态 | 外部进程(JSON-RPC) | SKILL.md + scripts 文件 |
| 提供 | Tools / Resources / Prompts | 决策手册 + 可选脚本 |
| 调用方式 | JSON-RPC tools/call |
把 SKILL.md 注入到 LLM context |
| 典型例子 | GitHub MCP(封装 GitHub API) | conventional-commit 风格手册 |
| 状态保持 | 进程持续运行,可有状态 | 无状态,文件只读 |
| 语言 | 任意(Python / TS / Rust / Go) | Markdown + 任意脚本语言 |
| 分发 | NPM / Pip / Binary | Git repo / marketplace tarball |
记忆口诀:MCP 是"接什么外部系统",Skill 是"怎么用这些系统"。 一个 Excel skill 配 Excel MCP server——前者教 LLM "什么时候要做透视表", 后者提供 "如何调 Excel API 真的去做"。
Voyager → 标准化:六年的路
把整条路线压成时间线:
timeline
title Procedural Memory 在 Agent 中的演进
section 概念期
2023.05 : Voyager : 可执行代码 + 自动课程
2023.10 : MemGPT : OS 类分层记忆
section 工程化
2024.06 : Hermes 原型 : SKILL.md 内部约定
2025.01 : agentskills.io : 第一个跨工具免费 marketplace
2025.06 : Anthropic Context Engineering : 把 skill 正名为"程序性记忆"工程实践
section 标准化
2025.12 : Anthropic Agent Skills spec : 开放标准发布
2026.01 : OpenAI Codex CLI + ChatGPT : 采用同一格式
2026.02 : agensi.io / skillsmp.com : 付费 marketplace 上线 (80/20 分账)
2026.04 : Pre-built skills : Anthropic 自发 PowerPoint / Excel / Word / PDF skill
下一节我们看在标准化之后,怎么写一份跨工具兼容的 SKILL.md。
8.11SKILL.md 的写作规范
Hermes 的 AGENTS.md 里有八条 skill 写作硬规则。这些规则是 PR 评审标准:
description≤ 60 字符,一句话,句号结尾。 避免营销词。- 引用工具时用 backtick,且必须是 Hermes 原生工具或 MCP server 名。
不要写 "use
grep",写 "usesearch_files"。 - platforms gating 要和实际脚本 import 一致。 POSIX-only 的代码必须声明 platforms。
- author 写真人,不写 "Hermes Agent"。
- section 顺序固定:标题 → 简介 → When to Use → Prerequisites → How to Run → Quick Reference → Procedure → Pitfalls → Verification。
- 复杂逻辑放
scripts/,不要让 LLM inline 写。 - 测试在
tests/skills/test_<skill>_skill.py,只用 stdlib + pytest + unittest.mock。 .env.example添加要清晰隔离,不污染周围内容。
8.12本章带走的
- Agent 的"程序性记忆"对应人脑的肌肉记忆。它是关于"做某类事情的方法"。
- Voyager (2023) 是开山之作:automatic curriculum + executable skill library + iterative prompting。
- Hermes 的 Skill 是 SKILL.md——人类可读的决策手册, 含 frontmatter、When to Use / NOT to Use、Prerequisites、Quick Reference。
- Skill 的核心工程招:作为 user message 追加注入, 不动 system prompt,保持 cache 命中。
- 三个来源:内置 / 用户自创 / agentskills.io。
- Agent 用
skill_manage(action="create", ...)自己创建新 skill。created_by: agent让 Curator 知道哪些归它管。 - Hermes vs Voyager:放弃自动 retrieval 换来人类可审计可分享。
- 8 条 SKILL.md 写作硬规则——本质都是为了让 LLM/人/审阅者三方都满意。
章末练习
- Easy 为什么 Skill 必须作为 user message 注入,而不是 system prompt?用 50 字回答。
- Easy 程序性记忆和陈述性记忆的核心区别是什么?分别给一个 Agent 应用场景。
-
Medium
给一个具体场景:你天天用
kubectl logs看某个特定 namespace 的 pod 日志, 要按时间、grep 关键字、过滤掉 healthcheck。写一份完整的 SKILL.md,遵守八条写作规则。 -
Medium
Voyager 的 "iterative prompting" 在 Hermes 里对应什么机制?
(提示:考虑 Curator 和
skill_manage(action="patch")) - Hard Hermes 的 Skill 没有"自动检索"——LLM 不会"在 100 个 skill 里挑相关的"。 为什么这种设计权衡值得?设想一个 Agent 有 500 个 skill 时会怎样。 然后想想:如何加一个"显式向量检索"机制而不破 cache?
-
Hard
读
tools/skill_usage.py(如果你下载了 Hermes 源码)。它存~/.hermes/skills/.usage.json里。 解释created_by: agent标记从哪里写入、被谁读取。这条数据链路对下章的 Curator 极重要。