[转][译] 从零开始构建 OpenClaw — 第六部分(持久化记忆)

12 阅读6分钟

[转][译] 从零开始构建 OpenClaw — 第一部分(智能体核心)

[转][译] 从零开始构建 OpenClaw — 第二部分(技能插件系统)

[转][译] 从零开始构建 OpenClaw — 第三部分(元技能)

[转][译] 从零开始构建 OpenClaw — 第四部分(工具循环检测)

[转][译] 从零开始构建 OpenClaw — 第五部分(对话压缩)

[转][译] 从零开始构建 OpenClaw — 第六部分(持久化记忆)

[转][译] 从零开始构建 OpenClaw — 第七部分(子智能体系统)

原文:Building Openclaw from Scratch — Part 6 (Persistent Memory)

你的智能体在会话中很智能。让它重构一个模块,它会读取、规划、编辑——非常优雅。开启新会话并问“我更喜欢哪种语言?”它茫然四顾。每个 /new 都是健忘症。智能体不知道你是谁,之前告诉过它什么,或者你的团队遵循哪些约定。

image.png

第 1-5 部分构建了智能体循环、技能插件、循环检测和上下文压缩。第 6 部分添加了缺失的部分:跨会话持续存在的持久化记忆。完整的 openclaw 项目通过约 5,000 行的向量嵌入、混合搜索和 SQLite 解决了这个问题——但你可以用 1%的代码获得 80%的价值。

问题

没有记忆,每个会话都从零开始。你告诉智能体“我更喜欢制表符而不是空格”是在星期一,到星期二它又回到了空格。你解释了你的项目架构,然后开启新会话又重新解释一遍。智能体无法学习。

会话文件存在于磁盘上,但它们是原始的 JSONL 转录——智能体不会重新读取它们。没有机制来传递知识。

解决方案

设计决策

完整的 Openclaw 记忆系统使用向量嵌入、余弦相似度、BM25 文本排序、MMR 多样性重排序、时间衰减以及带有 sqlite-vec 扩展的 SQLite。这很强大,但它也包含 14 个文件和多个 API 依赖。

对于 openclaw-mini,你选择了一条更简单的路径:纯 Markdown 文件 + 关键词搜索。智能体已经拥有了 write 和 edit 工具——它可以创建文件。你只需要三件事:

  1. 一个约定,用于指定记忆文件存放的位置
  2. 将主记忆文件自动加载到系统提示词中
  3. 一个搜索工具,用于在所有记忆文件中查找内容

没有新的依赖项。没有数据库。没有嵌入 API 调用。总计约 210 行。

实现过程

记忆搜索工具 — src/tools/memory-search.ts (~100 行)

这完全遵循现有 web_search 工具的模式:一个 createMemorySearchToolDefinition() 函数返回一个包含 name 、 parameters 和 execute 的工具对象。

// src/tools/memory-search.ts
const MEMORY_DIR = path.join(os.homedir(), ".openclaw-mini", "memory");
const MAX_MATCHES = 50;
const MAX_OUTPUT_CHARS = 10_000;
const CONTEXT_LINES = 2;
export function runMemorySearch(params: {
  query: string;
  file?: string;
}): MemorySearchResult {
  const keywords = params.query
    .toLowerCase()
    .split(/\s+/)
    .filter((k) => k.length > 0);
  // Discover *.md files in memory dir
  // For each file, find lines matching any keyword
  // Return matches with ±2 lines of context
}

搜索被有意设计得简单:将查询拆分为关键词,逐行扫描每个 .md 文件,并返回匹配的行及其上下文。输出限制为 50 个匹配项和 10,000 个字符,以避免使上下文窗口膨胀。如果记忆目录不存在,它将返回一个空结果并附带一条有用的提示词信息——而不是报错。

系统提示词注入 — src/system-prompt.ts (~60 行新增)

这里有两个新增。首先,一个 loadMemoryFile() 函数,用于读取 ~/.openclaw-mini/memory/MEMORY.md 并返回其内容(截断到 200 行):

// src/system-prompt.ts
export function loadMemoryFile(): string | null {
  try {
    const content = fs.readFileSync(MEMORY_FILE, "utf-8").trim();
    if (!content) return null;
    const lines = content.split("\n");
    if (lines.length > MEMORY_MAX_LINES) {
      return lines.slice(0, MEMORY_MAX_LINES).join("\n")
        + "\n\n[Truncated — use memory_search for full content]";
    }
    return content;
  } catch {
    return null;
  }
}

其次,在每一个系统提示词中注入一个 ## Memory 部分。这告诉智能体如何使用记忆——要保存什么,不保存什么,以及放在哪里。关键在于你不需要一个特殊的“保存记忆”工具。智能体已经有了 write 和 edit 。你只需要指令:

### Saving Memory
Use the existing write and edit tools to save to memory files:
- Save to ~/.openclaw-mini/memory/MEMORY.md for important, frequently-needed facts
- Save to ~/.openclaw-mini/memory/{topic}.md for detailed topic-specific notes
### What to Remember
- User preferences (coding style, preferred tools, naming conventions)
- Project context (tech stack, architecture decisions, key file locations)
- Corrections the user makes ("I prefer X over Y")
### What NOT to Remember
- Trivial one-off questions
- Sensitive data (passwords, tokens, secrets)

如果 MEMORY.md 存在,其内容会直接出现在系统提示词的 ### Current Memory (MEMORY.md) 部分下。如果它还不存在,智能体会看到一个告诉它如何创建它的消息。

连接设置 — src/entry.ts (~50 行新增)

入口点变更很简单:在启动时创建 ~/.openclaw-mini/memory/ ,注册 memory_search 工具,将 memoryContent 传递给 buildSystemPrompt ,并添加一个 /memory 命令。值得注意的一个细节是:每次提示词时记忆都会重新加载,而不是缓存。这意味着如果智能体在一个回合中向 MEMORY.md 写入内容,下一个回合将看到更新后的内容。

新命令

命令描述 /memory 列出记忆文件及其大小,预览 MEMORY.md

memory_search 工具会自动提供给智能体——它与 web_fetch 和 web_search 一起出现在工具列表中。

尝试使用

┌ openclaw-mini
│ model: anthropic/claude-sonnet-4-20250514
│ workspace: /Users/you/project
│ session: mini-1719432000000
│ tools: read, bash, edit, write, web_fetch, web_search, memory_search
│ memory: empty (~/.openclaw-mini/memory/)
└ /new /think /model /skills /verbose /compact /memory /quit

> remember that I always prefer TypeScript over JavaScript,
  and I use pnpm not npm
I'll save that to your memory file.
[tools: write]
Saved your preferences to ~/.openclaw-mini/memory/MEMORY.md.
(2.1s)
> /memory
Memory files (1):
  MEMORY.md (main) - 0.1 KB
  total: 0.1 KB
MEMORY.md preview:
# User Preferences
- Prefers TypeScript over JavaScript
- Uses pnpm (not npm)
> /new
New session: mini-1719432060000
> what package manager should I use for this project?
[tools: memory_search]
Based on your preferences, you should use **pnpm**. I can see from
your saved preferences that you prefer pnpm over npm.
(1.8s)

智能体可以在会话间保持记忆,因为 MEMORY.md 会被自动加载到每个系统提示词中。对于跨多个主题文件进行更深入的搜索,它使用 memory_search 工具。

你构建了什么

  • 持久跨会话记忆 — 智能体能够记住用户偏好、项目上下文和决策,并在 /new 和重启时保持
  • memory_search 工具 — 在所有记忆文件中进行关键词搜索,并提供上下文窗口
  • /memory 命令 — 检查智能体已记住的内容
  • 跨越 3 个文件共 210 行代码,无新的依赖项

下一步是什么

关键词搜索在小型记忆存储中效果很好,但随着记忆的增长,你可能需要语义搜索——即使精确的词语不匹配,也能找到相关概念的向量嵌入。这就是 Openclaw 的混合搜索(余弦相似度 + BM25)发挥作用的地方,但这将是另一部分的故事。