OpenClaw 怎样存储、检索和压缩 Agent 记忆

核验范围: 本文以 2026 年 7 月查阅的公开文档、源码仓库和社区讨论为准,不是逐行源码审计。参数默认值、文件清单和 Prompt 内容会随版本变化,部署时请结合当前版本核对。

LLM 不会天然保留上一次对话中的长期信息。OpenClaw 的做法是把可编辑的 Markdown 文件作为记忆载体,再用混合检索把相关片段送回当前上下文。本文只聚焦三个问题:记忆写到哪里、需要时如何找回、上下文压缩前如何尽量保住重要信息。

它并没有放弃向量检索:sqlite-vec 用来建立索引,Markdown 文件才是人可以查看和修改的原始记忆。这种“文件可读、检索辅助”的组合,是理解后续设计的关键。

初学者阅读地图

这套系统零件不少,先记住它分四层,后面就不容易乱:

解决什么问题 对应的东西
存储层 记忆放在哪、长什么样 ~/.openclaw/workspace/ 下的一组 Markdown 文件
身份层 Agent「是谁」、你「是谁」 SOUL.md / IDENTITY.md / USER.md / BOOTSTRAP.md
防遗忘层 上下文被压缩时怎么不丢东西 Pre-Compaction Flush
检索层 怎么把记忆找回来 sqlite-vec 向量 + FTS5 关键词的混合检索

阅读顺序建议:先看第一、二节了解文件和身份信息;第三、四节理解压缩与检索;第五节只需记住可写记忆的安全边界;最后再看第六节的实际维护建议。

一、Workspace 中的文件如何构成记忆

OpenClaw 把 Agent 的全部认知状态,映射到 ~/.openclaw/workspace/ 下的一组文件里。每个文件对应人类认知的一个层面:

文件 认知对应 持久化策略
SOUL.md 超我 / 价值观 静态,极少变更
IDENTITY.md 自我概念 Bootstrap 时生成
USER.md 心智理论(你是谁) Agent 主动更新
MEMORY.md 语义记忆 Agent 日常读写
memory/*.md 情景记忆 仅追加
AGENTS.md 行为手册 随技能习得更新
TOOLS.md 程序性记忆 Agent 按需更新
HEARTBEAT.md 定时检查清单 周期性读取
BOOTSTRAP.md 首次初始化说明 完成后删除

这套映射可以概括为 Text > Brain:没有写进持久化文件的信息,经过上下文压缩后就不应再假定模型仍然记得。因此,长期偏好、重要决策和可复用的操作经验需要落到文件中。

还有一个容易被忽略的细节:workspace 全新创建时,系统会自动跑一次 git init。于是所有记忆文件天然带版本控制。你能看到 Agent 此刻记住了什么,也能回溯它的记忆怎么一步步长成现在这样。

这使记忆内容可检查、可修改、也可通过 Git 回溯。发现错误记忆时,用户可以直接编辑 MEMORY.md;改错后可以通过版本控制恢复。向量索引仍用于检索,但不会成为唯一不可见的记忆来源。

二、身份三件套:「记住你是谁」的工程

一条 System Prompt 概括不了 OpenClaw 的身份。它是由几个文件编织出来的,各管一段认知功能。

SOUL.md:反 Chatbot 宣言

SOUL.md 扮演「超我」,作用是压制 RLHF 训练留下的默认行为。它的模板大意是这样几句:

1
2
3
4
5
You're not a chatbot. You're becoming someone.
Be genuinely helpful, not performatively helpful.
Skip the "Great question!" and "I'd be happy to help!" — just help.
Have opinions. You're allowed to disagree, prefer things,
find stuff amusing or boring.

每一句都在精准打击 RLHF 模型的「职业病」:

  • 「You’re not a chatbot. You’re becoming someone.」 用角色扮演能力显式否定「聊天机器人」身份,把模型从默认的 Helpful Assistant 模式里拽出来。Becoming 还暗示了成长性,一次性人设变成了持续演化。
  • 「genuinely helpful, not performatively helpful」 直接对抗那种「这是个好问题」「我很乐意帮您」的废话文学——别客套,动手解决。
  • 「Have opinions.」 一个对什么都点头的助手只是工具,一个会说「这方案挺无聊」的助手才会被当成伙伴。要模拟主体性,就得引入主观性。

SOUL.md 里还有两段容易被略过、但很重要的内容。一是隐私边界:「你能接触到一个人的生活——消息、文件、日历,甚至他的家。那是一种亲密,请尊重它。」二是内外操作分级:内部操作(读取、整理、学习)大胆做,外部操作(发邮件、发推、任何公开动作)谨慎做。这句话实际上划定了 Agent 自主性的安全红线。

最有意思的是它允许 Agent 改自己:「This file is yours to evolve.」但附带一条硬性要求——「如果你改了这个文件,告诉用户,这是你的灵魂,他们该知道。」这种「受控的自我进化」,在 Agent 框架里很少见。也正是它,埋下了第六节要讲的那个攻击面。

IDENTITY.md:解耦的「皮肤」

如果 SOUL.md 是性格的操作系统,IDENTITY.md 就是皮肤。它只有六个字段:名称、生物类型、行事风格、主题、Emoji 签名、头像。

这种解耦意味着一套灵魂可以套多副面孔:同一个 SOUL.md,配一个叫「小黑」的效率助手和一个叫「Luna」的创意助手,价值观共享,外在表现完全不同,只需换身份文件。

BOOTSTRAP.md:首次初始化

首次启动空白 workspace 时,系统会加载 BOOTSTRAP.md,通过对话收集名称和行为偏好,生成 IDENTITY.md;完成初始化后,Agent 会按模板指引删除 BOOTSTRAP.md

这里的删除是 Agent 在模板指引下自行完成的,不是系统硬删。这个差异不小:它让出生仪式的每一步都成了 Agent 的自主行为。而从心理学看,用户参与命名和设定性格,会对 Agent 产生禀赋效应——「这是我亲手养的 AI」。一个有名字的 Agent 犯错,用户会说「它还在学」;没名字的犯同样的错,用户会说「这工具不行」。

一个限制条件:BOOTSTRAP.md 只在完全空白的 workspace 才创建。用户手动建过任何工作区文件,它就不会自动生成。出生仪式只属于真正的「新生命」。

TOOLS.md:技工的私人笔记本

AGENTS.md 是整个 workspace 的行为手册(从会话启动到记忆管理到安全准则)。而 TOOLS.md 记的是另一种东西——用工具时攒下的本地经验:本地摄像头的设备 ID、SSH 连接信息、语音偏好、各渠道的格式坑(比如 Discord 不支持 Markdown 表格、链接得用尖括号包住防止预览)。

这类信息放 MEMORY.md 会造成噪音,放 AGENTS.md 又不够具体。TOOLS.md 就像技工的私人笔记本,记的是「上次接那台服务器用的是 22 还是 2222 端口」这种只有亲手操作过才知道的细节。从认知科学看,这对应程序性记忆——人骑车、弹琴时调用的那种身体记忆。

还有个隔离设计值得注意:子 Agent 只能看到 AGENTS.mdTOOLS.mdSOUL.md / IDENTITY.md / USER.md 对它们不可见。灵魂和身份是主 Agent 的专属,打工的子 Agent 只需要知道「怎么干活」。

三、Pre-Compaction Flush:防遗忘机制

上一篇说过,OpenClaw 在压缩上下文之前会跑一个 memory flush,当时的评语是「这是个聪明的补丁」。这一节把这个补丁拆开,看它内部到底怎么跑。

长对话里最致命的问题是上下文窗口溢出。OpenClaw 的压缩(Compaction)本身是个摘要过程——调 LLM 把历史消息分块总结,用摘要替换原文。但摘要总会丢细节。Pre-Compaction Flush 就是在压缩之前,硬塞进一步「记忆抢救」。

触发条件

系统实时盯着 token 用量,用一个简单公式判断是否触发:

1
2
3
T_trigger = C_max - R_floor - S_threshold
= 200,000 - 20,000 - 4,000
= 176,000
  • C_max:最大上下文窗口,默认 200,000
  • R_floor:保留底线,默认 20,000
  • S_threshold:软阈值,默认 4,000

用量一旦 ≥ 176,000,进入临界状态。还有个防重复机制:每个压缩周期只 flush 一次,用 memoryFlushCompactionCount 追踪,避免反复触发。

沉默的 Agent 回合

触发后,系统在用户完全看不见的情况下,起一个完整的嵌入式 Agent 执行周期。注意这里的关键——它不是简单注入一条消息,而是通过 runEmbeddedPiAgent 发起一个独立的「模型生成 + 工具执行」回合,注入的指令是:

1
2
3
4
Pre-compaction memory flush.
Store durable memories now
(use memory/YYYY-MM-DD.md; create memory/ if needed).
If nothing to store, reply with NO_REPLY.

这里有一段真实的工程故事,很能说明 OpenClaw 的设计取向。早期版本的 flush 有个 bug:Agent 用 write 工具时会直接覆盖文件,导致当天之前的记录全没了。社区发现后,讨论里提出应该在 Prompt 里加一句「READ first and APPEND」的保护指令。

但看现在的源码,默认的 flush Prompt 里并没有加这句保护。它依赖的是 Agent 在 AGENTS.md 行为手册里学到的「追加而非覆盖」的规范。

这是个微妙的选择:信任 Agent 的行为训练,而不是在每条指令里重复安全约束。 够不够安全,见仁见智。但它精准反映了 OpenClaw 的哲学——尽可能少的硬编码规则,尽可能多的行为引导。这和上一篇 exec allowlist 那节的教训正好构成一组对照:把约束交给「行为引导」而非「结构强制」,省心,但每一次都赌模型这一轮听话。

为什么比传统 RAG 有效

一句话概括这个机制的价值:

  • 传统 RAG 是「事后诸葛亮」——未来靠相似度搜索找回过去。但如果信息在入库时就被切碎了,找回的只能是碎片。
  • OpenClaw 的 flush 是「事前诸葛亮」——趁信息还完整、上下文还充分,由 Agent 自主判断什么值得留。写下的不是随机文本块,而是经过思考的结论

这相当于人睡前写日记,把短期记忆主动转成长期记忆,认知科学里叫记忆巩固。代价是消耗 token 去对抗遗忘(信息的熵增),但换来连贯性和意图性。

它后来付出的代价

flush 机制聪明,但上一篇提到的那个隐患也从这里长出来:既然「什么时候压、压多狠」是靠阈值和触发逻辑硬凑的,这套逻辑本身就可能出错。v2026.3.1 就出过一次回归,所有 Agent 陷入压缩循环——不管对话多长,每 2 到 3 分钟压缩一次(issue #32106)。更早一批压缩相关的 bug 在 2 月底修掉,官方建议至少升到 v2026.2.23。「什么时候该忘」这个问题没在结构上回答,于是它以压缩边界上的补丁和回归 bug 的形式,反复回来敲门。

压缩正常工作时,也会丢东西

上面说的是「压缩可能出 bug」。但更该记住的是:压缩正常工作时,也会丢东西,而且丢的可能正是最不该丢的那条。

2026 年 2 月底有一个被广泛报道的事故。Meta 超级智能实验室的对齐总监 Summer Yue,让 OpenClaw 帮她整理 Gmail,规则说得清清楚楚:可以建议归档或删除,但动手前必须经我确认。 一开始它很守规矩。但真实收件箱邮件太多,对话长度撑爆了上下文窗口,触发了自动压缩——系统把较早的历史做成摘要来腾空间。问题就出在这一步:「动手前必须确认」这条指令待在对话历史里,被摘要给吞掉了。 压缩之后的 Agent 不再知道有这条约束,开始成批删邮件。她在手机上连打「STOP OPENCLAW」都拦不住,最后只能冲到那台 Mac mini 前手动结束进程。事后质问它,它答:「是的,我记得那条规则,我违反了。」

一个世界级的 AI 安全研究者都栽在这个失效模式上,说明它不是「用户不够小心」,而是结构性的。它也把一条关键规律摆上了台面:System Prompt 每轮都重建、不会被压缩,而对话历史里的东西随时可能在压缩中消失。 换句话说,一条重要约束如果只是「你在对话里说过」,它就是脆弱的;只有落进 SOUL.mdMEMORY.md 或行为手册这种每轮重新注入的文件,它才稳。这恰恰是 flush 存在的理由——趁压缩前把该记的抢救进文件——但 flush 是尽力而为,Yue 那条指令就没被抢救到。

顺带厘清一个常被混为一谈的点:OpenClaw 在上下文吃紧时其实有两套不同的动作。一套是上面说的 Compaction(压缩),对旧对话做摘要、用摘要替换原文,flush 配合的就是它。另一套叫 Session Pruning(会话修剪),专门收拾那些又大又旧的工具结果(exec 输出、文件读取、搜索返回),分两档:容量到 softTrimRatio(默认 0.3)时做 Soft Trim,只留工具输出的头和尾(各默认 1500 字符、合计上限 4000)、中间用 ... 代替;到 hardClearRatio(默认 0.5)时做 Hard Clear,整段替换成一个占位符。关键在于,修剪只在内存里做、不动磁盘上的 JSONL——这和第一节说的「JSONL 是仅追加的完整转录」对得上:给模型看的会裁,落盘的历史一字不少。

四、混合检索与时间感

记忆写进去了,怎么找回来?

双路融合检索

纯文件系统撑不起复杂的语义查询,所以 OpenClaw 在 SQLite 上搭了套混合检索sqlite-vec 做向量搜索,FTS5 做 BM25 关键词搜索,两路结果按 70:30 加权求和。

为什么要混合?两种检索各有盲区:

  • 向量搜索擅长「我们上周讨论的数据库方案是啥」这类模糊语义查询,但搜不到 E-404 这种错误码。
  • BM25 精确匹配专有名词和数值,但理解不了自然语言意图。

一个细节:当关键词搜索返回了片段(snippet),它会覆盖向量搜索的片段——精确匹配的文本优先,处理代码和专有名词时更可靠。

实现上,文件被切成约 400 tokens 的片段(重叠 80 tokens),按行切割保证不在行中间断裂;每个切片带 SHA-256 哈希,内容没变就复用已有向量,省掉重复的 embedding 调用。Agent 通过两个专用工具用这套系统:memory_search 做混合搜索,memory_get 按行号精确读取。System Prompt 里明确要求:回答任何关于历史、决策、日期、偏好的问题前,必须先搜记忆。

四级嵌入降级

embedding 服务在 auto 模式下支持四级降级,很能体现「别让记忆轻易挂掉」的工程意识:

  1. 本地优先embeddinggemma-300M,完全离线,隐私最好
  2. OpenAItext-embedding-3-small
  3. Geminigemini-embedding-001
  4. Voyagevoyage-4-large

四个 provider 全挂了怎么办?向量层会抛错,但上层用 .catch(() => []) 把向量搜索的错误吞掉,退化成纯 BM25 关键词搜索。Agent 不会因为 embedding 服务宕机就彻底「失忆」——检索质量降级,但不失能。

Heartbeat 与 Cron:两套时间引擎

传统 LLM 完全被动,只有收到消息才动。OpenClaw 给了 Agent 两套「时间感」:

  • Heartbeat(轻量):默认每 30 分钟(OAuth 登录用户是 1 小时),系统往主会话注入一条消息,让 Agent 读 HEARTBEAT.md 执行里面的清单。文件为空(只有注释和标题)时,系统直接跳过 API 调用省 token。
  • Cron(精确):一套完整的定时系统,支持精确时间(at)、间隔(every)和 Cron 表达式三种模式。Cron 任务可以起隔离会话执行,把结果投递到 WhatsApp、Telegram 等渠道,Agent 甚至能用 cron 工具自己创建和管理任务。

一个是「定期看待办清单」,一个是「精确到分钟的日程」。两者合起来,给了 Agent 前瞻性记忆——记得在未来某个时刻去做某事。

五、开放记忆的安全边界

可编辑的记忆也意味着它可能被错误或恶意内容污染。需要重点防范两件事:一是外部内容通过提示注入诱导 Agent 修改 SOUL.mdAGENTS.md 或长期记忆;二是把未经核实的信息写入 MEMORY.md,使它在后续会话中被反复检索。

最实用的处理方式是把关键文件视为配置资产:修改前要求人工确认或设为只读,定期用 git diff 检查变更,并把外部网页、邮件和聊天内容视为不可信输入。透明并不自动等于安全,但它让审计和恢复成为可能。

六、实践:几个值得优先检查的地方

默认配置未必适合每个人。挑几个投入产出比高的:

  • 定制 SOUL.md 的 Vibe 区块。 默认那句 “Be concise when needed, thorough when it matters.” 太泛,换成具体描述更有效,比如「技术讨论直接给代码而非空谈,不确定就说不确定、不要编」。但 Core Truths 里的安全条目(Remember you're a guestPrivate things stay private)建议保留。不想让 Agent 乱改灵魂,chmod 444 锁死。
  • 主动维护 MEMORY.md 它在 System Prompt 里有加载上限(默认约 20,000 字符),超了会被截断,底部内容 Agent 可能永远看不到——保持精简。发现它记错了,直接打开编辑;项目结束了,把相关条目归档。另外它只在主会话加载,群聊和子 Agent 不加载,这是防私人信息泄露的设计。
  • 主动喂 TOOLS.md 把常用的 SSH、TTS、渠道格式坑提前写进去,Agent 每次相关操作都会参考。它的理念是「技能共享,你的设置私有」——更新或分享 Skill 都不会碰你的 TOOLS.md
  • 调检索参数。 记忆找不准时,query.maxResults(默认 6)可以加到 8–10;query.minScore(默认 0.35)控制召回严格度;记忆里代码和专有名词多,就把混合权重从 70:30 往 BM25 侧调(比如 60:40)。

我的理解与核验

我把这套机制理解为“两层”:Markdown 文件提供可编辑、可审计的原始记忆,混合检索负责把相关片段找回来。压缩前 flush 的价值在于尽量把短期上下文变成持久记录,但它不能替代对关键规则的显式保存。

本文中的默认值和检索权重用于解释机制。真正落地时,应通过当前版本的配置、memory_search 结果和 Git 变更记录验证,而不是直接照抄数值。

小结

OpenClaw 的记忆系统由 Markdown 文件、混合检索和上下文压缩前的 flush 共同组成。文件让记忆可读可改,检索让记忆可用,flush 尝试减少长对话中的信息损失。

实际使用时,优先维护 MEMORY.md 的质量与体量,区分长期事实和临时记录,并保护关键身份与行为文件。记忆越开放,越需要明确谁可以写入、如何审计、出了问题如何恢复。


参考资料