ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

claude-mem:给 Claude Code 装上跨会话持久记忆层

claude-mem:给 Claude Code 装上跨会话持久记忆层

如果只说一件事,claude-mem 给 Claude Code 装了一台“时光记录仪”。用过 Claude Code 的人应该都有过那种崩溃瞬间:上午花半小时和 Claude 把某个模块的设计约束梳理得明明白白,下午重启终端开新会话再问相关需求,它一脸茫然地反问“哪个模块?我们聊过吗?”。这倒真不是模型变笨了,而是 CLI 编码助手天然按会话隔离上下文,每个新会话启动就像一张白纸,历史讨论全都留在日志文件里,既没有被结构化入库,也不会自动被想起来。

claude-mem 这个项目就是冲着这块短板去的,定位很聚焦:在 Claude 的客户端和模型之间加一层持久记忆服务,把跨会话的关键信息沉淀下来,并在需要时按需“回忆”给 AI。它适合两类人。一类是重度依赖 Claude Code 做日常开发的工程师,希望 AI 记住当前项目的背景约定、公共决策和踩坑记录,而不是每次开会前都重新交代上下文;另一类是喜欢搭私有 AI 工作流的技术玩家,想用一个轻量且本地优先的记忆组件,也愿意自己折腾嵌入模型、检索阈值和数据存储。claude-mem 不承担推理,也不替代 Claude 本身,它只负责“AI 怎么记住上一轮的事”这一件事,但这一件事足够影响每天的编码体验。下面我会从设计思路、核心机制、实际配置和踩坑记录四个角度,把这个项目彻底拆开聊一聊。

1. claude-mem 到底在解决什么问题

1.1 Claude Code 的记忆困局

先讲清楚 Claude 为什么会“失忆”。Claude Code 这类 CLI 工具每次启动都会开启一个独立会话,它的上下文窗口只覆盖当前会话里的对话、工具调用和工具返回结果。关闭终端之后,会话数据留在历史日志文件里,却不会自动变成下一次对话的上下文。一个项目跑了几个星期之后,里面的长期决策、命名约定、问题修复过程和性能调优思路,全部散落在不同会话的日志中,没有任何结构化索引。你让 Claude 回忆“上个月我们为什么决定用 Postgres 而不是 MySQL”,它只能摊手。

麻烦的是会话日志本身也不好复用。手动翻日志找关键词,先得知道那次讨论大概是什么时候发生的;把几万字历史记录直接塞进 prompt,又面临 token 成本暴涨和关键信息被稀释的问题。我见过有人为了找回一段旧结论,把近两周的会话日志全部粘贴进新会话,结果 Claude 被无关内容带偏,回复里甚至开始引用另一个旧项目的技术栈。这个矛盾不是模型能力不够,而是工具链里缺了一个“记忆层”的位置。

1.2 全文回放为什么不是好方案

有人会立刻想到:直接把历史对话打包成上下文,一股脑儿注入给 Claude 不就行了?理论上可行,实操中却很容易失控。假设一个项目一个月内产生了 20 次会话,平均每次消耗 1 万 token,累计就是 20 万 token。这个量级已经远超主流模型的上下文窗口,更别说我们还需要给新的任务、工具结果和最终输出预留位置。就算强行压缩或截断,把 20 万 token 压进上下文带来的副作用也很明显:模型会在大量历史的噪声中迷失重点,甚至把过去某个实验性方案当成当前的有效结论来用。

如果把历史记录比作会议室,全文回放相当于把过去一个月所有会议录音原封不动地播给与会者听,正经议题反而被淹没在寒暄和扯皮里。正确的做法是先提炼会议纪要和结论,下次开会的时候只把与当前议题相关的几段记录调出来,其余留在档案室。claude-mem 选的正是这条路:摘要 + 检索。

1.3 摘要与向量检索,更接近人脑的回忆方式

claude-mem 的处理流程可以拆成两步。先让大模型把会话浓缩成高信息密度的“记忆条目”,剔除寒暄、临时性操作和低价值内容,只保留值得长期记住的事实、决策和问题。再把这些记忆条目转换成向量表示,当你在新会话里提问时,把查询语句同样转成向量,做语义相似度检索,找到最相关的几条旧记忆再注入给 Claude。

这套方案最吸引人的地方在于能用向量检索理解模糊表达。比如你之前讨论的是“Redis 挂了以后请求全部打到数据库”,下次你问“缓存雪崩时我们是怎么处理的”,语义相同但措辞完全不同,关键词搜索大概率失效,向量检索却能把这两段内容匹配上。相比全量回放,它只把少量高相关片段送进上下文,token 开销可控,噪声也小得多。记忆这件事,本来就不该靠“回放所有内容”,而是靠“准确回忆起该记得的东西”。

1.4 设计目标里的优先级排序

claude-mem 在功能设计上一开始就排好了优先级。第一是本地优先,数据留在用户自己的存储目录里,不带强制上传第三方服务的设计;第二是检索准确度,宁可少召回几条无关记忆,也不能让错误关联污染当前会话;第三是接入成本低,尽量通过 CLI 和 MCP 协议自然融入现有工具链,而不是逼着用户改变工作习惯。这三个优先级几乎决定了它的所有下游选型:默认采用本地向量存储,允许用户自选嵌入模型,把项目过滤作为检索链路中的硬性条件。把这套优先级想清楚,再看它的每个细节设计就会顺很多。

2. 核心设计与实现思路拆解

2.1 记忆分四层,而不是一锅炖

claude-mem 对记忆做了分层:原始会话日志、会话摘要、项目级上下文、全局偏好。原始会话日志是原料,来自 Claude Code 的本地会话记录,记录着每一次对话的完整内容。会话摘要是每次会话结束后由 claude-mem 调用大模型生成的要点总结,通常是四层里被检索最多的一种记忆。项目级上下文是长期稳定的背景说明,比如项目结构、技术栈、团队约定,这类内容不会被某个临时会话覆盖。全局偏好则记录用户的通用工作习惯,例如“代码注释统一用中文”“优先使用类型注解”“提交信息用 conventional style”。

四层配合使用比单一大杂烩更合理。全局偏好和项目上下文如果混在一起,容易出现“项目 A 的特殊约定被当成通用规则应用到项目 B”的情况。会话摘要单独存放,又能避免被全局偏好挤占检索空间。好比一个人大脑里既有长期记忆,也有短期工作记忆,两种记忆不会放在同一个抽屉里,检索时也不会互相干扰。

记忆层次来源写入频率典型内容
原始会话日志Claude Code 本地日志每次会话自动完整对话记录
会话摘要模型摘要生成每次会话结束决策、结论、问题
项目级上下文用户手动维护或定期提炼低频更新架构说明、技术栈、约定
全局偏好跨项目提炼低频更新编码风格、通用偏好

2.2 写入链路:从日志到可检索记忆

一次会话结束后,claude-mem 先把输出日志拿到手,做一轮清洗,去掉低价值内容,比如纯问候、单句命令、调试噪音。清洗干净后构造一个摘要提示:“总结这段对话中值得长期记住的事实、决策和问题,忽略临时性内容”。这一步的 prompt 设计很关键,如果只写“帮我总结一下”,模型很容易把过程性描述也写进摘要。实际效果好的摘要通常非常精炼,一条记忆就一两句话,强调“是什么结论”而不是“当时怎么做的”。

接下来会把摘要在适当位置切分成片段,再逐个生成 embedding 向量,连同会话 ID、项目 ID、时间戳一起写入存储引擎。为什么不让摘要保持一个整体再存?因为向量召回讲究粒度匹配,整段摘要太长时,查询向量和整段的相似度会被内部无关部分拉低;切成小片段后更容易命中局部相关的内容。这个切分要讲究分寸,太碎会丢失上下文,太粗又会降低召回精度。我实践下来的感受是,按自然段切分,每条大约控制在 100 到 300 字之间,效果比较均衡。

2.3 嵌入模型选型:本地还是远程

embedding 模型负责把文本映射成向量,它的质量直接决定检索精度。本地路径我推荐用 Ollama 跑开源的 bge-m3 或 nomic-embed-text,bge-m3 对中文语义支持相对友好,nomic-embed-text 处理英文代码注释和文档更顺手。如果主要业务是中文项目,优先测试 bge-m3;如果你整理的记忆大部分是英文技术文档,nomic-embed-text 会更轻快。这两个模型用 CPU 推理就够,处理一小段文本通常几十毫秒到一百毫秒,不需要专门上 GPU。

远程 embedding API 也能用,尤其当你机器性能确实太差时。但代价是文本要发到外部服务,这意味着摘要内容和历史结论会离开本机,对代码项目来说隐私风险不小。claude-mem 默认走本地,就是想把数据边界收紧,不让敏感代码片段在嵌入环节外泄。除非团队已经有统一的内部 embedding 服务,否则我更推荐本地推理,慢一点没关系,数据安全永远是第一位的。

2.4 存储引擎:为什么默认选 sqlite-vec

向量要存储,也要支撑快速检索。claude-mem 默认使用 sqlite-vec,本质上是给 SQLite 增加向量索引和近邻检索能力的扩展。有人会问为什么不用专门向量数据库,其实对个人开发者和中小项目来说,专门上向量库意味着多一个常驻服务、多一份运维负担。sqlite-vec 的优势是单文件、零依赖、随存随取,整个记忆库就是一个.db文件,备份时直接复制文件就能带走。

换个角度看,如果你的记忆库要支撑一个团队同时写入和查询,单文件方案并发能力确实有限,更适合迁移到 Postgres + pgvector 这类服务化存储。但 claude-mem 的定位很清醒,默认先服务个人和小团队场景,数据库文件方式最简单可靠。选择存储引擎的关键是匹配数据规模和使用人数,而不是追逐大而全的架构。几十万条记忆以内,sqlite-vec 跑起来非常从容。

2.5 检索与重排:只要最相关的几条

查询发生时,claude-mem 会对 query 做向量化,然后在向量库里做近似最近邻搜索,先拿回候选集合。候选集合不直接送进上下文,还要过一层过滤和排序:必须匹配当前项目 ID,过滤掉敏感词命中的条目,再叠加时间衰减和相似度分数做最终排序。时间衰减是很多人容易忽略的细节,上周讨论的方案通常比两个月前的约定更有参考价值,所以后期的时间权重会给得更高。

这里面的两个关键参数是 top_k 和 min_score。top_k 决定最终返回多少条记忆,min_score 决定相似度低于多少的条目直接丢弃。两个参数共同控制注入 token 成本。假设模型输入的上下文窗口是 20 万 token,你不想让旧记忆吃掉太多预算,通常把 top_k 控制在 5 条以内, min_score 设在 0.65 左右。算下来如果每条摘要平均 300 到 500 token,一次注入的记忆总量大约在 1500 到 2500 token,占比很小,却能提供关键的连续性。

2.6 注入给 Claude 的两种方式

有了记忆结果,还得知道怎么把它交到 Claude 手里。claude-mem 支持两种方式:一种是把检索结果预先注入系统提示词,适合需要固定背景的长期项目;另一种是通过 MCP 工具暴露检索能力,让 Claude 自己决定何时去查记忆。预注入的问题在于,不是每个会话都需要旧记忆,大量不相关的注入是浪费。MCP 工具方式则把决定权交给模型,当它分析出“这个问题依赖历史上下文”时就会主动调用检索工具,更自然也更省预算。

两种方式可以组合使用。对项目背景这种固定上下文用预注入,对决策记录和踩坑经验这种按需记忆用工具调用。先问自己一个问题:如果这段记忆不注入,Claude 是不是也能正常回答?能,就用工具方式按需取;不能,才考虑预注入。这样既能保持回答质量,又不会让上下文被历史内容堆满。

3. 实操:把 claude-mem 跑起来并接进工作流

3.1 环境准备与安装

先明确环境要求:Python 3.11 及以上版本,一个可用的嵌入服务。最容易上手的组合是 Ollama 加本地模型。安装完 Ollama 后拉取嵌入模型:

ollama pull bge-m3 ollama serve

然后再装 claude-mem 本体:

pip install claude-mem

装完跑一下claude-mem --help,确认命令树完整。如果你有 Docker 环境,也可以选择容器方式运行,把数据目录挂载到宿主机~/.claude-mem,方便在多台机器间迁移。容器化和本地安装最大的区别只是进程管理方式,内核逻辑没有变化,不必纠结哪种更“正统”,顺手就好。

3.2 初始化存储与关键参数

安装完成后先初始化记忆库:

claude-mem init --storage-dir ~/.claude-mem

这条命令会创建 SQLite 数据库、向量索引和默认配置文件。默认配置通常已经能跑,但我会建议打开配置文件手动看一遍,重点确认这几个参数:top_k、min_score、max_tokens。它们的含义前面已经说过,这里给一组保守起步值:

storage: dir: ~/.claude-mem engine: sqlite-vec embedding: provider: ollama model: bge-m3 retrieval: top_k: 5 min_score: 0.65 max_tokens: 3000 filters: exclude: ["password", "token", "api_key"]

filters.exclude是敏感词过滤白名单的关键入口,列表里的关键词一旦出现在记忆条目中,写入阶段就会被拦截。先把明显的密钥、口令、token 关键词加进去,后续再根据实际内容扩充。

3.3 通过 MCP 把记忆挂进 Claude Code

初始化完成后启动记忆服务:

claude-mem serve --port 8765

再把服务注册成 Claude Code 的 MCP 工具:

claude mcp add claude-mem -s local -- claude-mem mcp

注册完成后,新的 Claude Code 会话里应该能看到 claude-mem 提供的工具。验证方法很简单:直接问它“我们之前讨论过的缓存方案结论是什么”,如果模型察觉到自己需要历史信息,就会主动调用记忆检索工具,再基于返回结果回答。这里有个高频坑:MCP 注册成功但新会话里工具不出现。多半是会话没能加载最新 MCP 配置,重启 Claude Code,或者执行claude mcp list确认服务在列表里。

3.4 手动查询与维护命令

手动查询在日常维护里也很有用,命令不多但都很关键:

claude-mem search "上星期讨论的缓存方案" claude-mem recall --project my-service --limit 10 claude-mem stats claude-mem prune --older-than 90d

search走语义搜索,适合模糊回忆;recall按项目拉取,适合快速浏览某个项目的记忆轨迹;stats查看记忆库整体健康度;prune负责清理超期记忆。维护节奏建议每周跑一次 stats,观察记忆增长速度和各项目分布。如果发现某个项目的记忆量异常膨胀,就该考虑是不是摘要切分粒度过粗,或者检索时频繁用了不精确的项目过滤。

3.5 备份与迁移其实很简单

因为是单文件存储,备份逻辑被简化到极致。想备份记忆库,直接把~/.claude-mem目录打包即可:

tar -czf claude-mem-backup.tar.gz ~/.claude-mem

想迁移到另一台机器,把压缩包复制过去解压,再把 config 里的 storage.dir 指到对应目录,重新启动服务就完成了。如果只是临时快照,也可以用 SQLite 命令行:

sqlite3 ~/.claude-mem/mem.db ".backup './mem-backup.db'"

这个思路很适合定时任务,每天晚上自动跑一次,把记忆库当成一份普通数据资产保护起来,而不用依赖复杂的导出恢复流程。

4. 常见问题与排查技巧实录

4.1 嵌入模型连接失败或超时

最典型的错误是 Ollama 服务没启动或模型没拉取,报错长这样:Connection refused或model not found。排查时先执行ollama list确认模型存在,再用claude-mem check验证嵌入连通性。如果用的是远程 embedding API,检查网络和密钥配置是否正常。这里提一个容易忽略的细节:很多开发机上的代理会劫持本地回环流量,导致连接在 localhost 阶段就被切断,解决方法是在代理设置里放行 127.0.0.1,或者干脆把本地服务加入 no_proxy 环境变量。

4.2 记忆膨胀导致检索质量下降

记忆库运行时间越长,条目数量越大,可检索的未必越多,反而会带来越来越多的噪声关联。我自己的策略是定期运行:

claude-mem prune --older-than 90d

同时把已终止项目的记忆单独归档,不要让废弃项目的摘要继续搅乱新项目的语义空间。如果发现召回结果越来越“飘”,优先看 min_score 是不是设得太低。score 阈值一旦低于 0.5,模型很容易把只有轻微语义关联的结果也拉进来。另外,把 top_k 从 10 调到 5 可以有效减少尾部噪声,相似度排名靠后的记忆,大多数时候确实不值得占用上下文。

4.3 敏感信息误入记忆库

这不是小概率事件,我确实在调试时遇到过真实案例:某次会话里贴了完整的 API key,摘要模型没能识别敏感语义,当成普通事实记录进了记忆库。exclude 过滤配置只对新写入的内容生效,存量记忆里如果已经混进密钥,还得主动清理:

claude-mem delete --match api_key

如果担心历史库里已经混入大量敏感字段,最简单的办法是把整个库清掉重新积累,听上去可惜,但记忆的价值本来就该面向未来,而不是守着一段风险数据不放。更重要的一件事是改变使用习惯:不要在 Claude Code 会话里贴明文密钥,能引用环境变量就引用环境变量,能脱敏就脱敏,这是从源头切断泄漏路径。

4.4 与 Claude Code 新版本的兼容问题

Claude Code 会持续更新,更新之后它的日志格式、输出风格或终端交互方式可能变化,claude-mem 的解析器如果还没来得及适配,就会出现“该记的没记、不该记的全记了”的集群问题。遇到这种情况,最有效的手段是先看 claude-mem 的 changelog 是否标注了兼容性要求,确认新版和当前版本的差异。必要的时候回退 Claude Code 版本,或者反过来升级 claude-mem。

我个人的原则是:升级前先查依赖关系,不强行把两个工具的最新版绑在一起。版本更新的核心是稳定性,而不是追逐最新功能。等到仓库测试反馈确认适配后再升级,能省掉很多排查时间。

4.5 状态检查全通过,但 Claude 不调用检索工具

有一种情况比较隐蔽:claude-mem check报告正常,MCP 列表里也能看到工具,但 Claude 在回答里就是不主动调用记忆工具。这通常不是 claude-mem 的问题,而是 MCP 工具描述里的提示词还不够明确,模型不知道这个工具能用来做什么。遇到这种情况,可以手动在对话里明确提示:“你可以调用记忆检索工具查一下这个项目的历史上下文。”如果这样能触发调用,说明工具本身没问题,只是模型没有识别出这个任务的检索时机,后续可以通过给工具增加更丰富的描述文本来改善。

下面把常见的几种问题整理成一个速查表,方便直接对照处理:

现象可能原因快速处理方法
嵌入报 Connection refusedOllama 未启动执行 ollama serve
模型下拉失败模型未安装ollama pull bge-m3
检索结果噪声多min_score 过低调高到 0.65+
记忆数量暴涨没有定期清理执行 prune
MCP 工具不出现会话没加载配置执行 claude mcp list
敏感信息入库缺少排除词配置 filters.exclude

5. 我把它用在哪里,以及几点经验之谈

5.1 代码评审中的团队共识沉淀

我现在最常用的场景是代码评审。每次评审里如果出现类似“不要在业务代码里直接 new HttpClient”“SQL 查询务必走 repository 层”这样的共识,我都会确认这些内容被写进记忆库。几轮评审之后,Claude Code 在新会话里写相关代码时会主动翻旧账,引用这些约定。对于规范还没有完整文档化的团队来说,这相当于让 AI 从“会写代码”升级成“按团队风格写代码”,价值很快就体现出来了。

5.2 个人工作日志与项目回溯

我还用它来记工作日志。每天结束时让 claude-mem 把当天的关键结论留存下来,需要写周报或复盘时,直接用claude-mem recall拉出对应项目的记忆轨迹,就能快速还原这段时间解决过哪些问题、做过哪些取舍。比起翻聊天记录和 git log,这种方式更接近“用自己的话总结过的工作档案”,检索效率高出不少。

5.3 记忆工具不是攒得越多越好

用了两三周之后,我最大的体会是记忆质量远比记忆数量重要。如果什么都往里面塞,记忆库很快会变成另一个脏数据仓库,检索出来的东西反而帮倒忙。不要试图让 Claude 在每个会话里都翻旧账,也不要因为工具能记录就放弃人工筛选。设置好项目过滤、定期 prune、维护 exclude 列表,让记忆库保持小而精。我的标准是每个会话平均产生 3 到 5 条高质量记忆就足够了,两周下来足以覆盖绝大多数需要回忆的场景。

5.4 顺着这套思路还能扩出别的用法

claude-mem 的机制本质上是一个“本地优先的语义记忆中间件”,它不只能服务 Claude Code。我已经把它扩展到记录终端里的一些日常命令执行结论、特定场景下的排查笔记,甚至用它给本地文档做相关段落召回。摘要、嵌入、向量检索、按需注入,这四个步骤本身就是一个完整的记忆系统骨架,完全可以搬到个人知识库或笔记工具里。如果你也在搭建自己的 AI 工作流,不妨把它当作一个轻量级记忆中枢来用,先跑通小场景,再逐步扩展数据源。关键要领始终是:只沉淀共识和结论,不记录流水账,用的时候及时维护,它才会越用越顺手。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进