<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Memory-System | 有志者事竟成</title><link>https://www.liwenshen.com/tags/memory-system/</link><atom:link href="https://www.liwenshen.com/tags/memory-system/index.xml" rel="self" type="application/rss+xml"/><description>Memory-System</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>zh</language><lastBuildDate>Thu, 02 Jul 2026 10:00:00 +0800</lastBuildDate><image><url>https://www.liwenshen.com/media/icon_hu_dd5d76fef920c49e.png</url><title>Memory-System</title><link>https://www.liwenshen.com/tags/memory-system/</link></image><item><title>记忆系统：跨会话持久化的工程实现</title><link>https://www.liwenshen.com/note/hermes-agent-engineering/05-memory-system/</link><pubDate>Thu, 02 Jul 2026 10:00:00 +0800</pubDate><guid>https://www.liwenshen.com/note/hermes-agent-engineering/05-memory-system/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;系列：通过 Hermes 探秘 Agent 工程 | 第 5 篇&lt;/strong&gt;
上一篇：&lt;a href="https://www.liwenshen.com/note/hermes-agent-engineering/04-context-compression/"&gt;上下文压缩：让 Agent 在有限窗口里「记得住」&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="为什么-agent-需要记忆"&gt;为什么 Agent 需要「记忆」&lt;/h2&gt;
&lt;p&gt;Agent 有一个根本性缺陷：&lt;strong&gt;每次会话都是全新开始&lt;/strong&gt;。如果不做额外处理，你上次告诉 Hermes「我用了 Node.js 22」，这次它又问你「用哪个 Node 版本」——因为它对你一无所知。&lt;/p&gt;
&lt;p&gt;系统提示词的 Volatile 层包含了记忆快照（比如「用户偏好：简洁回复」），但那只是「这个会话内」的快照。一旦会话结束、新开会话，这些内容就完全没了。&lt;/p&gt;
&lt;p&gt;Hermes 的记忆系统解决的问题就是：&lt;strong&gt;把跨会话的信息持久化下来，并在每次对话开始时注入到系统提示词里&lt;/strong&gt;——让 Agent「记得你」。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="内置记忆两个文件没有固定-key"&gt;内置记忆：两个文件，没有固定 key&lt;/h2&gt;
&lt;p&gt;Hermes 的内置记忆&lt;strong&gt;不是&lt;/strong&gt;状态数据库里的 key-value 对，而是两个纯文本文件：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;~/.hermes/memories/MEMORY.md ← Agent 的个人笔记
~/.hermes/memories/USER.md ← 关于用户的信息
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;每个文件的内容就是一段由 &lt;code&gt;§&lt;/code&gt; 分隔符分割的自由文本列表，&lt;strong&gt;没有任何硬编码的 key&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;用户偏好用中文回复，风格简洁直接
§
当前项目使用 Vue 3 + FastAPI，数据库是 PostgreSQL 15
§
部署流程必须经过 DBA 审批，不能直接操作生产库
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;§&lt;/code&gt; 就是分隔符。没有 schema、没有 key 约束、没有枚举——Agent 写什么就是什么。&lt;/p&gt;
&lt;p&gt;记忆工具 &lt;code&gt;memory&lt;/code&gt; 只有两个 target：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#f92672"&gt;&amp;#34;enum&amp;#34;&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;memory&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;memory&lt;/code&gt; = Agent 自己的笔记（写入 MEMORY.md）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user&lt;/code&gt; = 关于用户的信息（写入 USER.md）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;存储规则是严格的——tool schema 描述里明确告诉模型什么不该存：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;❌ 任务进度（「PR 已提交」）&lt;/li&gt;
&lt;li&gt;❌ 临时 TODO（「今天要做 X」）&lt;/li&gt;
&lt;li&gt;❌ 7 天后会过时的信息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;只保存&amp;quot;能让未来少问用户一次&amp;quot;的事实。&lt;/p&gt;
&lt;p&gt;每个文件有独立的字符上限：&lt;code&gt;MEMORY.md&lt;/code&gt; 2200 字符，&lt;code&gt;USER.md&lt;/code&gt; 1375 字符——用字符数限制而非 token，是因为字符数与模型无关。&lt;/p&gt;
&lt;h2 id="外部记忆-provider不是-llm是外部服务"&gt;外部记忆 Provider：不是 LLM，是外部服务&lt;/h2&gt;
&lt;p&gt;Hermes 支持接入外部记忆 Provider（Honcho、Hindsight、Mem0、SuperMemory 等），但&lt;strong&gt;Provider 不是 LLM&lt;/strong&gt;——它们是独立的外部服务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Honcho&lt;/strong&gt;：基于嵌入向量的语义匹配&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hindsight&lt;/strong&gt;：基于会话的观察者模式&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mem0&lt;/strong&gt;：向量化记忆存储&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hermes 只允许&lt;strong&gt;同时注册一个外部 Provider&lt;/strong&gt;（通过 &lt;code&gt;MemoryManager.add_provider()&lt;/code&gt; 注册），防止工具 schema 膨胀和记忆语义冲突。内置存储始终可用，不依赖外部 Provider。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="记忆怎么加载到对话里"&gt;记忆怎么加载到对话里&lt;/h2&gt;
&lt;p&gt;这是最关键的问题：&lt;strong&gt;不同类型的记忆有不同的加载时机和注入位置。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="内置静态记忆--会话启动时写入-system-prompt"&gt;内置静态记忆 → 会话启动时写入 system prompt&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;会话开始 → MemoryStore.load_from_disk()
→ 读取 MEMORY.md 和 USER.md
→ 生成冻结快照 _system_prompt_snapshot
→ 注入到 Volatile 层
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;注入位置是系统提示词的 Volatile 层。因为 &lt;code&gt;load_from_disk()&lt;/code&gt; 调用一次后结果缓存在 &lt;code&gt;_system_prompt_snapshot&lt;/code&gt; 里，整个会话期间不再变化——这是 Prefix Caching 的要求。&lt;/p&gt;
&lt;p&gt;注意：&lt;strong&gt;内置记忆的注入位置是 system prompt，不是 user message。&lt;/strong&gt; 它作为系统提示词的一部分，在每次 API 调用时携带，多次调用之间内容不变。&lt;/p&gt;
&lt;h3 id="外部-provider-动态记忆--每轮开始注入-user-message"&gt;外部 Provider 动态记忆 → 每轮开始注入 user message&lt;/h3&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;每轮 API 调用前 → MemoryManager.prefetch_all(user_query)
→ 外部 Provider 执行语义匹配
→ 返回匹配到的文本
→ build_memory_context_block() 用 &amp;lt;memory-context&amp;gt; fence 包装
→ 注入到当前轮用户消息的末尾
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;关键代码路径（conversation_loop.py:770-781）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; idx &lt;span style="color:#f92672"&gt;==&lt;/span&gt; current_turn_user_idx &lt;span style="color:#f92672"&gt;and&lt;/span&gt; msg&lt;span style="color:#f92672"&gt;.&lt;/span&gt;get(&lt;span style="color:#e6db74"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; _ext_prefetch_cache:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _fenced &lt;span style="color:#f92672"&gt;=&lt;/span&gt; build_memory_context_block(_ext_prefetch_cache)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _injections&lt;span style="color:#f92672"&gt;.&lt;/span&gt;append(_fenced)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 注入到用户消息末尾&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; api_msg[&lt;span style="color:#e6db74"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _base &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(_injections)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注入位置是&lt;strong&gt;用户消息&lt;/strong&gt;，不是系统提示词。这样做的关键原因是：&lt;strong&gt;用户消息每次都重新发送，不会破坏前缀缓存&lt;/strong&gt;。如果把外部记忆注入 system prompt，会让系统提示词变化，导致既有缓存全部失效。&lt;/p&gt;
&lt;p&gt;所以两套记忆走的是完全不同的加载路径：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;来源&lt;/th&gt;
&lt;th&gt;加载时机&lt;/th&gt;
&lt;th&gt;注入位置&lt;/th&gt;
&lt;th&gt;生命周期&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;内置 MEMORY.md / USER.md&lt;/td&gt;
&lt;td&gt;会话启动一次&lt;/td&gt;
&lt;td&gt;Volatile 层（system prompt）&lt;/td&gt;
&lt;td&gt;整个会话不变&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;外部 Provider prefetch&lt;/td&gt;
&lt;td&gt;每轮 API 调用前&lt;/td&gt;
&lt;td&gt;当前轮 user message&lt;/td&gt;
&lt;td&gt;每轮重新查询&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="sync_all-不是写入记忆"&gt;sync_all 不是&amp;quot;写入记忆&amp;quot;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;sync_all()&lt;/code&gt; 每一轮后都被触发，但&lt;strong&gt;它不改变内置存储&lt;/strong&gt;。它只是一个通知机制，告诉外部 Provider「这轮对话发生了什么」——如果 Provider 需要异步写入自己的后端（比如向量化到向量数据库），它可以在写入完成后再处理。&lt;/p&gt;
&lt;p&gt;唯一改变内置存储的是 &lt;strong&gt;&lt;code&gt;memory&lt;/code&gt; 工具&lt;/strong&gt;——Agent 调用 &lt;code&gt;memory(action=&amp;quot;add&amp;quot;)&lt;/code&gt; 直接修改 MEMORY.md 文件。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="记忆的生命周期"&gt;记忆的生命周期&lt;/h2&gt;
&lt;p&gt;Hermes 的记忆不是一次性写入就完事了——它有完整的生命周期：&lt;/p&gt;
&lt;h3 id="1-写入memory-工具调用"&gt;1. 写入：memory 工具调用&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;memory(action&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;add&amp;#34;&lt;/span&gt;, target&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;, content&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;用户偏好用中文回复&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;memory(action&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;replace&amp;#34;&lt;/span&gt;, target&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;memory&amp;#34;&lt;/span&gt;, old_text&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;旧的偏好&amp;#34;&lt;/span&gt;, content&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;新的偏好&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;memory(action&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;remove&amp;#34;&lt;/span&gt;, target&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;memory&amp;#34;&lt;/span&gt;, old_text&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;需要删除的记忆&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;内置存储直接修改 &lt;code&gt;state.db&lt;/code&gt;。外部 Provider 收到 &lt;code&gt;on_memory_write&lt;/code&gt; 钩子调用，把这次变更异步同步到自己的后端。&lt;/p&gt;
&lt;h3 id="2-读取system-prompt-注入--prefetch"&gt;2. 读取：system prompt 注入 + prefetch&lt;/h3&gt;
&lt;p&gt;每次 API 调用前，Hermes 会调用所有已注册的 Provider，把它们的 &lt;code&gt;prefetch()&lt;/code&gt; 结果汇聚成一段记忆文本。&lt;/p&gt;
&lt;p&gt;注意这是&lt;strong&gt;两层&lt;/strong&gt;记忆读取：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层次&lt;/th&gt;
&lt;th&gt;来源&lt;/th&gt;
&lt;th&gt;注入位置&lt;/th&gt;
&lt;th&gt;时机&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;静态记忆&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.hermes/MEMORY.md&lt;/code&gt; + &lt;code&gt;~/.hermes/USER.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Volatile 层（固定文本）&lt;/td&gt;
&lt;td&gt;会话启动时构建一次&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;动态记忆&lt;/td&gt;
&lt;td&gt;&lt;code&gt;memory_manager.prefetch_all(query)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;memory-context&amp;gt;&lt;/code&gt; fence 块&lt;/td&gt;
&lt;td&gt;每轮 API 调用前&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;静态记忆（内置存储）走系统提示词的 Volatile 层，一天内多次调用不变化。动态记忆（外部 Provider）走每轮 prefetch，内容根据当前用户输入实时变化。&lt;/p&gt;
&lt;h3 id="3-更新sync_turn-钩子"&gt;3. 更新：sync_turn 钩子&lt;/h3&gt;
&lt;p&gt;每一轮 API 调用完成后，Hermes 触发：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;agent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_memory_manager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;sync_all(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user_content&lt;span style="color:#f92672"&gt;=&lt;/span&gt;user_message,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assistant_content&lt;span style="color:#f92672"&gt;=&lt;/span&gt;assistant_response,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; session_id&lt;span style="color:#f92672"&gt;=&lt;/span&gt;agent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;session_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; messages&lt;span style="color:#f92672"&gt;=&lt;/span&gt;messages &lt;span style="color:#75715e"&gt;# 完整的 OpenAI 格式消息&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但注意：这不是 Agent 主动写的记忆。&lt;code&gt;sync_all&lt;/code&gt; 是给外部 Provider 用的同步钩子——内置存储并不会因此被修改。&lt;/p&gt;
&lt;p&gt;内置记忆写入只有&lt;strong&gt;一个&lt;/strong&gt;触发路径：Agent 通过 &lt;code&gt;memory&lt;/code&gt; 工具显式调用 &lt;code&gt;memory(action=&amp;quot;add&amp;quot;, ...)&lt;/code&gt;——这是 Agent 的主动决策，不是自动同步。&lt;/p&gt;
&lt;h3 id="4-跨会话持久化session_switch"&gt;4. 跨会话持久化：session_switch&lt;/h3&gt;
&lt;p&gt;每次 &lt;code&gt;/new&lt;/code&gt;、&lt;code&gt;/resume&lt;/code&gt;、上下文压缩等 session 切换时：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;agent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_memory_manager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;on_session_switch(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; new_session_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; parent_session_id&lt;span style="color:#f92672"&gt;=&lt;/span&gt;old_session_id,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reason&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;compression&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;外部 Provider 使用这个钩子来刷新缓存的 session 状态——它们需要知道&amp;quot;现在切换到哪个 session 了&amp;quot;，以便把新的记忆写入正确的 session 记录。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="内置存储-vs-外部-provider分工清晰"&gt;内置存储 vs 外部 Provider：分工清晰&lt;/h2&gt;
&lt;p&gt;内置存储（&lt;code&gt;~/.hermes/MEMORY.md&lt;/code&gt; + &lt;code&gt;~/.hermes/USER.md&lt;/code&gt;）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;存 key-value 对&lt;/li&gt;
&lt;li&gt;读写直接由 &lt;code&gt;memory&lt;/code&gt; 工具触发&lt;/li&gt;
&lt;li&gt;不需要网络、不需要 LLM&lt;/li&gt;
&lt;li&gt;会话启动时读取一次 → Volatile 层 → 多次调用复用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;外部 Provider（Honcho/Mem0/Hindsight 等）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;存向量/嵌入/观察日志&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;prefetch()&lt;/code&gt; 每轮实时查询&lt;/li&gt;
&lt;li&gt;走 &lt;code&gt;&amp;lt;memory-context&amp;gt;&lt;/code&gt; fence 注入&lt;/li&gt;
&lt;li&gt;可以跨用户、跨 session 积累语义记忆&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;记忆系统不是&amp;quot;非此即彼&amp;quot;——两者同时工作，且互相独立。外部 Provider 挂了，内置的 Volatile 层记忆照常运行；没有外部 Provider，内置的也够用。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="与上下文压缩的关系"&gt;与上下文压缩的关系&lt;/h2&gt;
&lt;p&gt;内存系统和记忆系统的&lt;strong&gt;交叉点&lt;/strong&gt;在&lt;strong&gt;上下文压缩&lt;/strong&gt;上。&lt;/p&gt;
&lt;p&gt;每次触发压缩时，Hermes 会在压缩流程开始&lt;strong&gt;之前&lt;/strong&gt;调用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compression_insights &lt;span style="color:#f92672"&gt;=&lt;/span&gt; agent&lt;span style="color:#f92672"&gt;.&lt;/span&gt;_memory_manager&lt;span style="color:#f92672"&gt;.&lt;/span&gt;on_pre_compress(messages)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; compression_insights:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# 把 Provider 提供的洞察注入摘要模型的 prompt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;外部 Provider 用这个钩子告诉摘要模型&amp;quot;这部分信息要保留&amp;quot;——比如用户之前特别强调的技术选型、某个长期追踪的问题等。&lt;/p&gt;
&lt;p&gt;压缩完成后，生成的新摘要通过 &lt;code&gt;_invalidate_system_prompt()&lt;/code&gt; 标记为需要重建。下次 API 调用时，Volatile 层的记忆会被重新注入到新 summary 之后——包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内置 &lt;code&gt;MEMORY.md&lt;/code&gt; 的内容（原样）&lt;/li&gt;
&lt;li&gt;Provider &lt;code&gt;prefetch()&lt;/code&gt; 的结果（根据压缩后的对话重新查询）&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="工程启示"&gt;工程启示&lt;/h2&gt;
&lt;h3 id="1-记忆不是采集一切"&gt;1. 记忆不是&amp;quot;采集一切&amp;quot;&lt;/h3&gt;
&lt;p&gt;记忆越多≠越好。Hermes 在系统提示词里明确告诉模型&amp;quot;不要保存任务进度、不要保存 temp TODO、7天内会过时的信息不要保存&amp;quot;——因为这会导致系统提示词膨胀，反而让模型更难定位真正有用的信息。&lt;/p&gt;
&lt;h3 id="2-内置存储负责高频写入外部-provider-负责语义检索"&gt;2. 内置存储负责高频写入，外部 Provider 负责语义检索&lt;/h3&gt;
&lt;p&gt;内置存储走 memory 工具→数据库→Volatile 层，是&amp;quot;我知道有什么记忆&amp;quot;。外部 Provider 走语义匹配→prefetch→fence injection，是&amp;quot;我不知道但根据当前输入应该查什么&amp;quot;。两条路径互不干扰。&lt;/p&gt;
&lt;h3 id="3-记忆注入要做安全围栏"&gt;3. 记忆注入要做「安全围栏」&lt;/h3&gt;
&lt;p&gt;Hermes 对外部 Provider 返回的内容用 &lt;code&gt;&amp;lt;memory-context&amp;gt;&lt;/code&gt; 标签包裹，明确标注&amp;quot;这是回忆的新记忆，不是用户输入&amp;quot;。这个防护措施防止了记忆内容被误认为新的用户指令。如果没有这个围栏，恶意构造的记忆内容可能覆盖用户的真实意图——这就是记忆注入的 prompt 注入风险。&lt;/p&gt;
&lt;h3 id="4-记忆是分层注入的"&gt;4. 记忆是分层注入的&lt;/h3&gt;
&lt;p&gt;当前 Hermes 的记忆分四层协同工作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内置静态记忆（MEMORY.md）→ 会话级固定&lt;/li&gt;
&lt;li&gt;内置动态记忆（state.db KV）→ 会话级固定&lt;/li&gt;
&lt;li&gt;外部 Provider prefetch → 每轮动态变化&lt;/li&gt;
&lt;li&gt;压缩摘要（上一篇文章的产物）→ 若干轮后逐渐失效&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最终组成了一段&amp;quot;历史 + 现在 + 跨会话事实&amp;quot;混合而成的 Volatile 层——每一层都有不同的生命周期、不同的更新时机。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;Hermes 的记忆系统不是简单的 key-value 存储，而是一套多层协同的工程方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内置存储&lt;/strong&gt;提供零依赖的关键事实持久化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外部 Provider&lt;/strong&gt;提供语义级别的记忆检索&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静态注入&lt;/strong&gt;保证稳定的跨会话上下文&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动态 prefetch&lt;/strong&gt;保证当前对话的相关性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;记忆围栏&lt;/strong&gt;防止 Prompt 注入风险&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sync/pre_compress 钩子&lt;/strong&gt;保证 Provider 不会在错误的时机写入&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个设计的本质是：&lt;strong&gt;把 Agent 从「无状态对话」变成「有状态服务」&lt;/strong&gt;。每次会话开始时看到的不是空白画布，而是基于历史积累的上下文——这让 Agent 表现得更&amp;quot;懂你&amp;quot;。&lt;/p&gt;
&lt;p&gt;下一篇，我们将深入 Hermes 的工具调度系统——Agent 如何在调用 LLM 之前发现哪 50 个工具可用、如何在运行时动态加载/卸载工具集。&lt;/p&gt;</description></item></channel></rss>