☰
用claude-mem给Claude Code装上长期记忆,告别重复交代背景
2026/10/10 4:17:47 网站建设 项目流程

用 Claude Code 写了三个月项目,我最崩溃的时刻不是在调试,而是每次新开一个会话,都要把项目背景、接口约定、踩过的坑从头讲一遍。说得不夸张,光这段“开场白”就够写一篇 README 了。后来我找到 claude-mem,一个专门解决 AI 编程助手“金鱼记忆”的轻量级开源工具,才算是把这事儿根治了。这篇文章就把我接入它的完整过程、设计逻辑和踩过的坑都写出来,给同样被上下文丢失折磨的人做一个参考。无论你是刚接触 AI 辅助编程的新手,还是已经在多个项目里把 Claude Code 用成常规武器的人,只要受够了“每次对话都像第一次见面”,这篇内容都值得你花十分钟读完。

1. 为什么需要 claude-mem:一次会话的“金鱼记忆”

1.1 痛点:每次新会话,AI 都不认识你

很多人第一次用 Claude Code 的感受是“真香”,但用着用着就会发现一个很现实的问题:AI 的记忆只活在当前会话里。昨天刚和它定好的服务端接口协议、今天想拿来当成参考,新开一个会话之后它完全不记得;前天为了解决某个诡异 bug 花了一晚上改出来的约束,今天遇到类似问题时又踩了一遍。有时候我会觉得自己不是在和一个有持续能力的工程助手配合,而是在跟一个每过一个小时就失忆一次的新人配合。

这个问题不是某个模型能力弱,而是大语言模型本身的架构决定的。模型每次推理时只能看到当前请求里塞进去的内容,超出上下文窗口的部分等于不存在。常见的解决办法是把项目背景写进 CLAUDE.md 这类系统提示文件里,但 CLAUDE.md 需要手动维护,而且大多数是静态内容:项目结构、编码规范、常用命令。真正动态变化的“经验”是很难塞进去的,比如“这个模块之前重构过两次”“用户模块的缓存键规则已经改了”“本地数据库初始化方式和文档里写的已经不一样了”,这些需要人工维护,维护成本高了自然就没人维护。

另一个方案是每次开新会话时手动上传日志或粘贴聊天记录。我试过,听起来可行,实际上效率极低。几百行对话内容贴进去,先不说上下文窗口占用,单是“让对方快速定位关键决策”这件事就很费时间。时间久了,我的工作流就变成了:重用同一个会话不敢关,但同一个会话越长越容易混乱,最后只能硬着头皮重开。这是一个死循环。我需要的是一个能把过去的有效经验自动沉淀、下次自动带回来的能力,而不是一个记事本。

1.2 claude-mem 到底做了什么

claude-mem 就是冲着这个问题去的。它做的事情可以概括成三句话:把每次会话里值得记住的内容提取出来,存到本地数据库里;下一次新会话开始时,根据当前的提问或项目上下文,把相关内容自动找回来;把找回来的内容作为辅助信息注入到提示词里,让 AI“重新想起来”之前发生过什么。整个过程不需要我手动复制粘贴,也不需要维护一个越来越长的背景文档。

我第一次跑通它的时候,实际效果比想象中更直接。我在一个会话里说完“以后所有新增接口统一在响应里加 requestId,日志里通过 requestId 串联链路”,然后关掉会话,新开一个终端,问 Claude Code 一个问题:“新增接口时我需要保持什么约定?”它真的把 requestId 这个事说出来了,而且还补了一句“这个是之前的会话里定下来的”。那一瞬间确实有点震撼,因为我什么都没告诉它,它的“记忆”来自我上一个会话的对话记录。

当然, claude-mem 不是魔法。它不是把整段对话原封不动塞回去,而是提取摘要、结构化存储、根据相关性召回,本质上是一个带语义检索的本地记忆系统。它的优势在于可信、私密、可控。所有记录都存在本地,不依赖云服务;索引和检索可以做到毫秒级返回;哪些该记、哪些不该记可以通过规则控制。对于在意数据隐私的团队来说,这是很关键的一点:代码相关的东西不会为了记忆功能被送到外部服务。

2. 核心设计拆解:记忆是怎么被“写进去”和“捞出来”的

2.1 记忆写入链路:从对话到结构化记录

要理解 claude-mem,不需要深入源码,但要清楚它的完整链路,否则后续调参数、排错的时候会一头雾水。写入链路大致是这句话:事件触发、内容抽取、摘要生成、去重存储。

先说事件触发。常见的实现方式有两种:一种是监听 Claude Code 的 Hook 事件,比如每次工具调用结束、每轮对话结束之后,调用一次 claude-mem 的采集命令;另一种是后台守护进程持续扫描终端会话日志。我在实际使用中更推荐 Hook 方式,因为它是事件驱动,能精确知道“这条对话刚刚发生”,而不是靠轮询去猜。默认情况下,我在做文件重命名、代码编辑、设备调试这类动作时会触发记忆采集,Stop 事件之后会对整轮对话生成一份摘要。这个触发策略听起来简单,决定了记忆库里存的是“关键事件”,而不是一堆碎碎念。

接着说抽取。不是所有对话都值得记。我见过一些失败配置,把每个助手回复都当成记忆存,数据库很快被无意义内容淹没,检索时反而找不到重点。所以 claude-mem 核心的一步是过滤:明确的行为型决策(比如“缓存策略从 LRU 改成 TTL”)要记,简单寒暄、调试输出的碎片信息不记。在实现上,这一步通常有两层:第一层是用规则把明显无意义的文本滤掉,比如纯日志输出、单行命令执行结果;第二层是用模型对长文本做摘要,保留决策、约束、命名、路径这类高价值信息。

最后是存储。存的时候不是简单把文本往里扔,而是会打上项目、时间、会话、类型等标签,然后做向量化。这个过程有点像给每张便签纸贴好分类标签,再放到一个能按内容相似度检索的抽屉里。这样后面要“找一条记忆”的时候,按标签缩小范围,再按语义去挑最接近的几条,速度和准确率都有保证。我实测下来,如果只靠关键词搜索,很容易因为换了个说法就搜不到;只靠向量搜索,又容易把相似但不相关的记录也捞上来。两条腿走路才是这类工具的正解。

2.2 存储层:SQLite 向量库 + 关键词索引

claude-mem 的底层存储不需要像数据库服务器那么重,在绝大多数场景下,一个本地的 SQLite 文件完全够用。我第一次看它的目录结构时,发现就是一个memory.db加上几个配置和日志文件,干净得让人放心。SQLite 的好处不用多说:零部署、单文件、备份迁移方便,而且对本地场景来说性能绰绰有余。索引和检索都在本地完成,不依赖外网服务,这对于代码开发环境来说是刚需。

数据库表的设计也很有讲究。核心是记忆表,每一行代表一条记忆记录,通常包含这些字段:

字段说明
id记忆记录的唯一标识
project_key项目唯一标识,用来区分不同项目的记忆
session_id来源会话 ID,方便回看上下文
content摘要后的文本内容
embedding向量化后的内容向量,维度一般在 384 或更高
key_terms抽取的关键词,用于传统索引搜索
type记忆分类,比如决策、命令、约定、注意项
created_at / updated_at创建和更新时间
expires_at过期时间,用于自动淘汰旧记忆

双索引的设计值得展开。向量索引解决“语义相似”问题,关键词索引解决“精确匹配”问题。比如你在新会话里问“之前那个导出功能为什么不能用绝对路径”,如果向量索引工作正常,它会把过去“导出路径”相关的记忆都捞出来;但如果你记得某个精确的变量名或开关名,关键词索引可以直接命中。实际检索时,两条路并行,然后把优先级合并。这个设计和搜索引擎里的 recall 与 precision 权衡很像,经验是:向量召回的数量可以放宽,但最终注入上下文前一定要按相关性和时间做重排。

还有一个容易被忽略的点:过期机制。记忆不是越多越好。一个项目干得时间长了,大量过时的约定会互相矛盾,反而干扰新会话判断。所以 claude-mem 会在适当时候把太久远且没有再被引用的记录标记为过期,或者干脆物理删除。合理设置过期时间还能控制数据库体积。我自己的做法是,重要的架构决策手动置为长期有效,适配性的临时结论设一个较短的有效期,这样数据库里留下的基本是“还有价值”的信息。

2.3 记忆召回链路:查询重写与阈值控制

召回比写入更微妙。为什么?因为一个问题可能对应多个历史记忆,而真正有用的那一条可能表达方式和当前问题完全不同。 claude-mem 的召回链路大致是:把当前问题转成向量,去库里做最近邻搜索,同时用关键词走一遍精确匹配,然后合并、去重、按时间排序,最后把最相关的 N 条拼成一段上下文。

这里面最关键的参数是 topK 和相似度阈值。topK 控制最多捞回多少条,阈值控制多相似才算匹配。这两个值不是随便拍的。topK 设置太大会引入无关内容,占用上下文;太小又容易漏掉信息。我一般从 5 开始调:项目刚起步的时候 3 就够,项目复杂之后调整到 7。阈值则要看嵌入模型。一般来说,0.75 是一个比较稳妥的起点。怎么调呢?你可以在日志里看召回结果:如果找回的记忆明显不相关,就把阈值提高到 0.82 左右;如果经常找不到本该找到的东西,就降到 0.68 左右。

召回之后的注入策略同样重要。不是把几十条记忆全塞给 Claude Code,而是只保留与当前问题相关的 topN、压缩成一条简短的“历史记忆参考”,再放到系统提示词的末尾。这样做的好处有三个:不污染主问题、不占用过多上下文、让模型把记忆当作“辅助资料”而不是“用户要求”。我遇到过一种坑:把记忆直接丢进对话,模型容易把记忆内容和当前代码状态混淆,甚至开始自言自语“根据之前的记忆”,效果反而不好。所以,合适的做法是让模型只把记忆当参考,决策依据仍然以当前文件内容和用户输入为准。

3. 实操:接入 claude-mem 并跑通第一次记忆

3.1 安装与初始化

接入 claude-mem 的第一步是安装。我用的环境是 macOS + Node.js 18+,安装过程很顺利,基本就是全局安装一个命令行工具。打开终端,执行:

npm install -g claude-mem

如果你更倾向于从源码构建,也可以拉下仓库之后在本地装依赖。装完先跑一下版本确认:

claude-mem --version

看到版本号输出,就说明命令已经生效了。接下来需要初始化配置目录。我建议在用户主目录下做一次全局初始化,然后再到每个项目里单独配置:

claude-mem init

初始化会生成一个~/.claude-mem/或项目下的.claude-mem/目录,里面包含配置文件和初始数据库。默认配置通常已经能跑,但我建议打开配置文件看一眼,至少设置两项:项目名和语言偏好。项目名可以手动指定,也可以让工具根据当前 git 仓库地址自动生成。我喜欢用 git 仓库的短名称来作为项目名,这样同一个仓库不管在哪个目录 clone,记忆都是共享的,切换机器也不容易弄混。

初始化时如果遇到权限问题,常见原因是主目录下的配置目录被锁了或者 npm 安装时权限不够。这个时候不要一言不合就sudo,先检查一下当前用户对目标目录是否有读写权限。还有个容易被坑的点:如果你之前装过旧版本,升级后一定要重新跑一次 init,否则新的数据目录结构没建立,后面跑 Hook 的时候会静默失败。

3.2 注册到 Claude Code 的 Hook

安装完只是开始,真正要做的关键步骤是把 claude-mem 接到 Claude Code 的事件流里。Claude Code 支持通过配置文件注册 Hook,在这些事件发生时执行外部命令。我使用的接入方式非常简单,在 Claude Code 的配置文件里加上对应的 Hook 声明:

{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit|MultiEdit", "hooks": [ { "type": "command", "command": "claude-mem capture" } ] } ], "Stop": [ { "hooks": [ { "type": "command", "command": "claude-mem summarize" } ] } ] } }

简单解释下这段配置:当 Claude Code 写完文件或者完成一轮对话后,系统会调用claude-mem capture或claude-mem summarize采集记忆。两个事件的差别在于粒度:capture单条捕获,适合工具使用后立刻记录关键信息;summarize在整轮对话结束后生成摘要,适合总结本回合的决策和进展。

配置完成后,重启你的 Claude Code 会话,让 Hook 重新加载。你可以在终端里手动跑一下claude-mem status,看有没有采集到事件。第一次接入时,不要急着干活,先随便跟 Claude Code 聊两句,比如让它帮你创建一个小文件,然后立刻查看记忆库大小。如果记忆库还是空的,优先检查 Hook 路径是不是绝对路径、当前 shell 环境变量是否正常。我踩过一个典型的坑:在配置文件里写了相对路径,但 Claude Code 启动时的当前目录和说不清楚,结果命令根本找不到,后来全部改成绝对路径才稳定。

3.3 验证记忆生效

接入后,最需要验证的是“新会话能不能自动想起旧信息”。我建议用最简单的场景来测:开一个会话,明确告诉 Claude Code 一个约定,比如“以后所有接口返回字段命名统一用驼峰式”,然后正常结束会话。关闭终端,重开一个新终端,启用 Claude Code,不携带任何额外上下文,直接问一句:“我们之前在接口字段命名上有什么约定?”

如果 claude-mem 生效,它会在后台完成一轮召回,把之前那条记忆注入到模型上下文里,然后通过 Claude Code 的回答体现出来。我第一回测试时,模型回答的是“你上次提到接口字段命名统一用驼峰式,我会遵守这个约定”,这说明记忆已经被正确找到了。

如果没生效,不要慌。先跑claude-mem status看采集事件有没有落库,再跑claude-mem search "接口字段命名"手工测一下检索是否正常。手工能搜到、自动上下文里没有,说明问题出在召回注入环节;手工也搜不到,说明写入链路有问题。按这条思路来排,基本十分钟内能定位问题。记忆生效之后,你会很快感受到变化:新会话里 Claude Code 开始主动提起你之前定过的一些约束,有时候甚至会在做方案时引用旧决策,体验就像换了个“记得你”的同事,而不是每次都要重新认识的实习生。

4. 配置调优:让记忆更精准、更干净

4.1 控制记忆写入的关键参数

默认配置可以跑,但是想要好用,必须调参数。我自己的经验是,记忆系统最忌讳“什么都记”,因为一旦里面充满“用户说好的”“用户说不行”这种没有来龙去脉的碎片,召回时不仅帮助不大,还会干扰决策。所以配置里的过滤规则要好好研究。

常见需要关注的参数有这么几个:最短记忆长度、摘要长度、记忆类型过滤、去重开关。最短记忆长度的意思是,只有超过一定字数的内容才考虑入库,太短的往往是“好的”“继续”这类上下文不完整的话;摘要长度决定了单条记录的详细程度,太短会丢失信息,太长会浪费存储;类型过滤可以控制只记录决策类、约定类的内容,忽略纯任务执行日志;去重开关打开后,系统会对语义高度相似的新老记录做合并,避免同一个约束被存成十条不同的表述。

我在配置里通常会明确设置这样几条规则:命令执行形成的输出不采集、只有包含明确决策性动词的句子才采集、每条记忆最多保留 200 字摘要。这样的好处是数据库增长得特别慢,而且搜索时命中率很高。调这些参数没有绝对标准,完全取决于你项目的复杂度和使用场景。我的方法是在初始配置基础上跑一周,然后打开记忆库看入库记录,凡是看到明显无意义的记录,就回去加一条过滤规则。一开始可能觉得自己在调教一个过滤器,但调顺手以后,记忆库会变得非常“懂事”。

4.2 命名空间与多项目隔离

如果你只在单个项目里用 claude-mem,命名空间的问题可以忽略。但只要切换到第二个项目,你一定会遇到串记忆的问题:在一个项目里讨论的接口设计,被带到另一个项目里当成约束来参考,场面会非常尴尬。这也是我强烈推荐从一开始就配置项目隔离的原因。

在 claude-mem 里,可以用项目标识来区分不同的记忆空间。我比较喜欢用 git 仓库的短名称,因为它在机器上唯一,不管本地源码放在哪个目录,都不会串。具体配置方式很简单,在项目目录下放置一个.claude-mem/config.json,里面写上项目名:

claude-mem init --project my-service-api

这样,之后所有采集到的记忆都会带上my-service-api这个标签,召回时也只会在对应的项目空间里搜索。还有一个细节:如果你在 monorepo 里工作,根目录和子目录可能会产生不同的 cwd,导致项目标识不一致。我的做法是在每个子包的配置文件里故意统一声明完整的项目名,避免靠自动检测赋值,减少不确定性。

除了项目隔离,还可以配置“全局记忆”和“项目记忆”两层结构。全局记忆存个人的通用偏好,比如“我习惯用 pnpm 管理依赖”“所有代码注释用中文”;项目记忆存当前仓库的具体约定。这样既能共享个人习惯,又能避免技术债污染,双轨并存其实更贴近团队协作时的真实情况。

4.3 数据维护:清理、备份与迁移

用了一段时间后,记忆库会积累大量数据。定期维护不是可选项,而是必要操作。我一般每周用一次claude-mem inspect看统计状态,关注三个指标:记录总数、平均过期时间、本周新增记录量。如果新增量骤增,多半是某次调试过程被误录入了,这时需要检查触发规则而不是一直扩大数据库。

清理策略上,除了默认的过期淘汰,我还会手动标记一些记录为“archived”,一类是已经完成并且不再有参考价值的任务记录,另一类是已经被新决策完全覆盖的旧约定。这种方法比直接删除安全,因为过几天如果你发现新决策有问题,还能把旧记录翻出来做对比。备份更简单,整个.claude-mem目录打包就行,因为 SQLite 是单文件,我甚至直接把它和代码一起提交到仓库的私有分支,这样换电脑也能一键恢复。

迁移方面,我跨机器同步过两次。第一次直接把整个.claude-mem目录拷过去,发现没问题;第二次只拷了memory.db,也是可用的。但有一点需要注意:如果嵌入模型版本发生变化,之前存的向量与新生成的向量可能不在同一个向量空间里,检索效果会打折。所以升级之后最好重新跑一遍全量向量化,有些版本会提供claude-mem reindex之类的命令,没有的话就删掉旧向量库重新采集,不要心疼数据,带病运行的记忆系统比没有记忆更危险。

5. 常见问题与排错实录

5.1 记忆内容总是不完整或缺失

很多人在接入初期发现,明明对话里说得很清楚的事情,最后记忆库里面记录却是缺失的,只能看到一些零散的句子。我遇到过的第一个原因是摘要生成失败。摘要这一步通常要依赖模型或嵌入服务,如果模型调用超时、网络抖动、API Key 失效,都会导致摘要进程静默退出。这时候claude-mem status会显示当前记录数为 0 或增长异常,排查时先看日志里有没有 timeout 或者 authentication error。

第二个常见原因是事件触发条件太严格。比如把 Hook 的 matcher 限定为Write|Edit,那么只进行 read 文件、讨论方案的会话就不会触发采集,自然不会有记忆。解决方案是放宽 matcher,不要只盯着代码写入,也要考虑把Stop事件作为兜底,保证每轮对话至少产出一次摘要。第三个原因是当前工作目录不对。如果你用了相对路径的 Hook 命令,而 Claude Code 实际执行的目录和预期不一致,那么claude-mem会找不到项目配置,直接把当前记录当无项目数据处理。我再三强调绝对路径,这是最容易被忽略又最容易造成数据“丢失”的坑。

5.2 检索结果不相关或找不到旧记忆

数据库里有记录,但新会话想不起来,这个问题比写入缺失更令人抓狂。我遇到的情况是:关键词能搜到,但自动召回时没有。原因通常出在向量检索这一环:嵌入模型对某些术语不敏感,或者相似度阈值设置得过高。例如,你记忆里写的是“requestId”,新会话问的是“链路追踪标识”,两者语义上很接近,但向量距离未必小于阈值,于是就被过滤掉了。

解决办法是先用检索命令做一次手工测试,比如claude-mem search "链路追踪标识",如果它能搜到requestId的记录,说明召回链路本身没问题,只是自动注入时阈值太高;如果手工也搜不到,就需要降低阈值或者重跑向量索引。另外还有一种情况是多个匹配结果互相打架。默认按时间排序会把最近一条放在最前面,但如果有一条很早以前而且已经过时的记忆和当前问题相似度更高,会被误选。我的做法是增加类型权重,让“决策”“约定”这类记录高于普通的“聊天记录”,必要时在配置里把旧记录设为 less relevant,这样召回质量会显著提升。

5.3 Hook 未触发、权限问题和数据库损坏

Hook 类工具的常见毛病,基础命令能跑,但它没有在 Claude Code 的上下文中被触发。第一个可能原因是 Claude Code 的 Hook 配置不支持热加载,改完配置后必须重启会话,否则等于白改。第二个可能是 Hook 命令执行时环境变量与交互式终端不同,找不到claude-mem。这种情况下命令行里写claude-mem是不靠谱的,应该用which claude-mem查一下绝对路径,再写进配置,路径里有空格的话记得加引号。

权限问题主要出现在多用户机器上。如果memory.db被 root 或者其他用户创建,当前用户只有读权限没有写权限,采集命令会报错。修一下文件所有者或加上用户组写权限就行,不要为了快刀斩乱麻把整个数据目录chmod -R 777,会带来很多安全问题。数据库损坏我遇到过一次,直接表现是插入记录时提示database disk image is malformed。这种情况通常是不正常断电或拷贝中被打断导致的。先用备份恢复,如果没有备份,可以用 SQLite 自身的recover模式尝试抢救,但说实话能救回多少全凭运气。所以备份一定是在出问题之前做,而不是之后。

6. 一点使用体会与后续玩法

我对 claude-mem 的定位是“AI 编码助手的第二大脑”,但它并不只是给个人使用的效率工具。在一个团队协作环境里,如果大家都接入同一个记忆库规范,新成员上手时甚至可以通过查询历史决策来了解项目演进的来龙去脉。当然前提是团队约定好项目标识和数据维护流程,否则各写各的还是一团乱麻。

用到现在,我的体会是记忆工具真正考验人的地方不是安装配置,而是怎么判断“什么值得记”。系统给再多的开关,归根到底你还是得形成自己的使用节奏。我的节奏是:创建阶段性里程碑时,手动触发一次记忆固化,把重要接口变化、架构调整、已知风险点写清楚;平时则完全交给自动采集,只在检索结果不满意时才人工干预。

如果你想继续扩展,claude-mem 还能顺着文本分析的能力做不少事情。比如把它接到 commit message 的生成流程里,让 AI 在提交代码时自动参考历史决策;或者和 CI 流程集成,在代码审查时自动检查新改动是否违背旧约定。这些都是很自然的后续玩法,但我不鼓励一上来就铺开所有能力,先把基础链路跑顺、把自己的记忆筛选习惯培养起来,收获远比堆功能要大。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询