Clipping 微信公众号

Hermes,补上了 Agent 记忆系统最关键的一块

by jilolo 原文 ↗
Created: 2026-05-16

公众号名称:Halo咯咯

作者名称:jilolo

发布时间:2026-04-30 12:11

想必Hermes Agent对于大家来说已经极其不陌生了,今天我们就从Hermes开源的代码路径中来看下:它怎么构建提示词状态,怎么持久化会话,怎么刷新记忆,又怎么查询过去的对话。

简短地说:Hermes 不是只有一个记忆系统。它有四个。

  1. 一个很小、经过筛选的提示词记忆,存放在 MEMORY.mdUSER.md

  2. 一个可以搜索的 SQLite(轻量级嵌入式数据库)历史会话档案,通过 session_search 暴露出来。

  3. 由智能体管理的技能,充当过程记忆。

  4. 一个可选的 Honcho 层,用于更深的用户建模。

把这些串起来的关键设计很简单:为了缓存,让提示词保持稳定;其他东西都交给工具。

那我们直接进入正题。

Hermes 的上下文结构

在理解记忆之前,先要理解 Hermes 实际发送给模型的是什么。

系统提示词大致是这样组装的:

[0] 默认智能体身份
[1] 面向工具的行为指导
[2] Honcho 集成块(可选)
[3] 可选系统消息
[4] 持久化的 MEMORY.md 快照
[5] 持久化的 USER.md 快照
[6] 技能索引
[7] 上下文文件(AGENTS.md、SOUL.md、.cursorrules、.cursor/rules/*.mdc)
[8] 日期/时间 + 平台提示
[9] 对话历史
[10] 当前用户消息

这很关键,因为 Hermes 优化的是服务商侧提示词缓存,也就是让模型服务商复用稳定的提示词前缀,从而降低延迟和成本。源码里的提示词构建器对此写得很明确:稳定前缀应该尽可能长时间地保持稳定。

只这一个决定,就能解释 Hermes 记忆架构里的大部分设计。

如果某条信息每一轮都应该可用,Hermes 会尽量把它压得很小,然后只注入一次。如果它很大、属于历史信息,或者只是偶尔有用,Hermes 就把它移出提示词,等需要时再检索。

第一层:持久化的提示词记忆

内置记忆系统小得有点出人意料。

Hermes 把持久记忆存在 ~/.hermes/memories/ 下的两个文件里:

文件用途限制
MEMORY.md智能体关于环境、约定、工具怪癖、经验教训的笔记2,200 字符
USER.md用户画像:偏好、沟通风格、身份1,375 字符

这并不多。合起来大约 1,300 个 token。

而且这是有意为之。

会话开始时,Hermes 会加载这两个文件,把它们渲染成一个提示词块,然后在整个会话期间冻结这个快照。会话中途写入的内容会立刻持久化到磁盘,但不会修改已经构建好的系统提示词。那些变更只会在新会话开始时出现,或者在压缩触发提示词重建之后出现。

渲染出来的格式大概是这样:

══════════════════════════════════════════════
MEMORY (your personal notes) [67% — 1,474/2,200 chars]
══════════════════════════════════════════════
User's project is a Rust web service at ~/code/myapi using Axum + SQLx
§
This machine runs Ubuntu 22.04, has Docker and Podman installed
§
User prefers concise responses, dislikes verbose explanations

这里有几个细节我很喜欢。

  1. 它限制的是字符数,不是 token 数。

这样一来,记忆逻辑就不依赖具体模型。Hermes 不需要为了判断记忆是否已满,就去适配某个模型专属的 token 计数方式。

  1. 它使用简单的分隔符文件格式。

条目之间用 § 分隔。没有向量数据库,没有自定义二进制存储。就是普通文本文件。

  1. 它有意把系统提示词里的记忆压得很小。

这可能是整个设计里最重要的一点。Hermes 并不想把完整历史塞进提示词记忆。它只希望最高价值的事实留在那里。

  1. 它把记忆视为经过整理的状态,而不是日记。

这正是 Hermes 和 OpenClaw 很不一样的地方。

OpenClaw 的每日日志有一种追加式记录的味道。Hermes 明确往相反方向走。工具模式定义和测试说得很清楚:

  • 保存用户偏好

  • 保存环境事实

  • 保存反复出现的纠正

  • 保存稳定约定

  • 不保存任务进度

  • 不保存会话结果

  • 不保存临时 TODO 状态

说到底,Hermes 希望 MEMORY.mdUSER.md 始终保持热、紧凑,并且适合缓存。

memory 工具

Hermes 通过一个 memory 工具管理这些文件。它有三个动作:

  • add

  • replace

  • remove

当前工具表面上没有真正的读取动作,因为记忆已经在会话开始时注入到提示词里了。

一个不错的易用性细节是:replaceremove 使用子字符串匹配。你不需要内部 ID,只要传入现有条目里独一无二的一段文本即可。

例子:

memory(
    action="replace",
    target="memory",
    old_text="dark mode",
    content="User prefers light mode in VS Code, dark mode in terminal"
)

系统还会拒绝完全重复的内容,并在记忆进入提示词之前拦截危险内容。源码会扫描记忆条目,查找提示词注入、凭据外泄、SSH 后门提示以及不可见 Unicode 字符等模式。

这很合理。任何写入记忆的东西,实际上都会成为未来系统提示词的一部分。

第二层:用 session_search 做情景式回忆

如果说 MEMORY.mdUSER.md 是 Hermes 的热记忆,那么 session_search 就是它的长尾回忆系统。

所有历史会话都会存到 ~/.hermes/state.db。这是一个 SQLite 数据库,里面有:

  • sessions

  • messages

  • FTS5(SQLite 的全文搜索扩展)全文搜索索引

  • 通过 parent_session_id 连接起来的父子会话谱系

当模型需要回忆之前某次对话里的内容时,Hermes 搜索的不是 MEMORY.md。它搜索会话数据库。

流程大致如下:

对历史消息做 FTS5 搜索
-> 按会话分组结果
-> 解析父/子会话谱系
-> 加载最匹配的几个会话
-> 围绕相关匹配截断转录文本
-> 用便宜的辅助模型总结每个会话
-> 把聚焦后的回顾交还给主模型

这和那些试图对每一条记忆笔记做语义索引的系统,哲学上很不一样。

Hermes 基本上是在说:

  • 始终注入的记忆要小

  • 真正的历史放进 SQLite

  • 只有需要时才搜索历史

  • 搜到结果后先总结,再交给模型

这个设计很务实。

也比把漫长历史一股脑塞进每次提示词里机智。

文档把 session_search 描述为用来回答这类问题的工具:

  • “Did we discuss this last week?”

  • “What did we do about X?”

  • “As I mentioned before…”

换句话说,MEMORY.md 用来保存持久事实,session_search 用来做情景式回忆。

第三层:压缩和记忆刷新

Hermes 另一个聪明的地方,是它在压缩长对话之前会做什么。

随着会话变长,Hermes 最终会总结对话中间部分,以便留在模型上下文窗口以内。但总结是有损的,重要事实可能会消失。

所以 Hermes 会先做一次记忆刷新。

压缩前,它会注入一条合成的 system/user 指令,大意是:

  • 会话即将被压缩。

  • 保存任何值得记住的内容。

  • 优先保存用户偏好、纠正和反复出现的模式,而不是任务细节。

然后它额外跑一次模型调用,而且只开放 memory 工具。

如果模型判断某些东西应该熬过这次压缩,就会在对话被总结掉之前,把它写进 MEMORY.mdUSER.md

这是个很好的模式。

它给了模型最后一次机会,在对话中段被折叠之前,把耐久信息提炼出来。

更好的是,压缩之后,Hermes 会让缓存的系统提示词失效并重建,从磁盘重新加载记忆。这意味着压缩前刚刷进去的任何内容,都会成为下一个稳定提示词快照的一部分。

所以流程是:

长对话
→ 把持久事实刷新到记忆
→ 压缩旧轮次
→ 重建提示词
→ 带着更小的上下文和更新后的记忆继续

这种设计让 Hermes 看起来像一个真正的记忆架构,而不是临时外挂的笔记仓库。

第四层:作为过程记忆的技能

Hermes 的记忆故事不只是事实和转录文本。

它还有技能。

技能放在 ~/.hermes/skills/ 下,像可复用的知识文档。文档明确把它们称为智能体的过程记忆。

当 Hermes 发现一个不简单的工作流,修好一个棘手问题,或者学会一种更好的做法时,它可以把这些保存为技能,之后再复用。

这事很关键。

大多数记忆系统只关注语义回忆:名字、偏好、事实和总结。但智能体也需要记住怎么做事,而不只是记住发生过什么。

Hermes 通过把过程知识和提示词记忆分开来处理这个问题:

  • MEMORY.md / USER.md 保存紧凑、持久的事实

  • session_search 做情景式回忆

  • skills 保存可复用工作流

这里还有一个不错的 token 效率技巧。Hermes 不会把每个技能完整注入提示词。它只注入一个紧凑的技能索引,只有需要时才加载完整技能内容。

这样,过程记忆保持可用,但不必每一轮都支付完整 token 成本。

第五层:用 Honcho 做更深的用户建模

然后是可选的 Honcho 层。

如果本地记忆是 Hermes 经过整理的笔记本,那么 Honcho 就是它对更丰富用户模型的一次尝试。

默认情况下,Honcho 会以 hybrid 模式和内置记忆系统并行运行。它会增加:

  • 跨会话用户建模

  • 跨机器、跨平台连续性

  • 对用户上下文的语义搜索

  • 关于用户或 AI 同伴的、由 LLM 生成的辩证式回答

有意思的是,Hermes 集成它时并没有破坏提示词缓存。

第一轮和后续轮次是不一样的。

在一个会话的第一轮,预取到的 Honcho 上下文可以烘进缓存的系统提示词里。

到了后续轮次,Hermes 会避免修改那段稳定的系统提示词。它只是在 API 调用时,把 Honcho 召回内容附加到当前用户轮次上。这意味着:

  • 稳定前缀仍然稳定

  • 提示词缓存仍然有效

  • 第 N 轮可以消费第 N-1 轮之后后台预取到的上下文

这是一个很聪明的折中。

Honcho 自己还会建模两个同伴:

  • 用户

  • AI 助手

所以 Hermes 不只是想记住你。它也可以随着时间推移,建立对自身的表征。

这既酷,又有点疯狂。

Hermes 和 OpenClaw 有什么不同

OpenClaw

  • 记忆更接近 Markdown 优先的存储方式

  • 每日日志和长期记忆文件是主要事实来源

  • 记忆召回更依赖对存储笔记做混合搜索

Hermes

  • 提示词记忆被压得很小

  • 会话历史存在 SQLite,而不是提示词记忆文件里

  • 过去的工作通过 session_search 召回

  • 过程记忆被推入技能

  • 更深的用户建模可选地交给 Honcho

这里的关键洞察是:Hermes 比 OpenClaw 更有缓存意识。

OpenClaw 更偏向“记忆就是可搜索的存储知识”。Hermes 更偏向“记忆是热工作集加冷检索层”。

我确实觉得,这是生产级智能体更应该走的方向。

不是所有东西都值得住进系统提示词。

Hermes 做对了什么

看完仓库和文档之后,我觉得 Hermes 做对了三件大事。

  1. 它把热记忆和冷召回分开了。

这是核心的架构胜利。

总是重要的东西,放进小小的提示词记忆。只是偶尔重要的东西,搜索时再找。

  1. 它把提示词稳定性当成一等约束。

很多智能体系统谈记忆,却不谈缓存。Hermes 显然两者都在意。

冻结快照、延迟提示词更新、按轮次注入 Honcho、压缩后的提示词重建,这些都指向同一个设计原则:如果你想要好的延迟和成本,就不要随便改你的提示词。

  1. 它承认记忆是复数。

Hermes 没有假装一个存储层能解决所有问题。

它有:

  • 语义画像记忆

  • 情景式会话回忆

  • 通过技能实现的过程记忆

  • 通过 Honcho 可选实现的更高阶用户建模

这更接近智能体实际需要的东西。

结论

Hermes 的记忆系统不是一个巨型知识库,也不是换了个名头的向量库。它是一个分层的连续性架构。

中心是一小块经过整理的提示词记忆:MEMORY.mdUSER.md。外面是一套可搜索的 SQLite 历史,用于情景式回忆。再往外,是用于复用流程的技能系统。如果启用 Honcho,Hermes 还会在这一切之上加一层更深的用户模型。

最让我印象深刻的,是它背后的设计原则:记忆应该让智能体持续有用,同时不破坏提示词稳定性。

真正难的地方就在这里。

不是记得更多。而是在正确的层里,用正确的成本,记住正确的东西。

如果你喜欢这篇文章,别忘了 ​_关注​_我们!


AI在持续学习,你我也该更新了。


cover_image

Original jilolo Halo咯咯


内容效果不满意?点此反馈

输入关键词开始搜索