Hermes Agent 深度拆解:当AI Agent学会「记住」——从三层记忆架构、自进化闭环到多平台网关的工程全貌(2026)
写在前面
2026年的开源AI Agent领域,正在经历一场从「调用模型」到「管理记忆」的关键范式转移。
过去两年,大多数AI Agent框架的核心叙事是「prompt engineering」——怎么写好system prompt,怎么设计tool calling,怎么拼接ReAct循环。但当我们真正把这些框架跑起来,用到第二周、第三周,就会发现一个根本性的问题:它们不记得任何事。每次对话都是白板,每次任务都要重新解释上下文,所谓的「Agent」不过是披着自主外衣的「一次性API调用器」。
Hermes Agent的出现,正是对这个问题的一次正面回应。由知名AI研究实验室Nous Research于2026年2月开源的这个项目,GitHub Stars在发布后短短数月便突破21.6万,成为当年增长最快的开源AI项目之一。但star数字背后的真正创新,不是它接了多少模型,而是它解决了一个更根本的问题:怎样让一个AI Agent真正拥有持续学习和进化的能力。
本文将从工程视角,对Hermes Agent的核心架构进行深度拆解:三层记忆系统是如何协同工作的?自进化闭环的五环节是怎么跑起来的?多平台网关和Skill系统如何配合实现「越用越聪明」?以及——作为程序员,我们能从它的设计里学到什么。
一、背景:从「工具调用器」到「有记忆的伙伴」
1.1 AI Agent框架的现状与瓶颈
在深入Hermes Agent之前,我们先梳理一下2026年AI Agent框架的整体格局。
当前主流的Agent框架可以分为三类:
第一类:工具调用型(Tool Calling)
以LangChain、AutoGPT为代表,核心架构是「LLM + 工具列表 + ReAct循环」。模型负责决策何时调用工具,工具列表提供外部能力。这类框架的优点是简单直接,缺点是完全没有持久化能力——每次对话都是独立上下文,无法积累经验。
# 典型的ReAct循环
while not task_complete:
thought = llm.think(task, history)
action = llm.decide_action(thought, tools)
result = execute(action)
history.append((thought, action, result))
这种模式的问题在于:当用户第二天回来时,Agent已经完全不记得昨天做过什么、卡在哪里、哪些方案行不通。
第二类:多Agent协作型(Multi-Agent)
以CrewAI、AutoGen为代表,通过多个专门化的Agent协作完成任务。这类框架解决了「分工」问题,但同样没有解决「记忆」问题。每个Agent的上下文窗口是有限的,历史信息必须靠外部存储。
第三类:持久化记忆型(Memory-Aware)
这就是Hermes Agent所在的赛道。它的核心假设是:一个真正有用的AI Agent,必须能够跨会话积累知识,从经验中学习,并将学到的内容固化为可复用的技能。
Hermes Agent的官方slogan是「The agent that grows with you」——与你一起成长的Agent。这个定位直接点出了它的核心价值:不是一次性执行任务的工具,而是能够持续学习、变得越来越懂你的数字伙伴。
1.2 Nous Research是谁?
Hermes Agent出自Nous Research之手,这家实验室在AI开源社区有着良好声誉。他们此前开源过Hermes系列大模型(Hermes-3-70B、Nominal-8B等)以及Psyche系列推理模型,在LoRA微调社区有大量使用者。Nous Research的特点是:重视开源、重视实际部署体验、不搞过度营销。
这与某些「先放论文再看代码」的实验室形成鲜明对比。Hermes Agent从一开始就是完整可运行的项目,MIT协议,代码质量较高,文档相对完善。
1.3 数字对比:增长最快的Agent项目
- GitHub Stars: 21.6万+(持续增长中)
- Commits: 超过7,690次
- Contributors: 895人
- 技术栈: Python 88.5% + TypeScript 8.1%
- 内置Skills: 118个bundled + 12个optional
- 支持的模型Provider: 33个(OpenAI、Anthropic、OpenRouter、千问、GLM、Kimi等)
- 支持的平台: Telegram、Discord、Slack、WhatsApp、Signal、Email等12+平台
这些数字背后的工程量是巨大的:一个支持多平台、多模型、具备自进化能力的Agent系统,远不是「给LLM套个壳」那么简单。
二、核心架构:三层记忆系统详解
Hermes Agent最核心的创新,在于它的三层记忆架构(Three-Layer Memory System)。这不是简单的「把对话历史存下来」,而是一套经过精心设计的层次化信息管理机制,每一层解决不同的问题,对应认知科学中不同类型的记忆。
2.1 为什么需要三层?单层记忆的问题
在讨论三层架构之前,我们先理解单层记忆的局限性。
方案一:把所有历史都塞进上下文(Naive Context Injection)
这是最简单的方案,但问题显而易见:
- API成本爆炸式增长:每轮对话都在往context里追加历史,token费用翻倍
- 模型性能下降:上下文越长,模型的「近期关注」能力越弱
- 检索效率为零:想找某次特定对话,只能顺序扫描
方案二:只存最近N条消息(Fixed Window)
解决了成本问题,但丢失了长期知识。上个月解决过的bug,今天又遇到了,还是要从零分析。
方案三:外部向量数据库(Vector Retrieval)
这是当前的主流方案,ChromaDB、Pinecone、Milvus都可以用。但向量检索的问题是:相似度匹配不等于精确召回。「上次那个关于数据库优化的讨论」可能因为措辞不同而检索不到。
Hermes Agent的三层架构,正是对以上三种方案局限性的逐一破解。
2.2 Layer 1:内置记忆(Built-in Memory)
最底层,也是每次对话必定激活的记忆层。
两个核心文件:
~/.hermes/memories/
├── MEMORY.md # Agent个人笔记(~2,200字符上限)
└── USER.md # 用户画像(~1,375字符上限)
这两个Markdown文件的特点是:始终加载,始终可用。在每次会话开始时,它们会被注入到system prompt的最前面,模型可以立即访问。
<!-- MEMORY.md 示例 -->
# MEMORY.md - Agent Personal Notes
## 环境配置
- 工作目录:~/projects/myapp
- 常用语言:Python 3.11, TypeScript
- 包管理器:uv(首选), npm
## 使用约定
- 重大操作前必须确认
- 数据库修改使用trash替代rm
- commit message遵循Conventional Commits规范
## 技术发现
- 发现了uv比pip快3-5倍的包安装速度
- PostGIS在处理地理空间查询时比纯SQL效率高很多
<!-- USER.md 示例 -->
# USER.md - About Your Human
- **Name:** Alex
- **What to call them:** Alex
- **Timezone:** Asia/Shanghai
- **Preferred Language:** 中文
## Context
- 全栈工程师,主要做Web开发和数据管道
- 喜欢简洁直接的代码风格
- 对性能优化有强烈兴趣
- 在Mac上开发,Linux服务器部署
为什么是字符上限,而不是token上限?
这是一个非常精明的设计决策。用字符数而非token数作为上限,意味着:
- 开发者可以直观地估算文件大小(不用考虑模型的tokenize规则)
- 在不同模型间切换时,上限行为是一致的
- 当模型换用不同的tokenizer时,不会突然「爆仓」
为什么选择Markdown格式?
Markdown是程序员最熟悉的纯文本格式,有几个关键优势:
- 可以用版本控制(git)管理记忆的演化历史
- 模型可以直接读取和修改,不需要序列化/反序列化
- 可以轻松实现diff对比,看Agent的记忆发生了什么变化
- 可以通过简单的文本操作实现记忆的「压缩」和「遗忘」
触发条件:所有会话都会写入这两个文件,但写入时机经过精心设计——不是在每次工具调用后写入,而是在会话结束、任务完成后才写入。这避免了「边工作边改记忆」导致的上下文混乱。
2.3 Layer 2:外部记忆提供者(External Memory Providers)
第二层是可插拔的外部记忆后端,这是Hermes Agent区别于其他框架的核心差异化设计。
内置记忆(Layer 1)的问题是容量有限,且只能存储「事实性知识」。当我们需要:
- 大规模语义搜索
- 跨用户的团队记忆共享
- 结构化的知识图谱
- 复杂的时间序列查询
就需要外部记忆系统来承接。Hermes Agent设计了统一的Provider接口,目前支持8个外部后端:
| Provider | 特点 | 适用场景 |
|---|---|---|
| Honcho | Nous Research自研,专为Agent优化 | 默认选择,个人使用 |
| Holographic | 向量+图数据库混合 | 复杂关系推理 |
| Mem0 | 专注于AI Agent记忆的服务 | 需要API服务架构 |
| Hindsight | 基于对话的自动总结 | 长程任务追踪 |
| OpenViking | 开源替代,私有部署 | 数据主权要求 |
| RetainDB | 结构化SQL存储 | 需要精确查询 |
| ByteRover | 字节跳动内部开源版 | 大规模团队 |
| Supermemory | 链接收集+记忆融合 | 信息收集型任务 |
关键设计约束:单Provider激活原则
虽然可以安装多个Provider,但同时只允许激活一个。这个设计避免了记忆冲突(同一信息在两个Provider里不一致)和性能浪费(不需要同时写两份记忆)。
# 典型的Provider配置
# ~/.hermes/config.toml
[memory]
provider = "honcho" # 当前激活的Provider
[memory.honcho]
storage_path = "~/.hermes/honcho_db"
enable_semantic_search = true
这个Provider模式的设计非常值得学习:它用接口抽象替代了具体实现,让记忆系统可以随着用户需求升级而迁移数据,而不需要重写业务代码。
2.4 Layer 3:会话搜索(Session Search)
第三层是按需触发的历史会话检索,基于SQLite + FTS5全文索引实现。
这是解决「历史回溯」问题的终极方案:当用户问「上次我们讨论的那个数据库方案是什么来着」,Agent需要能够精确找到那次对话,而不是靠模糊的向量相似度碰运气。
FTS5全文索引的工作原理:
-- 会话历史表
CREATE TABLE sessions (
id INTEGER PRIMARY KEY,
session_id TEXT,
created_at TIMESTAMP,
summary TEXT
);
-- FTS5虚拟表
CREATE VIRTUAL TABLE sessions_fts USING fts5(
session_id,
summary,
content='sessions',
content_rowid='id'
);
-- 精确检索示例
SELECT session_id, summary, created_at
FROM sessions s
JOIN sessions_fts fts ON s.id = fts.rowid
WHERE sessions_fts MATCH 'docker postgresql 性能优化'
ORDER BY rank
LIMIT 5;
这个检索粒度可以精确到「某次会话中的某个工具调用」,而不仅仅是「某次会话的主题」。
三层协同工作流:
用户提问
↓
Layer 1: 读取 MEMORY.md + USER.md → 获取Agent自身知识 + 用户偏好
↓
Layer 2: 查询外部Provider → 获取长期语义记忆
↓
Layer 3: FTS5检索 → 找到相关的历史会话
↓
三者合并 → 构建完整上下文 → LLM推理
这个协同流程的设计哲学是:每一层解决不同粒度的问题,层与层之间通过清晰的接口隔离,而不是一股脑儿地塞给模型。
三、自进化闭环:五环节实现「越用越聪明」
Hermes Agent的灵魂,不仅在于它能「记住」,更在于它能「学习」。自进化闭环(Self-Improving Loop)是它的第二项核心创新,也是最接近「通用人工智能」特性的部分。
3.1 闭环的五环节
自进化闭环包含五个环节,形成一个持续运转的飞轮:
完成任务
↓
策划记忆(Memory Planning)
↓
创建Skill(Skill Creation)
↓
Skill自改进(Skill Refinement)
↓
FTS5召回(Session Search)
↓
用户建模(User Profiling)
↓
(下一轮任务 → 回到策划记忆)
环节一:策划记忆(Memory Planning)
触发时机:每次任务完成后。
系统会评估:「这次任务中,哪些信息值得长期记住?」不是所有对话内容都值得记忆——Agent需要学会「判断什么重要」。
判断标准包括:
- 任务是否涉及多个工具调用(5次以上触发技能提取)
- 是否有异常处理逻辑值得记录
- 是否发现了新的环境配置或依赖关系
- 用户是否有特别的偏好反馈
# 伪代码:记忆策划逻辑
def should_remember(task_result):
if task_result.tool_call_count > 5:
return True # 复杂任务,值得提取模式
if task_result.error_count > 0:
return True # 错误处理,值得记录
if "preference" in task_result.user_feedback:
return True # 偏好反馈,需要更新USER.md
if task_result.novel_discovery:
return True # 新发现,值得加入MEMORY.md
return False
这个评估过程本身由LLM驱动——让模型自己判断什么是重要的,这也是为什么它能适应不同用户的不同关注点。
环节二:创建Skill(Skill Creation)
当策划记忆判断某类任务「值得提取模式」时,Agent会自动从这次经验中生成一个Skill文件。
Skill文件是Hermes Agent知识复用的基本单元,采用Markdown格式,包含:
# Skill: docker-postgres-performance-check
description: |
Docker + PostgreSQL 性能检查与优化诊断流程。
适用于数据库响应慢、连接池耗尽等场景。
triggers:
- "数据库慢"
- "postgres性能"
- "连接池"
- "docker postgres"
steps:
- name: check_slow_queries
command: |
docker exec <container> psql -U postgres -c \
"SELECT pid, duration, query FROM pg_stat_activity WHERE state='active' AND duration > interval '5 seconds';"
description: 检查超过5秒的慢查询
- name: check_connection_count
command: |
docker exec <container> psql -U postgres -c \
"SELECT count(*) FROM pg_stat_activity;"
description: 检查当前连接数
- name: check_buffer_hit
command: |
docker exec <container> psql -U postgres -c \
"SELECT sum(heap_blks_read) as read, sum(heap_blks_hit) as hit, \
round(100 * sum(heap_blks_hit) / sum(heap_blks_hit + heap_blks_read), 2) as ratio \
FROM pg_statio_user_tables;"
description: 检查缓存命中率
context:
- PostgreSQL Docker容器需要开启pg_stat_statements扩展
- 连接数超过max_connections的80%需要告警
examples:
- input: "docker里的postgres跑得很慢"
output: 执行上述三个检查命令,给出分析报告
这个Skill文件的价值在于:它把一次具体的经验,转化成了可复用的模板。下次遇到类似问题,Agent不需要重新摸索,可以直接调用这个Skill。
环节三:Skill自改进(Skill Refinement)
这是整个闭环中最「智能」的部分。
当一个现有Skill在某次执行中失败时(用户反馈结果不好,或者执行过程中出错),系统不会简单地跳过这个Skill,而是会分析失败原因,并更新Skill文件。
# 伪代码:Skill自改进逻辑
def refine_skill(failed_skill, task_result):
analysis = llm.analyze(
f"Skill '{failed_skill.name}' 失败了。"
f"失败原因:{task_result.error_message}。"
f"原始Skill内容:{failed_skill.content}。"
f"请分析失败原因并提出修改建议。"
)
# 分析结果可能包括:
# 1. 更新trigger条件(更精确地匹配场景)
# 2. 修改step顺序或参数
# 3. 添加新的错误处理分支
# 4. 更新context信息(边界条件)
if analysis.should_update:
skill.update(analysis.suggested_changes)
skill.add_learning_note(analysis.reasoning)
这种机制的效果是:Agent不仅「做事」,而且「反思做事的方式」。一个被使用了100次的Skill,会比初次生成时更加健壮和精准。
环节四:FTS5召回(Session Search)
当Agent遇到新任务时,会先搜索历史会话,找找看之前是否处理过类似的问题。
这个召回机制是「按需」的,不是每次都搜——只有当当前任务触发了某种「熟悉感」时才会启动。比如用户提到了一个项目名、一个bug id、一个文件路径,Agent会自动检查历史中是否有相关内容。
# 召回逻辑示例
async def retrieve_relevant_context(current_task):
# 提取当前任务中的关键实体
entities = extract_entities(current_task)
# 逐个检索
for entity in entities:
results = await session_search(entity)
if results:
return results # 找到匹配,返回上下文
# 没有直接匹配,尝试语义搜索
semantic_results = await semantic_search(current_task)
return semantic_results
环节五:用户建模(User Profiling)
最后一环是用户偏好的持续建模。
Hermes Agent会从对话中学习用户的:
- 技术栈和经验水平(据此调整解释深度)
- 沟通风格(简洁型还是详细型)
- 决策偏好(快速试错 vs 充分论证)
- 工作节奏(活跃时段、任务类型模式)
这些信息最终写入USER.md,形成Agent对用户的「心智模型」。当新会话开始时,这个模型会指导Agent的行为策略——比如面对专家用户会直接给方案,面对新手用户会给出更多解释。
3.2 认知科学的对应关系
如果我们把自进化闭环与认知科学中的记忆类型对应起来,会发现设计者显然借鉴了认知科学的框架:
| 认知科学概念 | Hermes Agent实现 | 功能 |
|---|---|---|
| 程序性记忆(Procedural) | Skill文件 | 怎么做事的流程知识 |
| 语义记忆(Semantic) | MEMORY.md | 事实性、概念性知识 |
| 情景记忆(Episodic) | SQLite会话历史 | 具体经历的时间序列 |
| 工作记忆(Working) | 当前会话上下文 | 当前任务所需的即时信息 |
这种对应关系不是偶然的——设计者显然在构建一个仿生记忆系统,用计算的方式实现人类记忆的层级化特性。
四、Skill系统:知识复用的工程实践
4.1 Skill的分类
Hermes Agent内置了118个bundled Skills和12个optional Skills,覆盖了日常开发中最常见的场景:
Shell与系统管理类
- 文件操作、进程管理、日志分析
- Docker容器管理、Kubernetes操作
- Git高级操作(rebase、bisect、stash管理)
编程开发类
- 代码审查(Code Review)
- 测试用例生成
- 调试辅助(性能分析、内存泄漏检测)
- API文档生成
数据处理类
- SQL查询优化
- CSV/JSON数据处理
- 数据库迁移
- 数据可视化脚本生成
AI与模型类
- Prompt优化
- 模型输出评估
- RAG pipeline构建
- Fine-tuning数据集准备
4.2 Skill的加载与调用
当Agent处理用户请求时,会经历一个「Skill匹配」的过程:
# Skill匹配逻辑
async def find_relevant_skills(user_message):
# 1. 精确触发词匹配
exact_matches = []
for skill in all_skills:
for trigger in skill.triggers:
if trigger.lower() in user_message.lower():
exact_matches.append(skill)
break
# 2. 如果没有精确匹配,用语义搜索
if not exact_matches:
semantic_matches = await semantic_skill_search(user_message)
return semantic_matches
# 3. 返回匹配结果(按相关性排序)
return rank_by_relevance(exact_matches, user_message)
4.3 Skill的编写规范
Skill的编写质量直接决定了Agent能否正确复用。这里有几个关键设计原则:
描述要具体,不要泛泛而谈:
# ❌ 不好的描述
description: "数据库优化相关操作"
# ✅ 好的描述
description: |
PostgreSQL慢查询分析与优化流程。
适用场景:查询响应超过1秒、EXPLAIN ANALYZE显示Seq Scan过多、
索引失效或缺失、连接池耗尽等问题。
Step要有原子性:
每个step应该是独立的、可单独执行的。一个step不要做太多事情,这样当某个step失败时,可以精准定位问题。
examples是关键的质量门控:
examples字段用具体输入输出对来验证Skill的有效性。在Skill自动生成后,系统会运行这些examples,确保输出符合预期。
4.4 技能库的位置与管理
~/.hermes/skills/ # 用户自定义技能
├── docker-postgres-performance.md
├── api-error-debugger.md
└── git-conflict-resolver.md
~/.hermes/bundled_skills/ # 内置技能(不要直接修改)
├── shell/
├── docker/
├── git/
└── ...
这是一个非常实用的分离设计:内置Skill在更新版本时会覆盖,用户自定义Skill存放在独立目录,不会被覆盖。这种「可扩展的内核」模式,是优秀框架的共同特征。
五、多平台网关:消息路由的架构设计
5.1 网关的整体架构
Hermes Agent的多平台支持是通过一个统一的**消息网关(Gateway)**实现的,而不是为每个平台单独写适配代码。
用户消息(来自任意平台)
↓
Gateway Router(统一路由层)
↓
消息格式化(统一格式转换)
↓
Agent Core(核心处理逻辑)
↓
响应格式化(适配目标平台)
↓
各平台适配器(Platform Adapters)
↓
平台原生消息(Slack/Discord/Telegram...)
这个设计的核心思想是:把「跨平台」的问题,从「每个平台各写一套」,变成「写一个核心 + 多个轻量适配器」。
5.2 支持的平台
目前支持的平台包括:
即时通讯类
- Telegram(最推荐,API友好)
- Discord(适合社区Bot场景)
- Slack(企业场景)
- Signal
邮件与通知类
- Email(SMTP/IMAP集成)
- 系统通知(OS原生通知)
命令行与开发类
- CLI(终端直接交互)
- Web Dashboard(浏览器界面)
5.3 平台适配器的实现
每个平台适配器只需要处理两件事:
- 接收:把平台原生的消息格式 → 统一格式
- 发送:把统一响应格式 → 平台原生格式
# 统一消息格式
class UnifiedMessage:
sender: str # 发送者标识
content: str # 文本内容
attachments: list # 附件列表
timestamp: datetime # 时间戳
channel: str # 频道/群组
platform: str # 来源平台
# Telegram适配器示例
class TelegramAdapter:
def receive(self, update) -> UnifiedMessage:
return UnifiedMessage(
sender=update.message.from_user.id,
content=update.message.text,
attachments=self.extract_attachments(update.message),
timestamp=update.message.date,
channel=str(update.message.chat.id),
platform="telegram"
)
def send(self, unified: UnifiedMessage, chat_id: str):
self.bot.send_message(
chat_id=chat_id,
text=unified.content,
parse_mode="Markdown"
)
5.4 多平台同时在线
Hermes Agent支持同时连接多个平台,并且能够根据不同平台的特性调整交互策略:
- Telegram:支持Markdown格式,可以发送图片、文件、按钮
- Discord:支持Embed、Slash Commands
- Slack:支持Block Kit UI、线程回复
- Email:纯文本优先,附件处理为独立消息
这种「同一Agent,不同面孔」的体验,是它区别于单平台Bot的关键。
六、33个模型Provider:多模型架构
6.1 Provider设计
Hermes Agent支持33个模型Provider,这是通过一个统一的Provider接口实现的:
class ModelProvider(ABC):
@abstractmethod
async def complete(self, messages: list[Message]) -> str:
"""给定对话历史,返回模型补全"""
pass
@abstractmethod
async def count_tokens(self, text: str) -> int:
"""计算token数量(用于上下文管理)"""
pass
@abstractmethod
def get_max_context(self) -> int:
"""返回该模型的最大上下文长度"""
pass
这个接口设计非常干净,隐藏了各Provider之间的差异(API格式、认证方式、错误处理等),让核心逻辑可以完全不关心「用的是什么模型」。
6.2 主流Provider一览
| Provider | 说明 |
|---|---|
| OpenAI | GPT-4o、GPT-4o-mini等 |
| Anthropic | Claude 3.5 Sonnet、Claude 3 Opus |
| Gemini 1.5/2.0系列 | |
| OpenRouter | 聚合200+模型,统一API |
| 千问(Qwen) | Qwen3-Max、Qwen3-72B等 |
| 智谱(GLM) | GLM-5.2(开源榜第一) |
| Kimi | K2.5、K3(2.8万亿参数) |
| DeepSeek | DeepSeek-V3 |
| Grok | Grok-3 |
| Nous | Nous原生模型系列 |
6.3 模型选择的策略
有了这么多Provider,怎么选择?这涉及到一个在实际部署中非常重要的问题:性能与成本的平衡。
# 模型选择策略
class ModelSelector:
def select(self, task: Task) -> ModelProvider:
if task.type == "quick_clarification":
return self.fast_cheap # 简单问答,用便宜的模型
elif task.type == "code_generation":
return self.code_specialist # 写代码,用擅长代码的模型
elif task.type == "complex_reasoning":
return self.reasoning_powerhouse # 复杂推理,用最强模型
elif task.cost_budget == "low":
return self.open_source # 预算有限,用开源模型
return self.default # 默认选择
这个策略模式让不同任务可以使用最合适的模型,而不是「一刀切」地全部用最强模型(成本高)或全部用最便宜模型(效果差)。
七、深度代码实战:构建一个自定义记忆Provider
为了更好地理解Hermes Agent的架构,我们来实现一个自定义的外部记忆Provider。这个例子展示如何在不修改核心代码的情况下扩展记忆系统。
7.1 Provider接口定义
# providers/base.py
from abc import ABC, abstractmethod
from typing import Optional
from dataclasses import dataclass
@dataclass
class MemoryEntry:
id: str
content: str
created_at: float
tags: list[str]
access_count: int = 0
class MemoryProvider(ABC):
"""外部记忆Provider的统一接口"""
@abstractmethod
async def store(self, entry: MemoryEntry) -> bool:
"""存储一条记忆"""
pass
@abstractmethod
async def retrieve(
self,
query: str,
top_k: int = 5
) -> list[MemoryEntry]:
"""基于文本检索记忆"""
pass
@abstractmethod
async def update_access(self, entry_id: str) -> None:
"""更新访问计数(用于热度排序)"""
pass
@abstractmethod
async def compress(self, max_entries: int) -> int:
"""压缩记忆,保留最重要的N条"""
pass
7.2 实现一个基于SQLite的简单Provider
# providers/simple_sqlite_provider.py
import sqlite3
import json
from pathlib import Path
from typing import Optional
from providers.base import MemoryProvider, MemoryEntry
class SimpleSQLiteProvider(MemoryProvider):
"""基于SQLite的简单记忆Provider"""
def __init__(self, db_path: str = "~/.hermes/memories.db"):
self.db_path = Path(db_path).expanduser()
self._init_db()
def _init_db(self):
self.conn = sqlite3.connect(str(self.db_path))
self.conn.execute("""
CREATE TABLE IF NOT EXISTS memories (
id TEXT PRIMARY KEY,
content TEXT NOT NULL,
created_at REAL NOT NULL,
tags TEXT,
access_count INTEGER DEFAULT 0
)
""")
# 创建全文搜索索引
self.conn.execute("""
CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts
USING fts5(id, content, content='memories', content_rowid='rowid')
""")
self.conn.commit()
async def store(self, entry: MemoryEntry) -> bool:
try:
self.conn.execute("""
INSERT OR REPLACE INTO memories
(id, content, created_at, tags, access_count)
VALUES (?, ?, ?, ?, ?)
""", (
entry.id,
entry.content,
entry.created_at,
json.dumps(entry.tags),
entry.access_count
))
# 同步更新FTS索引
self.conn.execute("""
INSERT INTO memories_fts(id, content)
VALUES (?, ?)
""", (entry.id, entry.content))
self.conn.commit()
return True
except Exception as e:
print(f"存储记忆失败: {e}")
return False
async def retrieve(
self,
query: str,
top_k: int = 5
) -> list[MemoryEntry]:
cursor = self.conn.execute("""
SELECT m.id, m.content, m.created_at, m.tags, m.access_count,
bm25(memories_fts) as rank
FROM memories m
JOIN memories_fts fts ON m.id = fts.id
WHERE memories_fts MATCH ?
ORDER BY rank
LIMIT ?
""", (query, top_k))
results = []
for row in cursor:
results.append(MemoryEntry(
id=row[0],
content=row[1],
created_at=row[2],
tags=json.loads(row[3]) if row[3] else [],
access_count=row[4]
))
return results
async def update_access(self, entry_id: str) -> None:
self.conn.execute("""
UPDATE memories SET access_count = access_count + 1
WHERE id = ?
""", (entry_id,))
self.conn.commit()
async def compress(self, max_entries: int) -> int:
"""基于访问频率和新鲜度的混合压缩策略"""
cursor = self.conn.execute("""
SELECT id FROM memories
ORDER BY
access_count * 0.3 +
(created_at / 1e9) * 0.7
DESC
LIMIT -1 OFFSET ?
""", (max_entries,))
to_delete = [row[0] for row in cursor]
for entry_id in to_delete:
self.conn.execute("DELETE FROM memories WHERE id = ?", (entry_id,))
self.conn.commit()
return len(to_delete)
7.3 在配置中启用自定义Provider
# ~/.hermes/config.toml
[memory]
provider = "custom_sqlite" # 使用我们的自定义Provider
[memory.custom_sqlite]
db_path = "~/.hermes/my_memories.db"
max_entries = 500 # 最多保留500条记忆
这个例子展示了Hermes Agent Provider模式的力量:在不修改一行核心代码的情况下,我们用约100行代码实现了一个完全自定义的记忆系统。如果你需要把记忆存储在PostgreSQL中、MongoDB中,甚至云对象存储中,只需要实现同一个接口即可。
八、生产级部署:从安装到调优
8.1 安装流程
Hermes Agent的安装设计得非常友好,一行命令搞定:
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
安装脚本会自动处理:
- Python 3.11+ 环境(如果没有)
- uv包管理器(Nous推荐的新一代Python包管理工具)
- Node.js环境(用于Web Dashboard)
- 项目依赖的数据库、API客户端等
安装完成后,初始化配置:
hermes setup
这个命令会引导你:
- 选择默认模型Provider
- 配置API Key
- 选择初始启用的平台(Telegram/Discord等)
- 设置工作目录和记忆存储路径
8.2 Docker部署(推荐生产环境)
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY . /app
# 安装uv(比pip快5倍)
RUN pip install uv
# 安装依赖
RUN uv sync --frozen
# 非root用户运行
RUN useradd -m hermes
USER hermes
CMD ["python", "-m", "hermes_agent", "start"]
# docker-compose.yml
services:
hermes:
build: .
volumes:
- ./config:/home/hermes/.hermes
- hermes-data:/home/hermes/.hermes/data
environment:
- HERMES_DEFAULT_MODEL=openai/gpt-4o
- HERMES_LOG_LEVEL=info
restart: unless-stopped
ports:
- "3000:3000" # Web Dashboard
volumes:
hermes-data:
8.3 性能调优
上下文窗口管理:
# 控制每次加载的记忆量,避免context溢出
MAX_MEMORY_TOKENS = 8000 # 记忆占用最多8000 tokens
MAX_HISTORY_TURNS = 20 # 最近20轮对话
def build_context(memory_entries, session_history):
# 按优先级排序:记忆 > 最近对话 > 相关历史
context_parts = []
remaining_tokens = MAX_MEMORY_TOKENS
# 先加记忆
for entry in memory_entries:
if remaining_tokens <= 0:
break
tokens = estimate_tokens(entry.content)
if tokens <= remaining_tokens:
context_parts.append(entry)
remaining_tokens -= tokens
# 再加最近对话
for turn in session_history[-MAX_HISTORY_TURNS:]:
if remaining_tokens <= 0:
break
tokens = estimate_tokens(turn.content)
if tokens <= remaining_tokens:
context_parts.append(turn)
remaining_tokens -= tokens
return context_parts
冻结快照(Frozen Snapshot)优化:
这是防止API成本翻倍的技巧。当前的记忆系统需要把MEMORY.md和USER.md注入到每次请求的system prompt中。但每次对话,这些内容其实是不变的——只有用户的新消息在变化。
# LLM API前缀缓存优化
# 大多数LLM API服务(OpenAI、Anthropic等)会缓存相同的system prompt前缀
# 如果每次请求的system prompt开头部分完全相同,缓存命中后这部分不收费
# 所以关键设计是:system prompt的结构要稳定
SYSTEM_PROMPT_TEMPLATE = """{memory_section}
{user_profile_section}
{skill_section}
---
当前任务:{current_task}
"""
这样,包含了记忆内容的{memory_section}和{user_profile_section}部分,会被API服务缓存。只有不断变化的任务描述部分会被计费。
九、与其他框架的横向对比
9.1 与OpenClaw的对比
这是一个有意思的对比,因为OpenClaw正是运行当前会话的框架。两者虽然都是Agent框架,但设计哲学有显著差异:
| 维度 | Hermes Agent | OpenClaw |
|---|---|---|
| 核心焦点 | 记忆与自进化 | 工具链与任务执行 |
| 记忆系统 | 三层架构(内置+外部+会话) | MEMORY.md单文件 + 会话历史 |
| 自进化能力 | 完整闭环(策划→创建→改进) | 依赖外部Skill机制 |
| 多模型支持 | 33个Provider | 模型路由 |
| 平台支持 | 12+消息平台 | 主要桌面端 |
| 开源协议 | MIT | 闭源/私有部署 |
| 语言侧重 | Python(88%) | TypeScript/Node.js |
| 记忆格式 | Markdown + SQLite | Markdown文件 |
Hermes Agent在「记忆持久化」上更深入,OpenClaw在「工具链集成」上更强大。两者不是非此即彼的关系——实际上,已经有开发者尝试在OpenClaw中集成Hermes Agent的记忆能力。
9.2 与LangChain Agents的对比
LangChain的Agent(如ReAct、Plan-and-Execute)没有内置的持久化记忆系统,这是它被Hermes Agent「弯道超车」的主要原因。LangChain的优点是工具生态丰富,但作为个人助理使用时,每次都是「白板」启动,用户体验远不如Hermes Agent。
十、局限性与挑战
任何技术都有其局限性,Hermes Agent也不例外。诚实地面对这些局限,才能真正判断它是否适合自己的场景。
10.1 记忆膨胀问题
随着使用时间增长,MEMORY.md会逐渐逼近字符上限。当前的压缩策略(基于LRU或访问频率)可能会在压缩时丢失一些重要的长期记忆。这是一个开放的研究问题:如何判断一段记忆「是否仍然重要」?
10.2 技能冲突
当多个Skill对同一场景都有处理逻辑时,Agent需要做出选择。当前版本使用「后注册的Skill优先」策略,但在复杂场景下,这可能导致非预期行为。
10.3 幻觉风险
Skill自改进环节中,LLM对自己失败的「反思分析」可能产生幻觉——把不相关的原因当成失败根因,然后生成错误的修复方案。如何在这个环节引入验证机制(让Agent在新场景中实际测试改进后的Skill)是一个未解决的问题。
10.4 隐私考量
记忆系统天然涉及隐私问题。当Agent记住了你的密码、工作习惯、私人偏好时,这些数据的安全性完全取决于本地存储的保护程度。对于处理敏感信息的企业用户来说,这需要额外的安全加固。
十一、总结:记忆即智能
回望AI Agent的发展历程,我们经历了:
- 第一阶段:纯prompt工程——prompt写得好,Agent就表现好
- 第二阶段:工具调用工程——给Agent装备更多工具
- 第三阶段:工作流编排——多步骤任务的流程设计
而Hermes Agent指向的方向,是第四阶段:记忆驱动的持续学习。
真正的智能不在于单次任务的完成度,而在于能否从经验中学习并进化。当一个Agent能够在多次任务中积累知识、把成功经验固化为技能、从失败教训中更新策略,它才真正从「工具」变成了「伙伴」。
三层记忆架构让信息有了清晰的层次,自进化五环节让知识有了流动的方向,Provider模式让系统有了扩展的弹性。这些设计组合在一起,构成了一个在工程上扎实、在概念上清晰的系统。
更重要的是,Hermes Agent的开源性质,意味着这些设计思想可以被整个社区检验、改进和复用。当一个框架的核心思想成为公共知识,行业的整体水平就会向前推进。
这不是终点,而是一个新起点的开始。
关于作者:本文基于Hermes Agent开源项目(MIT License)的源码分析和公开文档撰写,所有代码示例均为基于接口设计的示例代码,非直接摘自源码。
参考资源:
- GitHub: https://github.com/NousResearch/hermes-agent
- 官方文档: https://docs.hermesagent.dev
- Nous Research: https://nousresearch.com