乐于分享
好东西不私藏

OpenClaw 开发系列:第7篇-记忆系统 — 让 Agent 拥有长期记忆

OpenClaw 开发系列:第7篇-记忆系统 — 让 Agent 拥有长期记忆
前置阅读第 6 篇 — 上下文引擎

本篇讲解 OpenClaw 的记忆系统。没有记忆的 Agent 只能记住当前会话的上下文——一旦会话结束,之前的交互信息全部丢失。记忆系统通过持久化存储和语义检索,让 Agent 拥有跨会话的长期记忆能力。


01

记忆系统架构

OpenClaw 的记忆系统由四个相互配合的组件构成。在进入细节之前,先理解它们各自的分工——如果你只需要改变记忆的「展示方式」,只需替换第一个组件;如果你想换一个向量数据库,只需替换Embedding Provider组件。这种分离设计让记忆系统的各个部分可以独立定制:

这四个组件通过四个注册方法暴露给插件开发者——这些方法都在 register(api) 回调中通过 api 对象调用:


02

独占插槽

与上下文引擎一样,记忆系统采用独占插槽机制:
{  "plugins": {    "slots": {      "memory": "memory-lancedb"    }  }}

系统提供两个内置的记忆插件:

也可以设为 "none" 禁用记忆系统。

切换后需要重启 Gateway:

openclaw gateway restart

03

记忆的工作流程

一条记忆从产生到被使用,经历以下流程:

04

组件详解

1. Memory Prompt Section Builder

记忆被检索出来后,需要以某种格式插入到 Agent 的系统提示中。registerMemoryPromptSection 定义了这个格式——它决定了 Agent 「看到」的记忆长什么样。

这个组件最容易定制。即使你使用内置的记忆存储(memory-lancedb),也可以只替换这个组件来改变记忆的展示风格:

api.registerMemoryPromptSection({  // 构建记忆提示段落  async build(memoryResults, context) {    if (!memoryResults.lengthreturn null;    const sections = memoryResults.map((result, index) => {      return `[记忆 ${index + 1}] (${result.metadata.source})\n${result.content}`;    });    return [      "以下是用户的相关历史记忆:",      "",      ...sections,      "",      "请在回复中自然地利用这些记忆信息,不要直接提及「记忆」这个词。",    ].join("\n");  },  // 提示段落的优先级(用于上下文引擎排序)  priority75,});

build 方法返回 null 时表示不需要注入任何记忆内容(如没有相关记忆被检索到)。


2. Memory Flush Plan Resolver

不是对话中的每一句话都需要持久化。registerMemoryFlushPlan 决定「哪些内容值得记住」——它是记忆写入的「守门人」。只有当 shouldFlush 返回 { flush: true } 时,内容才会被写入存储:

api.registerMemoryFlushPlan({  async shouldFlush(context) {    const { message, history } = context;    // 规则 1:用户明确说了「记住」    if(/记住|记得|以后.*记得|帮我记/i.test(message.text)) {      return {        flushtrue,        contentextractMemoryContent(message.text),        metadata: {          source"explicit",          timestamp: Date.now(),        },      };    }    // 规则 2:包含偏好或个人信息    if(isPreferenceOrPersonalInfo(message.text)) {      return {        flushtrue,        content: message.text,        metadata: {          source"preference",          timestamp: Date.now(),        },      };    }    // 规则 3:累积了一定量的对话后,提取关键信息    if(history.length > 10) {      return {        flushtrue,        contentsummarizeHistory(history),        metadata: {          source"auto-summary",          timestamp: Date.now(),        },      };    }    return { flushfalse };  },});

shouldFlush 的返回值:


3. Memory Runtime

Memory Runtime 是记忆系统的存储引擎,负责实际的读写操作。它是四个组件中最「底层」的一个——直接与数据库交互:

api.registerMemoryRuntime({  // 存储一条记忆  async store(entry) {    // entry 包含 content、embedding、metadata    await myVectorDB.insert({      idgenerateId(),      content: entry.content,      embedding: entry.embedding,      metadata: entry.metadata,    });  },  // 语义检索  async search(query, options) {    // query 是查询文本    // options 包含 limit、threshold 等参数    const queryEmbedding = await getEmbedding(query);    const results = await myVectorDB.search({      vector: queryEmbedding,      topK: options.limit ?? 5,      threshold: options.threshold ?? 0.7,    });    return results.map(r => ({      content: r.payload.content,      score: r.score,      metadata: r.payload.metadata,    }));  },  // 删除记忆  async delete(filter) {    await myVectorDB.delete(filter);  },  // 更新记忆  async update(id, updates) {    await myVectorDB.update(id, updates);  },});

Memory Runtime 不直接处理向量化——它接收已经生成好的 embedding。向量化由 Embedding Provider 负责。


4. Memory Embedding Provider

Embedding Provider 将文本转换为向量(高维数字数组),是语义检索的基础。所谓「语义检索」,就是根据含义而不是关键词来搜索——即使搜索词和记忆内容用词不同,只要含义相近就能匹配上。

例如,用户之前说过「我喜欢 Python」,现在问「我最擅长什么语言」,通过向量相似度也能匹配上。

api.registerMemoryEmbeddingProvider({  id"my-embedding",  async embed(texts) {    // texts 是字符串数组    // 返回对应的向量数组    const response = await fetch("https://api.example.com/embed", {      method"POST",      headers: { "Authorization"`Bearer ${apiKey}` },      bodyJSON.stringify({ texts }),    });    const data = await response.json();    return data.embeddings// number[][]  },});

一个记忆插件可以注册多个 Embedding Provider 适配器。这样做的好处是:你可以同时注册 OpenAI 的 embedding 和本地模型的 embedding,用户在配置中选择使用哪个,甚至设置回退策略(第一个不可用时自动切换到第二个):

// 注册多个 embedding 适配器api.registerMemoryEmbeddingProvider({  id"openai",        // 适配器 ID: "openai"  async embed(texts) { /* ... */ },});api.registerMemoryEmbeddingProvider({  id"gemini",        // 适配器 ID: "gemini"  async embed(texts) { /* ... */ },});api.registerMemoryEmbeddingProvider({  id"custom-local",  // 自定义适配器 ID  async embed(texts) { /* ... */ },});

用户通过配置选择使用哪个适配器:

{  "agents": {    "defaults": {      "memorySearch": {        "provider": "openai",        "fallback": "gemini"      }    }  }}
  • provider:首选的 embedding 适配器 ID

  • fallback:首选不可用时的回退适配器 ID

用户配置中引用的 ID 必须与插件注册的 ID 匹配。如果引用了不存在的 ID,系统会报错。


05

记忆系统的配置

完整的记忆系统配置示例:

{  "plugins": {    "slots": {      "memory": "memory-lancedb"    }  },  "agents": {    "defaults": {      "memorySearch": {        "provider": "openai",        "fallback": "gemini",        "topK": 5,        "threshold": 0.7      }    }  }}

06

记忆与上下文引擎的协作

记忆系统是上下文引擎的「数据提供方」:

上下文引擎在构建上下文时,会调用记忆系统的检索能力,获取相关记忆,然后通过 Prompt Section Builder 将其格式化为系统提示的一部分。

这就是为什么记忆系统的四个注册方法都是独立的——你可以只替换 Prompt Section Builder(改变记忆的展示方式),而不影响底层存储;也可以只替换 Embedding Provider(改变向量化模型),而不影响存储和展示。


07

实战:自定义记忆格式化插件

以下示例只注册 Prompt Section Builder,用于改变记忆在上下文中的展示方式:
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";export default definePluginEntry({  id"structured-memory-formatter",  name"Structured Memory Formatter",  version"1.0.0",  register(api) {    api.registerMemoryPromptSection({      async build(memoryResults, context) {        if (!memoryResults.lengthreturn null;        // 按来源分组        const bySource = new Map<stringtypeof memoryResults>();        for (const result of memoryResults) {          const source = result.metadata.source ?? "general";          if (!bySource.has(source)) bySource.set(source, []);          bySource.get(source)!.push(result);        }        const linesstring[] = ["## 用户背景信息"];        for (const [source, results] of bySource) {          const sourceLabelsRecord<stringstring> = {            explicit"用户明确要求记住的",            preference"用户偏好",            "auto-summary""历史对话摘要",            general"其他相关信息",          };          lines.push(`\n### ${sourceLabels[source] ?? source}`);          for (const r of results) {            const confidence = Math.round(r.score * 100);            lines.push(`- ${r.content} (相关度: ${confidence}%)`);          }        }        lines.push(          "\n请自然地利用以上信息,不要直接提及「记忆」。"        );        return lines.join("\n");      },      priority75,    });  },});

08

小结

本篇覆盖了 OpenClaw 记忆系统的完整架构:

  • 四个组件:Prompt Section Builder、Flush Plan Resolver、Memory Runtime、Embedding Provider

  • 独占插槽机制,同一时间只有一个记忆插件活跃

  • memory-core(基础)vs memory-lancedb(增强)两个内置选项

  • 记忆的工作流程:产生 → 判断 → 向量化 → 存储 → 检索 → 格式化 → 注入

  • Embedding Provider 支持多适配器注册和回退配置


下一篇:运行时 API — 深入 api.runtime。我们将学习插件运行时可用的辅助能力:存储、配置、审批、HTTP 请求等。