1. 项目整体设计与思路拆解
1.1 为什么需要 claude-mem 这类记忆工具
用过 Claude Code 或者经常和 Claude 聊天的人应该都有同感:单次会话里它能记住你交代的上下文,但一旦关闭终端、开启一个新会话,之前聊过的内容就像被格式化了一样,什么都不剩。你需要重新解释项目背景、重新说明偏好设置、重新强调哪些文件不要动。如果只是几句话还好,可当你在一个大项目里调试了三四个小时,积累了大量决策背景和排查记录之后,这种“失忆”带来的重复劳动真的让人抓狂。
claude-mem 的出现就是解决这个问题的。它是一个开源的“记忆扩展”工具,专门给 Claude Code 这类终端型 AI 助手补齐长期记忆能力。它不是简单的聊天记录备份,而是有选择性地把会话内容结构化存储到本地 SQLite 数据库中,并且能自动生成会话摘要。你新开一个会话,它可以基于历史记忆自动为你提供相关上下文,从而让 AI 的行为更连续、更“懂你”。
这个项目适合谁来用呢?首先是重度使用 Claude Code 写代码、做运维、搞数据分析的开发者;其次是那些需要在多轮对话中维持项目背景的技术团队;甚至包括喜欢在终端里用 AI 做研究、写文档的人。如果你只是偶尔问 ChatGPT 一句“怎么给列表去重”,那 claude-mem 对你的价值不大;但如果你每天都在终端里靠 AI 干几个小时的活,那它就是刚需工具。
从底层逻辑上看,claude-mem 的核心思路非常简单:把对话历史存下来,然后在需要的时候把它重新注入给模型。但这件简单的事要做好,关键在于“怎么存”、“存什么”、“何时注入”。这些设计决策直接决定了工具是否好用,也是我接下来要展开分析的重点。
1.2 核心工作流程与存储方案
claude-mem 的工作流程可以拆成三个阶段:拦截、归档、注入。
拦截阶段发生在你使用 Claude Code 的每一次会话中。claude-mem 以钩子(hook)方式接入 Claude Code 的事件流,监听每条消息、每次工具调用、每个文件读写。它不会盲目记录所有原始数据,而是经过筛选后只保留对长期记忆有价值的信息,比如用户的关键指令、项目决策、代码变更点、遇到的错误和解决办法。
归档阶段是它的核心。抓到的会话信息会被写入本地 SQLite 文件,并且按结构化格式存储。归档不只是存原文,它还会调用 Claude 对会话做一次摘要生成,把冗长的调试过程提炼成几行“结论性记忆”,比如“用户偏好使用 pnpm 而不是 npm”“项目的 API 网关超时已从 30s 调整为 120s”这类干净又具体的条目。这些摘要才是 claude-mem 后续用来注入上下文的“弹药”。
注入阶段则发生在你新开会话时。claude-mem 会先检查当前项目的会话历史,抽取与当前工作会话相关的记忆,然后以系统提示或上下文块的形式注入到 Claude 的输入中。这样,Claude 从一开始就“知道”你之前做过什么、你习惯怎么做,避免了重复解释的尴尬。
存储方案上,claude-mem 选择 SQLite 而不是 JSON 文件或传统的 MySQL,这个选型很聪明。SQLite 是单文件数据库,零配置、零服务进程,天然适合开发者在本地使用;同时它支持结构化查询,你可以用 SQL 精确检索“三天前关于数据库优化聊了什么”,这是纯文本 JSON 很难做到的。更关键的是,SQLite 的事务支持保证了写入的原子性,哪怕你正在疯狂发送消息,也不会出现记录损坏的问题。
-- claude-mem 底层存储的核心表结构示意 CREATE TABLE conversations ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_path TEXT NOT NULL, started_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, conversation_id INTEGER REFERENCES conversations(id), role TEXT NOT NULL, content TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL, source_conversation_id INTEGER, created_at TEXT DEFAULT CURRENT_TIMESTAMP );从实际使用体验来说,这个设计带来的直接好处是检索效率高、扩展性好。即便你积累了几个月、几千条会话记录,查询依然毫秒级响应。而且 SQLite 文件可以直接备份到网盘或同步工具,换机器时把文件拷过去就能无缝恢复历史记忆,这个便捷性在后面的实操章节我会再提到。
2. 核心细节解析与实操要点
2.1 安装与初始化全流程
claude-mem 的安装方式并不复杂,前提是你的机器上已经有 Go 环境或者通过预编译二进制安装。我实测下来,最稳妥的方式是直接从 GitHub Releases 下载对应平台的二进制文件,放到/usr/local/bin或者任意 PATH 目录下,然后执行一次初始化命令。
# 使用预编译二进制安装(以 Linux/macOS 为例) wget https://github.com/your-repo/claude-mem/releases/latest/download/claude-mem-linux-amd64 mv claude-mem-linux-amd64 /usr/local/bin/claude-mem chmod +x /usr/local/bin/claude-mem # 初始化配置 claude-mem init初始化命令会在你的 home 目录下创建claude-mem配置文件夹,里面包含配置文件和一个初始化的 SQLite 数据库文件。它还会自动检测你是否安装了 Claude Code,并给出钩子配置提示。如果你使用的是 Claude Code 的插件机制,它甚至会尝试自动帮你把钩子写进配置里,省去手动编辑的步骤。
这里我要强调一个容易踩坑的点:claude-mem 通过钩子机制与 Claude Code 交互,而 Claude Code 的钩子配置路径在不同版本里会有差异。早期版本是写在~/.claude/settings.json里,后来迁移到了项目级.claude/settings.json和用户级~/.claude/settings.json并存的结构。claude-mem 初始化时会把钩子的PreToolUse、PostToolUse、Stop等事件写入这些配置中,如果多个配置源同时存在,可能会发生钩子事件被重复触发或漏触发。
我建议的做法是:先执行claude-mem init,然后打开~/.claude/settings.json检查钩子配置是否完整,同时确认项目目录下没有覆盖性的.claude/settings.json文件。如果只在一个终端会话里用 Claude Code,保持最简配置就行;如果团队协作,还涉及到钩子配置是否需要提交到 Git 仓库的问题,这个我会在后面的常见问题部分展开。
2.2 记忆归档与摘要机制
claude-mem 最有价值的部分在于它的摘要机制。它不会把原始对话全文一股脑倒进长期记忆,因为那既不经济也不符合语言模型的输入限制。它的设计是“层层压缩”:先对单轮对话生成短摘要,再对多次会话生成阶段性总结,最后在需要时只释放最顶层的总结性记忆。
你可以想象成一个书籍归档系统:原始手稿放在仓库底层,目录卡片放在中间层,而最上层只有一本书的一句话简介。当你需要查找某个细节时,可以通过摘要中的线索反查原始会话记录,而不是要求模型消化整本“书”。
从实现层面看,claude-mem 在生成摘要时会向 Claude 发送一个专用提示词,要求它提炼出“可移植的、去上下文化的、面向未来复用”的信息。所谓“去上下文化”就是不要把“在刚才那个函数里”这种指代模糊的话写进摘要,而是明确写成“在utils/date.ts的formatDate函数中”。这个细节非常关键,因为如果摘要写得太笼统,新会话里的模型根本不知道你在指什么,注入再多次也白搭。
在我自己的项目里,我会定期检查生成的记忆条目,发现部分摘要把一些过于动态的内容也记住了,比如临时调试用的输出值“临时将超时时间改为 5000ms”,这种记忆如果不经过过滤,反而会污染后续会话的上下文。好在新版 claude-mem 支持通过配置项控制摘要的详细程度和过滤规则,我通常会把summary_max_tokens调低一点,让摘要更极简、更偏向结论而非过程。
另一个需要了解的操作是claude-mem compact。当你的历史对话积累得足够多时,Claude Code 的上下文窗口会出现拥挤,这时候 claude-mem 会自动触发压缩,把旧轮次对话压缩成摘要,释放窗口空间。这个过程是不可见的,但它会保留所有摘要供日后查询,相当于给会话做了一次“瘦身但不失忆”。
2.3 上下文窗口管理策略
用过 Claude 的朋友都知道,模型有固定的上下文窗口长度,比如 200k tokens。理论上窗口很大,但实际使用中,塞进去 200k tokens 之后,不仅成本高,而且模型注意力会被稀释,响应质量明显下降。所以管理上下文窗口不是“能用就行”,而是“精打细算”。
claude-mem 的策略是把上下文分成两类:核心记忆和参考记忆。核心记忆是与当前任务强相关的信息,比如你当前项目里正在重构的模块的决策记录,这部分必须放在模型可见的上下文里;参考记忆是那些“可能有用但不紧急”的历史信息,它们只保存在 SQLite 中,当检测到当前会话触发某些关键词时才按需注入。
这种分层策略实现的是一种“上下文按需调度”效果。举个例子:你在新会话里提到“继续优化数据库查询”,claude-mem 会检索之前所有关于“数据库优化”的记忆条目,把它们整理成一段摘要注入到 Claude 的上下文中;但如果你今天聊的是“写一个前端动画”,它就不会把数据库相关的历史记录塞进来,避免上下文被无关信息污染。
从我的使用感受来看,这种调度机制比“把全部历史都塞给模型”要高效得多。每次会话开始时,claude-mem 会输出一行“Loaded N memories from previous sessions”,让你能直观看到这次注入了多少历史记忆。如果 N 值异常大,说明检索规则可能过于宽泛;如果 N 始终为 0,则说明钩子配置或路径匹配出了问题。具体排查方法我放到第四章。
3. 实操过程与核心环节实现
3.1 与 Claude Code 的集成配置
如果你已经安装好 claude-mem,那么接下来最关键的一步就是确保它和 Claude Code 的集成生效。由于 claude-mem 以事件钩子的方式工作,它的配置本质就是向 Claude Code 的 settings 文件里注入一段 hooks 规则。
以我的环境为例,我的用户级配置文件~/.claude/settings.json里最终长这样:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "claude-mem hook PreToolUse" } ] } ], "PostToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "claude-mem hook PostToolUse" } ] } ], "Stop": [ { "hooks": [ { "type": "command", "command": "claude-mem hook Stop" } ] } ] } }这段配置的意思是:在执行 Bash 工具之前、之后,以及会话停止时,自动调用claude-mem hook相关命令,让 claude-mem 拿到事件并执行归档工作。
配置写好后,你可以通过一条测试命令验证钩子是否生效:
claude-mem test-hooks它会模拟一次 Claude Code 交互,检查各个钩子是否能正常被调用并向 SQLite 写入记录。如果输出显示每个事件都是OK,说明集成成功。如果出现FAIL,多半是环境变量问题,比如CLAUDE_MEM_DB_PATH指向的路径不存在,或者 claude-mem 二进制不在 Claude Code 子进程的 PATH 中。常见解决方案是把claude-mem软链到/usr/local/bin,同时把数据库路径显式写到配置里,避免依赖 shell 环境。
3.2 常用命令与配置参数解析
claude-mem 提供了一组 CLI 命令,常用程度各有不同。我把它们整理成一张速查表,方便大家对照使用。
| 命令 | 作用 | 典型场景 |
|---|---|---|
claude-mem init | 初始化配置和数据库 | 首次安装后使用 |
claude-mem hook | 供 Claude Code 钩子调用的内部命令 | 无需手动调用 |
claude-mem doctor | 检查环境配置和钩子状态 | 诊断集不成问题时 |
claude-mem list | 查看当前项目的历史记忆列表 | 快速回顾最近决策 |
claude-mem show <id> | 查看某条记忆的详情和原始来源 | 追溯某个结论的依据 |
claude-mem search <关键词> | 全文搜索记忆内容 | 查找历史解决方案 |
claude-mem stats | 查看数据库统计信息 | 评估记忆量增长情况 |
claude-mem compact | 压缩当前会话历史 | 上下文窗口不足时 |
配置参数方面,最核心的是CLAUDE_MEM_DB_PATH,它决定 SQLite 文件存放在哪里。默认情况下,claude-mem 会把数据库存储在~/.claude-mem/memory.db,但这个位置对多项目隔离不友好。我建议为每个项目设置独立的数据库文件,做法是在项目的根目录下创建一个.env文件,写上CLAUDE_MEM_DB_PATH=.claude-mem/memory.db,这样不同项目的记忆不会互相污染,备份和迁移也更加灵活。
还有几个值得关注的配置项:
CLAUDE_MEM_ENABLED:总开关,设为false时可以临时禁用记忆功能,适合排查问题。CLAUDE_MEM_MAX_MEMORIES:单次会话最多注入的记忆条数,默认 20,如果感觉上下文被塞得太满,可以调小。CLAUDE_MEM_SUMMARY_INTERVAL:摘要生成的触发间隔,比如每 10 条消息生成一次摘要,保持摘要不过期。
这些参数都可以在配置文件中定义,也可以作为环境变量动态设置。我个人习惯是环境变量与配置文件结合使用:init生成的配置文件负责全局默认值,项目级.env负责覆盖该项目的偏好。
3.3 数据管理与检索技巧
当 claude-mem 运行了一段时间后,你的 SQLite 数据库里会积累大量记忆。这时候管理和检索就成为新的问题。
先说管理。定期清理是必要的,因为不是所有记忆都有长期价值。我通常每周跑一次这个清理流程:先执行claude-mem list看看最近记忆,再用claude-mem search定位过时内容,最后直接用 SQL 删除无效条目。虽然 claude-mem 没有提供delete命令,但 SQLite 本身就是文件数据库,直接删记录完全可行。
# 查看数据库里有多少条记忆 claude-mem stats # 检索包含“数据库优化”关键词的历史记忆 claude-mem search "数据库优化" # 直接通过 sqlite3 删除过时记忆(谨慎操作) sqlite3 ~/.claude-mem/memory.db \ "DELETE FROM memories WHERE content LIKE '%临时%' AND created_at < date('now', '-30 day');"再说检索。claude-mem 的搜索命令基于 SQLite 的LIKE查询,虽然简单但足够应对大部分场景。如果你对检索有更高要求,比如支持语义搜索,那可以借助一些外部方案:把memory.db定期导入向量数据库,再用 embedding 做相似度检索。不过对于绝大多数 CLI 使用场景,claude-mem search配合grep管道过滤已经完全够用。
我实际用下来最舒服的一个操作是:在终端里定义一个别名,一键搜索当前项目的所有记忆。
alias mem='claude-mem search'这样直接mem nginx 超时就能看到之前调 nginx 配置文件时留下的一切结论,省去翻聊天记录的大量时间。这种工作流一旦建立起来,你会慢慢把它当成自己的“第二大脑”,用惯之后就再也回不去了。
4. 常见问题与排查技巧实录
4.1 记忆不生效:钩子配置失效排查
很多人安装完 claude-mem 后遇到的第一个问题就是:“我明明正常和 Claude 聊天,为什么新会话里它什么都不记得?”
这个问题 90% 出在钩子配置没有真正生效上。第一个要核查的是 Claude Code 的配置文件。因为 Claude Code 会合并用户级和项目级配置文件,如果你的项目里恰好有一个.claude/settings.json,它可能会覆盖掉用户级配置里的 hooks,导致 claude-mem 的钩子没有被加载。
排查方法其实很简单,直接在 Claude Code 会话里执行:
claude-mem doctor这个命令会打印出当前检测到的配置路径、数据库路径、钩子事件列表以及最近一次归档的文件记录。如果显示类似hooks: Not found,那就基本确定是配置被覆盖或者路径不对了。
解决办法也很直接:要么把 claude-mem 的 hooks 配置也复制到项目级.claude/settings.json中,要么干脆删掉项目级配置文件里你不需要的覆盖项。最一劳永逸的做法是写一个初始化脚本,在项目模板中默认包含 claude-mem 的 hooks 声明,这样新项目拉起来就有记忆功能,不会漏配。
另一个容易被忽略的点是环境变量 PATH。Claude Code 的钩子命令是在一个受限的 shell 环境里执行的,如果你的claude-mem命令装在类似~/.local/bin这种非标准路径,而该路径又没有存在于钩子执行的 PATH 环境中,就会静默失败。解决方式是把 claude-mem 的完整路径硬编码到 hooks 配置里,而不是依赖 PATH 解析:
{ "type": "command", "command": "/Users/yourname/.local/bin/claude-mem hook PostToolUse" }4.2 SQLite 数据库冲突与路径问题
当你在多个目录之间切换项目时,容易遇到数据库“串台”或“找不到库”的问题。我最初把CLAUDE_MEM_DB_PATH设置为某个绝对路径后,在另一个项目里也用了同一个数据库,结果两个项目的记忆混在了一起,搜索出来的内容牛头不对马嘴。虽然 claude-mem 本身会记录project_path字段,但在注入上下文时它会根据当前工作目录过滤记忆,不过一旦数据库路径指向同一个文件,不同项目的会话记录仍然会被写入同一个库中,这就会带来两个麻烦:一是数据量膨胀导致查询变慢;二是跨项目会话的上下文信息仍然会被注入,干扰模型判断。
为了避免这种混乱,我强烈建议每个项目都单独设置一个数据库路径。做法很简单,在你的项目根目录创建.env文件:
# 项目根目录/.env CLAUDE_MEM_DB_PATH=/absolute/path/to/your/project/.claude-mem/memory.db同时,还需要留意 SQLite 文件的并发写锁问题。当 Claude Code 同时触发多个钩子时,比如PreToolUse和PostToolUse在同一瞬间被触发,SQLite 默认的 journal 模式在极端情况下会报database is locked错误。如果遇到这个报错,可以在连接参数里指定更宽松的 busy timeout,或者绕开这个问题的最简单方法是把数据库路径改到本地固态硬盘上,千万不要放到网络共享盘,否则锁频发到你怀疑人生。
4.3 注入记忆过多导致上下文污染问题
记忆功能在带来便利的同时,也带来了一个隐性副作用:如果 claude-mem 注入的记忆条数过多、内容不够精炼,反而会把当前会话的核心任务冲淡。举例来说,在一次会话里,claude-mem 注入了 30 条记忆,其中 25 条都是关于旧模块的细节,而你这会儿正想让它集中精力写一个新功能,模型就会在你提供的庞杂背景中迷惑,回答的准确性和专注度都会下降。
我遇到过很明显的例子:我让 Claude 帮忙写一个 Python 脚本,结果它因为继承了太多“项目历史偏好”的记忆,反而在脚本开头加了跟任务无关的框架代码。后来我调整了CLAUDE_MEM_MAX_MEMORIES参数,从默认的 20 降到了 8,问题立刻缓解。在新会话开始时,我会直接告诉 Claude:“只关注我当前描述的任务,不要自动引入历史记忆中的代码风格。”这句话给当前会话设定了边界,效果比一味依赖记忆参数调整更直接。
调整记忆注入策略可以这样操作:
# 设置单次会话最多注入 8 条记忆 export CLAUDE_MEM_MAX_MEMORIES=8如果你发现某条记忆总是被错误触发,更精细的做法是利用 claude-mem 的配置过滤规则。例如在~/.claude-mem/config.toml中设置黑名单关键词,凡是内容中包含“临时”或“debug”的记忆条目都不会被注入,这样能有效防止临时调试信息混入正式上下文。
4.4 常见问题速查表
为了节省大家排查时间,我把最常遇到的问题整理成了下面这张速查表,每一行都是我在实际操作中踩过的坑或者观察到的典型情况。
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 新会话完全不记得旧内容 | 钩子配置未被加载 | 运行claude-mem doctor,检查并修复 hooks 配置 |
| 记忆写入总是报 database is locked | 多个钩子并发写同一个 SQLite 文件 | 将数据库迁移到本地 SSD,并检查是否放在网络挂载盘 |
| 注入的记忆太多,响应跑偏 | CLAUDE_MEM_MAX_MEMORIES太大 | 调小参数,例如改为 8 |
| claude-mem 命令不存在 | 安装目录不在 PATH 中 | 使用完整路径配置 hooks,或软链到/usr/local/bin |
| 项目之间的记忆串台 | 多个项目共用同一个数据库 | 每个项目设置独立的CLAUDE_MEM_DB_PATH |
| 记忆内容包含临时调试信息 | 摘要生成时未过滤动态内容 | 在配置中设置过滤关键词,排除“临时”等噪声 |
这张表不能覆盖所有问题,但大多数初学者遇到的坑基本都能在里面找到对应解法。等到你熟练使用之后,还可以结合自己的使用习惯,把更适合自己的记忆检索、注入策略慢慢打磨出来,让 claude-mem 真正长成你最顺手的个人知识库。
我在实际项目中已经连续使用 claude-mem 两个多月,最大的感受是:它并不是一个“装上就能用”的工具,而是需要你花一点时间调教——把它的存储路径、注入数量、过滤规则都调整得贴合自己的工作流之后,它才真正从一个“会记事的笔记本”变成“能自动递上资料的第二大脑”。如果你准备深度依赖它,我建议从一个小项目开始试验,先跑一两天,用claude-mem stats和search看看它记住了什么、漏掉了什么,再不断调整自己的表述习惯和对配置参数的预期。这样逐步磨合,远比一开始就全量启用、然后被海量记忆淹没要舒服得多。