1. 先聊聊我为什么会盯上 claude-mem
说实话,用 Claude Code 折腾过几个中型项目之后,我最头疼的就是每次新开一个会话,模型就把上一轮聊的上下文忘得一干二净。代码结构、选型理由、踩过的坑、客户偏好,全都得重新说一遍。项目稍微大一点,会话一换,AI 就跟失忆了一样,连昨天刚定的模块边界都要重新确认。
claude-mem 就是在这么个痛点背景下出现的。它是一个给 Claude Code 加“长时记忆”的开源命令行工具,核心思路很朴素:把会话里的关键信息自动提取出来,存到本地数据库里,下次开新会话的时候按需检索回来用。它不改变 Claude 本身,而是通过 Claude Code 的 hook 机制在会话前后做“记忆归档”和“记忆注入”,相当于给 AI 配了一个外挂笔记本。
如果你和我一样,用 Claude Code 做正经项目、希望跨会话保持上下文连贯、又不想把代码和对话记录交给第三方服务,那 claude-mem 这套本地优先的方案值得好好看看。下面我会把它的设计思路、安装配置、命令用法、踩坑经历一次性讲透。
2. 核心设计拆解:一个外挂笔记本是怎么工作的
2.1 记忆从哪来:hook 机制与自动提取
Claude Code 本身支持自定义 hook,就是在特定事件发生时执行外部脚本。claude-mem 盯上了两个关键节点:会话结束(PostToolUse 或 Stop)时,把整段对话喂给一个小型 Claude 模型,让它提炼出“值得长期记住的信息”;下一次会话启动前(SessionStart),再把库里相关的记忆塞进系统提示词里。
这套设计的聪明之处在于,它不需要改 Claude 的推理逻辑,也不依赖任何插件体系,纯粹用“外部存储 + 提示词注入”就把记忆问题解决了。提取动作放在会话结束后异步执行,不会阻塞正常的对话流程;注入动作放在新会话开头,Claude 一开始就知道“你是谁、你做过什么、接下来要干什么”。
提取质量的关键,在于给“记忆总结”部分的 prompt 设计。claude-mem 内部会引导模型输出结构化字段:主题、项目名、实体、偏好、结论、证据、时间戳。其中“实体”和“偏好”这两项最有价值——实体让记忆可以被检索归类,偏好让后续交互越来越贴合你的习惯。
2.2 记忆存哪:SQLite 与本地优先
所有记忆默认落在本地 SQLite 数据库文件里,路径在~/.claude-mem/claude-mem.db。选 SQLite 而不是 JSON 文件,是权衡后的结果:SQLite 支持结构化查询、事务、并发读,检索速度足够快,而且单个文件备份迁移都很方便。
数据库里有几张核心表:conversations 记录会话元信息,memories 存提取出的记忆条目,embeddings 存语义向量,projects 做项目维度隔离。memories 表里每条记录包含原始文本、摘要、实体列表、项目名、创建时间、最后访问时间、来源会话 ID。最后访问时间这个字段很重要,它是后面“遗忘曲线”调度的依据。
嵌入向量默认用本地模型生成,也支持 OpenAI 兼容接口。如果选了本地嵌入模型,整个链路完全离线,代码和对话记录不出你的机器,这对不少有保密要求的团队来说是硬性门槛。
2.3 记忆怎么用:语义检索与遗忘曲线
只存不取等于白存。claude-mem 的检索逻辑走的是“关键词 + 语义向量”双路召回:先用 SQLite 的 FTS5 全文索引做一次关键词过滤,再用向量余弦相似度做一次语义排序,最后取 Top K 注入新会话。
在本地模型上我实测下来,纯语义检索偶尔会召回一些表面相似、实际无关的旧信息,比如两个项目里都出现过“数据库连接池”这种通用词汇。但加上关键词召回做硬过滤之后,有效召回率明显提升。这个“混合检索”的取舍思路,做搜索或者 RAG 的同学应该很熟悉。
遗忘曲线的实现也很有意思。每条记忆有一个“热度值”,被检索命中一次就加一,长时间不被访问就逐步衰减。低于阈值且超过保留天数的记忆会被标记为可清理状态,但不会自动删除,需要你跑claude-mem archive手动归档清理。这种设计避免了“AI 自作主张忘事”的尴尬——机器只做标记,人类做最终决定。
3. 安装与接入:从零到让 Claude 记住你
3.1 安装 Python 包与依赖
claude-mem 是 Python 写的,安装用 pip 就行。我建议在虚拟环境里装,别直接怼进系统 Python,毕竟它依赖的包和别的项目容易起冲突。
python3 -m venv ~/.venvs/claude-mem source ~/.venvs/claude-mem/bin/activate pip install claude-mem装完之后先初始化目录,这一步会在~/.claude-mem/下生成配置文件config.toml和数据库文件:
claude-mem init如果你要开启语义检索,还需要一个嵌入模型。默认的 fastembed 本地模型开箱即用,第一次跑会自动下载模型文件到本地缓存;如果网络环境受限,也可以手动指定模型路径。
注意:国内网络环境下,模型下载可能会卡住。我踩过这个坑,解决办法是先把模型文件通过其他途径拉到本地,然后改配置里的
embedding.model_path指向本地目录,跑通之后再也不用管网络问题。
3.2 配置 Claude Code hook
接入 Claude Code 的核心是往它的配置文件里加 hook。Claude Code 的配置在~/.claude-code/settings.json,如果你用的是项目级配置,就放在项目的.claude/settings.json。在里面加这么一段:
{ "hooks": { "SessionStart": [ { "matcher": "", "hooks": [ { "type": "command", "command": "claude-mem summarize-hook" } ] } ], "Stop": [ { "matcher": "", "hooks": [ { "type": "command", "command": "claude-mem stream-hook" } ] } ] } }SessionStart 对应开场记忆注入,Stop 对应结束后的记忆沉淀。这两个 hook 一配,基本能力就通了。
有个细节:Stop hook 里我用的是stream-hook而不是summarize-hook。区别在于 stream-hook 会等 stdout 流结束再调用,确保 Claude 的完整回复落盘之后才开始提取,不会截断信息;代价是会话关闭会慢一两秒。如果对延迟敏感,可以换 summarize-hook,速度更快,但极端情况下可能漏掉末尾几句结论。
3.3 验证接线是否成功
配完 hook 别急着开工,先做一次端到端验证。随便开一个 Claude Code 会话,让它告诉你“今天是几号、项目名叫什么、你计划做什么”。结束会话,然后跑:
claude-mem list如果接线成功,你会看到刚才会话的记忆已经入库。再看一眼新会话的开头,Claude 应该能说出“之前的会话里我们讨论过哪些内容”。
我第一次验证时 list 出来是空的,排查了一圈才发现是 hook 命令的 PATH 问题:Claude Code 启动时的环境变量不包含我用虚拟环境的 bin 路径。解决方式是把 hook 命令改成绝对路径,比如/home/me/.venvs/claude-mem/bin/claude-mem stream-hook。这个坑很隐蔽,建议直接一步到位用绝对路径。
4. 命令行操作精讲:add、search、edit 组合拳
4.1 手工添加记忆:claude-mem add
自动提取不是万能的,有些信息你明确知道以后要用,与其等模型提炼,不如直接手工录入。claude-mem add就是干这个的:
claude-mem add "支付模块的回滚策略:任何非对账批次不允许自动回滚,必须人工确认"也可以用--project指定项目归属:
claude-mem add --project ecommerce "订单超时时间统一为 30 分钟,不要改成 20"我个人的习惯是,凡是涉及“数值、命名、规范、客户偏好”这类硬信息,一律手工加一条,不赌自动提取的准头。自动提取适合沉淀“背景、结论、决策过程”,手工录入适合沉淀“参数、边界、铁律”。两条腿走路,记忆质量才稳。
4.2 检索记忆:claude-mem search
search 是全命令里我用的最多的一个,虽然 hook 会自动注入记忆,但有时候你就是想确认一下“某些旧信息到底有没有记、记成什么样了”。
claude-mem search "支付 重试策略"输出会按相关度排序,每条显示摘要、来源项目、时间戳、命中次数。加--limit 20可以控制返回条数,默认 10 条。
实测下来,中文关键词的混合检索效果比纯英文场景稍微弱一点,尤其是同义词场景。比如搜“下单流程”和“购买流程”,语义向量能拉回来一部分,但不完全。我的对策是搜索时多换几个同义词,或者把最重要的词直接塞进--filter做硬匹配。
claude-mem search "库存扣减" --filter project=ecommerce4.3 编辑与归档:edit、archive 的日常用法
记忆串味了怎么办?比如我把 A 项目的部署脚本细节记到了 B 项目下,或者某条记忆的结论已经过时了。claude-mem edit可以交互式修改条目:
claude-mem edit 42它会列出 ID 为 42 的那条记忆让你逐字段修改。修改完会自动重新生成摘要,但嵌入向量不会自动重算,所以改完之后建议跑一次claude-mem reindex,否则旧向量还挂在那条记录上,语义检索可能召回过期文本。
对于那些“曾经有用、现在没用”的旧记忆,用 archive 处理:
claude-mem archive 100归档不是删库跑路,只是把条目从活跃检索池挪到归档表,search 默认不会搜到它,但可以claude-mem list --archived翻出来看。这个设计我很喜欢,既保持了检索质量,又保留了追溯能力——万一哪天发现某个旧结论还能复用,一键恢复就行。
5. 记忆管理的进阶玩法与参数调优
5.1 streaming 模式与实时记忆
默认情况下 claude-mem 是在会话结束后一次性处理整个对话。但长会话动辄几万字,一次性总结容易丢细节。streaming 模式解决了这个问题:它在对话进行中分段处理,每几百行输出就提取一次,记忆颗粒度更细。
开启方式是在 hook 命令里带参数:
"command": "claude-mem stream-hook --streaming"实测下来,streaming 模式的记忆条目数量大概是整段总结模式的两到三倍,单条记忆更具体,比如“用户要求支付失败时展示错误码而非通用提示”。代价是会话期间会有后台 CPU 消耗,笔记本风扇会转得勤一点,但单纯记忆提取的性能开销在可接受范围内。
如果你既要细颗粒度又不想频繁后台任务,可以折中:平时用整段总结,遇到特别重要的设计讨论会开一个新会话并临时加--streaming标志。
5.2 控制记忆注入量:token 预算管理
记忆注入不是越多越好。Claude 的上下文窗口有限,塞进去 50 条旧记忆,真正有用的可能只有 5 条,剩下的全是噪音,还会挤占正常对话的空间。claude-mem 的注入量有两个参数控制:max_tokens和max_memories_per_prompt。
[memory_injection] max_tokens = 1200 max_memories_per_prompt = 8我按 1200 token 和 8 条记忆的配置跑了大半个月,体感是“该记的都记得,不该记的也不来打扰”。如果把 max_tokens 调到 3000 以上,你会发现 Claude 开始频繁引用一些边角料记忆,回答反而变啰嗦了。这个参数不是越大越好,本质上是在“相关性召回”和“注意力稀释”之间做平衡。
5.3 项目隔离与多项目协同
如果你同时维护两三个项目,记忆互相串台是灾难。claude-mem 默认会按当前工作目录的根目录名做项目隔离,也可以用环境变量CLAUDE_MEM_PROJECT强制指定。
我的做法是在每个项目的.claude/settings.json里设置 hook 时带上项目参数:
"command": "claude-mem stream-hook --project ecommerce"项目隔离之后再配合search --filter project=<项目名>,不同项目的记忆就能井水不犯河水。不过要注意,项目隔离不等于物理隔离,数据还是在一个 SQLite 文件里。如果两个项目的敏感级别差异很大,建议给敏感项目单独建一个$HOME/.claude-mem-secret的数据库目录,彻底分开。
5.4 立即可用的配置参考
以下是我目前在用的完整配置,直接抄作业即可,稳定跑了几个项目没出大问题:
[general] default_command_timeout = 120 [memory_extraction] model = "claude-3-5-sonnet" min_tokens_to_summarize = 800 [memory_injection] max_tokens = 1200 max_memories_per_prompt = 8 [embedding] provider = "fastembed" model_path = "models/bge-small-en-v1.5" [storage] database_path = "~/.claude-mem/claude-mem.db" [streaming] chunk_size = 600 overlap = 100min_tokens_to_summarize = 800的意思是,对话不足 800 token 就不提取记忆。这个阈值很实用,因为它过滤掉了大量“你好”、“帮我看看这个报错”式的水会话,避免数据库被无意义信息填满。
6. 常见问题与排查技巧实录
6.1 问题速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| list 输出为空 | hook 没触发,或 PATH 里找不到 claude-mem | 检查 hook 配置,改用绝对路径 |
| 记忆注入后 Claude 不理会 | 注入内容被系统提示词截断 | 调低 max_tokens,或减少 max_memories |
| search 结果明显不相关 | 嵌入模型没跑,向量全空 | 跑一次 reindex,检查 embedding.provider |
| 数据库文件异常膨胀 | 高频流式提取+归档不及时 | 定期 archive,必要时重建索引 |
| 多项目记忆串台 | 项目隔离未生效 | 检查 .claude/settings.json,加 --project 参数 |
| hook 执行超时报错 | 提取模型响应太慢 | 调大 default_command_timeout,或换快模型 |
6.2 我实际踩过的三个坑
第一个坑是 PATH 问题,前面提到过,embedding 模型的下载也会触发类似问题。Claude Code 通过 GUI 启动时,继承的环境变量和你终端里的不一样,很多库路径、代理设置、模型缓存路径都会丢失。所以 hook 命令我统一用绝对路径写死,模型路径也全部显式配置,不指望系统环境。
第二个坑是流式提取的 chunk 重叠问题。我一开始把 overlap 设成 0,结果发现跨 chunk 的对话被切断了上下文,提取出来的记忆经常缺主语。overlap 调成 100 token 之后再没出现过这种情况。这个参数本质上是给文本切片加了“粘合剂”,宁可少量重复,也不要信息断裂。
第三个坑和滥用 closed book 模式有关。Claude Code 在某些模式下不会把系统提示词里的记忆显式读出来,表现得像“刚睡醒”。遇到这种情况,直接问一句“根据你掌握的历史信息,我们当前任务进行到哪了”,往往会触发它翻阅注入的记忆。这不算 bug,更像是提示词工程层面的交互技巧。
6.3 备份与迁移经验
claude-mem 的整个状态就是~/.claude-mem/目录,备份策略简单粗暴:打包带走。
tar czf claude-mem-backup.tar.gz ~/.claude-mem换机器恢复时,解包到同样的路径,装好 Python 包,配置好嵌入模型路径,直接就能用。数据库里记录的是本地路径和项目名,只要恢复目录结构一致,不需要额外迁移步骤。
有一点要提醒:如果你同时用多台设备开发同一个项目,claude-mem 目前没有同步机制,两边数据库会各自生长。我把这当作特性而不是缺点——每台机器保留自己的记忆上下文,反而避免了“另一台设备上的分支操作污染当前项目记忆”的混乱场景。真需要同步,手动导出导入即可。
7. 个人使用体会与扩展玩法
连续用了三个月之后,我最大的感受是:claude-mem 真正改变的不是 AI 的能力,而是我自己的协作习惯。以前我会刻意把关键决策写进 TODO 或者文档,生怕下次会话忘掉;现在我知道它会可靠地捞回旧上下文,反而更敢在对话里做快速试错,让“结论先出来、纪要后沉淀”。
推荐两个我现在离不开的扩展用法。第一个是把 claude-mem 和项目的 CHANGELOG 联动——每周跑一次claude-mem list --project <项目名> --format json,把本周新增记忆按主题归类,批量写入周报。等于每周自动生成一份“AI 视角的项目进展摘要”,比我自己回忆靠谱多了。第二个是用claude-mem search做旧代码决策考古——碰到“这个模块当初为什么设计成这样”的问题,搜一下能翻出当时的设计结论和备选方案,省去翻聊天记录的功夫。
如果你主要用 Claude 网页版而不是 Claude Code,claude-mem 暂时用不上,它的 hook 机制依赖 Code 的执行环境。但思路完全可以借鉴:任何“会话无状态”的 AI 工具,都可以用“外部记忆库 + 上下文注入”这套模式来补短板。把记忆做成可检索、可归档、可清理的数据资产,而不是锁在对话历史里,这方向在未来会越来越重要。
最后补一句使用原则:claude-mem 存储的对话和代码片段都在本地,别把它当成一个需要信任第三方的新服务来用。但反过来,也因为全本地,你自己要养成定期备份的习惯——数据在本地最大的风险不是泄露,而是硬盘坏了什么都没了。定时跑一次打包任务,三分钟的事,能换来整个开发记忆的安全感,这笔账怎么算都划算。