第 26 章

Memory 四层架构:Session Context → Daily Logs → MEMORY.md → Vector Index

第26章 Memory 四层架构:Session Context → Daily Logs → MEMORY.md → Vector Index

"The model only 'remembers' what gets saved to disk — there is no hidden state." —— OpenClaw 设计文档


26.1 为什么需要 Memory 分层?

大语言模型本质上是无状态的。每次推理调用结束,中间的激活值、注意力权重全部消失。在没有外部持久化机制的情况下,Agent 在对话结束后就会"失忆"——它不知道上周你告诉过它什么,不记得你的名字拼写偏好,也无法回顾三天前做过的决策。

OpenClaw 的 Memory 系统通过四层架构解决这个根本矛盾:将"需要记住的信息"外化为文件系统上的持久化数据,再在合适的时机重新注入到 Context Window 中。

这四层从"最短暂"到"最持久"依次为:

Session Context
    ↓ 压缩后可能晋升
Daily Logs
    ↓ 周期性整合
MEMORY.md(Long-term Memory)
    ↓ 并行建立
Vector Index(SQLite + Embeddings)

每一层都有明确的存储位置、写入时机和读取策略。理解这四层,是掌握 OpenClaw 状态管理的核心。


26.2 第一层:Session Context(会话上下文)

26.2.1 存储位置与文件格式

~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl

每一行是一条 JSON 消息记录,格式遵循对话历史标准:

{"role":"user","content":"帮我分析这份销售报告","timestamp":"2026-04-26T09:00:00Z"}
{"role":"assistant","content":"好的,请上传文件...","timestamp":"2026-04-26T09:00:02Z"}
{"role":"tool","name":"read_file","result":"...","timestamp":"2026-04-26T09:00:05Z"}

26.2.2 加载时机

Session Context 是最"实时"的一层,在整个当前对话期间始终存在于 Context Window 中。当 Agent 启动一个新的 Session 时,对应的 .jsonl 文件被完整读入(如果文件存在),随后每轮新消息追加写入同一文件。

关键约束:Context Window 是有限的(例如 200K tokens)。当 Session 历史过长,必须触发 Compaction 或 Pruning 机制(见第 27 章)。

26.2.3 适合存储的信息类型

信息类型 示例 生命周期
当前任务的中间状态 "刚刚处理了第 3 页,继续第 4 页" 仅限本次 Session
工具调用结果 读取到的文件内容、API 返回 仅限本次 Session
用户在本轮说的指令 "今天用正式语气" 仅限本次 Session
临时变量/草稿 代码片段、计算中间值 仅限本次 Session

Session Context 是高密度、短生命周期的信息层。信息丰富但易逝;Session 结束后,如果没有显式持久化,这些内容将永远消失。


26.3 第二层:Daily Logs(每日日志)

26.3.1 存储位置与文件格式

~/.openclaw/workspace/<workspaceId>/memory/YYYY-MM-DD.md

例如:

~/.openclaw/workspace/myproject/memory/2026-04-26.md
~/.openclaw/workspace/myproject/memory/2026-04-25.md

文件格式为普通 Markdown,由 Agent 自行决定内容结构,通常呈现为带时间戳的追加记录:

## 2026-04-26

### 09:15 — 销售报告分析

用户上传了 Q1-2026-sales.xlsx,发现华东区同比增长 23%,用户希望下周做 PPT 汇报。

### 14:30 — 代码审查

审查了 auth-service 的 PR #142,发现一个潜在的 SQL 注入点,已告知用户。

26.3.2 加载时机

每次 Session 启动时,OpenClaw 自动加载今日昨日两份日志文件(如果存在)。这确保 Agent 在新 Session 开始时,能够感知最近 48 小时内发生的重要事件,而不需要用户重新解释上下文。

# config 中的加载策略(默认)
dailyLogDays: 2   # 加载今日 + 昨日

26.3.3 写入时机

Daily Logs 的写入发生在两种情况:

  1. Compaction Pre-flush:当检测到 Context Window 即将满时,Agent 在压缩前将重要信息写入当日日志(详见第 27 章)。
  2. Session 结束钩子:Session 正常关闭时,Agent 可选择性地将关键收获总结写入日志。

26.3.4 适合存储的信息类型

信息类型 示例
今日完成的任务记录 "完成了数据库迁移脚本"
发现的重要问题 "发现线上环境的内存泄漏"
用户的偏好变化 "用户今天更倾向于简洁输出"
跨 Session 的连续任务进度 "还差第 4、5 章未完成"

Daily Logs 是中等密度、周期性生命周期的信息层,是 Session Context 与 MEMORY.md 之间的缓冲区。


26.4 第三层:Long-term Memory(MEMORY.md)

26.4.1 存储位置

~/.openclaw/workspace/<workspaceId>/MEMORY.md

这是一个单一的、精选的 Markdown 文件,存储跨越时间的核心知识。

26.4.2 内容示例

# Memory Index

- [用户基本信息](user_profile.md) — 偏好简洁、直接的回答风格,不喜欢过多的礼节性语言
- [项目架构决策](arch_decisions.md) — 使用微服务架构,服务间通过 gRPC 通信
- [代码规范](coding_standards.md) — 使用 TypeScript strict mode,禁止 any 类型
- [重要联系人](contacts.md) — 技术负责人:张三([email protected]

MEMORY.md 通常是一个索引文件,指向更详细的子文件;也可以是一个内联的紧凑摘要。

26.4.3 加载时机

MEMORY.md 仅在主私有 Session(Private Primary Session)启动时自动加载。这个设计是刻意的:

# Session 类型与 MEMORY.md 加载规则
session:
  primary_private:
    load_memory: true
  sub_session:
    load_memory: false
    inherit_context: minimal
  group_session:
    load_memory: false
    load_shared_context: true

26.4.4 写入时机

MEMORY.md 的更新是慎重的、有选择性的,发生在:

  1. Dreaming 进程:后台整合进程定期将 Daily Logs 中的重要信息晋升至 MEMORY.md
  2. 显式 Agent 决策:Agent 判断某条信息具有长期价值,主动写入
  3. 用户指令:用户直接要求"记住这件事"

26.4.5 适合存储的信息类型

信息类型 示例 更新频率
用户身份信息 名字、职位、团队 极少
长期偏好 语言风格、输出格式偏好 偶尔
项目核心决策 技术选型、架构决策 偶尔
重要约束 安全规则、禁止事项 极少
领域知识摘要 公司业务流程的关键节点 随项目演进

26.5 第四层:Vector Index(向量索引)

26.5.1 存储位置

~/.openclaw/memory/<agentId>.sqlite

SQLite 数据库中包含两个关键组件:

-- BM25 全文检索虚表
CREATE VIRTUAL TABLE memory_fts USING fts5(content, ...);

-- 向量存储表(sqlite-vec 扩展)
CREATE TABLE memory_embeddings (
    id INTEGER PRIMARY KEY,
    content TEXT,
    embedding FLOAT[768],  -- 维度取决于 Embedding 模型
    source_file TEXT,
    created_at INTEGER
);

26.5.2 加载时机

向量索引不会"加载"到 Context Window——它是一个按需查询的外部检索层。当 Agent 需要回忆某些信息时,发起语义搜索查询,检索结果以文本片段的形式注入上下文。

用户提问 → Agent 判断需要检索历史记忆 → 向量查询 → 返回 Top-K 片段 → 注入 Context → 生成回答

26.5.3 内容来源

向量索引索引的是其他三层的内容:Daily Logs、MEMORY.md,以及历史 Session 中被标记为值得检索的片段。这是一个派生层——其所有内容都来自上层文件,可以随时通过重新索引来重建。

# 重建向量索引(不会丢失任何原始信息)
openclaw memory rebuild-index --workspace myproject

26.5.4 适合存储的信息类型

检索场景 示例查询
历史决策回顾 "我们之前为什么选择 PostgreSQL?"
类似问题参考 "上次处理这类 bug 的方法是什么?"
文档语义搜索 "找出所有关于认证流程的讨论"
长期知识积累 "有没有关于性能优化的历史记录?"

26.6 文件即数据库:设计哲学深解

26.6.1 为什么选择 Markdown 而非数据库?

OpenClaw 做出了一个鲜明的技术决策:用 Markdown 文件而非关系型数据库或专有格式作为核心存储。这个决策背后有以下理由:

1. 人类可读性(Human Readability)

# 任何人都可以用普通工具查看 Agent 的记忆
cat ~/.openclaw/workspace/myproject/MEMORY.md
grep "数据库" ~/.openclaw/workspace/myproject/memory/2026-04-26.md

相比之下,SQLite 二进制文件、向量数据库的专有格式需要专门工具才能读取。

2. 可版本控制性(Version Control Friendly)

# Memory 可以纳入 Git 版本控制
cd ~/.openclaw/workspace/myproject
git init
git add memory/ MEMORY.md
git commit -m "Agent memory snapshot: 2026-04-26"

每次 MEMORY.md 的变更都可以追踪、回滚、对比 diff。

3. 可人工编辑性(Human Editable)

用户可以直接打开 Markdown 文件,手动修改、删除错误的记忆、添加外部知识。这种透明性是闭源记忆系统无法提供的。

4. 无隐藏状态(No Hidden State)

"The model only 'remembers' what gets saved to disk — there is no hidden state."

这句设计宣言意味着:Agent 的所有"记忆"都在文件系统上可见。不存在什么神秘的内部状态——你看到的就是它所知道的全部。

26.6.2 SQLite 作为加速层的角色

SQLite(向量索引层)是整个架构中唯一的"非人类可读"存储,但它的角色是加速层而非主存储

Markdown 文件(权威数据源)
    ↓ 异步索引
SQLite(检索加速层)
    ↓ 可随时重建

SQLite 中的向量和 BM25 索引完全是从 Markdown 文件派生出来的。如果 SQLite 损坏或删除:

openclaw memory rebuild-index --workspace myproject
# 重新读取所有 Markdown 文件,重建索引
# 不会丢失任何信息

这个设计确保了 Markdown 是"单一事实来源"(Single Source of Truth),SQLite 只是让搜索更快。


26.7 Memory 与 Context Window 的关系

Context Window 是 LLM 每次推理时实际"看到"的信息总量,Memory 系统的核心任务是决策哪些信息值得占用宝贵的 Context 空间

Context Window(200K tokens 示例)
┌──────────────────────────────────────────────┐
│ System Prompt(AGENTS.md / SOUL.md)    ~8K  │
│ MEMORY.md(如果是主 Session)           ~4K  │
│ Daily Logs(今日 + 昨日)               ~8K  │
│ Retrieved Memory Chunks(向量检索结果) ~4K  │
│ Current Session History                 余量  │
│ Available for new messages              ~176K │
└──────────────────────────────────────────────┘

当 Session 历史增长,可用空间减少,Compaction 机制介入(见第 27 章)。


26.8 不同 Session 类型的 Memory 加载差异

Session 类型 Session Context Daily Logs MEMORY.md Vector Index
主私有 Session 完整加载 今 + 昨 完整加载 按需检索
子 Session 当前任务上下文 不加载 不加载 受限检索
群组 Session 群组共享历史 不加载 不加载 共享工作区检索
只读沙箱 Session 当前上下文 只读 只读 只读检索

子 Session 的设计遵循最小上下文原则:它只获得完成当前子任务所需的最少信息,避免将无关的历史信息污染任务上下文,同时也减少 Token 消耗。


26.9 实践指南:什么信息该放哪一层

决策树

这条信息需要记住多久?
├── 只需要今天/这次对话 → Session Context(自动管理,无需操作)
├── 需要几天内可以回忆 → Daily Logs(Compaction 时自动写入)
├── 永久保留,每次 Session 都需要 → MEMORY.md(显式写入或 Dreaming 晋升)
└── 需要语义搜索找回 → Vector Index(自动索引,无需操作)

层级选择原则

原则 说明
越持久越精简 MEMORY.md 应只存核心知识,避免堆砌
不要手动管理 Session Context 它由系统自动维护
Daily Logs 允许冗余 流水账式记录没问题,Dreaming 会整合
Vector Index 是自动的 不需要手动决定"是否索引",系统自动完成
定期审查 MEMORY.md 过时的信息会占用 Context,应定期清理

常见错误

❌ 错误:把所有对话摘要都写入 MEMORY.md
   → 结果:MEMORY.md 膨胀到 50K tokens,每次都消耗大量 Context

✓ 正确:只将"影响 Agent 长期行为的决策"写入 MEMORY.md
   → 结果:MEMORY.md 保持在 2-4K tokens,高度精炼
❌ 错误:依赖 Session Context 存储重要决策(不做任何持久化)
   → 结果:Session 结束后,关键决策丢失

✓ 正确:在 Session 结束时,触发 Memory Flush,将关键点写入 Daily Logs

26.10 本章小结

OpenClaw 的四层 Memory 架构,是对"LLM 无状态"这一根本限制的系统性解决方案:

  1. Session Context — 当前对话的工作内存,自动管理
  2. Daily Logs — 近期记忆的缓冲区,追加写入,自动加载
  3. MEMORY.md — 长期精选记忆,谨慎维护,主 Session 加载
  4. Vector Index — 语义检索加速层,派生可重建

"文件即数据库"的设计哲学贯穿全局:Markdown 是权威数据源,SQLite 只是加速检索的派生层。这种设计让 Agent 的记忆系统对用户完全透明、完全可控,没有任何隐藏状态。

下一章,我们将深入 Compaction 机制——当 Context Window 接近极限时,系统如何在不丢失关键信息的前提下完成"记忆压缩"。


下一章:第27章 — Compaction 算法:触发公式、Pre-flush 机制与长会话信息保全

本章评分
4.6  / 5  (5 评分)

💬 留言讨论