当前实现里,长期记忆相关的数据源至少分三类。
默认路径是:
-
/MEMORY.md -
/memory//*.md
这里的语义最直接:
-
用户或系统明确写下来的长期记忆
通过 extraPaths 配置,可以把额外文件或目录纳入记忆源。
这些也必须是 markdown。
这不是默认长期记忆,而是可选来源。
两种路径会让 session 内容进入记忆相关系统:
-
builtin memory 开启 experimental sources: [“sessions”]
-
QMD backend 配置 sessions export/index
所以 session transcript 在 OpenClaw 里很特殊:
-
它天然是短期工作记忆的原材料
-
但在特定配置或导出链下,也可以成为长期记忆数据源
这里要非常明确。
机制是:
-
session transcript
-
context engine
-
system prompt / prompt assembly
特点:
-
面向当前或相邻几轮任务
-
直接参与 prompt
-
更强调当前状态连续性
机制是:
-
markdown 文件
-
可选 session transcript 索引/导出
-
sqlite / qmd 索引
-
memory search / memory get 按需召回
特点:
-
不直接自动进入 prompt
-
先索引、后检索
-
更强调跨会话持久性
所以不要把 “session transcript 被索引” 理解成“短期记忆和长期记忆没区别”。
区别仍然在于:
-
一个是当前运行时工作集
-
一个是可召回历史库
当前 builtin backend 使用:
-
node:sqlite
这是 Node 内建 sqlite 接口,通过 src/memory/sqlite.ts 做 require 包装。
从当前代码结构看,选择 sqlite 的工程动机很明显:
-
本地单机使用为主
-
零服务依赖
-
同时承载 metadata、FTS、embedding cache 很方便
-
配合扩展可以做向量检索
这非常符合 OpenClaw 的整体风格:
默认优先本地、可携带、低运维成本的内建能力。
因为不同 Node 发行版不一定带 node:sqlite。
所以这层包装的作用是:
-
把缺失 sqlite runtime 的情况变成可理解的错误
-
而不是让系统抛出模糊的 builtin module 错误
ensureMemoryIndexSchema(…) 定义了几张核心表。
存:
-
索引元信息
比如:
-
provider/model
-
chunk 配置
-
sources
-
vector dims
存:
-
path
-
source
-
hash
-
mtime
-
size
它的意义是:
记录当前有哪些源文件,以及这些文件内容是否变化。
存:
-
chunk id
-
path
-
source
-
起止行
-
hash
-
model
-
text
-
embedding
-
updated_at
这张表是 builtin recall 的核心文本块表。
存:
-
provider
-
model
-
provider_key
-
hash
-
embedding
-
dims
-
updated_at
这是非常值得学习的一张表。
它说明系统不是每次索引都重新算 embedding,而是:
对同样文本 hash 的 embedding 进行跨次缓存。
chunks_fts
用途:
-
关键词检索
-
支撑 hybrid recall
-
也支撑 FTS-only 降级模式
chunks_vec
用途:
-
向量检索
但它不是必然可用的,是:
-
可选
-
动态探测
-
带超时加载
这说明 builtin backend 本质上是:
sqlite 元数据表+ FTS5+ 可选 sqlite-vec+ embedding cache
而不是只有一张“向量表”。
因为这两者解决的问题不一样。
-
不依赖 embedding provider
-
对精确关键词/文件名/术语更敏感
-
可作为降级路径
-
对语义相近表达有更强召回
-
对口语化 query 更友好
这解释了为什么 current design 不是“纯 vector DB”:
-
纯 vector 容易丢掉精确 lexical hit
-
纯 FTS 又难处理语义近邻
所以 builtin backend 的技术选型体现的是:
不是追求“最潮数据库”,而是追求单机可用前提下的召回韧性。
resolveStorePath(…) 默认给的是:
-
stateDir/memory/
.sqlite
这意味着:
-
每个 agent 默认有自己独立的 memory DB
-
不同 agent 可以有不同记忆视图
这和 OpenClaw 其他能力按 agent 隔离的设计是一致的。
所以记忆不是全局一锅端,而是默认 agent-scoped。
builtin manager 索引 markdown 文件时会经过:
-
listMemoryFiles(…)
-
buildFileEntry(…)
-
chunkMarkdown(…)
负责:
-
找到 MEMORY.md
-
找到 memory//*.md
-
找到 extraPaths 下的 markdown
-
过滤掉 symlink
-
去重
负责:
-
读文件
-
算 hash
-
记录相对路径、mtime、size
负责:
-
按大致 token 预算切块
-
保留 overlap
-
记录每个块的 startLine / endLine
这里默认用的是一个粗略换算:
-
tokens * 4 ~= chars
这表明当前 chunking 是偏工程化近似,而不是 tokenizer 精细切块。
如果启用了 sessions source,或 QMD 需要导出 sessions,会走:
-
listSessionFilesForAgent(…)
-
buildSessionEntry(…)
buildSessionEntry(…) 很值得注意:
-
只提取 user / assistant
-
跳过 tool messages
-
只保留 text 内容
-
做 redactSensitiveText(…)
-
最终拼成:
-
User: …
-
Assistant: …
也就是说,session 进入长期记忆索引前不是原样 JSONL,而是:
提炼成对 recall 更友好的纯文本会话摘要流。
这个转换很重要,因为它明确告诉你:
-
长期记忆检索不想索引完整工具协议噪声
-
更重视人类可理解的 user/assistant 语义
MemoryManagerSyncOps 里有几种触发源:
-
warmSession(…)
-
dirty 时由 search(…) 触发
-
chokidar 监听 markdown 变化
-
onSessionTranscriptUpdate(…)
-
增量计算 bytes/messages 变化
-
定时刷新
所以 builtin backend 并不是“手动 rebuild 一次”的模式,而是:
watcher + on-demand sync + interval sync 的混合模型。
因为 session transcript 变化比 markdown 文件更频繁。
如果每写一行 JSONL 就立刻重建索引,代价太高。
所以系统会累计:
-
新增字节数
-
新增消息数
只有达到阈值才把该 session 标成 dirty 并触发同步。
这是一种很典型的“高频事件 -> 低频索引刷新”的节流设计。
当前代码里,如果没有 embedding provider,sync 会:
-
直接跳过 embedding sync
日志里明确叫:
-
FTS-only mode
这再次说明 builtin memory 的核心目标不是“非得有向量库”,而是:
先尽量让记忆系统有可用搜索,再争取更好的语义召回。
QMD backend 不是复用 builtin sqlite schema,而是使用:
-
QMD 命令行工具
-
QMD 自己维护的 index.sqlite
-
XDG config/cache 目录
QMD manager 会为每个 agent 准备:
-
agentStateDir/qmd/xdg-config
-
agentStateDir/qmd/xdg-cache/qmd/index.sqlite
所以它本质上是:
外部索引系统,以 OpenClaw agent 为作用域进行托管。
QMD 不直接认 OpenClaw 的 files/chunks 表,而是认 collections。
它会解析并维护:
-
default memory collections
-
custom path collections
-
optional sessions collection
每个 collection 有:
-
name
-
path
-
pattern
-
kind
这说明 QMD 路径更接近:
多集合文档索引系统
而 builtin 更接近:
单 sqlite 库里的多 source 混合索引
QMD 不是直接搜 OpenClaw session JSONL。
它会:
-
先导出 session 为 markdown 到 exportDir
-
再把这些导出文件纳入 collection
这样做的好处是:
-
QMD 处理的是统一文档格式
-
与 builtin 的 session text 抽取思路类似,都是先把 transcript 变成 recall 友好的文档
所以在 QMD 路径里,session memory 更像:
会话导出文档化,再交给外部索引器。
因为 QMD 有自己的额外风险:
-
CLI 不可用
-
collection 元数据损坏
-
update/search 超时
-
MCP/mcporter 链路问题
而 builtin 至少能在本地 sqlite 上生存。
这就是为什么 search-manager 的架构不是:
-
“qmd 开了就彻底替代 builtin”
而是:
-
“qmd 优先,builtin 保底”
-
builtin backend 的真实技术栈是 sqlite + FTS5 + 可选 sqlite-vec + embedding cache 。
-
长期记忆文件默认是 MEMORY.md 和 memory/*/.md ,可扩展到 extraPaths 和 sessions。
-
session transcript 进入长期记忆时会被转译成更适合 recall 的纯文本格式。
-
sync 不是手工 rebuild,而是 watcher、search、session update、interval 共同驱动。
-
QMD backend 是一套外部集合索引体系,不是 builtin sqlite 的简单替皮。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/252385.html