
如果你日常重度使用 Claude一定会遇到这个场景上周刚讨论清楚的接口方案这周换了个项目继续聊它完全想不起来。就跟人失忆一样所有上下文都得从零开始解释。我花了两周多时间做一个叫 claude-mem 的小工具专门解决这个“对话记忆丢失”的问题。它不是去微调模型也不改 Claude 的底层权重而是在模型外面加一层“记忆外挂”让 AI 在会话之间保留并召回关键信息。这篇博文主要围绕 claude-mem 的完整设计、实现思路和实操过程展开内容会比较细从为什么要做、整体架构怎么拆、代码怎么落到踩过的坑和排查技巧都会讲到。适合正在用 Claude 做开发、被上下文窗口折磨过的朋友也适合想给自己的 AI 工作流加记忆层的开发者。看完之后你至少能自己把模型外存、向量召回这套流程跑起来。1. 项目概述与核心需求1.1 claude-mem 到底做了什么claude-mem 是一个命令行工具也可以作为本地服务跑起来。它做三件看起来很朴素的事接受你喂给它的一段对话或摘要把它拆成可检索的记忆单元在对话结束后自动把关键信息压缩成摘要并生成语义向量在你下次提问之前把最相关的几条记忆捞出来拼进 prompt 里还给你。这三件事对应三个核心命令claude-mem store负责写入记忆claude-mem recall负责语义召回claude-mem summary负责把长对话压缩成摘要。这三者合起来Claude 就不再是“每次对话都清空缓存”的口香糖状态而是有了一份可以翻看的项目笔记。这个工具的技术底座选得特别轻没有引入重型中间件。存储用的是 SQLite 单文件数据库向量索引用的是 sqlite-vec 插件embedding 模型用的是本地小模型。整条链路没有一个外部 API 调用所有数据都在本机隐私方面更容易交代。对我这种需要反复调参的人来说最直观的收益就是排查问题的时候只需要盯一个目录不用去查云数据库账单。1.2 它解决的是哪些具体痛点AI 模型本身没有“长期记忆”这个概念它看到的是每次请求里拼进去的那几千个 token。换了新会话前面的内容就没了。但实际使用中我们的痛苦往往集中在下面几个点上连续几周讨论同一个项目每次都要重新贴历史背景浪费时间且容易漏信息。手动复制粘贴旧对话容易截断粘贴多了还超出上下文窗口限制。聊天记录散落在多个会话窗口里想回去找某个决定特别困难。没有记忆状态Claude 在处理跨时间跨模块的需求时表现得很“分裂”。下面这个对照表可以更直观地理解问题场景不使用 claude-mem使用 claude-mem三周后询问之前的架构选型需要先找聊天记录手动总结后粘贴进 prompt直接claude-mem recall 架构选型自动拿到摘要前端和你在同一个项目里协作信息只存在于你的会话切换会话就丢记忆统一落在仓库目录跨会话一致一次长对话超过上下文限制只能忍痛删掉早期内容自动 summary 压缩后存入关键信息不丢判断用户新方案是否和旧结论冲突靠人脑回忆召回旧记忆后模型能主动对比所以 claude-mem 的核心价值不是“让 Claude 变聪明”而是“让 Claude 有册子可以翻”。你不需要每次对话都从零给它交代背景。2. 设计思路记忆层为什么放在模型外面2.1 上下文窗口和记忆是两码事很多人有个误解认为把上下文窗口从 200K 加到 1M就等于给了模型长期记忆。这个想法忽略了一个关键事实上下文窗口只是“工作内存”不是“长期记忆”。举个例子你手里有一块白板和一间书房。白板再大也只能记录当前正在思考的问题书房里的笔记本才是你跨时间查阅历史信息的地方。工作内存的好处是读取快、实时性强坏处是容量有限、断电即失。长期记忆则必须做到三件事持久化存储、高效检索、按需更新。claude-mem 的思路就是老老实实做第二套系统。它不试图扩大模型上下文窗口也不假装自己能“在脑子里记住”而是把所有需要沉淀的内容落到一个本地仓库里然后等下次对话开始时把最可能的几条历史记录重新塞回上下文窗口。这样既不影响 Claude 本身的推理能力也能以极低的成本获得持续记忆。这个设计还有一个额外好处你可以把同一份记忆仓库用在不同的 AI 产品上。只要对方的接口允许你自定义 system prompt 前缀你就能把 claude-mem 召回的记忆喂进去。记忆不再被某个特定模型绑定。2.2 两层记忆架构短期事实与长期摘要实际对话里有两类信息需要保留。第一类是具体的事实型信息比如“用户的服务器地址是 x.x.x.x”“数据库连接串用了 xxx 格式”。这类信息必须原样保留不能随便改字。第二类是决策与背景型信息比如“当初为什么选 A 方案而不是 B 方案”。这类信息需要概括压缩提炼出结论和原因才能避免细节过多稀释重点。claude-mem 在实现上把这俩拆成了两层。第一层叫events.db专门存原始对话记录和短期事实。每次调用 API 收到的完整消息都会落一份在这里按会话 ID 分组时间戳标记清楚。这一层的作用是“溯源”一旦发现后面摘要写错了可以倒查原始内容。第二层叫memories.db存的是长对话的摘要条目和一个关键词表、一个 embedding 向量。每次会话结束claude-mem 会读取 events.db 里这个会话的完整对话让一个轻量本地模型生成摘要再切分成最多三百字左右的记忆单元生成向量后写入。之后你提问时recall 命令会在 memories.db 里做相似度搜索而不是去翻原始全量对话。把这两层分开的最大好处是召回速度和准确度都更好。搜索摘要比搜索原始记录快得多而且摘要本身已经去掉了废话不容易被“嗯啊好的”这类对话噪音干扰。2.3 为什么选向量召回而不是全文搜索第一版 claude-mem 用的是 SQLite 自带的全文索引当时觉得“搜索关键词”应该够用。结果用起来发现用户在不同时间询问同一个问题时措辞差异非常大。比如你最初把记忆写成“服务部署在 docker compose 里”后面你问的可能就是“容器编排那块我们怎么定的”。全文搜索完全无法把这两个表达关联起来因为它要求字面匹配。向量召回解决的就是这个问题。它把文本映射成数学向量语义相近的句子在向量空间里距离也近。所以不管你是问“容器编排”还是问“docker compose 相关决策”只要语义相似就能把旧记忆捞出来。我最后落地的时候用的是一套本地 embedding 模型模型参数不大单次推理大约几十毫秒对个人工具来说完全可接受。选择本地模型而不走云端 API还有一个很现实的理由对话摘要里往往包含敏感信息。如果每次生成 embedding 都要发送到外部接口隐私风险太大了。放在本机跑虽然速度慢一点但胜在可控。3. 核心实现细节与安装配置3.1 依赖环境和安装方式claude-mem 目前用 Python 3.11 编写主要是因为 SQLite 的向量插件生态在 Python 这边最成熟。安装非常简单直接通过包管理工具拉取即可pip install claude-mem装完之后建议先确认 sqlite-vec 扩展是否被正确加载claude-mem doctordoctor 命令会检查 Python 版本、SQLite 版本以及 sqlite-vec 扩展是否可用。如果最后一行提示“sqlite-vec extension loaded”说明环境没问题可以继续后面步骤。如果提示加载失败多半是 Python 的 sqlite3 模块编译时没有开扩展支持换一个官方编译的 Python 解释器基本能解决。注意claude-mem 不依赖外部大模型 API所有 embedding 都在本地完成。第一次跑的时候它会自动下载 embedding 小模型权重到本地缓存大概几百 MB。这个下载过程只做一次后面就完全离线可用了。3.2 初始化和目录结构安装完成后在使用前先初始化记忆仓库claude-mem init --home ~/.claude-mem它会创建三个文件/目录~/.claude-mem/ ├── config.toml ├── events.db └── memories.dbconfig.toml是配置文件里面写着模型名称、向量维度、召回数量、相似度阈值等参数。events.db存放原始对话消息按 session_id 和 msg_id 组织。memories.db存放摘要条目、标签表和 embedding 向量是召回操作的主要数据源。如果项目是多人的建议把--home指向项目目录比如--home ./project/.claude-mem。这样每个人在自己的机器上跑出来的记忆仓库不会互相污染团队协作时也能通过同步目录来共享部分记忆。下面这行命令可以改默认模型claude-mem config set model_name bge-small-zh模型名支持从本地 Hugging Face 格式加载。中文场景强烈建议选中文优化过的 embedding 模型否则语义召回效果会差很多。英文对话场景也可以换成 sentence-transformers 里的其他模型没有强制限制。3.3 核心命令速查我把常用的命令都列在下面这张表里后面讲解时也会反复引用命令作用常用参数claude-mem store 文本写入一条记忆--tag指定标签claude-mem recall 问题语义召回相关记忆--top-k 3取前三条claude-mem summary --session id生成会话摘要--horizon 1h只看最近一小时claude-mem update id修改某条记忆内容--merge合并到相近记忆claude-mem watch监视日志文件自动沉淀记忆--input ./log.txtclaude-mem doctor自检环境依赖无使用频率最高的就是store和recall。store接受一段文本内部会自动切分、生成向量、写入 SQLite。recall接受一个查询语句返回前 k 条最相关的记忆每条记忆后面还带一个相似度分数方便你判断到底相关到什么程度。例如claude-mem store 用户选择了 PostgreSQL 作为主存储理由是生态成熟且团队熟悉。 --tag database claude-mem recall 存储方案我们最后定了什么第二条命令会返回类似这样的结果(0.93) 用户选择了 PostgreSQL 作为主存储理由是生态成熟且团队熟悉。 (0.87) 数据库迁移方案讨论决定保留 MySQL 作为只读副本。分数越高代表越相关。这个结果可以直接作为 prompt 上下文的一部分拼接进下一次请求。4. 实操过程把记忆完整注入 Claude 会话4.1 从零开始接入命令行我以自己实际遇到的一个模拟项目为例讲清楚整套操作流程。项目代号就叫“某跨平台系统”需求是给客户做一个支持 Web 和移动端的会员管理后台。第一轮对话里和 Claude 讨论了 API 鉴权、数据库选型、前端技术栈三个主题聊了两个多小时。由于对话内容较多中途出现了上下文窗口逼近上限的提示。这时我使用 claude-mem 来把当时的关键结论沉淀下来。首先查看当前会话 IDclaude-mem summary --session sessions_20250315_webapi.json --horizon 2hsummary 命令会自动读取 events.db 里该 session 的原始消息然后生成一段压缩摘要。摘要内容类似下面这样会议主题某跨平台系统技术选型 1. API 鉴权方案决定采用 JWT refresh tokentoken 有效期 15 分钟。 2. 数据库主库选择 PostgreSQL因为团队经验丰富且需要全文检索功能。 3. 前端框架定为 Vue 3 TypeScript组件库使用某开源管理后台模板。 4. 手机端复用 Web 端代码通过响应式布局适配。每个项目点都会生成 embedding 并落到 memories.db 里。当天晚上第二场对话开始前我只需要执行claude-mem recall 技术选型结论把返回的结果作为 system prompt 的一段前缀拼接好再传给 Claude。这条 prompt 里包含了上周的决策Claude 就不会再重新给你列一份备选方案对比表而是直接在既定技术栈上继续深入。这就是“有记忆的工作流”和“无记忆的瞎聊”最大的区别。4.2 写一个自动注入记忆的调用脚本命令行手动用虽然直观但次数多了也烦。更舒服的做法是把它整合进自己的 API 调用脚本里让“召回记忆”和“拼接 prompt”成为一条流水线。下面这段脚本是我现在一直在用的简化版import subprocess from openai import OpenAI client OpenAI( base_urlhttp://localhost:8000/v1, api_keylocal, ) def recall_memories(query: str, top_k: int 3) - str: result subprocess.run( [claude-mem, recall, query, --top-k, str(top_k)], capture_outputTrue, textTrue, ) return result.stdout.strip() def build_messages(user_query: str): memory_block recall_memories(user_query) system_prompt ( 你是我的项目助手。这是之前对话中沉淀出的历史记忆\n f{memory_block}\n 请基于以上背景回答问题如果记忆与当前问题冲突请明确指出。 ) return [ {role: system, content: system_prompt}, {role: user, content: user_query}, ] resp client.chat.completions.create( modelclaude-3-5-sonnet-latest, messagesbuild_messages(数据库选型定了吗), ) print(resp.choices[0].message.content)关键点在于subprocess调用了 claude-mem 的 recall 命令把结果作为 system prompt 的一部分。这样每次提问都会自动带上最相关的三条记忆不需要手动复制粘贴。你可能会问为什么不直接用 Python 里面的 sqlite 查询模块因为 recall 命令里面还封装了向量相似度计算、过滤阈值、结果排序这些逻辑直接用命令行省掉自己重写一遍的功夫。4.3 在支持 MCP 的客户端里自动收记忆CLI 适合自己写脚本的场景但如果是在图形化客户端或者支持 MCP 协议的编辑器里干活手动执行命令就太别扭了。所以 claude-mem 同时也暴露了一套 MCP 工具注册之后AI 可以自主调用 memory_store 和 memory_recall 两个工具。配置方法不复杂先跑起来 MCP serverclaude-mem mcp-server --transport stdio然后在客户端配置里加一条{ mcpServers: { claude-mem: { command: claude-mem, args: [mcp-server, --transport, stdio], env: { CLAUDE_MEM_HOME: /home/user/.claude-mem } } } }配置好之后我让 AI 在每次回复完重要内容后自动把“最终决策和理由”写成一条记忆。你会发现AI 在长对话里自己知道什么时候该保存什么时候该查询。比如它会说“我去翻一下之前定的鉴权方案”然后调用 recall 工具。这种体验比命令行脚本要自然很多。不过这里有一个必须强调的坑MCP 工具调用是有额外 token 开销的。如果对话特别短或者只是闲聊让 AI 频繁执行 store 纯属浪费。所以我在配置里把 memory_store 工具描述写成了“仅当用户明确表示需要记住或当一个主题讨论结束并得出一致结论时才调用”。这样能大幅减少无用的记忆污染。5. 常见问题与排查技巧实录5.1 典型问题速查表用了一个多月我整理了一张问题排查表基本覆盖了最常见的坑问题现象可能原因解决办法召回结果完全无关embedding 模型是英文模型切换到中文优化模型重新生成索引相关记忆召不回相似度阈值设得过高调低score_threshold到 0.6 左右召回结果太多记忆拆分过大缩小chunk_size限制单条摘要长度记忆互相矛盾后续结论覆盖旧记忆时直接新增没走更新流程使用claude-mem update --merge合并存储文件膨胀过快把所有原始消息都存进 memories 层把 events.db 和 memories.db 分开管理长期记忆只存摘要embedding 生成速度慢模型文件过大换 tiny 量级模型或离线并行批量生成最容易踩的其实是第一个问题。很多人装完默认配置直接开始存中文结果发现模型是英文训练出来的语义相似度一塌糊涂。我自己第一次跑 recall 的时候甚至返回了完全无语的结果。后来换成中文优化模型召回质量立刻上来了。5.2 我踩过的三个坑第一个坑把每条消息都当成记忆存。最开始我写了个脚本把每次对话的所有历史消息一股脑丢给 store结果 memories.db 越来越大recall 出来一堆“嗯”“好的”“明白”之类的废话。后来我在 summary 层加了压缩逻辑只把“既有背景、最终结论、明确决策”这三类内容写入长期记忆低频噪音全部留在 events 层。第二个坑太相信向量相似度。向量召回并不能区分“这件事在后来已经被推翻了”和“这件事现在仍然有效”。比如第一天存储“方案选 A”第三天改成“方案选 B”但向量库里两条都还在。recall 时会同时返回两个矛盾结论Claude 就懵了。所以后来我引入了一个valid_until字段更新记忆时把旧条目标记为 expired召回时过滤掉。第三个坑多会话合并导致的上下文冲突。我把同一个项目的多个 session 都灌进同一个 memories.db 之后发现它们经常互相干扰。比如 A 会话讨论的是测试环境部署B 会话讨论的是生产环境部署两边的配置变量名都一样但值不同。召回时会把 A 的值带进 B 的语境。后来我让每条记忆强制带一个scope标签比如envtest或envprodrecall 时用--filter tagenv:test精确过滤问题才解决。5.3 排查思路和调试技巧如果你发现召回质量不稳定不要一上来就调阈值。先跑一遍 doctor 自检再手动存一条常识性记忆然后尝试用各种不同的说法去 recall 它。比如存“北京市是中国的首都”分别用“北京是什么地方”“中国政治中心在哪”试召回看分数差距。如果分数接近说明模型没问题如果分数差距很大说明模型选型有问题。另外claude-mem 提供了一个--debug参数会打印出检索时内部使用的相似度计算细节claude-mem recall 分布式锁实现 --debug输出里会显示查询向量和各候选向量的余弦相似度方便你判断到底是阈值问题还是模型问题。我通常会在调参前先跑这个命令收集几组分数再决定要不要动阈值。6. 后续扩展与个人心得6.1 几个值得做的扩展方向第一版跑通以后我觉得最值得扩展的方向是“分层摘要”。当前实现是会话结束生成一层摘要但如果一个项目跑几个月积累的记忆条目会非常多。更好的做法是按天、按周、按月做三次压缩生成一个“金字塔式”的记忆层级。天级摘要保留细节周级摘要提炼进度月级摘要只留重大决策。用户问“上周发生了什么”时去查周级摘要问“三个月前为什么换方案”时去查月级摘要。这个思路很适合长期维护的复杂项目。第二是跟知识图谱结合。把每条记忆里的主语、谓语、宾语抽出来形成三元组比如“鉴权方案-选定-JWT”。这样你不仅能语义召回还能做关系查询比如“所有跟鉴权相关的决策”。对团队知识库来说价值会比纯文本摘要大很多。第三是多级权限与多仓库隔离。现在 claude-mem 一条命令只能指向一个 home 目录不支持多个项目的记忆同时加载。如果把不同项目的记忆分门别类放在独立桶里再看到底用户当前所在的代码仓库是哪份自动拉取对应的记忆集合体验会好很多。毕竟一个开发者同时参与三四个项目是常态。6.2 我在实际使用中的体会从有这个想法到第一版能跑前后花了两周多。中间最花时间的不是向量检索也不是 SQLite 写入而是“到底哪些信息值得存”。一旦这个问题没想清楚后面所有增删改查都是在给垃圾数据做索引。我现在的经验是只存会给未来对话带来决策影响的结论不存过程。如果你也觉得一个会话结束后翻看记录时最值得留下的就是那三五行结论那你大概率能少走很多弯路。另外一个小技巧是每次用claude-mem store时尽可能用陈述句来写记忆而不是感叹句或对话片段。Claude 的大多数模型对完整陈述的理解和召回效果都更好。比如“这个项目必须用 elasticsearch 因为需要聚合查询”比“所以咱们用 ES 呗它那个聚合真快”更容易被后续的 recall 命中。目前这个工具我已经内置到日常的 AI 辅助开发流程里成了自己工作台的一部分。它其实证明了模型本身没有记忆并不重要只要你肯在架构上多写一层外挂很多问题都能绕过去。后续我想做的就是慢慢补齐上面说的分层摘要与知识图谱让记忆仓库真正变成一本可以翻阅的项目档案。