记忆不是幻觉(上):Hermes 怎么存住"认识你"这件事
会成长的AI:Hermes Agent 底层精讲 · 第四篇
这个系列的第一篇,我从一个场景开头:连续用 AI 工作三天,第四天打开新对话,它又把你当陌生人了。那篇我说 Hermes 用"持久记忆"解决了这个问题,但没展开讲。
这一篇就来把这件事说清楚。
Hermes 的记忆系统有四层,这篇讲前两层:持久化记忆文件和全文检索会话数据库。下一篇讲后两层:技能库和 Honcho 用户建模。四层合起来,才构成它说的"真正记住你"。
在进入技术细节之前,先想清楚一个问题:为什么 AI 的"记忆"是个难题?
大模型的记忆困境
大模型本质上是一个函数:给定输入,产生输出。它没有"状态",每次调用都是独立的。你今天和它说的话,不会自动影响明天它处理另一段话的结果。
业界对这个问题有不少解法。最常见的是 RAG(检索增强生成):把历史信息存起来,用的时候检索出来塞进上下文。这是对的方向,但存什么、怎么存、什么时候取,每套系统的设计差别很大,效果也差别很大。
Hermes 的设计思路是分层处理:不同重要程度、不同使用频率的信息,放进不同的存储层,用不同的方式取出来。这个分层设计是它的记忆系统最值得理解的部分。
先建立一个类比框架
在拆解每一层之前,先建立一个直觉模型,会更容易理解各层的分工。
想象你有一个新来的私人助理,你们要长期共事。他会用这样的方式管理和你相关的信息:
桌上的便利贴:把你最重要、最常用的背景信息写在便利贴上,贴在显眼的地方,每次工作前都先扫一眼。比如"客户偏好英文沟通""项目截止日是每周五""不要用 PowerPoint,用 Keynote"。便利贴空间有限,只放最关键的、需要每次都用到的内容。
档案柜:所有历史会议记录、邮件往来、项目文档,按照时间和主题分类归档。平时不会全部翻出来看,但当你说"上周我们讨论过那个问题,你还记得吗",他能走到档案柜前,找到那份记录,给你一个准确的答复。
Hermes 记忆系统的前两层,对应的就是这两样东西:持久化记忆文件是便利贴,FTS5 会话数据库是档案柜,而 session_search 工具是那个负责查档案的助理。
第一层:持久化记忆文件
持久化记忆文件存在 ~/.hermes/memories/ 目录下,有两个文件:
MEMORY.md,存放关于你的工作环境、项目背景、习惯偏好这类信息。比如"用户在上海,UTC+8 时区""主力语言是 Python 3.12""项目用 pyproject.toml 管理依赖""不喜欢代码注释超过 20%"。
USER.md,存放对你这个人的描述——沟通风格、工作方式、价值判断倾向等更偏"人设"的内容。比如"用户倾向于先自己探索,卡住了再问""对安全性比性能更敏感""回复喜欢直接给结论,不要铺垫"。
这两个文件的内容,每次会话开始时都会被注入进 System Prompt,也就是说 AI 每次回复之前都带着这些背景信息,不需要你重新介绍自己。
两个文件有字符上限:MEMORY.md 默认上限约 2200 字符(大约 800 个 Token),USER.md 约 1375 字符(约 500 个 Token)。限制存在是有道理的:这两个文件是"每次都要带着"的内容,越大每次对话的基础成本越高。官方的建议是把这里存的内容当便利贴用——只放真正每次都需要的核心事实,不要变成一份长篇自我介绍。
AI 怎么决定什么值得记住
这是整个记忆系统里我觉得设计最有意思的地方:记什么,是 AI 自己判断的,不是你手动填的。
Hermes 的判断逻辑大致分三类:这是一个纠正或者明确的指令吗?是的话存进 MEMORY.md。这是一个偏好或者行为模式吗?是的话存进用户画像 USER.md。这是一个关于环境或者约定的事实吗?也存进 MEMORY.md。
举个例子:你在对话里说"以后帮我写 commit message 时,第一行不要超过 50 个字符,动词用原形开头",Hermes 会把这条规则写进 MEMORY.md。你说"我一般下午效率更高,上午喜欢处理邮件",Hermes 会把这条写进 USER.md。
不是每轮对话都会触发写入,后台有一个定期的"review nudge"机制,在轮次结束后异步触发一个独立的小任务来判断是否需要更新记忆,不会阻塞用户正在等待的回复。这样的设计既保证了记忆的及时更新,又不让用户感知到等待。
默认配置下,AI 可以自由写入记忆,不需要你确认。如果你希望每次记忆变更都经过你批准,可以在配置文件里把 write_approval 设为 true。对隐私比较在意的用户,这是一个值得打开的选项。
第二层:FTS5 全文检索会话数据库
持久化记忆文件解决了"常驻背景"的问题,但它有个明显的局限:空间太小,只能放最核心的事实。
更大量的信息——你和 Hermes 所有的历史对话,包括每一条消息、每一次工具调用的结果——全部存在 ~/.hermes/state.db 这个 SQLite 数据库里,随着时间积累,这里会存下几十甚至几百次会话的完整记录。
这些内容不会每次都塞进上下文(那会直接把上下文窗口撑爆),而是通过 session_search 工具按需取用。
session_search 的三种模式
session_search 工具根据你传入的参数,自动推断应该用哪种模式工作,共有三种:
browse 模式(不传参数时触发):列出最近的会话列表,带有标题、时间戳、消息条数和内容预览。相当于翻看档案柜的目录索引。
discover 模式(传入搜索关键词时触发):用 FTS5 全文检索在所有历史会话里搜索相关内容。返回内容包括匹配片段、匹配消息前后各 5 条消息的上下文窗口,以及"bookend"——也就是这个会话最开始的 3 条和最末尾的 3 条消息。这个"bookend"设计很聪明:会话开头通常有任务背景,结尾通常有结论总结,光看这六条消息就能大致判断这个会话是否值得深入阅读。
scroll 模式(传入 session_id 和 around_message_id 时触发):以某条特定消息为中心,前后展开一个窗口,用于在已找到相关会话之后进一步浏览上下文细节。相当于在翻到了档案的某一页之后,继续往前翻或往后翻。
两张 FTS5 索引表
数据库里不是只有一张全文检索表,而是两张:
messages_fts:使用标准分词器建立的索引,适合大多数情况下的英文和空格分隔语言的搜索。messages_fts_trigram:使用 trigram 分词器的索引,专门用于支持中文、日文、韩文等 CJK 字符,以及英文的子串搜索(比如搜索"docker"能匹配到"Dockerfile")。
对我们中文用户来说,这个 trigram 索引是很贴心的设计。你在历史对话里搜索"部署方案",它不是在做字符串包含匹配,而是走了专门的中文搜索引擎,准确率和召回率都比简单字符串匹配强很多。
一个关键细节:不做 LLM 摘要
session_search 返回的是数据库里的原始消息,没有经过 LLM 二次摘要,也没有截断处理。这和很多 RAG 系统不同——那些系统通常会把检索出来的内容再喂给一个小模型做总结,才给你看。
Hermes 的做法是还原原始消息,让模型直接读真实对话,不经过中间层的信息损耗。这样做的代价是返回的内容可能更多、更原始;好处是信息完整,不会因为摘要步骤丢掉关键细节。
两层记忆的分工
现在可以把两层记忆的分工说清楚了:
持久化记忆文件(MEMORY.md + USER.md) 回答的是"关于你,有哪些事是每次对话都应该知道的"。它是随时可见的便利贴,永远在桌上,随时可以扫一眼。
FTS5 会话数据库(session_search) 回答的是"我们之前有没有讨论过某件具体的事"。它是装满历史记录的档案柜,平时不打开,有需要的时候能找到。
官方的表述也很清晰:记忆文件是为了"总是应该在上下文里的关键事实",而 session_search 是为了"上周我们讨论过 X,你还记得吗"这类按需回溯的查询。
两者配合起来,就覆盖了日常使用中两类最核心的"记住你"场景:一类是长期稳定的偏好和背景,一类是特定时间点发生的具体事情。
一个我觉得值得关注的细节
在研究这一块的时候,有一件事让我有点意外:Hermes 的记忆写入默认是不需要你确认的。AI 认为值得记住的内容,会直接写进你的 MEMORY.md。
这是一个设计上的取舍。好处是自动化程度高,你不需要每次都手动说"记住这件事",减少了使用摩擦。代价是记忆文件的内容不完全在你的控制下——如果 AI 判断失误,把一些你不想让它"永远记住"的内容写进了文件,你需要手动去清理。
如果你对这一点比较敏感,打开 write_approval: true 是合理的选择。虽然多了一个确认步骤,但你能精确控制哪些内容进入 AI 的长期记忆。这一点我自己的用法是先用默认配置跑一段时间,看看它自动写入的内容是否符合预期,再决定要不要开审批。
下一篇预告
这篇讲的是记忆系统的前两层:持久化记忆文件(随时在线的便利贴)和 FTS5 会话数据库(按需查询的档案柜)。
下一篇讲后两层:技能库——AI 把解决过的问题提炼成可复用方法论的地方;以及 Honcho 用户建模——比 USER.md 更深、更动态的用户理解层。这两层是 Hermes "越来越懂你"里最有意思的部分,也是和 OpenClaw 架构差异最大的地方。
夜雨聆风