💡阅读指南

上一节我们给 Hermes 写了 SOUL.md,让它说话的腔调整个变了。那是风格文件,决定了 Hermes 是什么样的人。

这一节我们打开 ~/.hermes/memories/ 目录看看,里面还有两个文件。它们不负责腔调,负责的是另一件事:Hermes 关于用户和项目都知道些什么。

读完会知道:持久记忆的几份文件各管什么、Hermes 会自觉记住什么又刻意跳过什么、容量满了之后它怎么给自己腾空间。

2.1 从灵魂到记忆

之前我们打开 ~/.hermes/SOUL.md 的时候,在里面写了鲁迅和王小波。从此以后 Hermes 说话就带了一点冷幽默和刻薄,像换了一个人。那是风格文件,它定义了 Hermes 是什么样的人。

但 Hermes 还需要知道另一类信息:关于这个用户、这个项目、这台机器的各种情况。它干活的时候需要了解这些东西。

那么这些情况写在哪呢?

打开 ~/.hermes/memories/ 目录看看。里面有两个文件:MEMORY.md 和 USER.md。SOUL.md 不在这,它在上一级目录 ~/.hermes/ 下面。三种文件,三种职责,分开存放。

2.2 MEMORY.md 里到底有什么

官方文档对 MEMORY.md 的定义只有一句话:

Agent's personal notes — environment facts, conventions, things learned

翻译过来就是:Hermes 自己的备忘录,记录的是环境信息、项目约定和学到的教训。它的作用是存储 Hermes 需要知道的信息,而不是待办清单,不是备忘录数据,也不是对话记录。

Hermes 有一套自己的判断标准,什么该记、什么不该记,分得很清楚。

它会主动记这些

下面这些信息是我的 MEMORY.md 里记录的真实信息:

  • 环境信息,比如「gingery-wiki 项目位置:/Users/Desktop/gingery-wiki/,包含 blog、coze 等子项目,skills/ 下有软链接被多个子项目引用」
  • 项目约定,比如「macOS 上用 sips 命令压缩图片:iPhone 原图 4032×3024 压缩为 JPG,宽度 1920px,质量 75%,保留原图和压缩版两份」
  • 被纠正过的事,比如「DeepSeek 模型不支持图片识别,需要分析图片时切到 Claude 或 GPT」
  • 已完成任务的摘要,比如「skills-collection 目录已重命名为 skills,46 个软链接已完成同步更新」
  • 工作流技巧,比如「文件输出位置:项目相关的放项目目录内,一次性交付物放 ~/Desktop/,临时文件放 /tmp/ 用完清理」
  • 明确要求记住的事情,比如「只有一张截图/图片时,自动用 open 命令打开,不要只提路径」

这些都是 Hermes 自己判断并写入的。你不需要特意说「请记住……」,它会在干活的过程中自己判断什么东西值得留一份摘要。

它不会记这些

反过来,下面这些它不会浪费容量去记:

  • 琐碎的信息,比如你问了一句「Python 是什么」,这不值得占一个条目
  • 搜索引擎就能回答的,比如「Python 3.12 支持 f-string 嵌套」,搜一下就有
  • 原始数据,比如大段代码、日志文件、数据表格。容量太宝贵了,不该塞这些
  • 会话级的临时信息,比如一次性的文件路径、临时调试用的上下文
  • 已经在 SOUL.md 里写过的内容

判断原则其实很简单:这条信息如果忘掉了,下次干活会不会出问题?如果会,就记下来;如果不会,就跳过。

USER.md 也是一样的格式

MEMORY.md 旁边还有一个 USER.md,格式完全一样,也是按 § 符号分隔的一条条信息。区别只在于内容的方向:MEMORY.md 记的是项目和环境的事,USER.md 记的是用户本人的事。

打开看几条 USER.md 条目就明白了:

code
Full-stack developer, works remote (UTC+8). Uses VS Code + Neovim,
prefers TypeScript for backend and React for frontend. Has Docker
Desktop and PostgreSQL 16 installed locally.

这一条登记了用户的基本画像:技术栈、工作方式、时区。知道这些之后,Hermes 给建议时就会优先推荐用户熟悉的工具和语言。

code
用户喜欢先了解整体方案再动手。遇到不确定的操作时,先问清楚再做,
不要直接执行修改。项目命名偏好简洁,用下划线不用驼峰。

这一条是关于沟通和协作习惯的。Hermes 知道之后,做事之前会先出方案让用户确认,不会闷头就改。

code
截图只有一张时,直接 open 打开图片让用户看,不要只给路径。

这一条是具体的交互偏好。简短、明确,一条就把一个交互习惯说清楚了。

code
代码改完先跑测试再提 PR,不要在本地只编译通过就推送。
测试覆盖率不能低于 80%,新功能必须补对应测试。

这一条是关于工作流程规范的。用户有明确的代码提交流程要求,Hermes 知道之后,写完代码会自动补上测试,不用等用户来提醒。

对比 MEMORY.md 和 USER.md 的条目,有一个明显的差异:MEMORY.md 里记的是客观事实(项目路径、命令参数、依赖关系),USER.md 里记的是主观偏好(喜欢什么做法、讨厌什么步骤、什么沟通方式有效)。两类信息分开存放,各自有各自的容量上限,互不干扰。

2.3 三份文件,三种职责

这三个文件分布在两个目录里:SOUL.md 在 ~/.hermes/ 根目录下,MEMORY.md 和 USER.md 在 ~/.hermes/memories/ 子目录里。分工如下:

SOUL.md 定义 Hermes 是什么样的人:说话风格、行事准则、价值观。这是亲手写的,几乎不变。

MEMORY.md 是 Hermes 关于环境和任务的笔记。一条条信息摘要,按 § 符号分隔排列。

USER.md 是 Hermes 关于用户的笔记。名字、时区、沟通偏好、忌讳事项,都记在这里。

SOUL.md 和 MEMORY.md、USER.md 的区别在于:前者是风格,后面两个是记忆。风格文件没有硬性字符上限,但同样会注入到提示词里,写太长一样占 Token。记忆文件则有严格的字符限制,由系统强制控制上限,不会超。

可以用一张表对比一下:

文件 职责 谁来写 容量限制 典型条目数
SOUL.md 人格与风格 手动编辑 不适用
MEMORY.md 环境信息、约定、经验、已完成任务 Hermes 自动 2,200 字符 8-15 条
USER.md 姓名、时区、偏好、忌讳 Hermes 自动 1,375 字符 5-10 条

SOUL.md 就像角色设定卡,亲手写一次就够了。MEMORY.md 和 USER.md 像是便签条,Hermes 在工作过程中,觉得什么东西值得记住,就往上写一条。

2.4 容量满了怎么办

两份记忆文件各自有严格的字符上限,由 Hermes 系统强制控制:

文件 字符上限 等价 Token 典型条目数
MEMORY.md 2,200 字符 ~800 tokens 8-15 条
USER.md 1,375 字符 ~500 tokens 5-10 条

这些限制写在 ~/.hermes/config.yaml 里,可以改,但一般不需要动。

会话开始时,系统提示词里会显示当前使用情况:

code
MEMORY (your personal notes) [67% — 1,474/2,200 chars]

Hermes 看到这个百分比,就知道还有多少空间。

满了之后会发生什么

当 Hermes 试图写入一条新条目,但加上之后会超出限制时,memory tool 会返回一个错误:

JSON
{
  "success": false,
  "error": "Memory at 2,100/2,200 chars. Adding this entry
            (250 chars) would exceed the limit. Replace or
            remove existing entries first.",
  "current_entries": ["..."],
  "usage": "2,100/2,200"
}

Hermes 收到这个错误之后,会做四件事:

  1. 读取当前所有条目(错误响应里已经列出来了)
  2. 找出可以删除或合并的条目
  3. 用 replace 把相关的几条合并成更紧凑的描述
  4. 然后再把新条目写进去

最佳实践是:当使用率超过 80%(系统提示词里能看到),就提前做合并,不要等到满了报错再处理。

但如果不喜欢这种自动写入的感觉,Hermes 也提供了一个开关:

code
hermes config set memory.auto_write false

关掉之后,Hermes 不再主动写记忆文件。当它觉得值得记住什么的时候,会先问意见:「我想记住以下内容,你看行不行」。同意了才写。

不过一开始使用的时候建议开着这个功能。用一段时间之后再打开 MEMORY.md 和 USER.md 看看里面长什么样,一条条摘要忠实地记录了这段时间和 Hermes 一起做的各种事情。

2.5 冻结与安全

MEMORY.md 和 USER.md 在每次对话开始时会被读入内存,冻结成一份快照,然后注入到系统提示词里。

注意是冻结。会话开始之后,Hermes 可以通过它的 memory tool 追加或修改记忆。修改会立即写入磁盘文件,但不会更新当前会话的系统提示词。为什么呢?因为系统提示词一旦生成就固定了,中途改它会破坏 LLM 的前缀缓存,导致性能下降。所以会话内的修改,要到下一次新会话才生效。

这个冻结设计有一个直接后果:如果想在当前会话里删除一条已经生效的记忆,是做不到的。改完文件之后必须新开一个会话,新的提示词才会拿到更新后的版本。

另外还有一个可能被忽略的点:记忆文件是有安全检查的。因为记忆内容要注入到系统提示词里,Hermes 会在写入之前扫描是否存在注入攻击或敏感信息泄露的迹象,比如隐藏的 Unicode 控制字符、prompt injection 模式、凭据泄露等。匹配到威胁模式的内容会被直接拦截。

2.6 不只是 MEMORY.md

MEMORY.md 覆盖的是一个有限集。2,200 个字符只能存最重要的东西。但如果想知道「三周前跟 Hermes 讨论过的那个数据库迁移方案」,光靠它是不够的。

这时候就要用到 session_search 了。

Hermes 会把所有的对话记录存在 ~/.hermes/state.db 里。这是一个 SQLite 数据库,用 FTS5 加了全文索引。当提到「之前是不是提过 BrightCart?」,Hermes 会调用 session_search 工具,直接在数据库里搜索,返回匹配的对话片段。

📌FTS5 全称 Full-Text Search 5,是 SQLite 内置的全文搜索引擎。它的效果类似在微信聊天记录里搜关键词——输入「数据库迁移」,所有提到这个词的对话段落都能找出来。FTS5 在本地运行,不走网络,不消耗 Token,也不会上传任何数据。

MEMORY.md 和 session_search 的分工很清晰:

对比 MEMORY.md session_search
容量 ~2,200 字符 无限(所有历史会话)
速度 即时(在系统提示里) ~20ms FTS5 查询
Token 成本 每次对话固定开销 按需查询,不占提示词
用途 关键信息始终在手 查找具体的历史讨论
管理方式 Hermes 自动维护 自动记录

MEMORY.md 存的是应该永远知道的事,session_search 查的是曾经发生过的事

也可以直接在终端里翻看历史会话:

code
hermes sessions list

它会列出所有历史会话,找到之后可以前后翻阅上下文。

2.7 三层记忆体系

到现在为止我们涉及了三层不同的记忆,每层的生存期、精度和用途都不一样。

第一层叫持久记忆层,就是 SOUL.md、MEMORY.md 和 USER.md 这三份文件。每次对话开始时被读入并冻结,注入到系统提示词里。这是最重要的信息,永远在手边。

第二层叫技能层,在 ~/.hermes/skills/ 目录下。每个技能是一个文件夹,里面有一份 SKILL.md 描述什么时候调用、怎么做。它不跟记忆一样每次对话都加载,只在被触发时才进入上下文。

第三层叫会话检索层,就是前面说的 session_search。state.db 里的 FTS5 全文索引,按需搜索,搜到了才占 Token。

这三层的关系可以用一个金字塔来表示:

code
        ┌──────────
        │ 持久记忆    每次对话必加载
        │ SOUL.md     固定开销
        │ MEMORY
        │ USER
       ┌┴──────────
       │   技能层     条件触发
       │  SKILL.md   触发时才加载
      ┌┴────────────
      │  会话检索层   按需搜索
      │ state.db    免费查询
      └──────────────

这三层不是互斥的,而是协同工作的。持久记忆确保重要信息永远在手边;技能层确保可以复用的流程能被自动捕捉;会话检索层确保过去说过的话可以随时翻出来。它们合在一起,才是 Hermes 完整的记忆体系。

2.8 ■ 原来如此

回过头来看,MEMORY.md 本身只是一个 Markdown 文件。但有意思的是它背后的这套分层设计。

Hermes 把记忆分成了三层:持久记忆(SOUL.md + MEMORY.md + USER.md)保证最重要的信息每次对话都在手边;技能层把重复出现的流程自动封装成可复用的技能,不占提示词空间;会话检索层用 SQLite 全文索引存储所有历史,随查随用。

这个设计有什么值得借鉴的?三个关键词:分级、自动、按需。

持久记忆是分级:事实和偏好分开存,风格和记忆分开管,各自的容量上限独立控制。技能层是自动:不需要手动创建,反复做同一件事时 Hermes 自己就会把它打包成技能。会话检索是按需:不提前加载,查到了才消耗 Token。

如果你将来要设计自己的 Agent,也可以用这个思路:把最重要的信息放在提示词里(持久层),把可复用的流程做成插件或工具(技能层),把海量的历史数据放到外部数据库里按需搜索(检索层)。三层分开管,比把所有东西都塞进系统提示词要灵活得多。

2.9 ■ 学点英语

中文 English 音标 说明
持久记忆 Persistent Memory /pərˈsɪstənt ˈmeməri/ 跨会话保存并持续影响 Agent 行为的记忆
记忆条目 Memory Entry /ˈmeməri ˈentri/ 一条可被读取和复用的记忆记录
记忆检索 Memory Retrieval /ˈmeməri rɪˈtriːvəl/ 从保存的记忆中找回相关信息
记忆更新 Memory Update /ˈmeməri ˈʌpdeɪt/ 新增、修改或删除长期记忆的过程