用Claude Code做了几个月实际项目之后,我最大的感受不是它能写多少代码,而是它太容易"失忆"了。新开一个会话,之前聊过半小时的架构决策、被否决的方案、用户明确表达过的偏好,通通归零。你不得不把背景重新粘贴一遍,甚至要把已经讨论过一遍的问题再从头解释。后来我找到了 claude-mem 这个开源工具,算是把这块短板补上了。这篇文章就围绕 claude-mem 展开,把我从安装、配置到日常使用、二次开发过程中积累的经验完整梳理出来。适合那些已经受够了重复交代背景、想让 AI 助手保持上下文连续性的开发者参考。
1. 痛点复盘:为什么Claude Code需要一套外部记忆
1.1 我在实际项目中反复撞上的"失忆"现场
先说一个最典型的场景。上周一我让 Claude Code 帮忙设计一个订单模块的数据库表结构,期间明确讨论过"不要用外键约束,因为后续分库分表会带来迁移麻烦",最后敲定了用逻辑关联 + 应用层校验的方案。到了周三,我要继续做订单模块的接口开发,打开一个新会话,让 Claude 先看一下表结构设计——它立刻开始建议我"给订单表加上外键约束来保证数据一致性"。那一刻我的血压是有点高的。
这不是个例。多文件项目里,技术债的来龙去脉、某个函数为什么写成当下这种奇怪的样子、哪些第三方库是经过调研才选进来的,这些信息都只存在于某一次会话的上下文中。一旦那个会话结束,就什么都没有了。CLAUDE.md 可以放一些长期原则,但它毕竟是静态文件,需要我手动维护,而且项目越大、演进越快,维护成本越高。
1.2 静态提示词和自动摘要都救不了场
很多人会想,CLAUDE.md 够用了吧?我一开始也这么认为。实际用下来发现两个问题:第一,CLAUDE.md 里写的是相对稳定的项目规范,比如技术栈、目录结构、代码风格,它不会记录"昨天我们否掉了 Redis 缓存方案"这种动态决策;第二,让 Claude 自己往 CLAUDE.md 里更新内容时,它往往会写得过于保守或者过于啰嗦,最后文件变成了大杂烩。
也有人依赖 Claude Code 自带的会话摘要功能。但那个摘要是跟着单次会话走的,无法在下次会话开始时自动注入到上下文中——你去翻历史记录,还得先记得那是哪一天的哪个会话。这种"手动检索"的模式,本质上和我自己翻便签没有区别。
1.3 claude-mem 解决的是哪一环
claude-mem 做的事情说白了就三步:把每次对话中值得记的信息自动提取成结构化的"记忆碎片",存到本地 SQLite;下次新会话启动时,把相关的记忆重新注入给 Claude;用遗忘算法控制哪些记忆保留、哪些过期。它和你自动维护的一本"项目回忆录"差不多,写得好不好不重要,重要的是不用你手动去写,也不用你手动去翻。
它本身是开源工具,核心是一个 CLI 加一个 TypeScript SDK,通过 hook 机制接入 Claude Code,同时也在往其他 AI 编码工具(Cline、Roo Code 等)扩展。下面我会从架构开始拆,因为它好用的原因恰恰在架构设计上。
2. claude-mem核心架构:记忆从采集到回放的全链路
2.1 四个关键组件各司其职
claude-mem 不是一个大而全的单一程序,而是由几个功能边界清晰的模块拼起来的。理解清楚每个模块负责什么,后续用起来才不容易踩坑。
第一个是 CLI 管理工具。它负责安装初始化、查看记忆库、删改记忆、运行遗忘清理,以及提供交互式 TUI 面板。日常管理基本都靠它。
第二个是 TypeScript SDK。如果你想把记忆能力嵌入自己的 Node.js 应用里,比如做一个内部工具,让每轮处理结果都能沉淀到同一个记忆库,就用 SDK 来做。它暴露的是加载记忆、添加记忆、搜索记忆这类底层 API。
第三个是插件/hook 体系。Claude Code 提供了 session start、session end、user prompt submit 等生命周期钩子,claude-mem 就是通过注册这些钩子实现"自动注入记忆、自动提取记忆"的。其他工具链(比如 Cline、Roo)则是通过 memory-tools 插件的方式接入同一套记忆库。
第四个是 SQLite 存储层。记忆数据以 JSON 结构存在本地的 SQLite 数据库中,每条记忆都带有类型标签、权重、时间戳、来源会话 ID 等信息,支撑按相关性和新鲜度排序查询。
2.2 一次会话中记忆是怎么流转的
我拿一次实际会话给你捋一遍完整流程。
会话开始前,Claude Code 触发 SessionStart 钩子,claude-mem 会从 SQLite 里把当前项目相关的记忆碎片拉出来,按权重和新鲜度排序,然后注入到系统提示词里。Claude 在生成第一段回复之前,就已经"看得到"之前沉淀过的决策、偏好和待办了。
会话进行中,它并不只是傻等。Claude 在处理任务时,会把一些临时提炼出的结论标记为候选记忆。比如它发现你多次手动修正某个接口的返回格式,会产生一条 tool_probability 类型的碎片,记录"这个用户在这类接口上偏向扁平 JSON 结构",后续行为会有意识对齐。这类碎片不一定要等会话结束才保存,有些会实时写入。
会话结束时,SessionEnd 钩子触发,claude-mem 对整场对话做一次"榨取",生成会话摘要,并把几条高权重信息正式落库。当然,不会全部写入,毕竟大部分对话内容对长期记忆没有价值。所以它要经过权重评估:用户明确表达的偏好、被否定的方案及原因、关键代码决策、待办事项,这些类型的记忆优先级高;日常寒暄、临时调试过程、错误尝试,基本会被丢弃。
2.3 记忆碎片的分类模型
claude-mem 的记忆不是一条条大段文本,而是一种带类型标签的碎片结构。我把常见类型整理成了表格:
| 记忆碎片类型 | 记录内容 | 典型示例 |
|---|---|---|
| insight | 项目洞察、用户偏好、结论 | "用户明确倾向 pnpm 作为包管理器" |
| decision | 方案权衡与最终决策 | "否决 Redis 缓存,原因:单机部署无并发瓶颈" |
| code | 关键代码结构与变更 | "auth 模块使用 JWT,refresh token 过期 7 天" |
| todo | 待办与后续计划 | "订单模块联调前需补事务回滚测试" |
| tool_probability | 工具使用倾向与频率 | "调试时习惯开 --verbose 输出" |
| session_summary | 单次会话摘要 | "完成用户模块重构,抽出 UserService" |
每条碎片还有权重字段和最后访问时间。权重越高、越是最近被用到,下次越容易被注入到新会话里。这样设计的直接好处是:不是所有历史记忆都无差别堆给 Claude,而是按当下任务相关性读取,确实能避免上下文被无关信息污染。
3. 接入Claude Code:安装、配置与首次实测
3.1 环境要求和安装方式
先看一下你的 Node 版本。claude-mem 要求 Node 22 及以上,这一点我在文档里看到时并不意外,它用了一些比较新的运行时特性。如果系统还是 Node 18,建议先用 nvm 把版本切上去再装,不然会直接报 engine 不匹配的错误。
安装很简单,全局装即可:
npm install -g claude-mem我建议全局安装而不是装到项目里,原因只有一个:记忆库是跨项目共享的一套基础设施。全局安装后,不管你在哪个项目目录里跑 claude-mem 命令,它都统一从同一个数据库读写。如果你装到某个项目里,那这个项目的记忆就和其他项目隔离了,跨项目复用反而变得别扭。
装完后先看一眼版本,确认环境正常:
claude-mem --version我这边装的是当时最新的稳定版,输出正常。
3.2 初始化做了什么
第一次使用前要跑 init 命令:
claude-mem init这个命令会做几件事:在 Claude Code 的配置文件里注册 hooks、创建本地数据库文件、生成基础目录结构。注册 hooks 时它会把配置写入到类似~/.claude/settings.json。完成之后你可以直接打开这个文件看,会看到类似这样的结构:
{ "hooks": { "SessionStart": [ { "type": "command", "command": "claude-mem load --check-safe" } ], "SessionEnd": [ { "type": "command", "command": "claude-mem store" } ] } }SessionStart 的 hook 负责在每次新会话开始时读取记忆,SessionEnd 的 hook 负责在会话结束时提取和保存记忆。--check-safe这个参数呢,我理解是让加载行为更保守一点,避免在某些不安全的上下文中强注入。具体字段可能随版本略有调整,但基本逻辑就是这一套。
值得提醒的是,如果你的 settings.json 里已经有过自定义 hooks,init 不会智能合并,而是直接覆盖。我就是吃了这个亏,后面会详细说。
3.3 配置检查:doctor 命令
装完之后别急着上手,先跑一遍健康检查:
claude-mem doctor它会检查 Node 版本、数据库是否可读写、hooks 是否正确注册、权限是否正常。我在第一次跑的时候它就提示了一个问题:数据库所在目录的权限是 755,建议收紧。这个东西虽然不影响使用,但它会提醒你注意——记忆库里面存的可能是项目的非公开信息。
doctor 通过后再打开 Claude Code,随便聊两句,然后退出。第二次进入时会发现 Claude 的回复里开始出现一些"记忆"了。我第一次测试的时候,让 Claude 记住"我喜欢用 pnpm,不用 npm",然后新开会话再问它"我的包管理器偏好是什么",它能答对。那一刻我就确定了,这东西不是玩具。
4. 日常操作实证:CLI命令、TUI面板与记忆编辑
4.1 TUI界面比想象中实用
不带任何参数运行:
claude-mem它会进入一个交互式 TUI 面板。界面逻辑有点像邮件客户端:左侧是记忆列表,右侧是选中记忆的详情。上方有过滤条件,可以按项目、按类型、按时间范围筛选。我在这个面板里主要做三件事:第一,快速浏览当前项目沉淀了哪些记忆;第二,发现某条记忆明显过期或者记错了,直接按快捷键进入编辑模式修改;第三,对重要记忆打标,让它在后续注入时获得更高权重。
TUI 操作不需要背命令,界面上会直接提示按键。唯一要适应的是,它是终端 UI,用惯了图形界面的人可能觉得不够现代,但对于我们这种整天泡在终端里的人,反而是加分项。
4.2 高频命令速查
除了 TUI,日常我也经常直接用命令操作。整理一份高频命令清单:
| 命令 | 功能 | 我的使用场景 |
|---|---|---|
claude-mem list | 列出当前项目记忆 | 快速扫一眼最近沉淀的内容 |
claude-mem search <关键词> | 全文检索记忆库 | 找某个历史决策 |
claude-mem get <id> | 查看单条记忆详情 | 确认一条记忆是否准确 |
claude-mem add --type insight --content "..." | 手动添加记忆 | 补录线上聊过但没被自动提取的信息 |
claude-mem edit <id> | 修改记忆内容 | 纠正错误的自动提取 |
claude-mem rm <id> | 删除单条记忆 | 清理隐私或过时内容 |
claude-mem export | 导出全部记忆为 JSON | 做备份或迁移 |
claude-mem wipe | 清空记忆库 | 项目翻篇时重置 |
list命令可以加--since指定时间窗口,配合--type过滤。比如我周三想看这周沉淀了哪些决策类记忆,就会跑:
claude-mem list --since "2025-06-01" --type decision4.3 手动补录记忆的方法
自动提取并不总是完美的,有些信息它就是没抓到。比如某个客户明确说过"订单号生成规则后续会改",这种偏口语化的表达,自动提取模块可能会认为它不够"项目相关"而丢弃。但你知道这句话很重要。
这时候就需要手动补录。我的惯用姿势是:
claude-mem add --type todo --content "订单号生成规则待调整,客户明确说过要改,优先级别高"手动添加的记忆权重默认不高,但你可以通过 TUI 面板再给它打标提升权重。补录之后,下个会话开始 Claude 就能看到这条 todo 了。实测下来,任务类记忆的注入效果比 insight 类型更直接,因为它天然是行动导向的。
5. 记忆的遗忘与收缩:防止记忆库变成垃圾场
5.1 为什么"全部记住"反而是灾难
有人可能会想:既然要记忆,那就把所有对话都存下来不是更好吗?在实际使用中这个想法完全行不通。原因有三点。
第一,上下文窗口有限。Claude Code 单次能承载的上下文就那么多字节,如果你把所有历史全塞进去,真正处理任务的空间就被挤占了,回复质量反而下降。
第二,记忆的时效性不同。"昨天调试时试了 5 个失败的方案"和"用户明确说订单号要改成雪花 ID"这两件事,重要性天差地别。前者过了今天就毫无价值,后者可能影响两周后的开发。
第三,旧记忆会和现状冲突。项目演进后,几个月前的决策可能已经被后来新决策覆盖了。如果不遗忘,Claude 就会在旧记忆和新事实之间来回摇摆,行为变得很奇怪。
5.2 遗忘算法:LRU、TOFU 和按类型遗忘
claude-mem 的遗忘机制不是简单的"超过 N 天就删",而是组合了多种策略。LRU(最近最少使用)挺好理解,长期没被读到的记忆先被淘汰;TOFU(基于时间的遗忘函数)会按时间衰减计算每条记忆的"存活分数",分越低越优先清理。
它还能按类型遗忘。比如 tool_probability 这类记忆更新频率高,它的半衰期设得很短,可能几天没用到就衰减到很低的权重;而 decision 类型的记忆半衰期设得很长,因为它代表的是长期有效的项目决策。
力度也是可调的。我建议不要上来就用默认值跑大项目,先跑两周,看看记忆库里残留的都是什么。如果发现很多三个月前的琐碎内容,可以把遗忘力度调高;如果发现重要的早期决策被误删了,就调低力度,同时把那些决策手动标记为高权重。
5.3 会话压缩与上下文回收
claude-mem 还有一个功能对长会话特别有用:会话压缩。当单次会话的上下文持续增长,接近 Claude 的窗口上限时,它会对早期内容做一次压缩,把大量对话浓缩成几条关键结论,替换掉原来的冗长内容。这样你不需要频繁开新会话,项目讨论可以一口气持续很久,而重要的结论不会丢失。
它的效果,我直观感受是长会话的"可用里程"变长了。以前聊到四五十轮的时候,Claude 开始出现"忘了前面说过的细节"的迹象,现在明显延后了。而且压缩动作本身是自动触发的,不需要我干预。
5.4 数据存在哪、隐私怎么处理
记忆数据默认存在本机,路径大致在用户目录下的.claude-mem/文件夹里,数据库是一个 SQLite 文件。它不会主动把记忆内容上传到远端,除非你自己配置了远程模型或额外服务。这一点对很多公司内部项目来说挺重要的——毕竟你不想让代码决策类记忆散落到第三方服务手里。
我自己的习惯是每周五用claude-mem export导出一份 JSON 备份到加密盘里,相当于给"项目大脑"做快照。同时跑一遍claude-mem doctor确认磁盘权限没问题。如果某个项目彻底交付了,我会直接claude-mem wipe清掉该项目相关记忆,避免下一个项目的 Claude 被完全无关的历史信息干扰。
6. 扩展与避坑:SDK集成路径和我的实测心得
6.1 把记忆引擎嵌入自己的 Node 应用
如果只把它当成 Claude Code 的附属插件,那有点浪费。claude-mem 的 TypeScript SDK 是很干净的,可以在你自己的 Node 应用里直接调用。
我试过一个场景:公司内部有个批量处理代码评审的脚本,历史评审结论一直没有沉淀。用 SDK 改写后,每次评审完成就把结论写入记忆库。效果就是,后续评审遇到类似模式时,脚本能自动把上次的结论拉出来提示我,省了不少重复判断。核心代码大概长这样:
import { MemoryStore } from "@photonicql/claude-mem"; import { addMemory, loadMemories } from "@photonicql/claude-mem"; import { fileURLToPath } from "url"; import path from "path"; const store = new MemoryStore({ dbPath: path.join(process.env.HOME, ".claude-mem", "memories.db") }); // 写入一条决策记忆 await addMemory(store, { type: "decision", content: "评审结论:所有对外接口必须显式声明超时时间", project: "api-gateway", metadata: { scope: "code-review" } }); // 读取相关记忆 const memories = await loadMemories(store, { project: "api-gateway", limit: 10 });SDK 的读写逻辑很直观,记忆的过滤、排序这些复杂逻辑都被封装好了。唯一要注意的是,如果你同时跑着 Claude Code 的 hook 和这个脚本,两边用的是同一个数据库,要注意并发写的问题。一般日常场景并发量很低,但如果你要写批量任务,建议在写入前做一次简单的冲突检测或者幂等处理。
6.2 memory-tools插件:让其他AI编码工具共享记忆
我在一些项目里用 Cline 和 Roo Code,之前它们和 Claude Code 的记忆是彼此隔离的。后来发现 claude-mem 有 memory-tools 的插件方案,能让这两类工具通过 tool calling 的方式,直接读写同一个记忆库。
这就带来了一个很舒服的体验:早上用 Claude Code 讨论方案,下午切换到 Cline 去执行实现,两边拿到的是同一套项目记忆。切换工具不再意味着切换"大脑"。配置方式和 Claude Code 的 hooks 不太一样,需要把这些工具接入 claude-mem 提供的 MCP 聚合服务。文档里写的步骤比较清楚,照着做就行。
6.3 我踩过的一串坑
接入过程不算完全顺滑,这里把我踩过的坑原原本本列出来,你能少走弯路。
第一个坑是 hooks 覆盖。我之前在 settings.json 里自定义过 SessionStart hook,用于自动加载某个环境变量文件。跑完claude-mem init之后,我的自定义 hook 不见了——它直接覆盖了整个 hooks 配置。这个问题的规避方法很简单:init 之前先把原配置备份好,之后再手动把两个 hook 合并。
第二个坑是 Node 版本。有一台工作机系统是 Ubuntu 20.04,自带的 Node 是 18,装的时候报 engine 不匹配,卡了很久才意识到是版本问题。升级 Node 之后就顺畅了。
第三个坑是记忆污染。有段时间我同时在弄两个项目,其中 A 项目的记忆偶尔会出现在 B 项目的会话里。排查下来发现,是因为两个项目共用同一个项目名标签(Claude 的 cwd 没变),记忆分类混淆了。后来我给记忆碎片都手动加上了项目路径元数据,过滤时按完整路径匹配,问题就解决了。
第四个坑是自动提取的质量。它会把一些临时调试信息当成重要决策来存。比如我为了测试临时写了一段 mock 数据,它竟然记成了"项目使用 mock 数据方案"。这种误判如果没发现,后续新会话里 Claude 可能会基于错误记忆给出明显偏离的回答。所以我的建议是:定期用 TUI 浏览最近的记忆,发现明显错误的直接删掉或者编辑,别让坏记忆越积越多。
6.4 一些实战建议
用了一段时间之后,我总结了几条比较务实的经验。
大的长期规范还是放 CLAUDE.md,比如技术栈、目录结构、代码风格这些稳定的东西;动态的决策、偏好、待办放 claude-mem。两者是互补关系,不是替代关系。
记忆要定期"体检"。可以给自己定个节奏:每两周花十分钟翻一遍记忆库,把过时的、错误的清理掉,把重要的提升权重。这十分钟花得很值,它能保证记忆库长期处于健康状态。
陷在上下文里的"新鲜记忆"和存进库里的"长期记忆"是有区别的。claude-mem 适合存的是后者,如果你今天讨论的某个细节明天就要用,那你不用管它,何必费这个存储总有一天会清理掉的临时上下文。
最后说个我个人的体会。以前我开新会话跟 Claude 协作,心里总得先打个腹稿,想着那句背景介绍怎么写得足够简短又包含所有关键信息。有 claude-mem 之后,这个负担基本没了。我只需要说"继续做订单模块",它自己就知道前因后果。这种"被记住"的感觉,用多了是真的回不去了。如果你也正在被反复交代上下文的循环折磨,我的建议是:别犹豫,给它一个周末的时间去试,你大概率会跟我有一样的结论。