想象这样一个场景:你花了半小时向 AI 助手解释你的项目架构、编码偏好和团队规范,得到了一次满意的协作体验。第二天再打开对话——它全忘了。你又得从头来一遍。这不是 AI 不够聪明的问题,而是记忆架构缺失的问题。OpenClaw 的 Memory 系统试图从根本上解决这个痛点:让 AI Agent 拥有持久、可检索、可自维护的记忆能力。

为什么 Agent 记忆这么难

LLM 的上下文窗口是有限的。即便是 200K Token 的窗口,也装不下你过去三个月的所有对话。更关键的是,上下文窗口是易失的——会话结束,一切归零。

传统的解决方案有几种,各有问题:

方案

问题

把所有历史塞进 System Prompt

Token 爆炸,成本高,模型注意力稀释

外部数据库 + RAG

架构复杂,需要额外的向量数据库服务

让模型”总结”历史

信息损失不可控,关键细节可能丢失

依赖平台的 Memory 功能

不透明、不可控、不可迁移

OpenClaw 的设计哲学是:记忆应该是纯文本文件,透明可审计,AI 可自维护,同时支持语义检索

两层记忆架构

OpenClaw 的记忆系统分为两层,对应人类记忆的”短期”和”长期”:

1
2
3
4
5
6
7
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,提醒模型在上下文被压缩之前把重要信息写入持久记忆。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"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 的嵌入提供商选择逻辑很务实:

1
2
3
4
配置了 local.modelPath 且文件存在? → 使用本地嵌入
可以解析 OpenAI 密钥? → 使用 OpenAI
可以解析 Gemini 密钥? → 使用 Gemini
都没有? → 记忆搜索禁用

本地模式使用 node-llama-cpp,默认模型是 embeddinggemma-300M-Q8_0.gguf(约 0.6 GB),首次使用时自动下载。对于不想依赖远程 API 的用户,这是一个完全自主的选项。

配置示例——使用 OpenAI 嵌入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"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.enabledtrue 时启用。

混合搜索:BM25 + 向量

纯向量搜索擅长语义匹配(“Mac Studio gateway host” vs “运行 gateway 的机器”),但在精确 Token 上较弱(ID、代码符号、错误字符串)。纯关键词搜索(BM25)正好相反。OpenClaw 选择了务实的中间路线:混合搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌─────────────────────────────────────────────────┐
│ memory_search 查询 │
└──────────────────────┬──────────────────────────┘

┌───────────┴───────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ 向量相似度 │ │ BM25 关键词 │
│ (语义匹配) │ │ (精确匹配) │
└──────┬───────┘ └──────┬───────┘
│ │
│ candidateMultiplier = 4
│ 各取 maxResults × 4 个候选
│ │
└──────────┬───────────┘

┌──────────────────┐
│ 加权分数合并 │
│ final = 0.7×vec │
│ + 0.3×text │
└────────┬─────────┘

Top K 结果返回

工作流程:

  1. 向量检索:按余弦相似度取前 maxResults × candidateMultiplier 个候选
  2. BM25 检索:按 FTS5 排名取同等数量的候选
  3. 分数转换:将 BM25 排名转换为 0-1 分数:textScore = 1 / (1 + max(0, bm25Rank))
  4. 加权合并finalScore = vectorWeight × vectorScore + textWeight × textScore

默认权重是向量 0.7、文本 0.3,可配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"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 中缓存了块嵌入——只要文本块没变,就不会重新嵌入。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"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 内存中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"agents": {
"defaults": {
"memorySearch": {
"store": {
"vector": {
"enabled": true,
"extensionPath": "/path/to/sqlite-vec"
}
}
}
}
}
}
// generated by hugo's coding agent

sqlite-vec 缺失时自动回退到进程内余弦相似度计算。这种”有加速就用,没有也不崩”的设计让系统对部署环境的要求降到最低。

会话记忆搜索(实验性)

除了 Markdown 记忆文件,OpenClaw 还支持索引会话记录本身——这意味着即使你没有显式”记住”某个信息,只要它出现在过去的对话中,memory_search 就有可能找到它。

1
2
3
4
5
6
7
8
9
10
11
{
"agents": {
"defaults": {
"memorySearch": {
"experimental": { "sessionMemory": true },
"sources": ["memory", "sessions"]
}
}
}
}
// generated by hugo's coding agent

会话索引是选择加入的(默认关闭),更新被去抖动并异步索引。这是一个有趣的方向——从”Agent 主动记录”扩展到”所有对话都可能被记住”。

需要注意的信任边界:会话日志存储在磁盘上(~/.openclaw/agents/<agentId>/sessions/*.jsonl),任何有文件系统访问权限的进程都可以读取。对于严格隔离需求,建议在独立的操作系统用户下运行 Agent。

额外记忆路径

如果你想让 Agent 的记忆范围超出默认工作空间——比如索引团队共享文档或项目笔记——可以通过 extraPaths 扩展:

1
2
3
4
5
6
7
8
9
10
{
"agents": {
"defaults": {
"memorySearch": {
"extraPaths": ["../team-docs", "/srv/shared-notes/overview.md"]
}
}
}
}
// generated by hugo's coding agent

完整的记忆生命周期

把所有组件串起来,OpenClaw 的记忆系统形成了一个完整的生命周期:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
对话中                    压缩前                    心跳周期
│ │ │
▼ ▼ ▼
用户说"记住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 真正”记住”你。



原文链接: Agent 设计最佳实践:Memory