目录:
想象这样一个场景:你花了半小时向 AI 助手解释你的项目架构、编码偏好和团队规范,得到了一次满意的协作体验。第二天再打开对话——它全忘了。你又得从头来一遍。这不是 AI 不够聪明的问题,而是记忆架构缺失的问题。OpenClaw 的 Memory 系统试图从根本上解决这个痛点:让 AI Agent 拥有持久、可检索、可自维护的记忆能力。
为什么 Agent 记忆这么难
LLM 的上下文窗口是有限的。即便是 200K Token 的窗口,也装不下你过去三个月的所有对话。更关键的是,上下文窗口是易失的——会话结束,一切归零。
传统的解决方案有几种,各有问题:
| 方案 | 问题 |
|---|---|
| 把所有历史塞进 System Prompt | Token 爆炸,成本高,模型注意力稀释 |
| 外部数据库 + RAG | 架构复杂,需要额外的向量数据库服务 |
| 让模型"总结"历史 | 信息损失不可控,关键细节可能丢失 |
| 依赖平台的 Memory 功能 | 不透明、不可控、不可迁移 |
OpenClaw 的设计哲学是:记忆应该是纯文本文件,透明可审计,AI 可自维护,同时支持语义检索。
两层记忆架构
OpenClaw 的记忆系统分为两层,对应人类记忆的"短期"和"长期":
workspace/
├── MEMORY.md ← 长期记忆(精炼后的重要信息)
├── memory/
│ ├── 2026-02-26.md ← 每日日志(原始记录)
│ ├── 2026-02-27.md
│ ├── 2026-02-28.md
│ └── heartbeat-state.json
每日日志:memory/YYYY-MM-DD.md
每日日志是最忠实的原始记录,采用仅追加(append-only)模式。Agent 在对话过程中把值得记录的信息写入当天的日志文件——决策、偏好、任务进展、关键事实,什么都可以写。
会话启动时,Agent 加载今天和昨天的日志。这个设计很巧妙:既保证了近期上下文的连续性,又不会因为历史太长撑爆上下文窗口。
长期记忆:MEMORY.md
MEMORY.md 是经过提炼的精华。它不是原始日志的堆叠,而是 Agent 定期从每日日志中抽取的持久性事实——用户偏好、项目架构、重要决策、反复出现的模式。
一个关键的安全设计:MEMORY.md 只在主要的私人会话中加载,绝不在群组上下文中加载。这防止了 Agent 在群聊中意外泄露你的个人信息。
何时写入记忆
写入规则很简洁:
- 决策、偏好、持久性事实 → 写入
MEMORY.md - 日常笔记、运行上下文 → 写入
memory/YYYY-MM-DD.md - 用户说"记住这个" → 立刻写入文件(不要只保存在上下文中)
这个设计让记忆的写入变得直觉化:如果你觉得某件事值得长期保留,就告诉 Agent"记住这个"。
自动记忆刷新:压缩前的抢救
上下文窗口不可能无限增长。当会话接近自动压缩(compaction)阈值时,OpenClaw 会触发一个静默的 Agent Turn,提醒模型在上下文被压缩之前把重要信息写入持久记忆。
{
"agents": {
"defaults": {
"compaction": {
"reserveTokensFloor": 20000,
"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."
}
}
}
}
}
// generated by hugo's coding agent
这个机制的几个设计细节值得注意:
- 软阈值触发:当 Token 估计超过
contextWindow - reserveTokensFloor - softThresholdTokens时才触发,不是每次都刷新 - 默认静默:Prompt 中包含
NO_REPLY,如果没有需要保存的内容,用户完全无感知 - 每个压缩周期只刷新一次:避免重复写入
- 只读工作空间跳过:如果会话在沙箱中以只读模式运行,跳过刷新
这就像人类在即将入睡前,把一天中最重要的事情记到笔记本上——不是事无巨细地记录,而是抢救性地保留关键信息。
向量记忆搜索:让记忆可检索
纯文本文件解决了记忆的存储问题,但没解决检索问题。当 MEMORY.md 和几十个每日日志积累到一定量级,Agent 不可能每次都全文阅读。这就需要语义搜索。
OpenClaw 在记忆文件上构建了一个小型向量索引,支持语义查询——即使措辞不同,也能找到相关笔记。
默认行为
- 默认启用,监视记忆文件变更(去抖动 1.5 秒)
- 索引范围:
MEMORY.md+memory/**/*.md+ 可选的extraPaths - 存储:每个 Agent 的 SQLite 文件(
~/.openclaw/memory/<agentId>.sqlite) - 支持多种嵌入提供商:OpenAI、Gemini、本地模型
嵌入提供商选择
OpenClaw 的嵌入提供商选择逻辑很务实:
配置了 local.modelPath 且文件存在? → 使用本地嵌入
可以解析 OpenAI 密钥? → 使用 OpenAI
可以解析 Gemini 密钥? → 使用 Gemini
都没有? → 记忆搜索禁用
本地模式使用 node-llama-cpp,默认模型是 embeddinggemma-300M-Q8_0.gguf(约 0.6 GB),首次使用时自动下载。对于不想依赖远程 API 的用户,这是一个完全自主的选项。
配置示例——使用 OpenAI 嵌入:
{
"agents": {
"defaults": {
"memorySearch": {
"provider": "openai",
"model": "text-embedding-3-small",
"remote": {
"baseUrl": "https://api.example.com/v1/",
"apiKey": "YOUR_API_KEY"
}
}
}
}
}
// generated by hugo's coding agent
记忆工具
Agent 通过两个工具与记忆系统交互:
memory_search— 语义搜索 Markdown 块(目标约 400 Token,80 Token 重叠),返回片段文本、文件路径、行范围和相关性分数memory_get— 按路径读取特定记忆文件内容,可指定起始行和行数
这两个工具只在 memorySearch.enabled 为 true 时启用。
混合搜索:BM25 + 向量
纯向量搜索擅长语义匹配(“Mac Studio gateway host” vs “运行 gateway 的机器”),但在精确 Token 上较弱(ID、代码符号、错误字符串)。纯关键词搜索(BM25)正好相反。OpenClaw 选择了务实的中间路线:混合搜索。
┌─────────────────────────────────────────────────┐
│ memory_search 查询 │
└──────────────────────┬──────────────────────────┘
│
┌───────────┴───────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ 向量相似度 │ │ BM25 关键词 │
│ (语义匹配) │ │ (精确匹配) │
└──────┬───────┘ └──────┬───────┘
│ │
│ candidateMultiplier = 4
│ 各取 maxResults × 4 个候选
│ │
└──────────┬───────────┘
▼
┌──────────────────┐
│ 加权分数合并 │
│ final = 0.7×vec │
│ + 0.3×text │
└────────┬─────────┘
▼
Top K 结果返回
工作流程:
- 向量检索:按余弦相似度取前
maxResults × candidateMultiplier个候选 - BM25 检索:按 FTS5 排名取同等数量的候选
- 分数转换:将 BM25 排名转换为 0-1 分数:
textScore = 1 / (1 + max(0, bm25Rank)) - 加权合并:
finalScore = vectorWeight × vectorScore + textWeight × textScore
默认权重是向量 0.7、文本 0.3,可配置:
{
"agents": {
"defaults": {
"memorySearch": {
"query": {
"hybrid": {
"enabled": true,
"vectorWeight": 0.7,
"textWeight": 0.3,
"candidateMultiplier": 4
}
}
}
}
}
}
// generated by hugo's coding agent
如果 sqlite-vec 扩展不可用,自动回退到纯向量搜索;如果嵌入提供商不可用,仍然运行 BM25 返回关键词匹配。不会硬失败——这是工程上的务实选择。
嵌入缓存与性能优化
频繁更新记忆文件(尤其是每日日志)会触发大量嵌入计算。OpenClaw 在 SQLite 中缓存了块嵌入——只要文本块没变,就不会重新嵌入。
{
"agents": {
"defaults": {
"memorySearch": {
"cache": {
"enabled": true,
"maxEntries": 50000
}
}
}
}
}
// generated by hugo's coding agent
此外,OpenAI 和 Gemini 的嵌入支持批量模式——把大量嵌入请求打包成一个批处理作业异步执行,不仅更快,OpenAI 的 Batch API 还提供折扣定价。对于大规模回填场景(比如首次索引几百个记忆文件),这是一个显著的成本优化。
SQLite 向量加速
当 sqlite-vec 扩展可用时,嵌入直接存储在 SQLite 虚拟表(vec0)中,向量距离查询在数据库内执行——不需要把每个嵌入加载到 JS 内存中。
{
"agents": {
"defaults": {
"memorySearch": {
"store": {
"vector": {
"enabled": true,
"extensionPath": "/path/to/sqlite-vec"
}
}
}
}
}
}
// generated by hugo's coding agent
sqlite-vec 缺失时自动回退到进程内余弦相似度计算。这种"有加速就用,没有也不崩"的设计让系统对部署环境的要求降到最低。
会话记忆搜索(实验性)
除了 Markdown 记忆文件,OpenClaw 还支持索引会话记录本身——这意味着即使你没有显式"记住"某个信息,只要它出现在过去的对话中,memory_search 就有可能找到它。
{
"agents": {
"defaults": {
"memorySearch": {
"experimental": { "sessionMemory": true },
"sources": ["memory", "sessions"]
}
}
}
}
// generated by hugo's coding agent
会话索引是选择加入的(默认关闭),更新被去抖动并异步索引。这是一个有趣的方向——从"Agent 主动记录"扩展到"所有对话都可能被记住"。
需要注意的信任边界:会话日志存储在磁盘上(~/.openclaw/agents/<agentId>/sessions/*.jsonl),任何有文件系统访问权限的进程都可以读取。对于严格隔离需求,建议在独立的操作系统用户下运行 Agent。
额外记忆路径
如果你想让 Agent 的记忆范围超出默认工作空间——比如索引团队共享文档或项目笔记——可以通过 extraPaths 扩展:
{
"agents": {
"defaults": {
"memorySearch": {
"extraPaths": ["../team-docs", "/srv/shared-notes/overview.md"]
}
}
}
}
// generated by hugo's coding agent
路径支持绝对和相对(相对于工作空间),目录会递归扫描 .md 文件。只索引 Markdown,符号链接被忽略。
完整的记忆生命周期
把所有组件串起来,OpenClaw 的记忆系统形成了一个完整的生命周期:
对话中 压缩前 心跳周期
│ │ │
▼ ▼ ▼
用户说"记住X" memoryFlush 触发 Agent 回顾每日日志
Agent 写入文件 抢救关键信息到文件 提炼到 MEMORY.md
│ │ │
└────────────┬───────────┘────────────────────────┘
▼
memory/*.md + MEMORY.md
│
▼
文件变更触发重新索引(去抖动 1.5s)
│
▼
SQLite 向量索引 + FTS5 全文索引
│
▼
memory_search / memory_get 供 Agent 检索
写入路径有三个入口:对话中显式写入、压缩前自动刷新、心跳周期中的记忆维护。
读取路径有两种:会话启动时加载近期日志和长期记忆(直接读文件),以及运行时通过 memory_search 语义检索(走索引)。
与其他方案的对比
| 维度 | ChatGPT Memory | Claude Projects | OpenClaw Memory |
|---|---|---|---|
| 存储介质 | 云端黑箱 | 项目知识库 | 本地 Markdown 文件 |
| 用户可见性 | 可查看条目 | 可上传文件 | 完全透明,可直接编辑 |
| 语义检索 | 不透明 | 不透明 | 混合搜索,权重可配置 |
| AI 可自维护 | 有限 | 否 | 完全可以 |
| 版本控制 | 无 | 无 | Git 原生支持 |
| 隐私 | 云端存储 | 云端存储 | 本地存储,完全自控 |
| 多 Agent 隔离 | N/A | N/A | 每个 Agent 独立索引 |
OpenClaw 的优势在于透明性和可控性。你可以直接打开 MEMORY.md 看 Agent 记住了什么,删掉不准确的信息,甚至用 Git 追踪记忆的变化历史。
设计启示
OpenClaw 的 Memory 系统给 Agent 记忆设计提供了几个值得借鉴的思路:
文件即记忆,不用数据库。用 Markdown 文件作为唯一的记忆事实来源,简单到不可能出错。任何文本编辑器都能查看和修改,Git 天然提供版本控制。复杂的数据库方案往往在"怎么让用户看到和修改记忆"这个问题上陷入困境。
两层分离:日志 vs 精炼。每日日志保证了信息不丢失,长期记忆保证了上下文窗口不爆炸。这种分离让 Agent 可以自主执行"回顾-提炼-遗忘"的记忆管理循环。
混合检索比纯向量更实用。现实中的记忆查询既有语义模糊的(“之前讨论过的那个部署方案”),也有精确的(“错误码 ERR_CONNECTION_REFUSED 的处理方式”)。BM25 + 向量的组合用最小的复杂度覆盖了两种场景。
隐私边界内建于架构。MEMORY.md 只在私人会话加载、每个 Agent 索引隔离、会话日志的文件系统权限——这些不是事后补丁,而是从第一天就考虑的设计约束。
优雅降级优于硬依赖。没有 sqlite-vec?回退到进程内计算。没有嵌入 API Key?回退到纯 BM25。嵌入提供商挂了?用缓存兜底。每个组件都可以独立失败而不拖垮整个系统。
记忆是 Agent 从"工具"进化为"助手"的关键能力。OpenClaw 的方案证明了一点:好的记忆系统不需要复杂的基础设施,一组 Markdown 文件 + 一个 SQLite 索引 + 一套清晰的读写协议,就已经足够让 AI 真正"记住"你。