OpenClaw 记忆架构拆解:分层文件、来源标记与两条召回通道各自负责什么
很多人第一次配 OpenClaw 的记忆,是从一个很具体的困惑开始的:我明明让它「记住我偏好 TypeScript」,第二天它照样问我用什么语言;或者反过来,某条早就作废的服务器名一直挂在上下文里赶不走。这类问题几乎都不是「模型记性差」,而是没搞清楚这句话被写进了哪个文件、那个文件会不会在会话开始时被注入。
OpenClaw 的记忆按官方文档的说法,本质是「一组纯文本文件加一个 SQLite 索引」。第一条设计原则就是没有隐藏状态:模型只记得被写进 agent 工作区文件里的东西,每个记忆面都能用文本编辑器打开和修改。所以搞懂记忆行为,等价于搞懂「哪些文件、谁写、什么时候读」。这篇只讲官方文档写明的分层、闸门和配置项;想先看网关-Agent-节点那层分工,对照 OpenClaw 整体架构。
先认清四个文件:写错地方,行为就全错
默认工作区在 ~/.openclaw/workspace,跟记忆相关的文件有四个:
USER.md(可选):稳定偏好、沟通风格、人际关系、在跑的项目上下文,写成祈使式指令,会话开始时加载,有单独的小预算。MEMORY.md:长期记忆,存耐久的非个人档案类事实与决定,会话开始时加载。memory/YYYY-MM-DD.md(也可以是memory/YYYY-MM-DD-<slug>.md):每日笔记。裸/new或/reset时自动加载今天和昨天的日期笔记;带 slug 的变体(例如自带 session-memory hook 写的)会跟纯日期文件一起被拾取。DREAMS.md(可选):梦境日记与 dreaming 扫描摘要,给人看的。
文档对 MEMORY.md 的定位说得很直白:它是紧凑的、经过策展的一层,不是原始逐字记录、每日流水账或穷尽式档案库。这条边界一破毛病就跟着来——MEMORY.md 超过 bootstrap 文件预算时,OpenClaw 保留磁盘上的完整文件,但把注入进上下文的那份截断。该做的是把细节挪进 memory/*.md 只留耐久摘要,或调高 bootstrap 上限。要看原始体积、注入体积和截断状态,用 /context list、/context detail 或 openclaw doctor。
五层 tier:真正要记住的是「谁能写」和「会不会自动注入」
官方架构页给了一张分层表,我按原文整理如下:
| 层级 | 载体 | 谁写 | 注入行为 |
|---|---|---|---|
| Instructions | AGENTS.md 与工作区指令文件 | 只有人 | 总是注入,会话开始时 |
| 策展核心 | MEMORY.md、USER.md | dreaming 整合;用户直接要求 | 总是注入,会话开始时,受预算约束 |
| 情景层 | memory/YYYY-MM-DD.md 每日笔记、会话记录 | agent 工作中写入;记忆冲刷;记录捕获 | 从不注入;可按需搜索 |
| 前瞻层 | 常驻意图(SQLite)与 cron 任务 | intent 工具;定时任务 | 仅在触发条件命中时 |
| 复盘层 | DREAMS.md、dreaming 报告 | dreaming 各阶段 | 从不注入;供人阅读 |
文档明确指出,最要紧的是策展核心与情景层之间那道边界:策展文件小、常驻上下文、只能通过带闸的整合流程写入;情景文件大、方便追加、只能通过显式搜索或升级通道触达。没有东西能绕过晋升闸门从情景层跨进策展核心。
provenance:来源标记写在 SQLite 列里,正文伪造不了
记忆索引里每一条都带来源元数据,以 SQLite 列的形式存——文档反复强调,模型没法用正文散文把这些列写出来。三组字段:来源类别闭集四选一,owner(主人在受信任渠道亲手输入)、agent(从主人内容推导)、untrusted(网页、工具输出、群聊非主人参与者等外部内容)、system(心跳提示词、cron 前导语这类脚手架);会话类型记录来源会话是交互式、cron、心跳还是子代理;观测时间戳与替代键给事实标日期并标明血统,让新观测顶掉旧的。分类保守:判断不了的,外部推导算 untrusted,脚手架算 system,绝不默认成 owner。
基于这套元数据有两条卫生规则,针对常驻型 agent 的经典失效模式——文档说生产审计发现,自动捕获的记忆里绝大多数是脚手架复述、心跳噪声和召回回环:
- 会话类型闸门:cron、心跳、子代理会话不产生耐久记忆候选,它们能写任务产物,但产出的东西都不具备晋升资格。
- 召回回环防护:从记忆注入进上下文的内容(bootstrap 文件、搜索结果、被召回的会话片段)会被结构性打标,不会再被当作新记忆抽取一次。用文档那句话说:一条被召回一百次的事实,仍然只是一条事实。
另外,工作区记忆文件在运维方信任边界之内,所以手写笔记不需要额外认证就具备晋升资格;而记忆冲刷按最低信任类别给整份文件记账。
写入路径:dreaming 是耐久记忆的唯一主写手
文档给的因果链是:日常工作中 agent 往每日笔记追加观察;在 compaction 总结长对话之前,记忆冲刷那一轮把还没落盘的上下文存进当天笔记,好让压缩擦不掉它(见 上下文压缩机制);会话结束时,会话记录变成可摄取的证据。这些都落进情景层,带着来源标记入索引等 dreaming 来收。
冲刷默认开着,关掉是设 agents.defaults.compaction.memoryFlush.enabled: false。想让这轮记账用本地模型跑,可以设一个只作用于冲刷这一轮的精确覆盖(它不继承当前会话的模型回退链):
{
"agents": {
"defaults": {
"compaction": { "memoryFlush": { "model": "ollama/qwen3:8b" } }
}
}
}
dreaming 默认开启,作为定时后台扫描运行,分三个阶段。轻度与 REM 阶段做去重、暂存候选、构建主题反思、记录强化,全程不碰长期记忆。决定晋升的是深度阶段,它串了两道闸:
- 确定性闸门。候选按加权信号排序(检索相关度、召回频次、查询多样性、时新性、跨天复现、概念丰富度),必须过全部阈值。这条逻辑值得记住:记忆能毕业是因为它一直有用,而不是因为它当初被写得笃定。来源类别为
untrusted或system的候选在任何提示词被构建之前就被结构性排除,文档特意说明这是前置条件而非扣分项。 - 整合步骤。过闸候选连同当前
MEMORY.md交给一次整合模型轮次,产出修订后的文件:合并重复、用替代键退休被顶替的条目、把来源引用保留成指向每日笔记的锚点。
整合结果不是无条件采纳的:必须通过结构校验、待在 bootstrap 预算之内、丢失的既有条目不超过一个有界比例,被拒的重写回落到追加式行为。替换 MEMORY.md 用乐观并发——构建整合输入时抓的内容哈希会在原子重命名前再校验一次,期间只要有别的东西改过文件,本次重写就中止走追加回退。每次被接受的重写都存下前像,并把变更摘要追加进 DREAMS.md。
召回的两条通道:一条不花模型调用,一条才是真子代理
召回按成本被拆成两条道。默认那条是确定性的、不增加延迟;升级那条会跑一个真的子代理,只留给确实需要的轮次。
一号通道常开、零模型调用,三个机制在符合条件的轮次上跑:Bootstrap 注入把 MEMORY.md 和 USER.md 按预算加载并逐轮刷新,所以长会话不用重启就能吃到整合结果;排序检索里 memory_search 用混合相关度乘指数时新性衰减(30 天半衰期)再乘重要度系数,重要度是 1 到 10 的整数、由写入方一次性打分,没打分的排名中性;触发注入允许写入方给条目挂短触发短语,每条入站消息对这些短语跑快速词法加向量预筛,匹配强度达到或超过 0.72 的条目注入成隐藏上下文块,每轮最多三条。
后两个信号写成同一行条目的尾随注释:
- Keep the gateway on loopback. <!-- trigger: gateway setup, network safety --> <!-- importance: 9 -->
触发短语用逗号或分号分隔;任一注释缺失时索引对应列保持 NULL,老条目因此保持中性、在写入方补上元数据之前不会成为触发候选。
这里有条硬规则不能忘:自动注入只限于策展层。MEMORY.md 和 USER.md 的条目才有资格,每日笔记和会话记录无论匹配多强都绝不自动注入。文档把这条定性为安全属性而非调参选择。
二号通道是升级。 那个阻塞式召回子代理是一次真正的 agent 轮次,能跨对话历史搜索和阅读(在 rememberAcrossConversations 允许的范围内)。默认只在两个确定性条件同时成立时才跑:一是消息表现出召回意图(明确提到过去、带时间措辞、直接追问先前的决定),二是一号通道没有强命中。mode: "always" 恢复无条件的回复前召回,mode: "off" 关掉。配置面见 活跃记忆与上下文引擎。
另有一层项目作用域:在 Git 仓库里跑的轮次,写出的记忆带 <!-- project: ... --> 标注,身份取自归一化后的 origin 远端。它只改排序和自动注入,并不切分文件;每个会话最多保留四个最近活跃的仓库键。
偏好和「记得去做」为什么各走一条路
USER.md 之所以不并进 MEMORY.md,文档给的理由是失效方式不同:基准测试显示,模型在若干轮之后就会停止应用一条「只是躺在上下文里」的偏好,而把相关指令在提问附近复述一遍,比更重的检索机制更能恢复遵从度。由此推出三条格式约定——条目写成祈使式指令(“Always”、“Never”、“Prefer”)而非观察句;每条带观测日期与生效/已被替代的状态;更新就地替代,绝不追加一条矛盾的,因为只追加的偏好历史会让模型按旧值作答。
「记得去做事」则被单列成前瞻记忆:上下文一长,前瞻性召回急剧衰减而回溯性召回还接近满分,且模型不能被信任去重新推断「这事是不是已经取消了」。所以 OpenClaw 把意图从模型里编译出来——时间型意图当场变成 cron 作业;事件型意图通过 intent 工具进 per-agent 的 SQLite 表,带关键词、触发嵌入、渠道与发送者范围、过期时间、触发次数预算、冷却时间这些机器可校验字段,匹配路径上不发生模型调用。防唠叨也是结构化的:默认冷却 24 小时、预算 3 次触发、90 天过期、每轮最多注入 3 条。
内置引擎:SQLite 索引提供什么,以及明确不提供什么
内置引擎是默认后端,索引存在 per-agent 的 SQLite 库里,起步不需要额外依赖。文档列出的能力:FTS5 关键词检索(BM25 打分)、向量检索、混合检索、按相关度/时新性/写入时重要度的确定性排序、混合结果上默认开启的 MMR 多样性排序、不需要召回模型的受信触发召回、通过三元组分词支持中日韩、可选的 sqlite-vec 加速。
默认用 OpenAI embeddings;只要 OPENAI_API_KEY 或 models.providers.openai.apiKey 已配好,向量检索不用额外配置就能工作。没有 embedding 供应商时只有关键词检索可用。显式指定供应商:
{
memory: {
search: {
provider: "openai",
},
},
}
索引层面几个数值值得记:OpenClaw 把 MEMORY.md、根目录已存在的 USER.md 和 memory/*.md 切块(默认 400 token、80 token 重叠)存进 ~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite;记忆文件变更触发防抖重建(默认 1.5 秒);embedding 供应商、模型、切块配置或作用域变化时自动重建。注意:OpenClaw 不会自动创建 USER.md。
常用命令行入口三条:
openclaw memory status # 看索引状态与供应商
openclaw memory search "query" # 命令行搜索
openclaw memory index --force # 强制重建索引
排查上有个区分很实用:openclaw memory status --deep 把本地向量存储和 embedding 供应商分开报告——Vector store: unavailable 指向 sqlite-vec 加载问题,Embeddings: unavailable 指向供应商鉴权或模型就绪问题。sqlite-vec 加载不了时会自动回落到进程内余弦相似度。
什么时候这套机制不适用,以及文档自己承认还没解决的
它不替你做策略强制。 文档讲「行动敏感型记忆」时说得很明白:记忆可以保存审批上下文,但不强制执行策略。硬性管控得靠审批设置、沙箱和定时任务,别指望在 MEMORY.md 里写一句「未经批准不要动生产」当护栏。
同一轮内的内容来源目前不传播。 这是文档自己列的限制:由工具或网页输出推导出的助手文本,会继承这一轮发送者的类别。此外并发重写仍留有一个毫秒级竞态窗口,文档把它标为「按设计接受的权衡」。
内置引擎不做学习型重排。 原 QMD 查询模式里的 cross-encoder 重排和 HyDE 生成不属于内置记忆;MMR 只减少重复结果,不是学习型相关度重排器。检索瓶颈若在重排上,内置引擎给不了。
换引擎要走插件。 除内置外,文档还列了 Honcho(AI 原生跨会话记忆,带用户建模、语义搜索和多 agent 感知)和 LanceDB(OpenAI 兼容 embeddings、自动召回与捕获);捆绑的 memory-wiki 把耐久知识编译成 wiki 知识库,它不替代活跃记忆插件——召回、晋升、dreaming 仍归后者。引擎选择走 plugins.slots.memory,插件装法见 OpenClaw 插件体系。
真正要动手调的地方其实不多:dreaming 在 plugins.entries.memory-core.config.dreaming,检索在 memory.search,升级通道在 plugins.entries.active-memory,冲刷在 agents.defaults.compaction.memoryFlush。剩下的功夫,花在把该进 MEMORY.md 的写对、把细节留在每日笔记里更划算。
延伸阅读
- 从头读起:OpenClaw 架构总览:网关、Agent、渠道、节点、插件五层各管什么,出问题该往哪查
- 本专题共 40 篇,完整分组目录见专题页
- OpenClaw 活跃记忆与上下文引擎:每一轮到底往上下文里塞了什么
- OpenClaw 上下文压缩与会话修剪:compaction 和 pruning 到底谁在动你的历史
本文依据 OpenClaw 官方仓库(github.com/openclaw/openclaw)docs/ 下的官方文档整理,核对日 2026-08-17。
我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述;
文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。
该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。