资讯动态

OpenClaw记忆系统搭建:向量库+分层存储实战

发布时间:2026/10/22 13:28:26 来源:尧图企业网站定制
如果你的OpenClaw装完只当个玩具聊天机器人那你其实根本不需要看这篇文章。但如果你跟我一样想让这个本地部署的AI代理真正记住你的偏好、记得上次任务的进展、甚至半个月后还能翻出某句关键结论那记忆系统就是绕不过去的必修课。我在给本地实例补齐记忆能力时前后折腾了两周从想明白什么该记、什么不该记到决定向量库和结构化文件如何分工再到写一组工具让代理自己完成记忆的写入、召回和清理最后沉淀成一套能直接复制到新部署上的模板。这篇就是完整的搭建思路适合那些已经跑通OpenClaw基础对话、正在纠结“怎么让它记住东西”的人。先说结论记忆系统不是一个数据库那么简单它是一整套“会自己记、能自己找、知道什么时候忘”的机制。下面我会按照我的搭建顺序把架构拆解、选型理由、代码实现、Prompt集成和踩坑记录一项一项讲清楚。1. 记忆到底是什么OpenClaw缺的不是数据是分层存储刚开始我犯过一个典型错误以为给OpenClaw接一个向量数据库就是“有记忆了”。结果数据写进去了一堆问它“我之前是不是说过某个需求”它翻出来的全是似曾相识但又完全不对的内容。后来我才想明白问题根源在于没有做记忆分层。模型的上下文窗口是有限的每次会话结束窗口里的对话历史就清空了。这本来不是缺陷而是成本约束。想让它拥有跨会话的记忆就得在外面搭一套存储体系。我把记忆拆成三个层级这也是整份模板的基本骨架记忆层级存储介质生命周期典型用途短期记忆会话上下文窗口单次会话内对话连贯性、任务执行状态工作记忆JSON临时文件任务执行期间多步任务中间状态、待办清单长期记忆向量库 结构化文件跨会话、数周以上用户偏好、历史事实、项目背景、操作习惯这套模板的核心工作就是把长期记忆真正建起来同时让短期记忆和工作记忆能和它顺畅衔接。很多人一上来就直奔向量数据库其实忽略了最重要的问题长期记忆里到底该存什么。1.1 短期记忆与工作记忆先让代理“别丢三落四”短期记忆就是模型窗口里的消息历史。OpenClaw默认会保留一定轮数的对话你可以通过类似max_turns的参数控制。这里我的经验很简单不要把窗口调太大。窗口拉长虽然能记住更多细节但超过一定轮数之后模型反而开始抓错重点经常被无关紧要的寒暄干扰判断。我实测下来普通任务用20到40条消息历史做上下文就够长文档类任务再单独走文档解析流程不要一概而论。工作记忆我用一个轻量JSON文件处理路径固定为./memory/session_state.json。代理在跑多步任务时把中间状态写进去比如当前进行到哪一步、某个文件的路径、已经收集到的几条信息。这样即使中途上下文被截断也能从工作记忆里恢复现场。这个文件在每轮任务结束后会清空或归档不参与长期记忆的构建。1.2 长期记忆向量库负责模糊检索结构化文件负责精确事实长期记忆是真正的重头戏。我采用的是“向量库 结构化文件”双写方案而不是单纯依赖向量库。原因很简单向量检索擅长“模糊找”。你问“我之前是不是聊过关于家庭账单自动分类的想法”它能根据语义把相关记录捞出来但如果你要的是一条精确事实比如“某处设备的接入密码是什么”向量召回就不可靠了容易召回到一群相似但完全错误的内容。所以在这份模板里长期记忆分两条线语义记忆写入向量库存的是概括后的经验、偏好、想法召回时用相似度检索。事实记忆写入结构化文件JSON或YAML每条记录有明确字段比如keyword: 接入密码、scene: 客厅设备、content: xxx召回时做精确匹配。两条线在配置层面共用同一组记忆接口Agent调用时不需要关心底层到底是向量库还是JSON文件只管用固定格式存取。这个解耦在后面换存储引擎的时候作用非常大。2. 基础环境搭配向量库选型与Embedding模型取舍记忆系统选型这件事很容易走极端。有人觉得必须上重型数据库有人觉得直接存文本文件就够。我两个方向都试过之后得出一个结论“够用”和“扩展性”需要平衡。这份模板默认采用“轻量起步、可迁移升级”的思路先把流程跑通数据量大了再说换引擎的事接口层保持不变。2.1 向量库选型我为什么放弃大而全的方案早期我图省事直接在主程序里强依赖一个专业的向量数据库服务。官方文档确实全检索质量也高但缺点同样明显需要单独维护服务进程内存占用不小和OpenClaw的启动流程联动时稍微换个版本就出现兼容问题。后来我把选择标准收敛成三条能嵌入进程内运行不依赖额外服务降低部署复杂度。检索质量可接受支持本地持久化重启不丢数据。接口简单后续要换底层引擎时改动尽量小。在这个标准下我最终选了本地嵌入式向量库纯Python环境几分钟就能装好数据落在本地一个目录里。将来如果数据量真的大到需要分布式检索再把存储层替换掉就行。当时我纠结过的几个方案差别如下方案部署方式优势劣势适用场景嵌入式向量库进程内无需额外服务、安装简单数据量大时性能瓶颈个人部署、中小规模独立向量数据库服务独立进程检索性能强、支持高并发部署复杂、资源占用高多用户、生产环境纯文本/JSON检索文件系统零依赖、适合精确记忆无语义检索能力事实记忆、配置类记忆这份模板的默认方案是语义记忆用嵌入式向量库事实记忆用JSON文件。后期如果数据量超过百万条向量再把嵌入式方案换成分散服务接口层不动。2.2 Embedding模型取舍本地推理优先云端接口慎用向量库选好之后更要紧的是Embedding模型的选择。我试过两条路线一条是调用云端大模型的Embedding接口效果确实好但每次写入和召回都要走网络延迟不稳定另一条是本地跑开源Embedding模型速度更快效果略弱。最终模板选了本地轻量模型原因很实际OpenClaw本身就跑在本地记忆系统如果每次都要联网请求Embedding接口那就是给自己埋雷。网络一抖动整个代理卡住体验非常糟糕。选Embedding模型时我重点看三个维度中文语义支持尽量选对中文预训练充分的模型检索中文内容时效果差距很大。我一开始用了一个偏英文优化的版本中文召回时好时坏换成中文优化版之后立竿见影。向量维度维度越高精度可能越好但存储和检索开销也越大。个人部署场景推荐用中等维度配置不用盲目追高。单机推理速度记忆写入和召回都会频繁调用Embedding单条文本推理时长最好控制在几十毫秒以内否则每次对话都明显变卡。另外提醒一句Embedding模型一旦定下来中途不要随便换。不同模型生成的向量分布不一样混用会导致召回质量骤降。后面我会专门讲切换模型的正确姿势。3. 核心搭建过程记忆写入、召回与遗忘的模板化实现这部分是整份模板的核心我把它拆成记忆写入、记忆召回、记忆遗忘三个模块。每个模块代码量都不大但组合起来就是一个完整可用的记忆系统。3.1 记忆写入先小结再入库别把流水账塞进长期记忆长期记忆最怕“什么都记”。如果每轮对话都原样存进去用不了几天向量库就被琐碎信息淹没到时候召回的全是噪声。我的解决办法是先让Agent对当前内容做一次小结再决定要不要写入长期记忆。写入流程是这样Agent完成任务或一段对话后调用记忆工具prepare_memory_write。工具内部先把当前上下文的核心内容压缩成一条结构化摘要。判断这条摘要是否值得进入长期记忆重复信息跳过、临时信息跳过只有明确的偏好、事实、经验结论才允许写入。值得写入的分别走两条线语义记忆转成向量写入向量库事实记忆写入JSON文件。核心逻辑参考如下def prepare_memory_write(context: str) - MemoryItem | None: summary summarize(context) # 用轻量模型或规则做压缩 if not is_worth_keeping(summary): return None item MemoryItem( contentsummary, keywordsextract_keywords(summary), timestampnow(), memory_typesemantic or fact ) return item def commit_memory(item: MemoryItem): if item.memory_type semantic: vector_store.add(item.content_embedding, meta{timestamp: item.timestamp}) else: facts_store.append(item)“先小结再入库”是整个系统不跑偏的关键。你可能觉得多一次模型调用有点浪费但对比“存了垃圾数据后每次召回都变差”的代价这点开销非常值得。我在实际测试里发现加上这个环节之后召回准确率至少提升了一个量级。3.2 记忆召回精确匹配优先语义检索兜底召回阶段我采用“先精确、后模糊”的两段式策略先查事实记忆的JSON文件做关键词精确匹配如果没命中再走向量库做语义检索。这样处理的好处很直接对“某个设备的接入密码是什么”这类问题能直接拿到准确答案对“上次聊过什么关于记账的想法”这类开放式问题则靠向量库找相似内容。召回的核心代码是这样def recall_memory(query: str) - list[MemoryItem]: # 第一段精确匹配 exact_hits facts_store.search( keywordextract_keywords(query), limit3 ) if exact_hits: return exact_hits # 第二段语义召回 query_vec embed(query) candidates vector_store.search(query_vec, top_k5) ranked rerank_by_time_and_relevance(candidates) return ranked这里有三个细节值得注意精确匹配优先级一定要高于语义检索否则事实类信息容易被“相似的废话”淹没。语义召回的top_k不要设太大个人代理场景取5左右就够太多反而让上下文变长、响应变慢。召回结果要附带时间戳让Agent在面对多个相似记忆时优先参考最近记录。3.3 记忆遗忘没人爱删东西但你迟早得删遗忘机制是我一开始完全忽略的部分。直到某天OpenClaw在一次会话里突然把半年前一条过期信息当成最新状态用我才意识到长期记忆如果没有遗忘机制就是一座越住越乱的老房子你只知道东西在里面但永远找不到哪件是新的。遗忘机制我做了两条规则时间衰减超过设定天数默认180天且从未被召回过的记忆进入待清理队列。主动覆盖同一关键词下写入新事实时旧事实自动标记为过期Agent检索时不优先返回过期内容。实现上每次写入时在JSON文件里加一个active字段向量库召回时用元数据里的timestamp参与排序。清理任务每天跑一次把过期队列里的内容彻底删除或归档到backup目录。归档而不是直接删是为了留一条后悔药跑一段时间确认没问题再清空归档目录。4. 把记忆融进日常对话会话上下文与长期记忆配合存储搭好之后剩下最关键的事怎么让OpenClaw在日常对话里知道“什么时候该查记忆、什么时候该写记忆”。这一步靠的不是改模型而是改System Prompt和工具暴露。4.1 在System Prompt里植入记忆使用规则我在OpenClaw的System Prompt里加了一段固定规则内容大致如下当用户提到“之前”“上次”“我记得”等回溯词时先调用recall_memory再回答。当对话中出现新的用户偏好、明确事实、长期计划时调用prepare_memory_write并commit_memory。当记忆召回结果与当前对话冲突时以当前对话为准并主动更新记忆。这三条规则看起来简单实测下来却能让记忆系统的使用率大幅提升。原因是Agent的自发行为不可靠必须用显式规则把记忆工具的使用时机框住否则工具就是个摆设。我一开始没写这些规则结果记忆工具偶尔被调用、偶尔不调用完全不稳定。加上规则之后行为一下就统一了。4.2 工具的One-Shot示例与容错光有规则还不够还要给OpenClaw准备工具调用的示例。因为模型在工具调用上偶尔会“自行发挥”参数格式写错、字段名写错、甚至把记忆工具当成闲聊对象。我的做法是在工具描述里附带一个严格的JSON调用示例。工具描述示例{ tool: recall_memory, description: 根据用户问题检索长期记忆返回结构化记忆条目列表。, usage_example: {query: 上次说的那个项目进展如何, limit: 3} }实测发现有了示例之后工具调用格式错误率明显下降。如果你发现自家代理经常把工具参数传错优先检查工具描述里有没有“用法示例”而不是盲目去换模型。4.3 会话开场先召回历史为了让跨会话体验连贯我还在对话开始时加了一步“开场召回”。具体做法是会话启动后先调用一次recall_memory输入是用户最近的几条消息摘要或固定的会话标识把相关记忆提前拉进上下文缓存。这样用户一开口问“上次那个事怎么样了”Agent不用现查现等响应速度体感快很多。实现上很简单就是在OpenClaw的会话初始化钩子里加一段代码def on_session_start(session_id: str, user_summary: str): history recall_memory(user_summary) inject_to_context(history, window_size3)这里有一个警告开场召回的记录不要全塞进上下文我最多注入3条最相关的记录防止无关记忆占据宝贵的上下文空间。有一次我贪多塞了10条进去结果代理回答时被一堆不相关的旧记忆干扰反而答非所问。5. 上线后必踩的几个坑与优化方向记忆系统上线跑了一周之后我从“看着它跑”进入“天天修修补补”阶段。这一节把手记里比较有代表性的几个问题整理出来希望能帮你少走弯路。5.1 向量检索相似度阈值不调召回就是玄学最开始我没设置相似度阈值结果召回返回的全是“语义上有点沾边但实际没用”的内容。用户问“上次说去哪儿吃饭”返回的是“关于食物的偏好记录”看起来相关实则完全没用。后来我给语义召回加了一道阈值过滤只有相似度分数达到预定阈值我这边最终定在0.75以上的记录才进入候选集。阈值要结合你使用的Embedding模型实际分布来定不能照抄网上的数字。我调试阈值的方法是抽样20条真实查询跑一遍召回观察返回分数的分布把阈值卡在“有用记录的最小分数”和“无用记录的最大分数”之间。这一步花不了多少时间但效果立竿见影。5.2 记忆碎片化一条完整事件被拆得七零八落另一个高频问题是Agent在写入记忆时喜欢把一件事拆成好几条碎片。比如“周末想去爬山”和“爬山要带水和干粮”被拆成两条独立记忆。碎片化的记忆在召回时很难拼回完整上下文反而让Agent理解偏差。对策是在prepare_memory_write阶段增加归并逻辑写入前查一下向量库里是否已有相似主题的记忆如果相似度高于一定阈值就更新旧记录而不是新增一条。归并让向量库里的记忆数量保持在可控范围每条记录的完整度也更高。做完这个改动后记忆数量没有爆发式增长召回结果也更完整。5.3 性能优化限制写入频率加一层短期缓存记忆写入是比较耗时的操作因为要跑摘要、要生成向量、要写库。如果Agent在每轮对话后都自动写入频繁的模型调用会让响应明显变慢。我加了两层优化一是写入频率限制同一会话内相似主题的记忆最多每5分钟写入一次短时间内重复触发只更新时间戳。二是召回缓存命中过的查询结果缓存30秒同一问题短期内反复问不再重复跑向量检索。做完这两层优化之后体感响应速度提升明显而且没有影响记忆质量。如果你自己部署时发现代理变慢优先检查是不是记忆工具调用太频繁其次才是模型推理耗时的问题。5.4 备份与版本管理记忆数据比代码更值钱最后提一个容易被忽略的点记忆数据要纳入备份方案。代码可以重新部署但记忆数据是一次次对话积累出来的丢了你连用户偏好都没了。我在部署机上加了一个每日定时任务把整个memory目录打包备份到另一个磁盘。同时在配置里把记忆接口层和存储层解耦这样即便某天想换向量库或升级Embedding模型旧数据也能通过导入工具做迁移。关于升级Embedding模型我额外提醒一句先做一次“全量重嵌入”再切换。把历史记忆全部用新模型重新生成向量新旧向量混用会出现分布不一致召回质量会明显下降。我在一次测试中直接换模型没做重嵌入召回准确率一夜回到解放前后来才意识到问题所在。5.5 下一步把记忆系统变成可反馈的闭环目前这套模板已经稳定跑了两周我认为下一步的优化方向是让用户可以对召回结果做反馈。比如Agent回答后如果用户表示“这个回答没用”这条信号就能用来调整召回策略和记录权重。这样记忆系统就从“静态存储”变成了“能自我修正的闭环”。我已经在测试一个简单的反馈接口把负反馈命中的记忆条目标记为低权重下次召回时自动降级。目前效果还不稳定但方向我比较看好。如果你也在跑OpenClaw记忆系统建议关注这个方向等反馈机制成熟之后记忆系统的实用性会再上一个台阶。模板初始化的目录结构参考如下memory/ ├── vector_db/ ├── facts.json ├── session_state.json └── backup/按这个结构初始化配合上面的配置和接口基本就能跑通整个记忆系统。我在实际搭建中还把配置参数抽到了一个单独的YAML文件里换部署环境时不用改代码只调参数这也是为什么我强调接口层与存储层解耦的原因。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑