OpenClaw(龙虾)源码学习-- 会话记忆
长期记忆
长期记忆,指的是跨会话、可被 memory_search / memory_get 工具检索的那部分“记忆”。

长期记忆的正文保存在工作区里的 Markdown 文件中:工作区根目录的 MEMORY.md、memory.md,以及 memory/ 目录下的 **/*.md。这些是普通文本文件,不是存在 MySQL 或其他关系型数据库里。
检索与索引存在哪?
-
为了能做语义搜索和全文检索,OpenClaw 会为这些文件建索引,索引存在 SQLite 里(默认路径类似
~/.openclaw/memory/<agentId>.sqlite,按 agent 隔离)。库里主要有:已索引文件列表(path、hash、mtime)、文本块(chunks)及向量(embedding)、以及可选的 FTS5 / sqlite-vec 用于全文与向量检索。也就是说:用 SQLite 做“记忆”的索引和检索,不用 SQLite 存记忆的原文;原文在 Markdown 文件里。 -
可选后端 qmd
若配置了 memory.backend: "qmd",会使用外部命令 qmd 及其自建索引(例如 $XDG_CACHE_HOME/qmd/index.sqlite),同样不是 MySQL。
「无限记忆」的组成
-
会话记忆:当前会话从创建到现在的所有用户消息与助手回复,保存在会话转录文件(sessionFile)中,由 Pi 的 SessionManager 管理,是“多轮对话上下文”的真正载体。
-
长期记忆:MEMORY.md 与 memory/*.md,通过 memory 工具读写与检索,与具体某次会话解耦。
-
会话记忆 + 长期记忆 共同支撑“之前说过的话 / 做过的事”在后续轮次仍可用;当上下文窗口不足时,通过压缩(Compaction) 将部分会话历史摘要化,而不是简单丢弃,从而在模型侧仍保留“完整性”的语义。
-
会话内:旧轮次不会硬删,而是被越来越“粗”的摘要替换(旧的几百条压成一条 summary),所以语义上还是被带着走。
-
会话间:通过 Memory 系统(MEMORY.md + memory/.md + 搜索)把长期决策、事实等写入“记忆文件”,后面可以跨会话检索再拼回上下文。
会话转录文件与“当前轮”的关系
要点:
-
会话转录文件里已经包含“上一轮(及更早)用户说了什么、助手回复了什么”。
-
本轮只负责生成当前这一条用户消息(commandBody):可以是纯文字、纯媒体说明(如
[User sent media without caption]+[media attached: ...])、或文字+媒体。 -
模型在调用时看到的是 【会话文件中已有历史】+【本轮新 user 消息】,因此“用户上一轮说的要求”自然落在历史里,“本轮只发文件”不会丢失意图。
多轮对话下请求上下文的完整性
-
同一会话由
sessionKey确定,对应唯一的sessionId和 sessionFile。 -
每一轮请求都会:
-
读取该 sessionFile(即“到上一轮为止”的完整历史),
-
将本轮的 prompt(commandBody)作为一条新的 user 消息追加到会话,
-
再将会话整体交给模型。
-
-
因此请求上下文的完整性由“同一会话 = 同一 sessionFile = 同一份历史”保证;不会出现“只带当前一句、不带前文”的情况。
会话转录的持久化与恢复

-
每个会话的 sessionId 和 sessionFile 路径由
resolveSessionFilePath(sessionId, sessionEntry, options)等逻辑解析,并可能持久化在 SessionEntry 中。 -
会话转录文件一般是 JSONL:首行为 session header,后续每行一条消息(user/assistant/system/tool 等)。Pi 的 SessionManager 负责读取、追加、以及在必要时参与压缩(见下节)。
-
因此“无限记忆”在会话维度的实现就是:同一会话永远写同一个 sessionFile,历史只增不改(压缩时由 Pi/OpenClaw 重写或摘要),从而保证多轮请求上下文的完整性。
长期记忆(MEMORY)与会话记忆的配合

-
会话记忆:只来自当前会话的 sessionFile,随会话存在,多轮对话的“之前说了什么”都在这里。
-
长期记忆:MEMORY.md 等由 memory 工具读写和检索,可跨会话;模型在需要时调用 memory_search / memory_get,把检索到的片段加入自己的推理。
-
两者配合:同一轮请求中,模型既能看到会话历史(含“上一轮你说要总结下一个文件”),又能按需拉取长期记忆,从而在“多轮 + 先说话后发文件”的场景下,既保证请求上下文的完整性,又可以利用更早的持久化知识。
流程要点摘要
| 阶段 | 动作 | 与“记住上下文”的关系 |
|---|---|---|
| 解析会话 | 按 sessionKey 找到/创建 SessionEntry,确定 sessionFile | 保证同一会话始终用同一文件 |
| 加载历史 | 读会话文件 JSONL,得到 messages | 多轮“记忆”的来源 |
| Memory Flush | 若 token 接近阈值,先跑一轮记忆写入 memory/*.md | 压缩前把重要信息持久化,避免丢在压缩里 |
| limitHistoryTurns | 只保留最近 N 轮 user 对话 | 先硬裁剪,再送模型,减少 token |
| 组装上下文 | 系统提示 + Skills + 裁剪/压缩后的历史 + 当前消息 | 实际发给模型的“当前上下文” |
| 发送请求 | runEmbeddedAttempt → Provider | 若 SDK 内部发现超长会触发 in-attempt compaction |
| 溢出处理 | 识别 overflow → 工具结果截断 / 显式 compaction → 重试 | 窗口过长时的补救,保证能继续用同一会话 |
| 保存 | 将本轮 assistant/tool 消息追加到会话文件 | 下一轮“记住”的本轮内容 |
更多推荐


所有评论(0)