文章目录

在这里插入图片描述

一. 引言:为什么要关注 OpenClaw 的记忆实现?🔍

在 Agent Memory 领域,大多数系统要么走云端托管路线(Mem0、Zep),要么走纯学术实验路线(ASMR)。OpenClaw 的记忆系统走了一条独特的中间路线:完全本地化、生产可用、工程成熟度高

它的独特之处在于:

  1. 没有外部依赖:不需要 Redis、Pinecone 或任何外部向量数据库,一个 SQLite 文件搞定一切 🏠
  2. 人机协同:记忆以 Markdown 文件形式存在,人类和 AI 都可以直接读写 ✍️
  3. 渐进增强:即使没有 Embedding API,也能通过纯 FTS 全文搜索工作 📉→📈
  4. 多语言支持:内置中、英、日、韩、阿、葡、西 8 种语言的查询优化 🌍

作为一个已经在生产环境中服务大量用户的系统,它的工程实践非常值得学习。

二. 设计哲学:“文件即记忆” 📝

2.1 人机协同的记忆模型

OpenClaw 的记忆设计深受 Unix 哲学影响——一切皆文件

Agent 的记忆不是存在某个黑盒数据库里,而是以人类可读的 Markdown 文件形式存在:

workspace/
├── MEMORY.md           # 长期记忆(精炼的核心知识)
├── USER.md             # 用户画像
├── memory/
│   ├── 2026-03-23.md   # 每日笔记
│   ├── 2026-03-22.md
│   └── ...

这种设计带来的好处是:

  • 透明性:用户可以随时打开文件查看 AI 记住了什么 👁️
  • 可编辑性:用户可以直接修改错误的记忆 ✏️
  • 版本控制:可以用 Git 追踪记忆的变化历史 📜
  • 可移植性:换一个 Agent 框架,记忆文件照样可用 🔄

2.2 隐私优先的本地化架构

OpenClaw 的整个记忆系统运行在本地:

  • 存储:SQLite 数据库存在 ~/.openclaw/state/memory/ 目录下
  • 索引:所有 chunk 和 embedding 都存在本地 SQLite
  • 检索:全文搜索(FTS5)完全本地运行
  • 可选的远程组件:仅 Embedding API 调用需要网络(且可用本地模型替代)
┌─────────────────────────────────────────┐
│              本地 (全部在本机)            │
│  ┌─────────┐  ┌──────────┐  ┌────────┐  │
│  │ .md文件  │→│ SQLite   │→│ FTS5   │  │
│  │         │  │ + chunks │  │ 全文索引│  │
│  └─────────┘  └──────────┘  └────────┘  │
│                    ↕                      │
│              ┌──────────┐                │
│              │sqlite-vec│                │
│              │ 向量索引  │                │
│              └──────────┘                │
└─────────────────────────────────────────┘
         ↕ (仅 Embedding 调用)
┌─────────────────────────────────────────┐
│     远程 API (可选,可用本地模型替代)     │
│  OpenAI / Gemini / Voyage / Mistral     │
│  Ollama (本地) / node-llama-cpp (本地)  │
└─────────────────────────────────────────┘

2.3 渐进增强:从纯文本到语义搜索

OpenClaw 的记忆系统支持三个渐进的能力等级

等级 条件 检索能力
Level 0 无 Embedding API 纯 FTS 全文检索(关键词匹配)
Level 1 有 Embedding API 向量语义检索
Level 2 Embedding + FTS Hybrid Search(向量 + 全文混合检索) ✅

这意味着即使用户没有配置任何 API key,记忆搜索功能依然可用——只是从语义理解降级为关键词匹配。从源码中可以看到这段逻辑:

// manager.ts - search() 方法
if (!this.provider) {
  // FTS-only mode: no embedding provider available
  if (!this.fts.enabled || !this.fts.available) {
    return [];
  }
  // Extract keywords for better FTS matching
  const keywords = extractKeywords(cleaned);
  // ...
}

三. 整体架构总览 🏗️

3.1 数据流全景图

用户/Agent 写入 Markdown 文件
         │
         ▼
   ┌──────────────┐
   │ 文件监听器    │  chokidar (watch 模式)
   │ (Watcher)     │  支持 debounce (1500ms)
   └──────┬───────┘
          │ 文件变化事件
          ▼
   ┌──────────────┐
   │  同步引擎     │  manager-sync-ops.ts
   │  (Sync)       │  增量同步,hash 比对
   └──────┬───────┘
          │
    ┌─────┴──────┐
    ▼            ▼
┌────────┐  ┌────────────┐
│ 分块    │  │ Embedding  │
│(Chunk)  │  │ (多提供商) │
│400 tok  │  │            │
│overlap  │  │ OpenAI     │
│80 tok   │  │ Gemini     │
└───┬────┘  │ Voyage     │
    │       │ Mistral    │
    │       │ Ollama     │
    │       │ Local      │
    │       └─────┬──────┘
    │             │
    ▼             ▼
┌──────────────────────┐
│      SQLite 存储      │
│                       │
│  files  表 (文件元数据)│
│  chunks 表 (分块+向量) │
│  chunks_fts (FTS5索引) │
│  chunks_vec (向量索引) │
│  embedding_cache      │
└──────────┬───────────┘
           │
           ▼ 用户查询到来
┌──────────────────────┐
│   Hybrid Search       │
│                       │
│  向量检索 (70%权重)   │
│  + FTS检索 (30%权重)  │
│  → 融合排序            │
│  → MMR去重            │
│  → 时序衰减            │
│  → 引用溯源            │
└──────────┬───────────┘
           │
           ▼
    注入 LLM 上下文

3.2 核心模块清单

基于源码 src/memory/ 目录,OpenClaw 的记忆系统由以下核心模块组成:

模块 文件 职责
管理器 manager.ts 核心入口,单例管理,搜索调度
同步操作 manager-sync-ops.ts 文件监听、增量同步、分块、索引
Embedding操作 manager-embedding-ops.ts 向量嵌入、批处理、重试、缓存
搜索操作 manager-search.ts 向量检索、FTS 检索实现
混合检索 hybrid.ts 向量 + FTS 结果融合排序
MMR去重 mmr.ts 最大边际相关性重排序
时序衰减 temporal-decay.ts 基于时间的分数衰减
查询扩展 query-expansion.ts 多语言停用词、关键词提取
数据库Schema memory-schema.ts SQLite 表结构定义
向量扩展 sqlite-vec.ts sqlite-vec 加载和管理
Embedding提供商 embeddings-*.ts 6 种 Embedding 提供商适配
会话记忆 session-files.ts 从对话历史中提取记忆
批量嵌入 batch-*.ts OpenAI/Gemini/Voyage 批量 API
多模态 multimodal.ts 图片等非文本记忆支持

3.3 文件组织结构

src/memory/
├── manager.ts                    # 🎯 核心管理器(入口)
├── manager-sync-ops.ts           # 同步操作基类
├── manager-embedding-ops.ts      # Embedding 操作
├── manager-search.ts             # 搜索实现
├── manager-runtime.ts            # 运行时辅助
│
├── hybrid.ts                     # 🔀 Hybrid Search 融合
├── mmr.ts                        # 🎲 MMR 去重排序
├── temporal-decay.ts             # ⏰ 时序衰减
├── query-expansion.ts            # 🌍 多语言查询扩展
│
├── memory-schema.ts              # 📊 数据库 Schema
├── sqlite.ts                     # SQLite 工具
├── sqlite-vec.ts                 # sqlite-vec 向量扩展
│
├── embeddings.ts                 # Embedding 提供商工厂
├── embeddings-openai.ts          # OpenAI Embedding
├── embeddings-gemini.ts          # Gemini Embedding
├── embeddings-voyage.ts          # Voyage Embedding
├── embeddings-mistral.ts         # Mistral Embedding
├── embeddings-ollama.ts          # Ollama Embedding
├── node-llama.ts                 # 本地 GGUF 模型
│
├── batch-*.ts                    # 批量 Embedding 处理
├── session-files.ts              # 会话记忆提取
├── multimodal.ts                 # 多模态支持
├── internal.ts                   # 内部工具函数
├── fs-utils.ts                   # 文件系统工具
└── types.ts                      # 类型定义

四. 记忆的生命周期 🔄

4.1 写入阶段:记忆的诞生

记忆的写入有两种方式:

方式一:Agent 自主写入

Agent 在对话过程中通过文件操作工具(write/edit)直接写入 Markdown 文件。例如:

<!-- Agent 写入 memory/2026-03-23.md -->
## 今日记录
- 主人对 ASMR (Supermemory) 的多Agent记忆系统很感兴趣
- 需要为 Agent Memory 综述博客生成封面图
- 主人的 CSDN 博客风格:结构化强、技术深入、有emoji

方式二:用户手动编辑

用户直接打开 MEMORY.md 或 daily notes 进行编辑。系统通过文件监听自动感知变化。

4.2 索引阶段:从文本到向量

当文件发生变化时,同步引擎自动触发索引流程:

  1. 文件发现:Watcher 检测到 .md 文件变化
  2. Hash 比对:通过文件 hash 判断是否需要重新索引
  3. 分块(Chunking):将文件内容按 400 tokens 分块,重叠 80 tokens
  4. Embedding:调用提供商 API 将每个 chunk 转为向量
  5. 存储:向量和文本同时写入 SQLite 的 chunks 表和 chunks_fts FTS5 虚拟表

4.3 检索阶段:Hybrid Search

当用户提问触发记忆搜索时:

  1. 向量检索:将查询 Embedding 后,在 chunks_vec 表中做 KNN 搜索
  2. FTS 检索:构建 FTS5 查询,进行全文匹配
  3. 融合排序:按 0.7 * vectorScore + 0.3 * textScore 加权合并
  4. 后处理:MMR 去重 → 时序衰减 → 分数过滤 → 截断返回

4.4 呈现阶段:注入 LLM 上下文

搜索结果以结构化 JSON 返回给 Agent,包含:

  • snippet:匹配的文本片段(最大 700 字符)
  • path:来源文件路径
  • startLine / endLine:行号范围
  • score:综合得分
  • citation:引用标记(如 Source: memory/2026-03-23.md#L12-L20

五. 关键设计决策剖析 🎯

5.1 为什么选 SQLite 而不是专业向量数据库?

OpenClaw 选择 SQLite + sqlite-vec 扩展 而非 Pinecone/Weaviate/Qdrant 等专业向量数据库,原因包括:

  • 零部署成本:SQLite 是嵌入式数据库,无需启动独立服务 🚀
  • 单文件存储:整个记忆索引是一个 .sqlite 文件,备份/迁移极简 📦
  • 无网络依赖:完全本地运行,保障隐私 🔒
  • 事务支持:SQLite 原生支持 ACID 事务,保证数据一致性 ✅

当然,这个选择也有代价——sqlite-vec 的性能不如专业向量数据库。但对于个人 Agent 的记忆规模(通常几千到几万条 chunks),SQLite 完全够用。

5.2 为什么用 Markdown 文件而不是数据库直存?

直接把记忆存入数据库是更"干净"的方案,但 OpenClaw 坚持以文件为主的原因是:

  • 人机协同:用户可以用任何文本编辑器查看和修改记忆
  • Git 友好:可以把记忆文件纳入版本控制
  • Framework 无关:记忆文件不依赖 OpenClaw 的格式,换框架也能用
  • 调试友好:出问题时直接看文件就知道 Agent 记住了什么

数据库(SQLite)只是索引层——真正的记忆存在于文件中,数据库可以随时重建。

5.3 FTS-Only 降级模式:无 Embedding 也能用

这是一个非常巧妙的设计。当用户没有配置任何 Embedding API 时,系统自动降级为纯 FTS 搜索模式,并启用查询扩展机制来弥补语义理解的不足:

// query-expansion.ts
// 将对话式查询转为关键词
// "之前讨论的那个方案" → ["讨论", "方案"]
// "that thing we discussed about the API" → ["discussed", "API"]
export function extractKeywords(query: string): string[] {
  const tokens = tokenize(query);
  return tokens.filter(t => !isQueryStopWordToken(t) && isValidKeyword(t));
}

该模块内置了 中、英、日、韩、阿、西、葡 7 种语言的停用词表,确保跨语言场景下的查询质量。

5.4 多 Embedding 提供商支持的工程考量

OpenClaw 支持 6 种 Embedding 提供商,并通过 auto 模式自动选择最佳提供商:

提供商 默认模型 特点
OpenAI text-embedding-3-small 最成熟、生态最好
Gemini gemini-embedding-001 支持多模态(图片Embedding)
Voyage voyage-4-large 高精度、专业级
Mistral mistral-embed 欧洲替代方案
Ollama nomic-embed-text 完全本地,无需API
Local embeddinggemma-300m GGUF 模型,通过 node-llama-cpp

还支持降级回退(Fallback):如果主提供商不可用,自动切换到备选提供商。

六. 核心类继承体系 🧬

OpenClaw 的记忆管理器采用三层继承结构,职责分明:

MemoryManagerSyncOps (manager-sync-ops.ts)
    │  职责:文件监听、同步、分块、索引
    │  方法:ensureWatcher(), runSync(), indexFile()
    ▼
MemoryManagerEmbeddingOps (manager-embedding-ops.ts)
    │  职责:Embedding 计算、批处理、缓存、重试
    │  方法:embedBatchWithRetry(), embedQueryWithTimeout()
    ▼
MemoryIndexManager (manager.ts)
    │  职责:对外暴露搜索接口、状态管理、生命周期
    │  方法:search(), readFile(), status(), close()
    ▼
    实现 MemorySearchManager 接口

6.1 MemoryManagerSyncOps:同步操作基类

负责最底层的文件操作:

  • 文件监听:使用 chokidar 监听 workspace 下的 .md 文件变化,debounce 1500ms
  • 增量同步:通过文件 hash 比对,只重新索引变化的文件
  • 分块策略:默认 400 tokens 一块,重叠 80 tokens,确保语义连贯
  • 会话监听:监听 session transcript 文件变化,支持对话历史记忆化

6.2 MemoryManagerEmbeddingOps:Embedding 操作层

负责向量计算相关的所有操作:

  • 批量嵌入:支持 OpenAI/Gemini/Voyage 的 batch API,降低成本
  • Embedding 缓存embedding_cache 表缓存已计算的向量,避免重复计算
  • 超时处理:embedQueryWithTimeout 确保单次查询不会无限等待
  • 错误重试:网络错误、限流(429)自动重试

6.3 MemoryIndexManager:面向外部的完整管理器

最终暴露给上层的管理器,实现 MemorySearchManager 接口:

interface MemorySearchManager {
  search(query: string, opts?): Promise<MemorySearchResult[]>;
  readFile(params): Promise<{ text: string; path: string }>;
  status(): MemoryProviderStatus;
  sync?(params?): Promise<void>;
  probeEmbeddingAvailability(): Promise<MemoryEmbeddingProbeResult>;
  probeVectorAvailability(): Promise<boolean>;
  close?(): Promise<void>;
}

关键设计:单例模式 + 缓存。通过 INDEX_CACHE Map 确保每个 agentId 只有一个管理器实例,避免重复创建数据库连接。

七. 配置体系详解 ⚙️

7.1 ResolvedMemorySearchConfig 配置结构

OpenClaw 的记忆系统拥有极其细粒度的配置能力:

type ResolvedMemorySearchConfig = {
  enabled: boolean;                    // 是否启用
  sources: Array<"memory" | "sessions">;  // 数据来源
  extraPaths: string[];                // 额外监听路径
  provider: "openai" | "gemini" | ... | "auto";  // Embedding 提供商
  store: {
    driver: "sqlite";
    path: string;                      // SQLite 文件路径
    vector: { enabled: boolean; extensionPath?: string };
  };
  chunking: {
    tokens: number;     // 分块大小 (默认400)
    overlap: number;    // 重叠大小 (默认80)
  };
  sync: {
    onSessionStart: boolean;   // 会话开始时同步
    onSearch: boolean;         // 搜索时同步
    watch: boolean;            // 文件监听
    watchDebounceMs: number;   // 监听防抖 (默认1500ms)
    intervalMinutes: number;   // 定期同步间隔
  };
  query: {
    maxResults: number;    // 最大返回结果数 (默认6)
    minScore: number;      // 最低分数阈值 (默认0.35)
    hybrid: {
      enabled: boolean;
      vectorWeight: number;    // 向量权重 (默认0.7)
      textWeight: number;      // 文本权重 (默认0.3)
      candidateMultiplier: number;  // 候选倍数 (默认4)
      mmr: { enabled: boolean; lambda: number };
      temporalDecay: { enabled: boolean; halfLifeDays: number };
    };
  };
  cache: { enabled: boolean; maxEntries?: number };
};

7.2 关键配置项速查表

配置项 默认值 说明
chunking.tokens 400 每个 chunk 的 token 数
chunking.overlap 80 chunk 之间的重叠 token 数
query.maxResults 6 每次搜索最多返回的结果数
query.minScore 0.35 最低相关性分数阈值
query.hybrid.vectorWeight 0.7 向量检索权重
query.hybrid.textWeight 0.3 全文检索权重
query.hybrid.mmr.lambda 0.7 MMR 多样性参数(0=纯多样性,1=纯相关性)
query.hybrid.temporalDecay.halfLifeDays 30 时序衰减半衰期(天)
sync.watchDebounceMs 1500 文件变化后等待多久再同步

八. 与同类系统的架构对比 📊

维度 OpenClaw Mem0 Zep Letta
存储 SQLite (本地) 向量DB + 图谱 (云) PostgreSQL (云) 分层内存
索引 sqlite-vec + FTS5 云端向量索引 云端 内置
文件可读 ✅ Markdown 部分
零依赖 ❌ 需要云服务 ❌ 需要PG 部分
降级能力 ✅ FTS-only
多语言FTS ✅ 8种语言
多模态 ✅ 图片

九. 小结与下篇预告 📢

本文从宏观层面梳理了 OpenClaw 记忆系统的设计哲学和整体架构。核心要点回顾:

  1. “文件即记忆” 的设计哲学,实现人机协同 📝
  2. SQLite + sqlite-vec + FTS5 的全本地存储栈 🏠
  3. 渐进增强 的三级能力模型(FTS-only → 向量 → Hybrid)📈
  4. 三层继承 的管理器架构(Sync → Embedding → Index)🧬

参考文献

  1. OpenClaw 源码:github.com/openclaw/openclawsrc/memory/ 目录
  2. sqlite-vec:github.com/asg017/sqlite-vec — SQLite 向量扩展
  3. SQLite FTS5:sqlite.org/fts5.html — 全文搜索引擎
  4. chokidar:github.com/paulmillr/chokidar — 文件监听库

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐