所有文章
Hermes 的记忆究竟是怎么回事?

Hermes 的记忆究竟是怎么回事?

··阅读约 13 分钟·5,129 字·1 阅读
HermesAI记忆HindsightAgent源码解读

我是零一,同你一起见证AI颠覆世界。

Hermes 刚上手时很好用,感觉它能真的记住你,但用过一段时间后记忆就不行了,究竟怎么回事,本文同你一探究竟。

昨天我跟 Hermes 说:"把上次的 Docker 部署笔记总结一下。"它回我:"请使用 session_search 工具查询相关会话。"我又问它部署偏好,它说:"未找到相关记忆。"

不是它不想记。是 MEMORY.md 满了 96%,那条端口映射刚被新内容顶掉。USER.md 也已经 98%。再聊几个项目,Hermes 就要在"记这条新经验"和"删那条旧配置"之间做选择。

我试过把上限改大。三个月下来发现,容量只是表层问题。真正的麻烦是:你不知道什么该记、什么时候召回、旧信息怎么处理,以及它记住的东西到底能不能相信。

这篇不是安装教程。我按程序员视角拆开看:

  • Hermes 自带的记忆到底怎么跑
  • 第三方记忆值不值得接
  • 实测之后我自己的选择和配置
  • 给一个推荐起步配置

Hermes 还是装不下

刚开始用 Hermes 时我没觉得"记忆"是个问题。后来才知道 MEMORY.md 有 2200 字符限制。

用了几个月,几件事接连发生:

  • 它开始忘端口映射,因为我把它顶掉了
  • 它每次都把无关的旧偏好塞进 system prompt
  • 长会话里它的 system prompt 比对话本身还长

问题不在容量。问题是没有分层。什么该常驻、什么该按需查、什么该忘记、什么该记下来——这些没人管,全靠 Agent 自己判断。

这就引出两个现实问题:

  • Hermes 自己的记忆机制到底是怎么工作的?能不能撑住真实使用?
  • 第三方记忆(Mem0、Hindsight 等)能不能补上能力短板,代价是什么?

Hermes 自带记忆:源码视角

~/.hermes/ 下和记忆相关的核心只有 3 个文件:

~/.hermes/
├── config.yaml
├── memories/MEMORY.md
├── memories/USER.md
├── state.db
└── hermes-agent/
    ├── agent/agent_init.py
    ├── tools/memory_tool.py
    └── tools/session_search_tool.py

第一层:Markdown 笔记

agent_init.py 启动时创建 MemoryStore,调用 load_from_disk()MEMORY.mdUSER.md 读进内存,生成 _system_prompt_snapshot 注入 system prompt。全会话期间不变。

写入靠单一的 memory 工具,区分三种 action:

memory(action="add", target="memory", content="...")
memory(action="replace", target="memory", old_text="...", new_content="...")
memory(action="remove", target="user", old_text="...")

写入链路:

模型决定要不要记
   ↓
校验 action / target
   ↓
威胁模式扫描
   ↓
解析 § entries、检查字符上限
   ↓
文件锁
   ↓
临时文件 + atomic replace
   ↓
MEMORY.md / USER.md

几个不容易看到的细节:

  • 限制按字符算,不按 token 算。换模型不会让容量突然变化。
  • replace 用短字符串匹配,不用 entry ID。不够具体会被打回。
  • 每轮最多三次记忆整理失败,超过就让 Agent 先回答用户。这是为了防止它为了存一条偏好把整轮预算烧光。
  • drift protection:用 shell 或 patch 直接改坏文件,工具会拒绝并保留 .bak 快照。

第二层:SQLite 历史会话

session_search 工具直接查 state.db

state.db → SQLite message store → FTS5/BM25 → 原始历史消息

整个过程零 LLM 调用,返回的是数据库里的真实消息,不是重新生成的摘要。

看起来所有会话都被记住了,但实际 session_search 的调用率极低,一般要你主动叫它调,另外能搜到的信息总是不符合预期。

三个设计限制

  • 2200 字符硬上限,虽然可调但会消耗 token,Hermes 的记忆文件容量有限
  • 每次会话都全文注入。不管你问 Docker 还是公众号,所有核心记忆都挤进 system prompt。
  • 会话中写入不更新当前 prompt。调 memory 写入的内容,下一次 /new 才生效。这是为了保护 prefix cache,但用户体感就是"它写了但没用上"。

所以严格点说:

Hermes 自带两层持久化记忆:Markdown 是整理好的笔记,SQLite 是完整的历史档案。

挂上去的第三方记忆

config.yaml 里的 memory.provider 字段可以挂载一个外部 Provider。官方内置 8 个可选:

Provider类型关键差异
holographic本地 SQLite + FTS5零凭证、信任评分、HRR 检索
hindsightDaemon + PostgreSQL知识图谱、实体消解、反思
mem0云 API生态最大、自动去重
honcho云 API跨会话用户建模、辩证推理
openviking文件系统风格浏览
retaindb云 API7 类记忆类型
supermemory云 APIProfile 召回
byteroverCLI + tree分层检索

同时只能启用一个外部 Provider。不是做不到,是多个 Provider 会带来工具 schema 膨胀、重复召回和冲突写入。

挂载后,Provider 通过 MemoryProvider 接口接入:

memory:
  provider: hindsight

走这条链路:

agent_init.py → MemoryManager.add_provider() → provider.initialize()

它不是替换MEMORY.md 仍然在跑,第三方 Provider 只是 additive。

源码之外,我把当前主流的 Agent memory 方案看了一遍。最终选了本地 docker 一键部署的 Hindsight 首先体验了下。docker-compose 配置如下,端口可以自定义:

services:
  hindsight:
    image: ghcr.io/vectorize-io/hindsight:latest
    container_name: hindsight
    restart: unless-stopped
    ports:
      - "9999:9999"
      - "8089:8888"
    volumes:
      - ./data:/home/hindsight/.pg0
    environment:
      - HINDSIGHT_API_LLM_PROVIDER=openai
      - HINDSIGHT_API_LLM_API_KEY= # 你自己的key
      - HINDSIGHT_API_LLM_MODEL=deepseek-v4-flash
      - HINDSIGHT_API_LLM_BASE_URL=https://api.01story.com/v1
      - HINDSIGHT_API_HOST=0.0.0.0
      - HINDSIGHT_API_PORT=8888
      - HINDSIGHT_API_LOG_LEVEL=info
      - HF_ENDPOINT=https://hf-mirror.com

Hindsight 怎么工作

Hindsight 把过程拆成三个动作。

Retain:输入对话后,独立 LLM 抽取事实、实体、时间和关系,写入 memory bank。它不是原样保存聊天记录,写入本身就是一次知识加工。

Recall:TEMPR 同时跑语义、关键词、图关系和时间检索,再用 RRF 融合、cross-encoder 重排。

Reflect:先查 mental models,再查 observations,必要时回到 raw facts,循环检索后生成带引用的答案。

Hindsight 论文在 LongMemEval 上报告 91.4%,但这个数字依赖数据集、模型和配置。装上不会自动达到。

Hindsight 会自动记录实体和关系并提供了可视化,效果还不错,如下:

Image

一个常被忽略的时序限制

每轮的外部 Provider 块注入到 system prompt,但它是上一轮 prefetch 的结果,不是当前轮的:

第 N 轮:
  用户消息
   ↓
  system prompt(MEMORY.md 块 + 外部 Provider 块 [来自上轮])
   ↓
  Agent 回复
   ↓
  sync_turn 写入库
   ↓
  prefetch 用本轮 query 检索
   ↓
  缓存给下一轮

第 N+1 轮:
  用户消息
   ↓
  system prompt(MEMORY.md 块不变 + 外部 Provider 块 [新内容])
   ↓
  Agent 回复

第一轮永远召不回任何历史记忆。短句、问候语也经常被忽略。这是 Hermes 的通用机制,不是 Hindsight 专属。因为 system prompt 会不断变化,缓存命中会变得很糟糕。

Token 消耗

项目谁花多少
MEMORY.md 全文注入每轮必带约 2K tokens
Hindsight 自动 prefetch每轮注入默认 budget=mid,约 80 条事实,2.5K—2.8K tokens
Hindsight retain异步,每轮一次fact extraction 一次 LLM 调用
Hindsight reflect按需800—3000ms 内 1 次 LLM 调用
session_search必要时零 LLM,纯 SQLite FTS5

几个绕不开的现实问题

  • 自动写入不等于高质量写入。Retain 依赖 LLM,漏抽、错抽都会污染 observation。
  • 两套记忆不会自动保持一致。删 MEMORY.md 不会同步删 Hindsight。
  • 中文不是装完即最佳。默认 embedding 偏英文,中文效果还要看 embedding、reranker 和全文分词器。
  • 隐私和安全要自己管。Hindsight 的 Memory Defense 默认关闭且不回扫历史;MCP 默认无认证。

3 个省 token 的习惯

  • 控召回量recall_budgetmid 调到 low,召回条数从约 80 条降到 15—20 条;再配 recall_max_tokens=2048 兜底。
  • 一个领域一个会话。写公众号时让 Hindsight 召回 Docker 经验,反而干扰判断。聊完就 /new
  • MEMORY.md 只放高频稳定的事实。临时状态留在 session history。

装之前想清楚的 2 件事

  • 你的场景真的需要跨 30+ 个会话保持上下文吗?5—10 个会话的工作流,Hermes 内置 + session_search 已经够用。
  • 你能接受高额的 token 消耗吗?不写清楚这个流程,过几个月两边就开始打架。

小结

回到开头那两个问题:

  • Hermes 自带的记忆够不够? 简单场景够,复杂场景不够。容量硬上限、每次全文注入、会话中写入不立刻生效是三个核心限制。
  • 第三方记忆值不值得接? 看场景。5—10 个会话的工作流没必要接;长期跨项目偏好才值得。Hindsight 是有真本事的方案,但 token 消耗会上一个台阶,建议第三方记忆使用低成本模型

真正好用的 Agent 记忆,不是把所有事情永久记住,而是把重要的内容放在正确的层级,并在真正需要时准确找回来。

你的 MEMORY.md 用了多少?评论区报个使用率。

现在就能做的一件事:打开 ~/.hermes/memories/MEMORY.md,把"临时项目状态"那类条目删掉,给真正需要常驻的信息留空间。

本篇主要分析了下 Hermes 的记忆实现,下一期再一起看下当前主流的 AGENT 记忆工具的思路与方案。

觉得这篇有用的话,求一个点赞~3q,关注我了解 AI 最新动态。