OpenClaw 是一个开源的个人 AI 助手框架,支持多通道(WhatsApp/Telegram/Slack/Discord 等)。本文解析其记忆(Memory)功能的实现原理。

一、记忆系统的设计理念

OpenClaw 的记忆系统设计目标是:让 AI 助手记住用户说过的话,跨会话持久化,随时可检索。

1.1 双源记忆架构

OpenClaw 采用「双源」记忆模型:

记忆源 说明 特点
Memory MEMORY.md 或 memory/ 目录下的 Markdown 文件 用户主动维护,结构化知识
Sessions 会话历史的 JSONL 转录文件 自动记录,对话历史

1.2 核心能力

  • 持久化存储:SQLite 本地数据库
  • 语义检索:向量相似度搜索
  • 关键词检索:FTS5 全文搜索
  • Embedding 缓存:避免重复计算
  • 多模态支持:图片、音频等文件也能索引

二、技术架构

2.1 存储层

2.2 数据表结构

files 表:记录索引的文件

1
2
3
4
5
6
7
CREATE TABLE files (
path TEXT PRIMARY KEY, -- 相对路径
source TEXT NOT NULL, -- memory 或 sessions
hash TEXT NOT NULL, -- 内容哈希
mtime INTEGER NOT NULL, -- 修改时间
size INTEGER NOT NULL -- 文件大小
);

chunks 表:存储文本块和向量

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE chunks (
id TEXT PRIMARY KEY, -- 块 ID
path TEXT NOT NULL, -- 来源文件
source TEXT NOT NULL, -- memory 或 sessions
start_line INTEGER NOT NULL, -- 起始行
end_line INTEGER NOT NULL, -- 结束行
hash TEXT NOT NULL, -- 内容哈希
model TEXT NOT NULL, -- Embedding 模型
text TEXT NOT NULL, -- 文本内容
embedding TEXT NOT NULL, -- 向量(JSON)
updated_at INTEGER NOT NULL -- 更新时间
);

embedding_cache 表:缓存 Embedding 结果

1
2
3
4
5
6
7
8
9
10
CREATE TABLE embedding_cache (
provider TEXT NOT NULL, -- Embedding 提供商
model TEXT NOT NULL, -- 模型名称
provider_key TEXT NOT NULL, -- 提供商键
hash TEXT NOT NULL, -- 内容哈希
embedding TEXT NOT NULL, -- 向量
dims INTEGER, -- 向量维度
updated_at INTEGER NOT NULL,
PRIMARY KEY (provider, model, provider_key, hash)
);

三、核心实现

3.1 文件索引流程

分块策略:Markdown 文件按段落/标题分块

1
2
3
4
5
6
7
export type MemoryChunk = {
startLine: number; // 起始行号
endLine: number; // 结束行号
text: string; // 文本内容
hash: string; // 内容哈希
embeddingInput?: EmbeddingInput;
};

3.2 Session 索引流程

Session 文件处理

1
2
3
4
5
6
7
8
9
export type SessionFileEntry = {
path: string; // 相对路径
absPath: string; // 绝对路径
mtimeMs: number; // 修改时间
size: number; // 文件大小
hash: string; // 内容哈希
content: string; // 提取的对话内容
lineMap: number[]; // 行号映射
};

3.3 搜索流程

搜索结果结构

1
2
3
4
5
6
7
8
9
export type MemorySearchResult = {
path: string; // 文件路径
startLine: number; // 起始行
endLine: number; // 结束行
score: number; // 相似度分数
snippet: string; // 内容片段
source: MemorySource; // memory 或 sessions
citation?: string; // 引用标注
};

四、查询优化:Query Expansion

当向量搜索不可用时,OpenClaw 会从对话式查询中提取关键词:

1
2
3
// 用户查询: "昨天讨论的那个方案"
// 提取关键词: ["讨论", "方案"]
// 用于 FTS 搜索

停用词过滤:中英文常见停用词会被过滤,提高搜索精度。


五、多模态支持

5.1 支持的模态

模态 说明
image 图片文件
audio 音频文件
video 视频文件

5.2 多模态索引

1
2
3
4
5
6
7
export type MemoryFileEntry = {
path: string;
kind?: "markdown" | "multimodal";
modality?: MemoryMultimodalModality; // image/audio/video
mimeType?: string;
dataHash?: string; // 二进制数据的哈希
};

六、架构设计亮点

6.1 Embedding 缓存

避免对相同内容重复计算 Embedding:

1
2
// 缓存键: (provider, model, provider_key, hash)
// 命中缓存直接返回向量,无需调用 API

6.2 双模式搜索

  • 向量搜索:语义相似度,适合概念性查询
  • FTS 搜索:关键词匹配,适合精确查询

两种模式可以结合使用,提高召回率。

6.3 增量更新

通过文件哈希和修改时间判断是否需要重新索引:

1
2
3
4
// 只有变更的文件才重新处理
if (existingHash !== newHash || existingMtime < fileMtime) {
// 重新索引
}

七、后端工程师视角:如何借鉴

7.1 核心技术选型

需求 OpenClaw 方案 可借鉴点
存储 SQLite + sqlite-vec 轻量级向量存储
向量 Embedding API 抽象 Provider 接口
全文搜索 SQLite FTS5 内置能力,无需额外服务
缓存 SQLite 表 持久化缓存,避免重复计算

7.2 实现最小化记忆系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 伪代码:最小化记忆系统
type MemorySystem struct {
db *sql.DB // SQLite
embedder Embedder // Embedding 接口
}

func (m *MemorySystem) Index(content string) error {
// 1. 分块
chunks := chunkText(content)

// 2. 生成 Embedding
for _, chunk := range chunks {
embedding := m.embedder.Embed(chunk.Text)

// 3. 存储到 SQLite
m.db.Exec(`
INSERT INTO chunks (id, text, embedding)
VALUES (?, ?, ?)
`, chunk.ID, chunk.Text, embedding)
}

return nil
}

func (m *MemorySystem) Search(query string, k int) []Result {
// 1. 查询向量
queryVec := m.embedder.Embed(query)

// 2. 向量相似度搜索
results := m.vectorSearch(queryVec, k)

return results
}

7.3 关键设计决策

  1. SQLite vs 专用向量数据库:小规模场景 SQLite 足够
  2. Embedding 缓存:必须做,API 调用成本高
  3. 增量索引:必须做,避免全量重建
  4. 双源记忆:用户主动记录 + 自动对话记录

八、参考资料


OpenClaw 的记忆系统展示了如何用轻量级技术栈构建实用的 AI 助手记忆功能:SQLite + 向量 + FTS + Embedding 缓存,核心代码精简,架构清晰。