技能系统:从经验中学习的闭环
本章核心源码:
agent/skill_utils.py(442 行)、tools/skills_tool.py(1376 行)、tools/skill_manager_tool.py(742 行)
定位:本章分析 Hermes "self-improving" 理念的核心实现——技能系统。技能不是静态文档,而是 agent 从工作中提炼、在使用中改进、按条件加载的过程性知识。 前置依赖:第 5 章(提示词系统)、第 6 章(工具系统)。适用场景:想理解"self-improving agent"如何在工程上实现。
为什么技能系统是 Hermes 的核心差异
大多数 agent 框架有工具系统("agent 能做什么")和 prompt 系统("agent 知道什么")。Hermes 多了第三个系统——技能系统("agent 学到了什么")。
三者的关系:
| 系统 | 内容类型 | 变化频率 | 来源 |
|---|---|---|---|
| 工具 | 可执行的函数 | 低(开发者添加) | 代码 |
| Prompt | 身份和规则 | 低(配置/硬编码) | 配置文件 |
| 技能 | 过程性知识 | 高(agent 自主创建/改进) | 工作经验 |
技能填补了一个空白:agent 在解决一个复杂问题后,如果没有技能系统,下次遇到同类问题需要从头探索。有了技能系统,agent 可以把解决过程提炼为可复用的技能——下次直接检索使用。
这就是 Learning Loop 赌注的工程实现。
技能的数据模型
每个技能是一个 Markdown 文件,带有 YAML frontmatter:
---
metadata:
hermes:
fallback_for_toolsets: [web]
tags: [automation, workflow]
platforms: [macos, linux]
---
# 使用 Exa API 搜索学术论文
## 问题
标准 web_search 对学术论文的检索效果不佳...
## 方案
使用 Exa 的 neural search 模式,配合 category=research...
## 示例
```python
result = web_search(query="transformer attention mechanism", ...)
### Frontmatter 解析
`parse_frontmatter()`(`agent/skill_utils.py:52`)负责解析 YAML frontmatter:
```python
# agent/skill_utils.py:52-86
def parse_frontmatter(content: str) -> Tuple[Dict[str, Any], str]:
if not content.startswith("---"):
return {}, content
end_match = re.search(r"\n---\s*\n", content[3:])
yaml_content = content[3 : end_match.start() + 3]
try:
parsed = yaml_load(yaml_content) # CSafeLoader(快速)+ fallback
except Exception:
# Fallback: simple key:value parsing for malformed YAML
for line in yaml_content.strip().split("\n"):
key, value = line.split(":", 1)
frontmatter[key.strip()] = value.strip()
return frontmatter, body
设计选择:优先使用 CSafeLoader(C 实现,快);如果 YAML 格式有问题,fallback 到逐行 key: value 解析。这让系统对格式不完美的技能文件保持鲁棒。
条件加载
Frontmatter 支持多种条件字段:
平台过滤(agent/skill_utils.py:92):
platforms: [macos, linux] # 只在 macOS 和 Linux 上加载
skill_matches_platform() 将 platforms 列表与 sys.platform 比较。缺失或空表示兼容所有平台。
工具依赖(agent/skill_utils.py:240):
metadata:
hermes:
fallback_for_toolsets: [web] # 当 web toolset 不可用时激活
requires_toolsets: [terminal] # 需要 terminal toolset 才加载
requires_tools: [web_search] # 需要特定工具才加载
extract_skill_conditions() 提取这些条件,由 build_skills_system_prompt() 在构建技能索引时评估。
禁用列表(agent/skill_utils.py:121):
# config.yaml
skills:
disabled: [deprecated-skill]
platform_disabled:
telegram: [desktop-only-skill]
get_disabled_skill_names() 支持全局禁用和按平台禁用。Gateway 的 Telegram 平台可以禁用只在桌面端有意义的技能。
技能的生命周期
graph LR
A["发现<br/>扫描 skills/ 目录"] --> B["索引<br/>提取 name + description"]
B --> C["注入<br/>写入 system prompt"]
C --> D["检索<br/>skill_view 按需加载"]
D --> E["使用<br/>模型根据内容执行"]
E --> F["创建<br/>skill_manage create"]
F --> G["改进<br/>skill_manage patch"]
G --> D
阶段 1:发现
get_all_skills_dirs()(agent/skill_utils.py:226)返回技能搜索路径:
# agent/skill_utils.py:226-234
def get_all_skills_dirs() -> List[Path]:
dirs = [get_hermes_home() / "skills"] # 当前 HERMES_HOME/skills/(总是第一位)
dirs.extend(get_external_skills_dirs()) # config.yaml 中的 external_dirs
return dirs
阶段 2:索引
build_skills_system_prompt()(agent/prompt_builder.py:529)扫描所有技能目录,为每个技能提取名称和一句话描述,生成索引列表注入 system prompt。
索引只包含标题和描述——完整内容在模型需要时通过工具按需加载。这个设计控制了 system prompt 的 token 开销:26 个技能类别的索引只占几百 token,但完整内容可能超过 50K token。
阶段 3-4:检索与使用
模型通过 skill_view 工具按名称检索技能的完整内容:
# tools/skills_tool.py:787
def skill_view(name: str, file_path: str = None, task_id: str = None) -> str:
# 在所有 skills 目录中搜索匹配的技能文件
# 返回完整的 Markdown 内容
阶段 5-6:创建与改进
这是 Learning Loop 的核心。模型通过 skill_manage 工具创建和改进技能:
# tools/skill_manager_tool.py:569
def skill_manage(action: str, name: str = None, content: str = None,
patch: str = None, ...):
# action = "create": 创建新技能
# action = "patch": 改进现有技能
# action = "delete": 删除技能
SKILLS_GUIDANCE(agent/prompt_builder.py:164)引导模型的行为:
"After completing a complex task (5+ tool calls), fixing a tricky error, or discovering a non-trivial workflow, save the approach as a skill... When using a skill and finding it outdated, incomplete, or wrong, patch it immediately with skill_manage(action='patch') — don't wait to be asked."
两个关键指令:
- 自主创建:完成复杂任务后主动保存为技能
- 使用时改进:发现技能过时或不准确时立即 patch
这让技能系统形成了一个正反馈循环:使用越多 → 发现越多问题 → 改进越多 → 质量越高 → 使用越有效。
Nudge 机制:主动触发学习
agent 不会"忘记"创建技能——AIAgent 中的 nudge 机制会定期提醒:
# run_agent.py:1072-1076
self._skill_nudge_interval = 10 # 每 10 个 tool iteration 检查
self._iters_since_skill = 0
当模型在一次对话中使用了 10+ 个工具调用但没有触碰 skill_manage,编排器会在 _spawn_background_review() 中启动一个后台 agent 审查对话内容,判断是否值得创建技能(详见第 4 章)。
与 agentskills.io 的对接
Hermes 的技能格式兼容 agentskills.io 开放标准。这意味着:
- 社区创建的技能可以直接放入当前
HERMES_HOME/skills/(默认 profile 下表现为~/.hermes/skills/) - Hermes 创建的技能可以分享到 Skills Hub
- 不同 agent 框架之间的技能可以互通
设计哲学:基于来源的信任分级(Source-based credibility)
并非所有技能享有同等信任。Hermes 实现了一套信任分级机制(tools/skills_guard.py:41-49),根据技能的来源决定如何解读其安全性发现:builtin 技能(Hermes 团队编写)即使触发"dangerous"级发现也被允许执行;trusted-source 技能允许"caution"但拦截"dangerous";community-sourced 技能连"caution"级发现也会被拦截。同一个安全发现,因为编写者不同,处理策略完全不同——这是来源即信誉的设计原则。
技能 vs 工具 vs Prompt:三种知识的区分
| 维度 | 工具 | Prompt | 技能 |
|---|---|---|---|
| 形式 | Python 函数 | 文本块 | Markdown 文件 |
| 变更频率 | 低(版本发布) | 低(配置修改) | 高(agent 自主创建) |
| 作者 | 开发者 | 开发者/用户 | Agent 自身 |
| 加载方式 | 模块导入 | 构建时注入 system prompt | 索引注入 + 按需检索 |
| 可改进 | 否(需改代码) | 手动 | Agent 自动 patch |
这个区分不是学术分类,而是有工程后果的:如果技能被实现为工具,每次添加技能就需要修改代码;如果技能被实现为 prompt,所有技能内容都会占用 system prompt token。Markdown + 按需检索的方案既让 agent 自主管理,又控制了 token 开销。
设计启示
技能系统的设计展示了 "self-improving" 的工程实现路径:
- 低成本创建:Markdown 文件 + YAML frontmatter,没有 schema 验证、没有编译、没有注册步骤
- 渐进式加载:索引在 system prompt,完整内容按需检索——控制基础 token 开销
- 正反馈循环:使用时发现问题 → 立即 patch → 下次使用时更好
- Nudge 而非强制:通过 background review 提醒,不强制每次都创建技能
第 9 章将分析子代理与委托机制——另一种扩展 agent 能力的方式。
设计赌注回扣:本章是 Learning Loop 赌注的核心实现。技能系统让 Hermes 从"能用工具做事的 agent"升级为"能从经验中学习的 agent"。
SKILLS_GUIDANCE的"使用时立即 patch"指令和_spawn_background_review的自动审查构成了完整的学习闭环。
版本演化说明
本章核心分析基于 Hermes Agent v0.8.0(2026 年 4 月)。 技能文件发现、
skill_manage和基础 skills 目录迁移,在 v0.3.0 发布窗口之前就已出现;v0.4.0 发布窗口又加入了 background review 驱动的自动审查。之后 v0.5.0-v0.8.0 之间继续补足条件加载、展示层和 agentskills.io 兼容细节。