内存配置参考
本页面涵盖了 OpenClaw 内存搜索的完整配置项。有关概念概述(文件布局、内存工具、何时写入内存以及自动刷新),请参阅内存。内存搜索默认值
- 默认启用。
- 监视内存文件的变化(防抖动)。
- 在
agents.defaults.memorySearch下配置内存搜索(而不是顶级memorySearch)。 - 默认使用远程嵌入。如果未设置
memorySearch.provider,OpenClaw 将自动选择:- 如果配置了
memorySearch.local.modelPath并且该文件存在,则使用local。 - 如果可以解析 OpenAI 密钥,则使用
openai。 - 如果可以解析 Gemini 密钥,则使用
gemini。 - 如果可以解析 Voyage 密钥,则使用
voyage。 - 如果可以解析 Mistral 密钥,则使用
mistral。 - 否则,内存搜索将保持禁用状态,直到进行配置。
- 如果配置了
- 本地模式使用 node-llama-cpp,可能需要
pnpm approve-builds。 - 使用 sqlite-vec(如果可用)来加速 SQLite 内部的向量搜索。
memorySearch.provider = "ollama"也支持本地/自托管的 Ollama 嵌入(/api/embeddings),但不会自动选择。
models.providers.*.apiKey 或环境变量中解析密钥。Codex OAuth 仅涵盖聊天/补全,并且不满足
内存搜索的嵌入需求。对于 Gemini,请使用 GEMINI_API_KEY 或
models.providers.google.apiKey。对于 Voyage,请使用 VOYAGE_API_KEY 或
models.providers.voyage.apiKey。对于 Mistral,请使用 MISTRAL_API_KEY 或
models.providers.mistral.apiKey。Ollama 通常不需要真正的 API
密钥(当本地策略需要时,像 OLLAMA_API_KEY=ollama-local 这样的占位符就足够了)。
当使用自定义 OpenAI 兼容端点时,
请设置 memorySearch.remote.apiKey(以及可选的 memorySearch.remote.headers)。
QMD 后端(实验性)
设置memory.backend = "qmd" 以将内置的 SQLite 索引器替换为
QMD:一个本地优先的搜索 sidecar,它结合了
BM25 + 向量 + 重排序。Markdown 仍然是单一事实来源;OpenClaw 外壳
程序调用 QMD 进行检索。要点:
先决条件
- 默认情况下禁用。需按配置选择加入 (
memory.backend = "qmd")。 - 单独安装 QMD CLI(
bun install -g https://github.com/tobi/qmd或获取 一个发行版)并确保qmd二进制文件位于网关的PATH上。 - QMD 需要一个允许扩展的 SQLite 构建(在 macOS 上
为
brew install sqlite)。 - QMD 通过 Bun +
node-llama-cpp完全在本地运行,并在首次使用时自动从 HuggingFace 下载 GGUF 模型(无需单独的 Ollama 守护进程)。 - 网关通过设置
XDG_CONFIG_HOME和XDG_CACHE_HOME,在~/.openclaw/agents/<agentId>/qmd/下的自包含 XDG 主目录中 运行 QMD。 - 操作系统支持:一旦安装了 macOS + SQLite,Linux 和 Bun 即可直接开箱即用。 Windows 最好通过 WSL2 获得支持。
Sidecar 如何运行
- 网关在
~/.openclaw/agents/<agentId>/qmd/下写入一个自包含的 QMD 主目录 (配置 + 缓存 + sqlite 数据库)。 - 集合通过
qmd collection add从memory.qmd.paths创建(加上默认工作区内存文件),然后qmd update+qmd embed在启动时和可配置的间隔(memory.qmd.update.interval,默认 5 m)运行。 - 网关现在在启动时初始化 QMD 管理器,因此即使在进行第一次
memory_search调用之前,定期更新计时器也已启动。 - 启动刷新现在默认在后台运行,因此不会阻塞聊天启动;设置
memory.qmd.update.waitForBootSync = true以保持之前的阻塞行为。 - 搜索通过
memory.qmd.searchMode运行(默认qmd search --json;也支持vsearch和query)。如果所选模式在您的 QMD 构建上拒绝标志,OpenClaw 将使用qmd query重试。如果 QMD 失败或二进制文件丢失,OpenClaw 将自动回退到内置 SQLite 管理器,以便内存工具继续工作。 - OpenClaw 目前不公开 QMD 嵌入批量大小调整;批量行为由 QMD 本身控制。
- 首次搜索可能会很慢:在第一次
qmd query运行时,QMD 可能会下载本地 GGUF 模型(重排序器/查询扩展)。-
当 OpenClaw 运行 QMD 时,会自动设置
XDG_CONFIG_HOME/XDG_CACHE_HOME。 -
如果您想手动预下载模型(并预热 OpenClaw 使用的相同索引),请使用代理的 XDG 目录运行一次性查询。
OpenClaw 的 QMD 状态位于您的 state dir 下(默认为
~/.openclaw)。您可以通过导出与 OpenClaw 使用的相同 XDG 变量,将qmd指向完全相同的索引:
-
当 OpenClaw 运行 QMD 时,会自动设置
Config surface (memory.qmd.*)
command(默认qmd):覆盖可执行文件路径。searchMode(默认search):选择哪个 QMD 命令支持memory_search(search、vsearch、query)。includeDefaultMemory(默认true):自动索引MEMORY.md+memory/**/*.md。paths[]:添加额外的目录/文件(path,可选pattern,可选 稳定name)。sessions:选择加入会话 JSONL 索引(enabled,retentionDays,exportDir)。update:控制刷新节奏和维护执行: (interval,debounceMs,onBoot,waitForBootSync,embedInterval,commandTimeoutMs,updateTimeoutMs,embedTimeoutMs)。limits:限制召回负载(maxResults,maxSnippetChars,maxInjectedChars,timeoutMs)。scope:与session.sendPolicy具有相同的架构。 默认为仅私信(deny所有,allow直接聊天);放宽限制以在群组/频道中显示 QMD 命中。match.keyPrefix匹配 规范化 的会话密钥(小写,去掉任何 前导agent:<id>:)。示例:discord:channel:。match.rawKeyPrefix匹配 原始 会话密钥(小写),包括agent:<id>:。示例:agent:main:discord:。- 旧版:
match.keyPrefix: "agent:..."仍被视为原始密钥前缀, 但为清晰起见,首选rawKeyPrefix。
- 当
scope拒绝搜索时,OpenClaw 会记录一条警告,其中包含派生的channel/chatType,以便更轻松地调试空结果。 - 源自工作区之外的代码片段会在
memory_search结果中显示为qmd/<collection>/<relative-path>;memory_get能识别该前缀,并从已配置的 QMD 集合根目录进行读取。 - 当
memory.qmd.sessions.enabled = true时,OpenClaw 会将经过净化的会话 转录(用户/助手对话轮次)导出到~/.openclaw/agents/<id>/qmd/sessions/下的专用 QMD 集合中,以便memory_search可以在不触及内置 SQLite 索引的情况下 回顾最近的对话。 - 当
memory.citations为auto/on时,memory_search代码片段现在包含Source: <path#line>页脚; 设置memory.citations = "off"可将路径元数据保留在内部(代理仍会收到用于memory_get的路径,但片段文本会省略页脚,且系统提示 会警告代理不要引用它)。
QMD 示例
引用与回退
- 无论使用何种后端(
auto/on/off),memory.citations均适用。 - 当
qmd运行时,我们会标记status().backend = "qmd",以便诊断信息显示 是哪个引擎提供了结果。如果 QMD 子进程退出或无法解析 JSON 输出, 搜索管理器将记录警告并返回内置提供商(现有的 Markdown 嵌入), 直到 QMD 恢复。
其他内存路径
如果要索引默认工作区布局之外的 Markdown 文件,请添加 显式路径:- 路径可以是绝对路径,也可以是相对于工作区的路径。
- 目录会被递归扫描以查找
.md文件。 - 默认情况下,仅索引 Markdown 文件。
- 如果
memorySearch.multimodal.enabled = true,OpenClaw 也会仅对extraPaths下支持的图像/音频文件进行索引。默认内存根目录(MEMORY.md、memory.md、memory/**/*.md)保持为仅限 Markdown。 - 符号链接(文件或目录)会被忽略。
多模态内存文件(Gemini 图像 + 音频)
OpenClaw 可以在使用 Gemini embedding 2 时,从memorySearch.extraPaths 索引图像和音频文件:
- 目前多模态存储仅支持
gemini-embedding-2-preview。 - 多模态索引仅适用于通过
memorySearch.extraPaths发现的文件。 - 此阶段支持的模态:图像和音频。
- 启用多模态存储时,
memorySearch.fallback必须保持为"none"。 - 匹配的图像/音频文件字节将在索引期间上传到配置的 Gemini embedding 端点。
- 支持的图像扩展名:
.jpg、.jpeg、.png、.webp、.gif、.heic、.heif。 - 支持的音频扩展名:
.mp3、.wav、.ogg、.opus、.m4a、.aac、.flac。 - 搜索查询仍为文本,但 Gemini 可以将这些文本查询与索引的图像/音频嵌入进行比较。
memory_get仍然仅读取 Markdown;二进制文件可搜索,但不会作为原始文件内容返回。
Gemini 嵌入(原生)
将提供商设置为gemini 以直接使用 Gemini 嵌入 API:
remote.baseUrl是可选的(默认为 Gemini API 基础 URL)。- 如果需要,
remote.headers允许您添加额外的标头。 - 默认模型:
gemini-embedding-001。 gemini-embedding-2-preview也受支持:8192 令牌限制和可配置维度(768 / 1536 / 3072,默认为 3072)。
Gemini Embedding 2(预览版)
需要重新索引: 从gemini-embedding-001(768 维) 切换到gemini-embedding-2-preview(3072 维)会改变向量大小。如果您在 768、1536 和 3072 之间更改outputDimensionality也是如此。 OpenClaw 在检测到模型或维度更改时会自动重新索引。
自定义 OpenAI 兼容端点
如果您想使用自定义的 OpenAI 兼容端点(OpenRouter、vLLM 或代理), 您可以使用remote 配置与 OpenAI 提供商:
memorySearch.provider = "local" 或设置
memorySearch.fallback = "none"。
回退
memorySearch.fallback可以是openai、gemini、voyage、mistral、ollama、local或none。- 仅当主要嵌入提供商失败时才会使用回退提供商。
批量索引(OpenAI + Gemini + Voyage)
- 默认禁用。设置
agents.defaults.memorySearch.remote.batch.enabled = true以针对大规模语料库索引(OpenAI、Gemini 和 Voyage)启用。 - 默认行为会等待批次完成;如有需要,请调整
remote.batch.wait、remote.batch.pollIntervalMs和remote.batch.timeoutMinutes。 - 设置
remote.batch.concurrency以控制我们并行提交多少个批处理作业(默认:2)。 - 当使用
memorySearch.provider = "openai"或"gemini"时,批量模式生效,并使用相应的 API 密钥。 - Gemini 批处理作业使用异步嵌入批量端点,并要求 Gemini Batch API 可用。
- 对于大规模回填,OpenAI 通常是我们支持的最快选项,因为我们可以在单个批处理作业中提交许多嵌入请求,并让 OpenAI 异步处理它们。
- OpenAI 为 Batch API 工作负载提供折扣价格,因此大型索引运行通常比同步发送相同请求更便宜。
- 有关详细信息,请参阅 OpenAI Batch API 文档和定价:
记忆工具如何工作
memory_search对来自MEMORY.md+memory/**/*.md的 Markdown 块(目标约 400 个 token,80 个 token 重叠)进行语义搜索。它返回片段文本(限制约 700 个字符)、文件路径、行范围、评分、提供商/模型,以及我们是否从本地回退到了远程嵌入。不返回完整的文件内容。memory_get读取特定的内存 Markdown 文件(相对于工作区),可选择从起始行开始读取 N 行。拒绝MEMORY.md/memory/之外的路径。- 只有当
memorySearch.enabled对该代理解析为 true 时,这两个工具才会启用。
索引内容与时机
- 文件类型:仅限 Markdown(
MEMORY.md、memory/**/*.md)。 - 索引存储:位于
~/.openclaw/memory/<agentId>.sqlite的每个代理 SQLite 数据库(可通过agents.defaults.memorySearch.store.path配置,支持{agentId}token)。 - 新鲜度:对
MEMORY.md+memory/的监视器会将索引标记为脏(防抖 1.5 秒)。同步在会话开始、搜索或按间隔安排,并异步运行。会话记录使用增量阈值来触发后台同步。 - 重建索引触发器:索引存储嵌入 提供商/模型 + 端点指纹 + 分块参数。如果其中任何一项发生变化,OpenClaw 会自动重置并重新索引整个存储。
混合搜索 (BM25 + 向量)
启用后,OpenClaw 结合了:- 向量相似度(语义匹配,措辞可以不同)
- BM25 关键词相关性(精确的 token,如 ID、环境变量、代码符号)
为何采用混合搜索
向量搜索非常擅长处理“意思相同”的情况:- “Mac Studio 网关主机” vs “运行网关的机器”
- “文件更新防抖” vs “避免在每次写入时进行索引”
- ID (
a828e60、b3b9895a...) - 代码符号 (
memorySearch.query.hybrid) - 错误字符串(“sqlite-vec unavailable”)
我们如何合并结果(当前的设计)
实现草图:- 从两侧检索候选池:
- 向量:按余弦相似度取前
maxResults * candidateMultiplier个结果。 - BM25:按 FTS5 BM25 排名(越低越好)取前
maxResults * candidateMultiplier个结果。
- 将 BM25 排名转换为 0..1 左右的分数:
textScore = 1 / (1 + max(0, bm25Rank))
- 按块 ID 合并候选集并计算加权分数:
finalScore = vectorWeight * vectorScore + textWeight * textScore
vectorWeight+textWeight在配置解析时会被归一化为 1.0,因此权重的表现类似于百分比。- 如果嵌入不可用(或者提供商返回零向量),我们仍然运行 BM25 并返回关键词匹配项。
- 如果无法创建 FTS5,我们保持仅向量搜索(不会硬性失败)。
后处理流水线
在合并向量和关键词分数后,两个可选的后处理阶段 会在结果列表到达 Agent 之前对其进行细化:MMR 重排序(多样性)
当混合搜索返回结果时,多个块可能包含相似或重叠的内容。 例如,搜索“家庭网络设置”可能会返回五个几乎相同的片段, 它们来自不同的每日笔记,但都提到了相同的路由器配置。 **MMR(最大边际相关性)**会对结果进行重新排序,以平衡相关性和多样性, 确保热门结果覆盖查询的不同方面,而不是重复相同的信息。 工作原理:- 结果根据其原始相关性(向量 + BM25 加权分数)进行评分。
- MMR 迭代选择能最大化以下值的结果:
lambda x relevance - (1-lambda) x max_similarity_to_selected。 - 结果之间的相似度是通过对分词后的内容使用 Jaccard 文本相似度来衡量的。
lambda 参数控制这种权衡:
lambda = 1.0— 纯相关性(无多样性惩罚)lambda = 0.0— 最大多样性(忽略相关性)- 默认值:
0.7(平衡,略偏向相关性)
memory_search 返回了冗余或近似重复的片段,尤其是对于那些经常在几天内重复类似信息的每日笔记时。
时间衰减(近期性提升)
拥有每日笔记的 Agent 随着时间推移会积累数百个带日期的文件。如果没有衰减,六个月前措辞得当的笔记可能会在排名上压过昨天关于同一主题的更新。 时间衰减根据每个结果的年龄对分数应用指数乘数,从而使近期的记忆自然排名更高,而旧的记忆则逐渐淡出:lambda = ln(2) / halfLifeDays。
使用默认的 30 天半衰期:
- 今天的笔记:原始分数的 100%
- 7 天前:~84%
- 30 天前:50%
- 90 天前:12.5%
- 180 天前:~1.6%
MEMORY.md(根记忆文件)memory/中不带日期的文件(例如memory/projects.md、memory/network.md)- 这些包含持久的参考信息,应始终正常排名。
memory/YYYY-MM-DD.md)使用从文件名提取的日期。其他来源(例如会话记录)则回退到文件修改时间(mtime)。
示例 — 查询:“what’s Rod’s work schedule?”
给定这些记忆文件(今天是 2 月 10 日):
混合搜索配置
这两个功能均在memorySearch.query.hybrid 下配置:
- 仅 MMR — 当您有许多相似的笔记且新旧程度无关时很有用。
- 仅时间衰减 — 当近期相关性很重要但您的结果已经足够多样化时很有用。
- 两者皆用 — 推荐用于拥有大量长期运行的每日笔记历史的智能体。
嵌入缓存
OpenClaw 可以在 SQLite 中缓存 分块嵌入(chunk embeddings),以便重新索引和频繁更新(尤其是会话记录)时不会对未更改的文本重新进行嵌入。 配置:会话记忆搜索(实验性)
您可以选择性地索引 会话记录 并通过memory_search 展示它们。
这受控于一个实验性标志。
- 会话索引是 可选加入(opt-in) 的(默认关闭)。
- 会话更新是防抖动的,一旦超过增量阈值,就会 异步索引(尽力而为)。
memory_search永远不会阻塞索引;在后台同步完成之前,结果可能会稍微有些过时。- 结果仍然仅包含片段;
memory_get仍仅限于记忆文件。 - 会话索引是按智能体隔离的(仅索引该智能体的会话日志)。
- 会话日志存储在磁盘上 (
~/.openclaw/agents/<agentId>/sessions/*.jsonl)。任何拥有文件系统访问权限的进程/用户都可以读取它们,因此请将磁盘访问视为信任边界。为了更严格的隔离,请在不同的操作系统用户或主机下运行智能体。
SQLite 向量加速 (sqlite-vec)
当 sqlite-vec 扩展可用时,OpenClaw 会将嵌入存储在 SQLite 虚拟表 (vec0) 中,并在
数据库内执行向量距离查询。这使得搜索速度很快,而无需将每个嵌入都加载到 JS 中。
配置(可选):
enabled默认为 true;禁用时,搜索将回退到对存储的嵌入进行进程内 余弦相似度计算。- 如果缺少 sqlite-vec 扩展或加载失败,OpenClaw 会记录错误并继续使用 JS 后备方案(无向量表)。
extensionPath会覆盖捆绑的 sqlite-vec 路径(适用于自定义构建或非标准安装位置)。
本地嵌入模型自动下载
- 默认本地嵌入模型:
hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf(~0.6 GB)。 - 当
memorySearch.provider = "local"时,node-llama-cpp会解析modelPath;如果 GGUF 文件丢失,它会 自动下载 到缓存(如果设置了local.modelCacheDir则下载到该位置),然后加载它。下载会在重试时恢复。 - 原生构建要求:运行
pnpm approve-builds,选择node-llama-cpp,然后执行pnpm rebuild node-llama-cpp。 - 后备方案:如果本地设置失败且
memorySearch.fallback = "openai",我们会自动切换到远程嵌入(除非被覆盖,否则为openai/text-embedding-3-small)并记录原因。
自定义 OpenAI 兼容端点示例
remote.*优先于models.providers.openai.*。remote.headers与 OpenAI 标头合并;发生键冲突时,远程端优先。省略remote.headers以使用 OpenAI 默认值。
本页面源自 openclaw/openclaw,由 BeaversLab 翻译,遵循 MIT 协议 发布。