OpenClaw 为什么总“失忆”?双层记忆 + 三层防御,让它真正记住你

发布时间:2026/7/29 5:18:58

OpenClaw 为什么总“失忆”?双层记忆 + 三层防御,让它真正记住你 很多朋友在使用 OpenClaw 会遇到失忆的问题询问为什么我的小龙虾记不住我一件事前天说过昨天说过今天还是忘记但是每次它都信誓旦旦的跟我说记住了。这种事不是孤例子之前有过例子有人告诉她的 OpenClaw检查这个收件箱提供归档或删除建议。在我发话之前不要做任何事。在测试收件箱上正常工作了几个星期。但当她告诉 OpenClaw 去处理真正的收件箱包含数千条消息时小龙虾开始删除电子邮件并忽略了她的停止命令。原因就是对话上下文窗口被填满了Agent 压缩了历史记录而那个在我发话之前不要做任何事的指令——只是在聊天中从未保存到文件中从上下文中消失了。要想了解这个问题的来龙去脉就得先了解 OpenClaw 设计的双层记忆系统。然后再听听 OpenClaw 代码库的维护者给出的调整建议最大限度避免你的小龙虾的“遗忘症”。二、记忆系统的存储架构OpenClaw 的记忆系统采用了双层记忆架构设计将记忆整体分为两类每日日志动态记忆和长期记忆静态记忆。记忆类型存储格式存储路径产生方式静态记忆Markdown~/.openclaw/workspace/MEMORY.md 和 memory/*.md手动创建 自动生成动态记忆JSONL~/.openclaw/agents/{agentId}/sessions/*.jsonl自动记录这种设计架构非常符合人类大脑的记忆机制。人的记忆也是由一个个重点片段组成的我们能记住的也只是某个特定的时刻、某件具体的事件。把这些片段串联起来才形成了人类所谓的记忆至于每天发生的琐事终究会随着时间而被淡忘。因此此种记忆机制也可以说是实现了工程学的最大程度仿生。2.1. 对话内容产生动态记忆每次用户与 Agent 交互时系统会自动将对话内容追加到 JSONL 格式的会话日志文件中这是最原始的、未经处理过的记忆。动态记忆的 JSONL 格式示例如下{ type: message, message: { role: user, content: 帮我写一个 Python 爬虫 } } { type: message, message: { role: assistant, content: 好的我来帮你写... } }2.2. 静态记忆的产生静态记忆是整个系统的长期记忆用于存储在长期对话中提炼出来的需要系统重点记住的内容比如用户的性格、回答偏好、项目的约定等。静态记忆的产生途径分为以下三种•途径一用户手动创建用户可以直接编辑 MEMORY.md 文件写入需要 Agent 长期记住的信息比如你要称呼我为老板、我喜欢简洁的回复等。•途径二session-memory Hook 自动转换当用户执行 /new 命令重置会话时系统会触发 session-memory Hook自动将上一个会话的关键内容转换为 Markdown 文件。大致流程为1. 读取会话日志从 JSONL 文件中提取最近 N 条默认 15 条user/assistant 消息2. 生成语义化文件名使用 LLM 根据对话内容生成描述性 slug如 api-design、bug-fix3. 写入 Markdown 文件生成 memory/YYYY-MM-DD-{slug}.md 文件•途径三Memory Flush 自动写入当会话上下文接近 token 限制时系统会在压缩compaction前触发一个特殊的 Agent 回合在该回合中Agent 被明确指示将需要持久保存的重要信息写入 memory/YYYY-MM-DD.md 文件。特性MEMORY.mdmemory/*.md用途核心长期记忆按时间组织的会话记忆内容类型用户偏好、重要信息、工作流程等具体会话的摘要和细节更新方式用户手动维护为主系统自动生成为主命名规则固定为 MEMORY.mdYYYY-MM-DD(-{slug}).md检索优先级平等由向量相似度决定平等由向量相似度决定整个静态记忆系统的设计核心在于途径三往往健忘的问题就出在这一步即如何进行历史对话信息的压缩。OpenClaw 的处理方法是使用 LLM 直接对历史对话信息进行处理首先通过 Memory Flush 进行一次记忆筛选让 Agent 自己判断什么是durable memories持久记忆然后通过 LLM 对历史消息进行有损摘要默认只要求保留 decisions, TODOs, open questions, constraints不保留具体数值、时间点等细节。这是 OpenClaw 在记忆上的平衡通过把历史会话记录JSONL 格式压缩成记忆Markdown 格式来避免上下文溢出的问题赋予系统记忆的能力。但与此同时带来的问题也显而易见在进行压缩时难免会有信息的流失这在大多数场景下是合理的但对于具体几点这类精确信息确实是弱点。但这不是 bug而是在长期记忆完整性和系统效率/成本之间的设计取舍。用户如果有重要的精确信息可以主动要求 Agent 记录到长期记忆中。这个设计使得LLM是否足够“聪明”变得很重要在实际使用中 OpenClaw 就不把我认为需要记忆、重要的信息保留下来。个人觉得这是在目前阶段能找到的最优解决方案了有一定作用成本也不至于太高。三、记忆信息的检索与调用当产生并保存一个记忆文件.md时后台会自动触发索引构建流程并在需要时进行检索。3.1. 索引构建默认情况下只有 Markdown 文件会被索引而 JSONL 会话日志不会被索引。Markdown 文件首先被分块而后每个块同时生成向量 Embedding和文本 Token分别存入 sqlite-vec 和 FTS5 索引。这两个都是 SQLite 扩展意味着整个系统只依赖一个轻量级数据库文件不需要部署 ES 或者 Milvus。3.2. 记忆搜索OpenClaw 采用关键词 向量的混合加权搜索结合了两者的优势•向量搜索基于语义相似度通过计算余弦相似度找到意思相近的内容•BM25 关键词搜索基于词频统计使用 SQLite 内置的 FTS5 全文检索引擎找到包含精确关键词的内容两个引擎的搜索结果按照 70:30 的权重权重比例来自经验数据合并最终得分 0.7 × 向量相似度 0.3 × BM25 得分且只有得分超过 0.35 的结果才会被返回。3.3. Agent 如何查找记忆OpenClaw 通过两个核心工具实现与记忆系统的交互memory_search语义搜索该工具用来调用记忆检索功能可搜索 MEMORY.md 和 memory/*.md 文件返回相关度排序的记忆片段包含路径、行号和得分。返回格式示例{ results: [ { path: memory/2026-01-10.md, startLine: 15, endLine: 20, score: 0.85, snippet: 用户提到喜欢蓝色特别是天空蓝..., source: memory }, { path: MEMORY.md, startLine: 5, endLine: 8, score: 0.72, snippet: 颜色偏好蓝色系..., source: memory } ], provider: openai, model: text-embedding-3-small }memory_get精确读取此工具用来精准读取记忆文件可按文件和行范围进行定向读取返回指定文件片段的完整内容。返回格式示例{ text: 用户提到喜欢蓝色特别是天空蓝。\n\n在选择UI时偏好冷色调。\n\n..., path: memory/2026-01-10.md }3.4. Agent 什么情况下搜索记忆Agent 被明确指示必须在特定场景下使用记忆工具在回答有关过去工作、决策、日期、人物、偏好或待办事项的问题之前必须先运行 memory_search 搜索 MEMORY.md 和 memory/*.md 文件然后使用 memory_get 提取所需的行。3.5. Agent 主动写入记忆在 OpenClaw 的交互中Agent 也可以主动写入记忆文件使用标准的文件操作工具。比如当 Agent 判断需要记住某些信息时会使用 exec 或 write 工具写入对应的 memory/YYYY-MM-DD.md 文件。3.6. 交互的安全边界为了保证整个系统的安全边界对 memory_get 制定了严格的路径限制规定其只能读取到特定位置的文件• MEMORY.md / memory.md• memory/*.md• 配置的 extraPaths 中的 .md 文件四、优缺点通过双层记忆架构实现了从无状态工具到有记忆伙伴的进化。它将记忆从上下文中剥离出来构建了一个分层的、可搜索的、持久化的知识管理架构使得 Agent 能够不间断地积累知识、记住用户偏好、延续历史上下文。这种设计不仅解决了上下文窗口有限的问题还为 AI 助手提供了更接近人类记忆的工作方式使得 Agent 能够在长期交互中保持一致性和连续性真正成为用户的可靠伙伴。但也还存在很多问题无法解决。比如固定开销无法消除System Prompt、工具定义每次都要发送、压缩是惰性的、记忆检索是增量成本、工具调用有额外成本。记忆层的真正价值不是降低单次成本而是使无限长的对话成为可能没有记忆层上下文会爆炸同时保持相关信息的可访问性压缩后仍可通过搜索找回。五、如何最大限度避免只要在以下三个方面做些调整你就能领先于 95% 的 OpenClaw 用户1.将持久有效的规则放在文件中而不是在聊天中提供。你的 MEMORY.md 和 AGENTS.md 文件不受压缩操作影响但在对话中输入的指令无法保证。2.检查记忆刷新是否启用以及是否有足够的缓冲区触发。OpenClaw 有一个内置的安全网用于在进行压缩操作之前保存上下文但大多数人从未检查过它是否在运行或给它足够的空间触发。3.强制检索记忆。在 AGENTS.md 中添加一条规则在行动前搜索记忆。没有它Agent就会猜测而不是检查它的记录。当 Agent 忘记某件事时不外乎是发生了以下三件事之一原因一从未被存储•原因指令只存在于对话中从未被写入文件•结果当压缩被触发或开启新会话时它便消失了•典型案例Meta 对齐总监 Summer Yue 的邮件删除事故•这是最常见的原因原因二压缩操作改变了上下文内容•原因长时间会话触发令牌限制压缩操作对旧消息进行归纳处理•结果摘要存在信息损耗丢失细节、一些微妙差别和特定的约束条件•影响Agent现在根据归纳结果运行而非你最初提供的指令原因三会话修剪了工具结果•原因为优化缓存会话会修剪旧工具结果•结果Agent忘记了工具之前返回的内容•特点临时性磁盘上的记录没有变化但模型无法看到工具先前响应这个请求的输出快速判断• 忘记了偏好设置可能从未写入 MEMORY.md 原因一• 忘记了工具返回的内容可能是修剪原因三• 忘记了整个对话压缩或会话重置原因二5.1. 压缩与修剪大多数用户都混淆了压缩和修剪它们是完全不同的系统压缩• 将整个对话历史总结为一段紧凑的摘要改变了模型未来会看到的内容• 当上下文窗口被填满时触发• 影响一切用户消息、助手消息、工具调用• 反应性的当溢出即将发生时触发而不是提前• 有损的永久性的修剪• 仅在内存中修剪旧工具结果仅针对单个请求• 磁盘上的会话历史记录未受影响• 只影响 toolResult 消息用户和助手消息不会被修改• 不会触及工具结果中的图像• 无损的临时性的修剪是朋友它能减少膨胀而且不会破坏对话上下文。压缩是危险的因为它会改变模型看到的内容。建议启用 cache-ttl 模式每5分钟从上下文中删除旧的或不再需要的信息以保持上下文的简洁和相关性。{ agents: { defaults: { contextPruning: { mode: cache-ttl, ttl: 5m } } } }5.2. 核查你的Agent的上下文包含了什么在做配置的改动之前建议在 OpenClaw 会话中运行/context list。用于查看 OpenClaw 当前会话的上下文信息即模型看到的内容。 Context breakdownWorkspace: /path/to/workspaceBootstrap max/file: 20,000 charsSandbox: modenon-main sandboxedfalseSystem prompt (run): 38,412 chars (~9,603 tok) (Project Context 23,901 chars (~5,976 tok))Injected workspace files:- AGENTS.md: OK | raw 1,742 chars (~436 tok) | injected 1,742 chars (~436 tok)- TOOLS.md: TRUNCATED | raw 54,210 chars (~13,553 tok) | injected 20,962 chars (~5,241 tok)Skills list (system prompt text): 2,184 chars (~546 tok) (12 skills)Skills: frontend-design, oracle, …Tools: read, edit, write, exec, process, browser, …Tool list (system prompt text): 1,032 chars (~258 tok)Tool schemas (JSON): 31,988 chars (~7,997 tok)Session tokens (cached): 14,250 total / ctx32,000需要检查的内容•MEMORY.md 是否加载如果显示缺失或未列出就表示该文件不在上下文中•是否有什么东西被截断超过 2 万个字符的文件会按文件截断。所有引导文件的总字符数最多为 15 万个字符•注入的字符是否和原始字符一样如果不是说明内容被截断如果文件被截断则调整配置中的字符数限制。每个文件的字符数限制为 bootstrapMaxChars默认 20000。总字符数限制为 bootstrapTotalMaxChars默认 150000。5.3. 压缩实际做了什么压缩生命周期随着上下文信息不断增加工具输出也越来越多最终接近阈值。接下来会发生什么呢最佳方案维护性压缩。上下文即将达到极限。压缩前的内存刷新首先生效。Agent程序会在压缩开始前自动将重要的上下文保存到磁盘用户不会察觉到这一过程。然后压缩程序会汇总较早的对话历史记录。Agent程序会继续处理汇总后的内容包括最近的消息以及磁盘上的所有内容。错误发生溢出恢复。上下文过大API 拒绝了请求。现在 OpenClaw 正在进行补救措施。它一次性压缩所有内容只是为了恢复工作。没有进行内存刷新也没有先将重要数据保存到磁盘。导致上下文丢失最大。5.4. 压缩破坏了什么不会在压缩操作中保留的内容• 嵌入在对话中的指令头号杀手• 在会话中给出的偏好、更正和决策• 压缩操作前共享的所有图像• 工具结果和它们的上下文• 原指令的一些细微之处和特异性摘要存在信息损耗会在压缩操作中保留的内容• 所有工作区文件SOUL.md、AGENTS.md、USER.md、MEMORY.md、TOOLS.md• 每日记忆日志通过搜索按需获取不重新注入• Agent在压缩操作发生前写入磁盘的任何内容关于 OpenClaw 的记忆最重要的原则是如果没有写入文件就不存在。六、三层防御机制单靠任何一种机制都不够你需要三者协同运作第一层压缩前记忆刷新这是你可以做出的最有用的配置更改。OpenClaw 内置了压缩前记忆刷新功能在压缩前会触发一个静默的Agent轮次提醒模型将任何重要的内容写入磁盘。推荐配置{ agents: { defaults: { compaction: { reserveTokensFloor: 40000, memoryFlush: { enabled:true, softThresholdTokens: 4000, systemPrompt: Session nearing compaction. Store durable memories now., prompt: Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store. } } } } }•reserveTokensFloor: 40000为记忆刷新轮次和压缩摘要保留足够的空间•memoryFlush.enabled: true确保刷新功能已启用•softThresholdTokens: 4000距离预留底限多远时触发刷新第二层手动记忆管理虽然有自动刷新机制但经验丰富的 OpenClaw 用户会通过手动保存来补充这一机制。在切换任务、给出复杂新指令或做出重要决定时告诉Agent将此保存到 MEMORY.md或将今天的关键决定写入记忆文件/compact命令从上面介绍看下来大多数人会认为压缩应该避免其实不然它可以按你的需求进行。掌握这个命令的诀窍在于1. 告诉Agent将当前上下文保存到记忆文件2. 发送/compact手动触发压缩3. 然后给出新指令新指令会进入压缩后的全新上下文从而拥有最长的生命周期。它们不会在下次压缩时首先被删除。为什么需要手动和自动两种方式自动刷新会在达到令牌阈值时触发它是基于时间而非相关性的。而手动保存则是基于相关性的你知道何时发生了重要事件。两者结合起来可以满足这两种需求。你也可以告诉 Agent 压缩时的优先考虑事项/compact 关注决策与开放性问题第三层文件架构工作区分为两类引导文件SOUL.md, AGENTS.md, USER.md, IDENTITY.md, TOOLS.md, MEMORY.md, HEARTBEAT.md, BOOTSTRAP.md在每个会话开始时加载到上下文中。压缩对它们没有影响因为它们在每个轮次中都从磁盘重新加载。记忆目录包含每日日志memory/YYYY-MM-DD.md。这些文件并非是通过引导程序注入的而是通过 memory_search/memory_get 按需调用。各文件的作用•SOUL.md说明Agent是谁沟通语气、个性、情感风格道德界限以及Agent与你的关系•AGENTS.md说明Agent如何操作工作流规则和决策框架、工具使用约定以及响应长度指南•USER.md你是谁你的项目、客户、当前优先事项、沟通偏好、关键人物和关系、技术环境细节•MEMORY.md在每次会话中都应保持不变的内容你做出的决策及依据、Agent学到的偏好、从过去错误中学到的规则•每日日志memory/YYYY-MM-DD.md你每天工作的上下文今天发生了什么、对话中做出的决策、活跃任务及其状态将以下记忆协议添加到你的 AGENTS.md 中## 记忆协议 - 在回答有关过去工作的问题之前先搜索记忆 - 在开始任何新任务之前检查今天的记忆以获取活跃上下文 - 当你学到重要的东西时立即将其写入相应的文件 - 当你纠正一个错误时将更正作为规则添加到 MEMORY.md - 当会话结束或上下文很大时总结并写入 memory/YYYY-MM-DD.md七、检索如果Agent无法在记忆文件中找到需要的信息那么记忆文件就毫无用处。两个记忆访问工具•memory_search在记忆文件中搜索包括 MEMORY.md、每日日志、记忆目录中的所有内容。默认情况下它使用关键词和基于语义的匹配。•memory_get按文件和行范围进行定向读取。如果文件不存在则优雅地返回空文本。将这个检索协议添加到你的 AGENTS.md 文件中## 检索协议 在进行重要的工作之前 1. memory_search 项目/主题/用户偏好 2. 如果需要用 memory_get 获取引用的文件块 3. 然后继续执行任务搜索路径的选择方案一内置搜索默认设置最简单。内置系统自动索引 MEMORY.md 文件和记忆目录中的所有内容使用混合搜索关键词 语义。方案一增强 路径extraPaths内置搜索支持索引工作区之外的其他 Markdown 文件无需额外安装。{ agents: { defaults: { memorySearch: { enabled:true, provider: local, extraPaths: [ ~/Documents/Obsidian/ProjectNotes/**/*.md, ~/Documents/specs/**/*.md ] } } } }当您需要搜索大型存储库数千个文件、过去的会议记录或多个独立集合时请升级到方案二。方案二QMDQMD查询 Markdown 文档是一个实验性的记忆后端可以取代内置的索引器用于搜索工作区之外的内容例如 Obsidian 库、项目文档、会议记录、过去的会话记录。{ memory: { backend: qmd, qmd: { searchMode: search, includeDefaultMemory:true, sessions: { enabled:true }, paths: [ { name: obsidian, path: ~/Documents/Obsidian, pattern: **/*.md } ] } } }八、故障排除我的Agent不记得我的偏好偏好是否写入了 MEMORY.md 文件如果它只存在于对话中就不是持久的。运行/context list看看 MEMORY.md 是否真的加载了memory_search 无返回或似乎被禁用运行/context list并检查你的记忆文件实际是否存在。如果文件不存在则意味着没有东西可供搜索。它不记得浏览器或工具说了什么那是会话修剪不是压缩。工具结果在缓存 TTL 后被清除。将重要的工具输出写入记忆文件或重新运行工具。压缩发生得太晚我收到了溢出错误不要等到溢出。在事情变得严重之前主动使用/compact进行压缩。如果卡在溢出死锁中使用/new重置或通过 openclaw sessions CLI 恢复。压缩前内存刷新没有运行如果一个轮次导致 Token 数据大幅越过软阈值就会绕过刷新。检查你的配置看看该功能是否启用。我的Agent在长时间会话后忘记了它的工具已知问题尤其是长时间运行的 Discord / 飞书会话。使用/new重置会话Agent可以从中断处继续进行。我的Agent一夜之间忘记了一切会话在每日重置时获得新的会话 ID默认为当地时间凌晨 4:00。这本质上是一个新的会话只有引导文件和可搜索的记忆会延续。完整配置方案一内置记忆搜索无需额外安装使用 embeddinggemma 模型的本地混合搜索{ agents: { defaults: { compaction: { reserveTokensFloor: 40000, memoryFlush: { enabled:true, softThresholdTokens: 4000, systemPrompt: Session nearing compaction. Store durable memories now., prompt: Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store. } }, memorySearch: { enabled:true, provider: local, local: { modelPath: hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf }, query: { hybrid: { enabled:true, vectorWeight: 0.7, textWeight: 0.3 } }, cache: { enabled:true } }, contextPruning: { mode: cache-ttl, ttl: 5m } } } }方案二QMD 后端相同的压缩和修剪配置但将内置搜索替换为 QMD{ agents: { defaults: { compaction: { reserveTokensFloor: 40000, memoryFlush: { enabled:true, softThresholdTokens: 4000, systemPrompt: Session nearing compaction. Store durable memories now., prompt: Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store. } }, contextPruning: { mode: cache-ttl, ttl: 5m } } }, memory: { backend: qmd, qmd: { searchMode: search, includeDefaultMemory:true, sessions: { enabled:true }, paths: [ { name: obsidian, path: ~/Documents/Obsidian, pattern: **/*.md } ] } } }记忆问题概述层级功能启用方法工作区文件不受压缩影响的身份和指令结构化 SOUL.md、AGENTS.md、USER.md、MEMORY.md压缩前刷新压缩前的自动安全网验证 memoryFlush.enabled: true 调整 reserveTokensFloor手动记忆保存基于相关性的重要决策保存习惯任务切换前保存到记忆策略性 /compact重要新指令前清理空间/compact 在新上下文前而不是溢出后会话修剪修剪工具膨胀以延迟压缩 节省缓存contextPruning.mode: cache-ttl混合搜索即使措辞不同也能找到记忆memorySearch 中 query.hybrid.enabled: true额外路径方案一无需切换后端即可索引外部文档memorySearch.extraPaths 用于小型文档集QMD方案二搜索整个知识库memory.backend: qmdGit 备份所有记忆文件的完整历史、差异、回滚在工作区中 git init自动提交 cron记忆卫生防止引导膨胀和上下文浪费每周将每日日志提炼到 MEMORY.md命令参考命令功能使用时机/context list显示加载的工作区文件、大小、截断状态调试任何记忆问题时首先检查/context detail特定文件注入的深度分析怀疑文件被截断或缺失时/compact [指令]手动触发压缩可选焦点指导添加重要新指令前不是溢出后/status显示模型、提供商、会话信息验证哪个模型处于活动状态/new 或 /reset全新会话干净上下文陷入溢出死锁或开始新工作时/verbose切换工具调用详细信息的详细模式验证记忆搜索是否实际触发并返回结果如果你用过一段时间 OpenClaw大概率都会经历一个阶段你开始不是在用 AI而是在“反复教它认识你”。记忆不是能力而是系统工程。欢迎朋友们在评论区留言 你是怎么“让 AI 记住你”的 你踩过哪些记忆相关的坑-END-参考从架构到代码深入理解 OpenClaw 的双源记忆系统https://velvetshark.com/openclaw-memory-masterclass推荐阅读从生成到分享我把 OpenClaw 腾讯文档技能跑通了数字员工 OpenClaw 能值多少钱百万美元级别的评测告诉你给 OpenClaw 装了一套“学习系统”Self-Improving AutoSkillAgent 开始自己进化了让 OpenClaw 替你打工三我不止给它装了 30 Skills 还创建了技能让OpenClaw替你打工每日摘要、获取社交网站信息、量化模拟回测实战教程踩坑让你的OpenClaw替你打工从0到1跑通小红书运营全流程实战教程给 OpenClaw 接入10000工具和数据为你盯盘给出独家策略

相关新闻