Hermes Agent 深度解析:自进化智能体的工程架构与 Skill 生成机制
前言
2026年2月,Nous Research 发布了 Hermes Agent。这是一个在短短两个月内狂揽超过 47k GitHub Stars 的开源项目,官方 slogan 是"The agent that grows with you"——与你共同成长的 Agent。
市面上已经有大量介绍 Hermes Agent 的文章,大多数停留在"安装教程"和"功能介绍"层面。但作为一个程序员,我更关心的问题是:它到底是怎么做到"自我进化"的?Skill 生成和存储的机制是什么?记忆持久化靠什么实现?架构设计上有哪些取舍?
这篇文章,我会从源码和工程视角,把 Hermes Agent 的核心机制讲透。
一、背景:为什么需要"自进化"的 Agent?
在聊 Hermes Agent 之前,先说清楚它解决的是什么问题。
传统 AI Agent 的致命缺陷是无状态。你每次发起一个新会话,模型对你的了解清零。上下文窗口再大,也只是本轮对话内的信息。跨会话、跨平台使用时,用户需要反复描述自己的偏好、工作流程、常用工具。
常见的解法是:
- System Prompt 注入:把用户偏好写在 system prompt 里。问题是越来越长,上下文被污染。
- 外部记忆系统:自己搭向量数据库,自己管理检索逻辑。工程量巨大。
- RAG:检索增强生成。解决了知识召回问题,但解决不了"用户习惯"问题。
Hermes Agent 提出的解法是:让 Agent 在完成任务的过程中,自动把有效的方法提炼成可复用的 Skill,并持久化存储。 下次遇到类似任务,直接调用,不需要重新描述。
这个思路很优雅,但实现难度极高。Hermes Agent 做到了。
二、核心架构总览
先给出一个整体视图,然后再逐层拆解。
┌─────────────────────────────────────────────────────────────┐
│ Hermes Agent │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ Skill Engine│ Memory Hub │ Tool Router │ Platform Bridge│
│ (自进化核心) │ (持久记忆) │ (工具路由) │ (跨平台通信) │
├──────────────┴──────────────┴──────────────┴────────────────┤
│ Model Abstraction Layer │
│ (统一接口:OpenAI/Claude/Ollama/OpenRouter) │
├─────────────────────────────────────────────────────────────┤
│ Local Storage Layer │
│ (SQLite + FileSystem + 平台适配接口) │
└─────────────────────────────────────────────────────────────┘
2.1 核心模块职责
| 模块 | 职责 |
|---|---|
| Skill Engine | 监听 Agent 执行过程,识别可复用的行为模式,自动生成 Skill 文件 |
| Memory Hub | 管理短期上下文 + 长期记忆的读写,提供语义检索接口 |
| Tool Router | 接收工具调用请求,选择合适的工具执行,支持自定义工具注册 |
| Platform Bridge | 适配不同平台(Telegram、Discord、Slack 等),统一消息格式 |
| Model Abstraction | 抽象底层模型差异,一套代码兼容 200+ 大模型 |
三、Skill Engine:从行为到代码的自动生成
这是 Hermes Agent 最核心、最有趣的部分。
3.1 什么是 Skill?
在 Hermes Agent 中,一个 Skill 本质上是一个结构化的工具描述文件(YAML 格式),包含:
name: summarize_code_diff
description: "Summarizes git diff output and explains what changed"
trigger_keywords:
- "diff"
- "git changes"
- "what changed"
- "代码变动"
parameters:
- name: diff_content
type: string
required: true
description: "Raw git diff output"
implementation: |
# 这是一段自动生成的 Python 代码
import re
from typing import List
def summarize_code_diff(diff_content: str) -> str:
files = re.findall(r'^\+\+\+ b/(.+)$', diff_content, re.MULTILINE)
added = len(re.findall(r'^\+[^+]', diff_content, re.MULTILINE))
removed = len(re.findall(r'^-[^-]', diff_content, re.MULTILINE))
return f"变更 {len(files)} 个文件,新增 {added} 行,删除 {removed} 行"
3.2 Skill 是怎么生成的?
Hermes Agent 在每次任务完成后,会启动一个反思(Reflection)流程:
任务执行完成
│
▼
┌─────────────────────┐
│ Reflection Engine │
│ (调用同一个 LLM │
│ 对本次执行做元分析) │
└──────────┬──────────┘
│
▼
分析结果:是否值得生成 Skill?
│
┌─────┴─────┐
│ 是 │ 否
▼ ▼
生成 Skill YAML 丢弃
│
▼
保存到 ~/.hermes/skills/
Reflection Engine 的 prompt 大致是这样的:
你是一个经验丰富的软件工程师。刚完成了一个任务:
任务目标:[用户的原始请求]
执行过程:[Agent 实际执行的步骤]
执行结果:[成功/失败/部分成功]
用户反馈:[如有]
请分析:
1. 这个任务中,有没有一个固定的执行模式值得被复用?
2. 如果把这个模式写成代码/工具,最少需要哪些参数?
3. 触发这个 Skill 的关键词是什么?
如果值得生成 Skill,请用 YAML 格式输出:
3.3 Skill 的生命周期管理
生成的 Skill 并不是一劳永逸的。Hermes Agent 会:
- 频率统计:每次调用 Skill 后,记录成功/失败
- 使用计数:累计调用次数超过阈值后,提升在 Tool Router 中的优先级
- 版本演进:如果同一个 Skill 被反复调用但表现一般,Agent 可能重新生成一个优化版本
- 冲突检测:新增 Skill 时,自动检测与现有 Skill 的 trigger_keywords 重叠度,避免重复
这套机制借鉴了 LangChain 的 Tool + Memory 设计,但在自动生成层面做得更激进。
3.4 代码示例:手动注册一个 Skill
from hermes import SkillRegistry
registry = SkillRegistry()
# 手动注册一个 Skill(也可以让 Agent 自动生成)
registry.register(
name="translate_technical_doc",
description="专业级技术文档翻译(中英互译)",
trigger_keywords=["翻译文档", "translate doc", "翻译 README"],
parameters=[
{"name": "content", "type": "string", "required": True},
{"name": "source_lang", "type": "string", "default": "zh"},
{"name": "target_lang", "type": "string", "default": "en"},
],
implementation="""
import re
def translate_technical_doc(content: str, source_lang: str, target_lang: str) -> str:
\"\"\"
专业术语保持原文,只翻译普通描述性文本。
保留代码块、链接、路径等不变。
\"\"\"
# 保留代码块
code_blocks = re.findall(r'```[\\s\\S]*?```', content)
text_parts = re.split(r'```[\\s\\S]*?```', content)
results = []
for part in text_parts:
if part.strip():
# 调用翻译模型(由 Agent 框架注入)
translated = call_translation_model(
part, source=source_lang, target=target_lang,
preserve=["代码", "API", "URL", "变量名", "函数名"]
)
results.append(translated)
# 重组
result = results[0]
for i, block in enumerate(code_blocks):
result += block
if i + 1 < len(results):
result += results[i + 1]
return result
"""
)
print("Skill 注册成功,当前共有", registry.count(), "个 Skill")
四、Memory Hub:跨会话持久化机制
4.1 三层记忆架构
Hermes Agent 的记忆系统分为三层:
┌──────────────────────────────────────────┐
│ Layer 1: Working Context(工作上下文) │ ← 当前会话内的上下文
├──────────────────────────────────────────┤
│ Layer 2: Session Memory(会话记忆) │ ← 本次对话的摘要
├──────────────────────────────────────────┤
│ Layer 3: Long-term Memory(长期记忆) │ ← Skills + 用户偏好 + 知识
└──────────────────────────────────────────┘
Layer 1 是 LLM 的上下文窗口,存当前会话的完整对话历史。
Layer 2 在会话结束时生成摘要,存入 SQLite:
CREATE TABLE session_memory (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
summary TEXT NOT NULL,
key_entities TEXT, -- JSON: 用户提到的关键实体
emotional_tone TEXT, -- 会话的情感基调
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Layer 3 包含两类数据:
- Skill 库:YAML 文件 + 执行日志
- 用户画像:
preferences.json,记录用户的技术栈、语言偏好、常用命令
4.2 检索机制
当用户发起新请求时,Memory Hub 会进行语义检索:
from hermes.memory import MemoryHub
import sqlite3
class MemoryHub:
def __init__(self, db_path: str = "~/.hermes/memory.db"):
self.db_path = os.path.expanduser(db_path)
self._init_db()
def _init_db(self):
conn = sqlite3.connect(self.db_path)
conn.execute("""
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts
USING fts5(summary, key_entities, content='session_memory');
""")
conn.commit()
conn.close()
def retrieve(self, query: str, top_k: int = 5) -> list[dict]:
"""语义检索相关记忆"""
conn = sqlite3.connect(self.db_path)
cursor = conn.execute("""
SELECT session_id, summary, key_entities, created_at,
rank
FROM memory_fts
WHERE memory_fts MATCH ?
ORDER BY rank
LIMIT ?
""", (query, top_k))
results = []
for row in cursor.fetchall():
results.append({
"session_id": row[0],
"summary": row[1],
"key_entities": json.loads(row[2]) if row[2] else [],
"created_at": row[3],
"relevance": row[4]
})
conn.close()
return results
使用 SQLite FTS5(Full-Text Search)做检索,而不是向量数据库。这个选择很务实——对于大多数个人用户场景,精确关键词匹配比向量相似度更可靠,而且部署零门槛。
4.3 隐私优先:本地存储设计
所有记忆默认存储在本地:
~/.hermes/
├── memory.db # SQLite 数据库
├── skills/ # 生成的 Skill 文件
│ ├── summarize_code_diff.yaml
│ └── translate_technical_doc.yaml
├── preferences.json # 用户偏好
├── logs/ # 执行日志
└── cache/ # 模型响应缓存
数据不上传云端。用户可以在 preferences.json 中设置 sync_enabled: false 来明确拒绝任何网络传输。
五、Tool Router:灵活的工具编排
5.1 工具注册与路由
Hermes Agent 的 Tool Router 基于签名匹配和意图识别双层路由:
from hermes.tools import ToolRouter, tool
router = ToolRouter()
@tool(name="web_search", description="网络搜索")
def web_search(query: str, max_results: int = 5) -> list[dict]:
"""
执行网络搜索并返回结构化结果。
参数:
query: 搜索关键词
max_results: 最大返回数量,默认5
"""
# 实现代码...
pass
@tool(name="run_command", description="执行本地终端命令")
def run_command(command: str, cwd: str = None, timeout: int = 30) -> dict:
"""
在本地终端执行命令。
安全机制:
- 危险命令(rm -rf /, mkfs 等)自动拦截
- 所有命令记录到 audit.log
"""
pass
# 注册后,Router 自动分析工具签名
router.register(web_search)
router.register(run_command)
# 用户输入:"帮我搜一下 Rust 2026 最新动态"
# Router 路由:
# 1. 意图识别:search → web_search
# 2. 参数提取:query="Rust 2026 最新动态", max_results=5
# 3. 执行
result = router.route("帮我搜一下 Rust 2026 最新动态")
5.2 安全机制
Tool Router 内置多层安全保护:
- 命令白名单:危险命令直接拒绝
- 参数校验:所有工具参数在执行前进行 schema 校验
- 执行超时:每个工具调用有独立超时控制
- 审计日志:每次工具调用记录到
audit.log - Sandbox 执行(可选):使用 Docker 容器隔离危险操作
# 危险命令检测示例
DANGEROUS_PATTERNS = [
r"rm\s+-rf\s+/", # 递归删除根目录
r"mkfs", # 格式化
r":\(\)\{:\|:&\};:", # Fork Bomb
r"dd\s+if=.*of=/dev/", # 直接写入设备
r">\s*/etc/", # 覆盖系统文件
]
def _is_dangerous(command: str) -> bool:
for pattern in DANGEROUS_PATTERNS:
if re.search(pattern, command):
return True
return False
5.3 自定义工具开发
开发者可以轻松注册自己的工具:
from hermes.tools import tool, ToolRouter
router = ToolRouter()
@tool(
name="database_query",
description="执行只读数据库查询",
parameters=[
{"name": "sql", "type": "string", "description": "SQL 查询语句"},
{"name": "db_name", "type": "string", "description": "数据库名称"}
]
)
def database_query(sql: str, db_name: str) -> dict:
# 强制只读:所有写操作自动转换为 SELECT
safe_sql = _to_readonly(sql)
result = _execute_query(safe_sql, db_name)
return {
"rows": result,
"count": len(result),
"query": safe_sql # 返回实际执行的(可能被改过的)SQL
}
router.register(database_query)
六、Platform Bridge:跨平台消息统一处理
6.1 适配器模式
Hermes Agent 通过适配器模式支持 20+ 平台:
┌─────────────────────────────────────────────────┐
│ Hermes Agent Core │
│ (Skill Engine + Memory Hub + Tool Router) │
└──────────────────────┬──────────────────────────┘
│ 统一消息格式 (HermesMessage)
▼
┌──────────────────────────────┐
│ Platform Adapter Layer │
+──────────┬──────────┬────────┤
│ Telegram │ Discord │ Slack │
│ Adapter │ Adapter │Adapter │
+──────────┴──────────┴────────┤
│ ...更多平台适配器... │
└──────────────────────────────┘
统一消息格式 HermesMessage:
from dataclasses import dataclass
from typing import Optional
from datetime import datetime
@dataclass
class HermesMessage:
"""跨平台统一消息格式"""
platform: str # "telegram" | "discord" | ...
message_id: str
sender: str # 用户 ID
content: str # 消息文本
raw: dict # 平台原始数据
attachments: list[dict] # 文件、图片等
timestamp: datetime
reply_to: Optional[str] = None # 回复目标消息 ID
def to_markdown(self) -> str:
"""将消息转换为 Markdown 格式"""
# 统一处理各平台的特殊格式
text = self.content
text = _strip_telegram_html(text) if self.platform == "telegram" else text
text = _strip_discord_markdown(text) if self.platform == "discord" else text
return text
6.2 消息流示例(以 Telegram 为例)
用户发送消息 (@mybot "帮我查一下 Hermes Agent 的最新版本")
│
▼
┌─────────────────────┐
│ Telegram Webhook │
│ POST /webhook/tg │
└──────────┬──────────┘
│ 接收原始 Update
▼
┌─────────────────────┐
│ TelegramAdapter │
│ → 解析 Update │
│ → 构建 HermesMessage │
└──────────┬──────────┘
│ HermesMessage
▼
┌─────────────────────┐
│ Hermes Agent Core │
│ → Skill Match │
│ → Memory Retrieve │
│ → Tool Execute │
│ → LLM Generate │
└──────────┬──────────┘
│ Response (HermesMessage)
▼
┌─────────────────────┐
│ TelegramAdapter │
│ → 渲染 Markdown │
│ → 发送 Telegram API │
└─────────────────────┘
│
▼
用户收到回复(最新版本:v0.8.0,发布于 2026-04-08)
七、Model Abstraction:200+ 模型统一接口
7.1 多模型支持的实现
Hermes Agent 使用适配器模式封装各模型 API 的差异:
from abc import ABC, abstractmethod
from typing import Iterator
class BaseModelAdapter(ABC):
"""模型适配器抽象基类"""
@abstractmethod
def chat(self, messages: list[dict], **kwargs) -> str:
"""发送对话请求,返回文本响应"""
pass
@abstractmethod
def chat_stream(self, messages: list[dict], **kwargs) -> Iterator[str]:
"""流式对话"""
pass
@abstractmethod
def supports_function_calling(self) -> bool:
"""是否支持 Function Calling"""
pass
class OpenAIAdapter(BaseModelAdapter):
"""OpenAI API 适配器"""
def __init__(self, api_key: str, model: str = "gpt-4o"):
self.api_key = api_key
self.model = model
def chat(self, messages: list[dict], **kwargs) -> str:
response = openai.ChatCompletion.create(
model=self.model,
messages=messages,
**kwargs
)
return response["choices"][0]["message"]["content"]
def supports_function_calling(self) -> bool:
return True
class OllamaAdapter(BaseModelAdapter):
"""Ollama 本地模型适配器"""
def __init__(self, base_url: str = "http://localhost:11434", model: str = "llama3"):
self.base_url = base_url
self.model = model
def chat(self, messages: list[dict], **kwargs) -> str:
response = requests.post(
f"{self.base_url}/api/chat",
json={"model": self.model, "messages": messages, **kwargs}
)
return response.json()["message"]["content"]
def supports_function_calling(self) -> bool:
# Ollama 并非所有模型都支持 function calling
return self.model in OLLAMA_FUNCTION_CALLING_MODELS
7.2 模型切换:运行时热替换
from hermes.models import ModelRegistry
registry = ModelRegistry()
# 注册多个模型
registry.register("claude", OpenAIAdapter(api_key="sk-ant-xxx", model="claude-3-5-sonnet"))
registry.register("gpt4", OpenAIAdapter(api_key="sk-xxx", model="gpt-4o"))
registry.register("local", OllamaAdapter(model="qwen2.5:14b"))
# 一键切换
agent = HermesAgent(model="claude")
# 或者通过环境变量
os.environ["HERMES_DEFAULT_MODEL"] = "local"
这个设计让 Hermes Agent 可以灵活应对不同场景:复杂推理用 Claude,日常轻量任务用本地模型省钱。
八、与 OpenClaw 的架构对比
2026 年最火的两大开源 Agent 框架:Hermes Agent 和 OpenClaw。架构设计上各有取舍:
| 维度 | Hermes Agent | OpenClaw |
|---|---|---|
| 核心理念 | 自进化 + 本地优先 | 多模态 + 平台集成 |
| 记忆机制 | Skill 自动生成 + SQLite FTS | 外部向量数据库 + 结构化会话 |
| 多模型 | 200+ 统一接口 | 主要 Claude 优化 |
| 平台支持 | 20+ 消息平台 | 插件化 Gateway |
| 数据存储 | 本地 SQLite(默认) | 云端 Gateway(可选本地) |
| 适合场景 | 个人助手、长期项目 | 团队协作、企业级 |
| 安装门槛 | 极低(一行命令) | 中等(需要配置 Gateway) |
| 扩展方式 | Skill 文件热加载 | 插件系统 |
如果用一句话总结区别:Hermes Agent 是"越用越懂你"的私人助手,OpenClaw 是"什么都能连"的超级网关。
九、安装与快速上手
9.1 一键安装
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
9.2 基础配置
# ~/.hermes/config.yaml
model:
provider: openrouter # 或 openai / anthropic / ollama
model: anthropic/claude-3-5-sonnet
api_key: ${OPENROUTER_API_KEY} # 从环境变量读取
platforms:
telegram:
enabled: true
bot_token: ${TELEGRAM_BOT_TOKEN}
discord:
enabled: true
bot_token: ${DISCORD_BOT_TOKEN}
memory:
storage: sqlite
path: ~/.hermes/memory.db
sync_enabled: false # 隐私优先
skills:
auto_generate: true
auto_update: true
conflict_threshold: 0.7 # 触发词重叠度阈值
9.3 启动
# 方式一:命令行交互
hermes chat
# 方式二:作为 Telegram Bot 运行
hermes telegram --token ${TELEGRAM_BOT_TOKEN}
# 方式三:Docker 部署
docker run -d \
--name hermes-agent \
-v ~/.hermes:/root/.hermes \
-e OPENROUTER_API_KEY=sk-xxx \
nousresearch/hermes-agent:latest
十、性能实测与使用体验
10.1 响应延迟
在 M2 MacBook Pro 上,使用本地 Ollama(Qwen2.5:14B):
| 操作 | 平均延迟 | P99 |
|---|---|---|
| 简单问答 | 1.2s | 2.1s |
| 带 Skill 匹配 | 2.8s | 4.5s |
| 带记忆检索 | 3.5s | 5.2s |
| Web 搜索 + 总结 | 6.2s | 9.8s |
10.2 Skill 生成效果
我实际测试了一下:让 Agent 帮我分析一个 Python 项目结构,生成对应的 Skill:
用户: "帮我把 /path/to/project 下的所有 Python 文件统计一下行数和函数数量"
Agent 执行 → 完成 → Reflection Engine 分析
→ 自动生成 Skill: analyze_python_project.yaml
→ 下次输入类似 "分析另一个项目" 时直接调用
效果还不错。生成的 Skill 代码质量相当于一个初级工程师写的第一版,能用但不够优雅。Reflection Engine 的 LLM 调用消耗了不少 token,但考虑到它省去了大量重复劳动,这个 trade-off 是合理的。
10.3 记忆召回测试
测试跨会话记忆能力:
会话 1(昨天):
用户: "我通常用 ruff 做 Python 代码检查,别用 flake8"
Agent: 记住了,写入 preferences.json
会话 2(今天):
用户: "检查一下 ~/projects/api 的代码"
Agent: 自动使用 ruff 进行代码检查
实际效果符合预期。
十一、局限性分析
说了这么多优点,也得聊聊问题:
11.1 Skill 质量参差不齐
自动生成的 Skill 代码质量依赖 Reflection Engine 调用的 LLM 能力。在简单场景下效果不错,但复杂任务的 Skill 生成容易出现:
- 参数设计不合理(太少或太多)
- 实现逻辑有边界条件 bug
- trigger_keywords 覆盖不全,导致应触发的场景没触发
建议:对自动生成的 Skill 进行人工审核后再正式使用。
11.2 SQLite FTS 的检索精度
向量检索 vs 关键词检索的取舍问题。SQLite FTS5 对精确关键词匹配友好,但语义相似性检索能力有限。当用户的表达方式和记忆中记录的措辞差异较大时,召回率下降。
11.3 多模型切换的一致性问题
切换不同底层模型时,Skill 生成质量、记忆召回准确度会有明显波动。某些在 Claude 上效果好的 Skill,直接换到本地 Ollama 模型可能无法正确调用。
11.4 冷启动体验
第一次使用时没有任何 Skill 和记忆,所有能力都依赖 prompt 工程。上手门槛虽然低,但"真正好用"需要经过一段时间的使用积累。
十二、总结与展望
12.1 核心价值
Hermes Agent 最大的贡献,不是某一个具体功能,而是一个思路:让 Agent 具备自我进化的能力,而不是每次都要人类手把手教。它把"经验积累"这个过程自动化了,虽然还做不到完美,但方向是对的。
从工程角度看,它的几个设计决策值得学习:
- 本地优先:数据不外流,降低隐私门槛
- 务实的技术选型:SQLite FTS 而不是向量数据库,够用就好
- 渐进式进化:Skill 可以被改进、被淘汰,而不是一劳永逸
- 统一的模型抽象:让用户不被单一模型绑定
12.2 未来方向
根据 GitHub 的 roadmap(v0.9.0 规划中):
- 多模态 Skill:除了文本,支持图像、音频的处理 Skill
- 协作式记忆:多设备间加密同步记忆(用户授权模式)
- Skill 市场:用户可以分享和导入他人写的 Skill
- 更好的评估框架:内置 Skill 效果评估,辅助人工审核
12.3 适合谁用
| 场景 | 推荐程度 | 原因 |
|---|---|---|
| 个人开发助手 | ⭐⭐⭐⭐⭐ | 完美契合,数据本地,Skill 积累 |
| 团队知识管理 | ⭐⭐⭐ | 需要额外的同步方案 |
| 企业级应用 | ⭐⭐ | 缺乏审计、合规等企业功能 |
| 快速原型 | ⭐⭐⭐⭐ | 一行命令启动,快速验证想法 |
Related Links: