我研读了 Hermes Agent 的记忆系统,它弥补了 OpenClaw 的设计缺陷

6
分类技术博客
作者Manthan Gupta
来源跳转
发表时间

内容

如果你之前阅读过我关于 ChatGPT 记忆Claude 记忆Clawdbot 记忆 的文章,你应该已经知道我一直在思考同一个问题:这些智能体究竟如何记忆?

Hermes 智能体对我来说尤其有趣,因为这次我不必仅仅通过行为来逆向工程一切。Hermes 是开源的,其 代码仓库文档 都是公开的。因此,我可以直接研究构建提示状态、持久化会话、刷新记忆和查询过去对话的代码路径。

简而言之:Hermes 并不是只有一个记忆系统,它有 四个

  • 一个非常小、精心策划的提示记忆,存储在 MEMORY.md 和 USER.md 中

  • 一个通过 session_search 暴露的可搜索的 SQLite 过去会话存档

  • 智能体管理的技能,类似于程序记忆

  • 一个可选的 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 个令牌合并。

而且这是故意的。

在会话开始时,Hermes 加载这两个文件,将它们渲染成一个提示块,然后 冻结该快照以备后续会话使用。会话中的写入会立即持久化到磁盘,但不会改变已经构建的系统提示。这些更改仅在新会话开始时或在压缩触发的提示重建后才会显示。

渲染的格式如下:

══════════════════════════════════════════════ MEMORY (您的个人笔记)[67% — 1,
474/2,
200 字符] ══════════════════════════════════════════════ 用户的项目是基于 Axum+SQLx 的 Rust Web 服务,位于~/code/myapi § 该机器运行 Ubuntu 22.04,安装了 Docker 和 Podman § 用户更喜欢简洁的回应,不喜欢冗长的解释

这里有几个我喜欢的微妙设计选择:

1. 使用字符限制,而不是令牌限制

这使得记忆逻辑模型与模型无关。Hermes 不需要模型特定的标记化来决定记忆是否已满。

2. 使用简单的基于分隔符的文件格式

条目用 § 分隔。没有向量数据库。没有自定义二进制存储。只是纯文本文件。

3. 故意保持系统提示记忆小

这可能是整个设计中最重要的部分。Hermes 并不试图将其整个历史都塞进提示记忆中。它只想要最高价值的事实在那里。

4. 将记忆视为精选状态,而不是日记

这与 Clawdbot 有很大不同。

Clawdbot 有一种类似追加日志的每日日志风格。Hermes 明确地向相反方向推动。工具模式和测试表明:

  • 保存用户偏好

  • 保存环境事实

  • 保存重复修正

  • 保存稳定约定

  • 不保存任务进度

  • 不保存会话结果

  • 不保存临时待办事项状态

事实上,Hermes 希望 MEMORY.mdUSER.md 保持热度、紧凑和缓存友好。

Hermes 通过一个具有三个操作的 memory 工具来管理这些文件:

  • 添加

  • 替换

  • 删除

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

一个好的可用性细节是 replaceremove 使用 子字符串匹配。您不需要内部 ID。您只需传递现有条目的唯一子字符串。

例子:

memory(
  action = "replace",
  target = "memory",
  old_text = "暗黑模式",
  content = "用户更喜欢 VS Code 中的浅色模式,终端中的暗黑模式"
)

系统还会拒绝精确的重复内容,并在任何内容进入提示记忆之前阻止危险的内容。源代码会扫描记忆条目中的提示注入模式、凭证提取字符串、SSH 后门提示和不可见的 Unicode 字符。

这是有道理的。写入记忆的任何内容都有效地成为未来的系统提示的一部分。

第二层:session_search 用于情景回忆

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

所有过去的会话都存储在 ~/.hermes/state.db 中,这是一个 SQLite 数据库,包含:

  • 会话表

  • 消息表

  • FTS5 全文搜索索引

  • 通过 parent_session_id 的血统链接

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

管道如下:

FTS5 搜索过去的消息
-> 按会话分组结果
-> 解析父/子血统
-> 加载顶部匹配会话
-> 在相关匹配周围截断转录
-> 用廉价的辅助模型总结每个会话
-> 将聚焦的总结返回给主模型

这是一种非常不同的哲学,不同于试图对每个记忆笔记进行语义索引的系统。

Hermes 基本上是在说:

  • 保持总是注入的记忆小

  • 将真实的历史存储在 SQLite 中

  • 仅在需要时搜索历史

  • 在返回之前总结结果

这是一个务实的设计。

它也比盲目地将长历史记录塞进每个提示中更便宜。

文档将 session_search 描述为一种回答问题的方式,例如:

  • “我们上周讨论过这个问题吗?”

  • “我们对 X 做了什么?”

  • “就像我之前提到的那样……”

换句话说,MEMORY.md 用于 持久事实,而 session_search 用于 情景回忆

第三层:压缩和记忆刷新

Hermes 另一个巧妙的部分是它在压缩长对话 之前 会发生的事情。

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

所以 Hermes 首先会进行 记忆刷新

在压缩之前,它会注入一个合成的系统/用户指令,基本上是这样说的:

会话正在被压缩。
保存值得记住的内容。
优先保存用户偏好、更正和重复模式,而不是任务特定的细节。

然后它只运行一个额外的模型调用,并使用 memory 工具。

如果模型决定某些内容应该在压缩后仍然存在,它会将其写入 MEMORY.mdUSER.md,然后再总结对话。

这是一个非常好的模式。

它给了模型最后一次机会来提炼在对话中间部分被压缩之前的持久部分。

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

所以流程是:

长对话
→ 将持久事实刷新到记忆中
→ 压缩旧回合
→ 重建提示
→ 继续使用较小的上下文和更新的记忆

这使得 Hermes 感觉像一个实际的记忆架构,而不是一个附加的笔记存储。

第四层:技能作为程序记忆

Hermes 的记忆故事不仅仅是事实和记录。

它还有 技能

技能位于 ~/.hermes/skills/ 下,类似于可重用的知识文档。文档明确将它们描述为智能体的 程序记忆

当 Hermes 发现一个非平凡的工作流程、修复一个棘手的问题或学习一种更好的方法来做某事时,它可以将该技能保存并在以后重用。

这是一件大事。

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

Hermes 通过将程序知识与提示记忆分离来处理这个问题:

  • MEMORY.md / USER.md 用于紧凑的持久事实

  • session_search 用于情景回忆

  • 技能用于可重用的工作流程

这里还有一个很好的令牌效率技巧。Hermes 不会盲目地将每个技能注入提示中。它注入一个紧凑的 技能索引,并且仅在需要时加载完整的技能内容。

这使得程序记忆在不需要支付每次回合的全部令牌成本的情况下是可用的。

第 5 层:Honcho 实现更深入的用户建模

接下来是可选的 Honcho 层。

如果本地记忆是 Hermes 精心策划的笔记本,那么 Honcho 就是它尝试建立的更丰富用户模型。

Honcho 默认以 混合 模式与内置记忆系统一起运行。它添加了:

  • 跨会话用户建模

  • 跨机器和跨平台连续性

  • 基于用户上下文的语义搜索

  • 关于用户或 AI 对等体的辩证的、LLM 生成的答案

有趣的部分在于 Hermes 如何在不破坏提示缓存的情况下集成 Honcho。

第一轮对话与后续对话

在会话的第一轮,预取的 Honcho 上下文可以被整合到缓存的系统提示中。

后续对话中,Hermes 避免修改稳定的系统提示。相反,它仅在 API 调用时将 Honcho 回忆附加到当前用户对话。这意味着:

  • 稳定的前缀保持稳定

  • 提示缓存仍然有效

  • 对话轮次 N 可以在对话轮次 N-1 之后在后台预取上下文

这是一个非常聪明的折衷方案。

Honcho 本身也对两个对等体进行建模:

  • 用户

  • AI 助手

因此,Hermes 不仅仅是试图记住你,还可以随着时间的推移建立起对自己的表示。

这既酷又有点激进。

Hermes 与 Clawdbot 的区别

由于我最近写了关于 Clawdbot 的文章,因此进行明确的比较是值得的。

Clawdbot

  • 记忆更接近于基于 Markdown 的存储

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

  • 记忆回忆依赖于存储笔记的混合搜索

Hermes

  • 提示记忆被积极地限制

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

  • 过去的工作通过 session_search 回忆

  • 程序记忆被推送到技能中

  • 更深入的用户建模可选地委托给 Honcho

这里的关键洞察是,Hermes 比 Clawdbot 更关注缓存

Clawdbot 更倾向于“记忆作为可搜索的知识存储”。Hermes 更倾向于“记忆作为热工作集加上冷检索层”。

我实际上认为这是生产代理的正确方向。

并非所有内容都值得存储在系统提示中。

Hermes 的优势

在阅读了代码仓库和文档后,我认为 Hermes 有三个明显的优势。

1. 它将热记忆与冷回忆分开

这是核心架构优势。

对于总是重要的内容,使用小提示记忆。对于只在某些时候重要的内容,使用搜索。

2. 它将提示稳定性作为一等公民的约束

许多代理系统在谈论记忆时没有谈论缓存。Hermes 显然同时关注这两者。

冻结快照、延迟提示更新、逐轮 Honcho 注入和压缩会话重建都指向相同的设计原则:如果想要获得良好的延迟和成本,不要随意修改提示。

3. 它承认记忆是多元的

Hermes 并不假装一个存储就能解决所有问题。

它具有:

  • 语义配置文件记忆

  • 片段会话回忆

  • 通过技能的程序记忆

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

这对代理实际需要什么是一个更现实的看法。

结论

Hermes 的记忆系统不是一个巨大的知识库,也不是一个 glorified 矢量存储。它是一个分层连续性架构。

在中心,是一个微小的精选提示记忆:MEMORY.mdUSER.md。围绕它的是一个用于片段回忆的可搜索 SQLite 历史。再往外,是一个用于程序重用的技能系统。如果启用 Honcho,Hermes 会在所有其他功能之上添加更深入的用户模型。

我最印象深刻的是其底层的设计原则:记忆应该帮助代理保持有用,而不会破坏提示稳定性

这才是真正的诀窍。

不是记住更多东西。而是记住正确的事情,在正确的层级,以正确的成本。

参考资料

评论

(0)
未配置登录方式
暂无评论