跟我一起学 OpenClaw(07):Memory 向量搜索系统深度——从「AI 失忆」到「长期记忆」的工程实践
1. 为什么你的 AI 助手总在「失忆」Context Window 的真实边界先描述一个我遇到过的真实场景。有个朋友用 OpenClaw 搭了个私人助理前两周体验很好第三周开始出问题他上周明确说过「我的周报模板用 Markdown 表格不要用列表」结果这周助手又给他生成了一堆无序列表。他反复强调助手反复忘。他一度以为是模型太笨换了个更大的模型问题依旧。这不是模型笨这是 Context Window 的物理边界在起作用。无论你用的是 128K 还是 200K 上下文只要对话轮次够多、内容够长早期信息一定会被挤出去。更麻烦的是每次新会话开始时上下文是空的——模型对你的偏好、项目背景、历史决策一无所知。这就是「AI 失忆」的根源它没有持久化的外部记忆只有临时的对话窗口。OpenClaw 的 Memory 模块解决的正是这个问题。它不是在模型内部做文章而是在模型外部建了一套可检索、可分层、可衰减的记忆系统。核心链路是把记忆文本切块、向量化、写入索引查询时同时跑向量语义召回和 BM25 关键词召回再用 MMR 做多样性去重最后叠加时间衰减做混合排序。整套流程下来助手就能在几个月后依然准确回忆起你当初说过的偏好。这篇文章适合两类人一是已经被「AI 失忆」折磨过的 OpenClaw 用户二是想理解向量搜索 关键词混合检索工程落地的开发者。我会从配置片段讲到验证请求再到真实报错排查每一步都可以直接复制跟做。调用通道统一走 TaoToken 的 API 入口Key 和 Base URL 一次配好后面所有 embedding 和对话请求都复用同一套通道排错时不用在多个服务商之间来回切换。2. TaoToken 前置统一 Key 与 API 通道让 Memory 调用不再东拼西凑在动手配 Memory 之前先把调用通道理顺。OpenClaw 的 Memory 系统在向量化阶段需要调用 embedding 模型在对话阶段需要调用 chat 模型。如果你分别用不同服务商的 Key排错时会非常痛苦embedding 报 401 你以为是向量库问题chat 报超时你以为是网络问题实际上是两个 Key 各自的状态不同。我的做法是统一走 TaoToken。它的 API 入口是https://taotoken.net/api兼容 OpenAI 风格的请求格式所以 OpenClaw 里凡是填baseURL或apiBase的地方都指向这个地址。Key 在控制台的 API Keys 页面生成生成后复制出来后面配置里用环境变量引用不要硬编码进配置文件。具体操作路径先打开https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentmemory_vector_searchutm_campaignrewrite生成一个 Key。然后在终端里导出环境变量export TAOTOKEN_API_KEYsk-你的实际Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你用的是 Windows PowerShell换成$env:TAOTOKEN_API_KEYsk-你的实际Key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api这里有个容易踩的坑OpenClaw 的配置文件里baseURL末尾不要多加/v1。TaoToken 的入口已经处理了路径拼接你写https://taotoken.net/api就行写https://taotoken.net/api/v1反而会导致 404。我实测下来这个细节在第一次配置时最容易出错。另外Memory 的 embedding 模型和对话模型可以共用同一个 Key但建议在配置里分开写 model ID方便后续单独调整。比如 embedding 用text-embedding-3-small对话用gpt-4o-mini或你习惯的模型。TaoToken 的模型列表可以在模型对话页面查看https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmemory_vector_searchutm_campaignrewrite。配好之后先做一次最小验证确认通道是通的curl -s https://taotoken.net/api/models \ -H Authorization: Bearer $TAOTOKEN_API_KEY | head -c 500如果返回 JSON 里能看到模型列表说明 Key 和 Base URL 都没问题。这一步花两分钟能省掉后面半小时的无效排查。3. 可复制配置Memory 向量搜索 BM25 MMR 完整 settings 片段现在进入核心配置。OpenClaw 的 Memory 配置写在~/.openclaw/openclaw.json或你项目根目录的openclaw.json里。下面这份是我实测可用的完整片段路径和字段名与 OpenClaw 当前版本一致你可以直接复制后改 Key 引用。{ agents: { defaults: { memorySearch: { provider: openai, baseURL: https://taotoken.net/api, apiKey: ${env:TAOTOKEN_API_KEY}, model: text-embedding-3-small, chunkSize: 400, overlap: 50, indexStrategy: incremental, query: { hybrid: { enabled: true, vectorWeight: 0.7, textWeight: 0.3, mmr: { enabled: true, lambda: 0.7 }, temporalDecay: { enabled: true, halfLifeDays: 30, maxAgeDays: 365, boostRecent: { enabled: true, windowDays: 7, boostFactor: 1.5 } } } } } } }, compaction: { memoryFlush: { enabled: true, softThresholdTokens: 4000, systemPrompt: Session nearing compaction. Store durable memories now., prompt: Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store. } } }逐段解释关键参数。chunkSize: 400表示每个记忆块约 400 tokenoverlap: 50表示相邻块重叠 50 token防止一句话被切断后语义丢失。indexStrategy: incremental是增量索引只对新写入的记忆做向量化不会每次全量重建省时间也省钱。hybrid里的vectorWeight: 0.7和textWeight: 0.3是混合检索的权重。向量占七成负责语义召回BM25 占三成负责精确匹配。这个比例适合大多数场景。如果你发现代码片段、ID、数字经常搜不到可以把textWeight提到 0.4。mmr.lambda: 0.7控制多样性与相关性的平衡。lambda 越接近 1 越偏相关性越接近 0 越偏多样性。0.7 是个稳妥的起点能避免返回一堆几乎相同的记忆块。temporalDecay.halfLifeDays: 30表示记忆的「半衰期」是 30 天。一条 30 天前的记忆分数会衰减到原来的一半180 天前的记忆衰减到约 1.5%。boostRecent则给 7 天内的新记忆额外 1.5 倍加权确保最近的修改优先被召回。配置写完后重启 OpenClaw 让配置生效openclaw restart openclaw memory statusmemory status会输出索引覆盖率、向量库连接状态、embedding 模型可用性。如果这里显示embedding: unreachable先回去检查第 2 节的 Key 和 Base URL。4. 验证请求一次写入多轮召回看 Memory 是否真的「记住」了配置对不对不能只看状态要实际写入一条记忆再召回。我用的验证方法是写一条带具体数字和专有名词的偏好然后换几种问法去搜看能不能命中。先写入一条测试记忆。在 OpenClaw 的 workspace 里创建memory/2026-03-19.md## 2026-03-19 用户偏好记录 ### 开发环境 - Node.js 版本18.17.0 - 数据库PostgreSQL 15端口 5432 - 周报模板必须用 Markdown 表格禁止用无序列表 ### 项目决策 - 前端框架选 React 18 TypeScript - 样式方案用 Tailwind CSS不用 Bootstrap然后触发索引重建第一次写入后需要手动建索引openclaw memory index --force --verbose--verbose会打印每个块的向量化进度。正常输出类似[memory] chunking memory/2026-03-19.md - 3 chunks [memory] embedding chunk 1/3 ... ok [memory] embedding chunk 2/3 ... ok [memory] embedding chunk 3/3 ... ok [memory] index written: 3 vectors接下来做多轮召回验证。第一轮用语义问法openclaw memory search 我的周报格式要求是什么 --verbose预期命中「周报模板必须用 Markdown 表格」这一块。第二轮用关键词问法故意用记忆里没有的原词openclaw memory search 数据库端口 --verbose预期命中「PostgreSQL 15端口 5432」。第三轮测试时间衰减先搜一个近期记忆再搜一个你手动改成 180 天前的旧记忆对比分数openclaw memory search 前端框架选型 --verbose输出里会显示vector_score、text_score、decay_factor、final_score四个值。如果decay_factor对旧记忆明显小于 1说明时间衰减生效了。如果你想直接通过 API 验证 embedding 通道可以发一个最小请求curl -s https://taotoken.net/api/embeddings \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:text-embedding-3-small,input:周报模板用 Markdown 表格}返回里data[0].embedding是一个长度 1536 的数组说明向量化通道完全正常。这一步能帮你把「Memory 配置问题」和「API 通道问题」彻底分开。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 逐个击破配置 Memory 的过程中报错基本集中在四类。我把真实遇到过的报错原文和排查路径列出来你对照着看。报错一401 Unauthorized或invalid api key这是最常见的一类。先确认环境变量是否真的被 OpenClaw 进程读到openclaw config get agents.defaults.memorySearch.apiKey如果输出是${env:TAOTOKEN_API_KEY}而不是实际 Key说明环境变量没展开。检查你导出变量的终端和启动 OpenClaw 的终端是不是同一个。用 systemd 或 Docker 启动的话要在 service 文件或 compose 里显式传环境变量。另外确认 Key 没有多余空格复制时容易带上换行。报错二local proxy failed或connection refused这个报错通常出现在baseURL写错或本地网络策略拦截时。先确认地址是https://taotoken.net/api不是http也不是带/v1的变体。然后用 curl 单独测curl -v https://taotoken.net/api/models -H Authorization: Bearer $TAOTOKEN_API_KEY如果 curl 通但 OpenClaw 不通检查 OpenClaw 是否配置了额外的 HTTP 代理环境变量HTTP_PROXY/HTTPS_PROXY把它清掉再试。报错三reading choices或cannot read property choices of undefined这个报错说明请求发出去了但返回体结构不符合预期。常见原因是baseURL指向了一个不兼容 OpenAI 格式的端点或者模型 ID 写错了。确认model字段是 TaoToken 支持的 embedding 模型名比如text-embedding-3-small。如果你把对话模型名填到了 embedding 配置里也会触发这个错。用第 4 节的 curl 命令单独测 embedding 端点能快速定位。报错四OAuth相关报错或token expired如果你之前用 OAuth 方式登录过某个服务OpenClaw 可能缓存了旧的凭证。清理缓存后重新用 API Key 方式配置rm -rf ~/.openclaw/.auth-cache openclaw restart然后确认配置里provider是openai而不是oauthapiKey引用的是 TaoToken 的 Key。排查完这四类基本 95% 的 Memory 接入问题都能解决。如果还不行去接入文档页面看最新的配置示例https://taotoken.net/docs?utm_sourcetaotoken_aicg_blog_endutm_contentmemory_vector_searchutm_campaignrewrite。6. 长期记忆的工程化把 Memory 用成习惯而不是一次性配置配置跑通只是开始。真正让 Memory 发挥价值的是把它变成日常习惯。我自己的做法是每天结束工作时让 OpenClaw 把当天的重要决策和偏好写入memory/YYYY-MM-DD.md每周花五分钟把值得长期保留的内容整理进MEMORY.md每月跑一次openclaw memory index --force重建索引清理掉过期的向量。如果你经常做编码类任务可以把 Memory 和 Coding Plan 结合使用让助手在写代码时自动召回项目约定和历史决策。Coding Plan 的入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentmemory_vector_searchutm_campaignrewrite配好之后 Memory 的召回结果会直接进入编码上下文不用你手动粘贴。还有一个实用技巧在MEMORY.md里用固定的二级标题分类比如「## 用户偏好」「## 项目决策」「## 重要事实」。这样即使不做向量搜索你直接打开文件也能快速定位。向量搜索是给模型用的Markdown 结构是给人用的两者不冲突。最后提醒一点Memory 的 embedding 调用会产生费用虽然单次很便宜但如果你每天写入大量日志一个月下来也不容忽视。建议开启查询缓存和 embedding 缓存并在配置里设置rateLimit避免意外的高频调用。具体参数可以参考第 3 节的配置片段按你的实际用量调整chunkSize和索引频率。到这里你的 OpenClaw 应该已经具备了从「失忆」到「长期记忆」的完整能力。接下来就是持续写入、定期整理、按需调参让它真正成为你工作流的一部分。