前言:Agent 能保存一段偏好并不稀奇,真正难的是决定记什么、怎样召回、何时把一次经验升级成可复用流程,以及长期运行后如何整理这些内容。本文把 Hermes 的内置 Memory、外部 Provider、Background Review、Skill 和 Curator 放回同一条学习闭环。
系列导航:这组文章按“总览 → 入口 → 运行时 → 扩展 → 状态 → 协作 → 安全”的顺序展开;第一次阅读可以从总览开始,带着具体问题也可以直接跳到专题。
- 01|Hermes Agent 架构总览:从 Agent Loop 到智能体平台
- 02|Hermes Desktop 架构:Electron 如何连接本地 Python Agent
- 03|Hermes Agent 运行时:AIAgent、模型协议与工具执行
- 04|Hermes Agent 扩展体系:Skill、Plugin 与 MCP
- 05|实战:同一个 Markdown 检查器的 Skill、Plugin 与 MCP 三种实现
- 06|Hermes Agent 的持续学习系统:Memory、Review、Skill 与 Curator
- 07|Hermes Agent 的长会话架构:Prompt Cache、压缩与 SessionDB
- 08|Hermes Agent 的多 Agent 与自动化:从 Goal 到 Kanban
- 09|Hermes Agent 安全架构:审批、沙箱、回滚与凭据隔离
先分清历史、记忆和请求上下文
刚接触 Agent 记忆时,我很容易把“聊天历史、长期记忆、上下文压缩”混成一层。Hermes 的实现恰好说明了为什么必须拆开:SessionDB 保存证据,Memory 保存精选事实,Context Engine 决定某次请求实际携带多少历史。
| 状态层 | 回答的问题 | 主要存储 | 取用方式 |
|---|---|---|---|
| SessionDB | 过去具体发生了什么? | state.db 中的原始消息、Prompt、用量与关系 | 恢复会话、FTS5 session_search |
| Persistent Memory | 以后每次都值得知道什么? | MEMORY.md、USER.md,可叠加一个外部 Provider | 会话快照、按轮语义召回、Provider 工具 |
| Context Engine | 本次模型请求应该带什么? | 会话内状态或插件自己的索引 | select_context()、compress() |
flowchart LR
Turn[完成一个 Turn] --> DB[SessionDB 原始历史]
DB --> Search[session_search 精确回看]
Turn -->|可选精选写入| Builtin[内置精选记忆]
Turn -->|异步同步| External[外部 Memory Provider]
Builtin --> Snapshot[下次 Prompt 快照]
External --> Recall[下轮语义召回]
DB --> Engine[上下文引擎
Context Engine]
Engine --> Request[本次请求消息]
Snapshot --> Request
Recall --> RequestSessionDB 是事实底账,不是“模型脑内记忆”
SQLite 保存会话、消息、完整 System Prompt、模型用量、父子关系和压缩归档。session_search 直接在 FTS5 中查询真实消息,不需要再调用一个 LLM 做摘要;它适合回答“上周我们在哪个会话讨论过这个错误”,而不是把所有历史都塞进每轮 Prompt。
因此压缩也不等于删除历史。默认 in-place compaction 只替换当前会话的活跃消息视图,原始行会软归档并继续接受全文检索。Memory 写错了也不需要从 SessionDB 反推真相:可以回到原消息核对,再修正精选记忆。
记忆系统:文件快照、语义召回和后台学习
Hermes 的记忆不是一个单独向量库,而是两层可叠加机制:内置 MemoryStore 负责小而稳定的常驻事实,可选 MemoryProvider 负责更深的语义召回、用户建模与后端工具。两者可以同时工作,但外部 Provider 一次只允许激活一个。
内置 MemoryStore:有界、可读、由 Agent 自己维护
| 文件 | 保存什么 | 默认上限 | 在 Prompt 中的位置 |
|---|---|---|---|
MEMORY.md | 环境事实、项目约定、工具坑点、完成事项 | 2,200 字符 | Volatile tier 的冻结快照 |
USER.md | 用户身份、表达偏好、工作习惯和期望 | 1,375 字符 | Volatile tier 的冻结快照 |
文件位于当前 Profile 的 $HERMES_HOME/memories/。MemoryStore.load_from_disk() 在 Agent 初始化时读取、解析并安全扫描条目,然后生成本会话的 System Prompt 快照。memory 工具提供 add、replace 和 remove;replace/remove 使用唯一子串定位,底层通过锁、临时文件和原子 rename 更新。
它刻意不做“满了自动丢旧内容”。写入超过上限时,工具返回当前 entries 和容量错误,让 Agent 在同一 Turn 里先合并或移除,再重试。精确重复会被拒绝,隐形 Unicode、提示注入和凭据外传模式也会在进入未来 System Prompt 前被阻止。
flowchart TD
Start[新会话或 Prompt 重建] --> Load[读取 MEMORY.md 与 USER.md]
Load --> Scan[解析与安全扫描]
Scan --> Freeze[生成冻结 Prompt 快照]
Freeze --> Turn[对话循环
Conversation Loop]
Turn --> Tool[记忆增改删
memory add / replace / remove]
Tool --> Gate{写入审批开启?}
Gate -->|是| Pending[进入待审批队列]
Gate -->|否或批准| Atomic[原子更新磁盘文件]
Atomic --> Next[新会话或压缩重建后可见]冻结快照解释了一个常见疑问:当前 Turn 的 memory Tool Result 会告诉模型写入成功,但旧 Prompt 不会随即改字节;正常情况下,新内容到下个会话才成为常驻上下文。如果本会话发生上下文压缩,Prompt 重建路径会重新加载磁盘记忆,此时新条目也会进入更新后的快照。
外部 Memory Provider:每轮召回,但不污染干净历史
MemoryProvider 是另一份契约。它可以提供静态 system_prompt_block()、按问题召回的 prefetch()、Turn 完成后的 sync_turn()、Provider 专属工具,以及 session switch、compression、delegation 等生命周期 Hook。MemoryManager 负责选择、超时、工具路由和错误隔离。
sequenceDiagram
participant U as 用户消息
participant T as 回合前置阶段
Turn Prologue
participant M as 记忆管理器
MemoryManager
participant P as 外部记忆提供方
External Provider
participant L as 对话循环
Conversation Loop
U->>T: 非琐碎问题
T->>M: 回合开始与预取
on_turn_start + prefetch_all
M->>P: 有超时边界的 prefetch
P-->>M: 相关记忆
M-->>T: 记忆上下文边界
memory-context fence
T->>L: 写入 user api_content sidecar
L-->>U: 先交付最终回答
L->>M: 同步与排队预取
sync_all + queue_prefetch_all
M->>P: 单线程 FIFO 后台同步源码细节比“后台召回”四个字更精确:外部 prefetch 在独立线程执行,但 Turn 会在一个很短的超时边界内等待;超时或 Provider 异常就 fail-open,本轮不注入。召回内容被包进 <memory-context> fence,并和 Plugin context 一起写入当前 user message 的 api_content sidecar;数据库里仍保留干净的用户原话,同时以后重放能复现当时真正发给 Provider 的字节。
Turn 结束后的写入走另一条路:sync_all() 与 next-turn prefetch 进入单 worker 队列,保证 Turn N 先于 Turn N+1 落到外部后端,也避免一个慢网络调用让界面一直显示“运行中”。内置 memory 写入只有在真正提交成功、没有处于 staged 状态时,才通过 notify_memory_tool_write() 镜像给外部 Provider。
后台 Review:Hermes 的“自我学习”发生在哪里
项目首页强调 closed learning loop,源码对应的是 agent/background_review.py 和 Turn Finalizer 中的 nudge 计数。达到记忆或 Skill 的 review cadence 后,主回答先交付给用户,随后才启动一个后台 AIAgent fork,回看本轮是否出现值得保存的偏好、稳定事实、可复用步骤或对既有 Skill 的纠正。
flowchart LR
Turn[主 Turn 完成] --> Nudge{达到 Review cadence?}
Nudge -->|否| End[结束]
Nudge -->|是| Fork[后台 Review Agent]
Fork --> Whitelist[只允许 memory / skill 工具]
Whitelist --> Decide{值得长期保存?}
Decide -->|事实与偏好| Mem[写 MEMORY / USER]
Decide -->|可复用流程| Skill[创建或修补 Agent-owned Skill]
Mem --> Approval[可选写入审批]
Skill --> Approval
Approval --> Future[未来会话与任务复用]这个 fork 不重新连接外部 Memory Provider,避免一次 Review 自己又被召回、同步和二次学习;它共享父 Agent 的内置 MemoryStore,工具白名单限制为 memory/skills,自己的 nudge 计数也被关闭。操作者可以给 Memory 与 Skill 分别开启 write approval,也可以把 Review 路由到更便宜的 auxiliary model。之后的 Curator 主要维护 Agent-created Skills,并可按配置归档长期未使用的 bundled Skill;Hub Skill 始终不碰,自动流程从不硬删除,显式 purge 也会先留下 ledger 与可恢复 blob。
四种“记住”应该怎么选
| 机制 | 适合内容 | 何时进入模型上下文 | 主要代价 |
|---|---|---|---|
| MEMORY / USER | 少量关键事实与偏好 | 会话快照常驻 | 每轮固定 token;容量严格 |
| External Provider | 语义检索、用户建模、知识图谱 | 按轮召回或显式工具调用 | 网络延迟、外部数据治理 |
| session_search | 过去对话的原始证据 | 模型主动搜索时 | 需要形成检索查询 |
| Skill | “如何完成一类任务”的程序性知识 | 索引匹配后按需加载 | 需要维护与验证流程 |
从“记住事实”到“学会方法”
单独看文件记忆、向量召回或反思学习都不算新概念。Hermes 更有意思的地方,是把它们放进同一条可观察、可审批的生命周期:事实和偏好进入 Memory,可复用的方法进入 Skill,后台 Review 负责提炼,Curator 再整理 Agent 创建的 Skill。
flowchart TD
Turn[正常对话完成] --> Review[后台复盘
Background Review]
Review --> Decide{学到了什么?}
Decide -->|事实与偏好| Memory[记忆与用户文件
MEMORY.md / USER.md]
Decide -->|可复用方法| Skill[智能体创建的技能
Agent-created Skill]
Skill --> Curator[Curator 整理与归档]
Memory --> Recall[后续 Turn 召回]
Curator --> Recall
Recall --> Turn这条闭环仍然有边界:后台 Review 在主回答交付之后运行,使用受限工具集;Memory 和 Skill 写入可以要求审批;外部 Memory Provider 的召回以本轮 sidecar 进入请求,不会把数据库中的用户原话改写成召回文本。
Background Review 和 Curator 不是同一个后台任务
| 组件 | 运行时机 | 主要输入 | 主要输出 |
|---|---|---|---|
| Background Review | 主回答交付后的后台学习 | 刚完成的 Turn、现有 Memory 与 Skill | 新增或修改 Memory;创建、改进 Skill |
| Curator | 按周期审查 Skill Library | Agent-created Skill、使用记录与配置允许的候选 | 保留、整理或软归档低价值 Skill |
Review 关心的是“这一轮学到了什么”,Curator 关心的是“长期积累的 Skill 还值不值得继续占据发现面”。当前实现不会在后台静默硬删除 Skill;归档保留恢复路径,真正清理需要显式 purge。配置也可以允许 Curator 归档长期未使用的 Bundled Skill,但它的主要治理对象仍是 Agent 创建的内容。
参考资料与源码入口
题图来源:《湖、森林与公园》,创作者 IlonaBurschl,来自 Pixabay;依据 Pixabay Content License(Pixabay 内容许可)使用。