如果你也习惯在终端里跟 Claude CLI 打交道,肯定撞过这么一堵墙:昨天还在聊服务拆分方案,今天重新打开一个会话,模型一脸无辜地反问“你们这个项目的技术栈是什么”。这不是能力问题,是记忆问题。Claude CLI 默认是“无状态”的,每次会话结束,上下文就清空了。claude-mem 这个工具就是专门来解决这件事的:它可以给终端里的 AI 加上持久记忆,把对话里产生的事实、决定、偏好、待办都存下来,下次会话开始之前自动注入。
装上之后最直观的感受是,AI 不再是“每次见面都重新加好友”的关系,而是像拥有一个不会忘事的笔记本。这篇文章我会讲清楚它是怎么设计的、怎么配置、我在日常使用里踩过哪些坑、以及怎么把它真正用成自己的外挂记忆。
1. 为什么我决定给终端里的 AI 加一层长期记忆
先说我自己的使用场景:我大量时间是在终端里通过 CLI 和 Claude 聊天。不是简单问一句“这个函数怎么优化”,而是真正参与一个长期项目。这意味着对话里有大量上下文:项目的目录结构、命名约定、关键配置项、已经做过的技术决策。
问题在于,每次新建会话,这些上下文全部归零。我试过几种缓解办法,效果都不理想。
第一种是“每次重新交代”。在最前面复制一大段项目背景,把关键文件路径和约定写清楚。这对短会话有效,但一旦项目复杂到一定程度,光背景描述就得占掉一大段,而且我自己的表述难免有遗漏,模型理解出来的东西每次还不一样。
第二种是“用输出文件接力”。对话结束后把结论贴到笔记软件里,下次再贴回去。但这个流程太重,我经常要么没记,要么记了之后找不到当时的讨论细节。
第三种是“保持一个长会话不关”。这在终端里根本不现实,网络波动、机器重启、切目录换机器,会话说没就没。
所以当我看到 claude-mem 这类方案时,第一个反应是:这解决了最根本的“状态”问题。它的思路和我做工程时的习惯很像——不要把关键信息放在脑子里或者靠口头传递,而是落盘,做成可检索、可复用的数据。
工具本身做的事情可以拆成几块:记录对话里值得记住的信息,把信息结构化存下来,在需要的时候精确找回来,并且在创建新会话时自动把相关记忆拼到上下文里。听起来简单,但“记住什么、忘掉什么、怎么找回来”这三件事,背后全是设计取舍。
这也是这篇文章会重点展开的地方。它适合谁?适合那些每天要在 CLI 里和 AI 反复讨论同一个项目的人,适合想让技术决策、代码约定、运维命令形成“组织资产”的人,也适合想研究 AI 工具如何做持久化封装的人。它不是那种装完就摆着的玩具,而是一个需要你稍微花点心思调教、但回报很高的生产工具。
2. 工作原理:一个记忆系统该有的样子
在动手配之前,我建议先花五分钟理解 claude-mem 的运行机制。它本质上不是在做玄学“记忆”,而是做工程里最常见的三条链路:采集、存储、检索。理解清楚这三条链路,后面配置和排错都会顺利很多。
2.1 记忆从哪里来:三种采集方式
第一是手动保存。你在终端里直接输入一条命令,把此刻对话里最重要的一句结论存下来。比如:
claude-mem remember "订单服务超时阈值统一改成 30 秒,之前是 10 秒,已经跟组里确认过"这种方式的优点是精准。因为你在当下最清楚哪句话是值得留的,哪句话只是过程噪声。我一般会在对话里遇到明显的结论、决策、数字时随手记一条。
第二是自动抽取。这是 claude-mem 比较有想象力的部分。它会监听一个会话的完整文本流(通常是通过包装 Claude CLI 的方式来获得输入输出),会话结束后把整段对话交给一个抽取模块,让它生成一批结构化记忆。比如对话里讨论了“缓存策略”,它就可能抽出“项目使用 Redis 做缓存,key 前缀是 cart:”。这种方式适合对话密集、信息量大的场景,但也容易把废话也存进去,后面需要配置过滤规则。
第三是从已有历史导入。如果你之前已经导出了 Claude CLI 的对话记录,或者手头有成批的 Markdown 笔记,可以用导入命令批量处理,让工具帮你把非结构化的文字拆成记忆条目。
我实际用下来觉得,三种方式各有分工。手动保存管“关键节点”,自动抽取管“过程沉淀”,导入管“历史迁移”。只靠任何一种都不太完美,自动抽取最省事,但在信息提炼上偶尔会跑偏。
2.2 记忆存在哪儿:从 SQLite 到向量索引
采集到的记忆不是随便放在文本文件里,而是进入一个设计过的存储层。claude-mem 默认用 SQLite 作为主存储,这很符合命令行工具的气质:单文件、零服务、好备份、跨平台。你不需要装数据库,不需要维护一个常驻进程,数据就是一个.db文件躺在用户目录下。
表结构我理解下来大概会分成几个区:记忆正文、类别标签、来源会话标识、创建时间、以及后续要讲的向量字段。用关系型数据库存的好处是,你可以很容易地按类型筛选、按时间排序、按来源去重。
但只有 SQLite 还不够,因为普通 SQL 的 LIKE 查询在文本模糊匹配上既慢又不聪明。所以 claude-mem 一般会叠一层全文索引,即 SQLite 自带的 FTS5 模块。FTS5 会把文本切成词元,支持快速的短语匹配和关键词高亮。这可以理解成给笔记做了一个倒排索引,比逐行扫描快一个量级。
针对语义搜索,它还会维护一个向量化副本。每条记忆在写入时可以生成一个固定维度的向量,把字符串变成一串数字。查询时拿你的问题生成向量,再和库里所有记忆向量算相似度,返回最接近的几条。数据量在几千条以内,这种向量检索完全不需要专门的服务端,加载到内存里暴力算也能毫秒级返回。
我建议你不要把它们想得太复杂。数据库负责“精确查”,全文索引负责“关键词查”,向量负责“意思相近但字面对不上”的查。三套东西配合起来,覆盖了绝大多数记忆检索场景。
2.3 记忆怎么找回来:搜索、注入与“遗忘”
有存有取,这套系统才算闭环。检索有两种入口。一种是主动搜索,你在终端里输入:
claude-mem search "超时时间"它会返回一批记忆条目,带上时间、来源、相似度分数。另一种是被动注入,这才是把记忆变成 AI “长期记忆”的关键:你开启一个新会话之前,claude-mem 会先根据当前工作目录、最近保存的相关记忆、以及你配置的兴趣类型,筛选出最相关的几条记忆,生成一段摘要文本,拼进送给模型的第一条消息里。于是模型一开始就知道“这个项目的约定有哪些”,而不是靠你重新说。
这里就引出一个容易被忽视但很重要的设计:“遗忘机制”。记忆系统最高频的失败模式不是存不下来,而是存了太多过期的、互相冲突的东西。所以 claude-mem 提供了删除单条记忆的命令,也提供了按时间轴批量清理的策略:
claude-mem forget <memory_id> claude-mem prune --older 90d我会定期跑一次清理,并且把“什么叫值得保留”想清楚,不然记忆库很快就会变成垃圾堆。
3. 从零开始配置一套可用的记忆系统
明白了原理,就可以实际把它跑起来了。下面是我认为比较稳妥的一套从零搭建流程,按照这个顺序来,你会少踩很多坑。
3.1 初始化数据库与配置文件
安装完成后,第一步不是直接开聊,而是先初始化:
claude-mem init --db ~/.claude-mem/memory.db这个命令会做三件事:创建用户目录、生成空的 SQLite 数据库、生成一份默认配置文件。配置文件一般是 TOML 或 YAML 格式,我习惯用 TOML,阅读起来更清爽。
你可能会问,直接用默认配置行不行?能跑,但不建议。默认值为了稳重通常会比较保守,比如不会自动抽取、注入量很小。你要根据自己的使用方式逐项调。几个我比较关注的配置项:
| 配置项 | 作用 | 我的建议值 |
|---|---|---|
db_path | 数据库文件位置 | 放在独立目录,别跟项目混一起 |
auto_capture | 是否在会话结束后自动抽取 | 项目稳定后打开 |
capture_interval | 会话多久自动存一次 | 按对话轮数,不按时间 |
default_types | 新记忆默认分类类型 | facts, decisions |
inject_max_tokens | 注入到新会话里的记忆上限 | 600-1000 |
language | 抽取摘要的语言 | 中文项目就设为中文 |
配置文件的完整参数我不在这里罗列,重点是你要意识到:这是一份需要“调参”的配置,不是装好就能毕业。
初始化之后,可以跑一个健康检查命令,比如claude-mem stats。它会告诉你当前库里有多少条记忆、多少条带着向量、数据库体积多大。这是我每轮清理前必看的指标。
3.2 设计你自己的记忆类型
这里的“类型”不是技术概念,而是你对记忆所做的分类。它直接决定了自动抽取模块怎么处理信息,也决定了注入时怎么挑内容。我把自己的记忆类型设计成一个简单的表格:
| 类型 | 含义 | 示例 |
|---|---|---|
facts | 项目事实,不会频繁改变 | 服务名、目录结构、依赖列表 |
decisions | 技术决策及原因 | 为什么选 PostgreSQL 而不是 MySQL |
commands | 常用命令和部署流程 | 构建命令、重启命令、测试命令 |
preferences | 个人/团队偏好 | 代码注释用中文、缩进用空格 |
todos | 待办和未完成事项 | 需要移除旧的支付回调 |
为什么要做这么细?因为注入记忆的时候,你通常不会想把“待办”也一股脑塞给模型,它需要的是项目事实和约定。有了类型字段,注入引擎就可以实现“按类型白名单”注入:只注入facts和decisions,不注入todos。
自动分类靠什么实现?claude-mem 会用心智规则或者小模型判断。但我实际经验是,手动保存时主动指定类型比自动分类准得多:
claude-mem remember "统一走 HTTP 接口,不要直接连数据库" --type decisions强烈建议手动的都带上--type,自动抽取产出的记忆,定期抽查一次分类质量。分类错了不会导致数据丢失,但会导致检索时漏掉,所以值不值得花时间,取决于你有多依赖搜索。
3.3 接入 CLI 的两种主流方式
记忆系统搭好了,下一步要知道它是怎么“截获”对话的。目前我看到的主流做法有两种,各有取舍。
第一种是 wrapper 函数,本质上你不再直接调用 Claude CLI,而是调用一个你自定义的函数。先让 claude-mem 把当前相关记忆准备好,再调用真正的 CLI,结束之后再把会话文本交给抽取模块。在我的 shell 配置里大概是这样的:
claude() { # 第一步:生成记忆注入前缀 local inject="$(claude-mem inject --format prompt --max-tokens 800)" # 第二步:把注入文本和用户输入一并交给真正的 claude 命令 command claude "$@" --system "$(cat <<EOF $inject EOF )" }这种方式优点是实现简单、版本兼容性好,无论 CLI 本身怎么变,你都能在最外层拼接文本。缺点是注入比较“粗暴”,你不能精确感知内部状态,而且如果 CLI 本身不允许追加 system 消息,你只能把注入内容放在第一个用户消息里。
第二种是 hook 机制。较新版本的终端 CLI 通常会提供某种事件钩子,比如在会话结束触发时执行一个指定命令。你可以把抽取逻辑挂在会话结束事件上,把注入逻辑挂在会话开始事件上。这样做更优雅,因为不用包装命令,claude-mem 可以看到准确的原始输入输出。
但 hook 有两个前提:你的 CLI 版本支持,而且你所在的环境能正确传参。我在实际用的时候发现,不同版本对钩子脚本返回值的要求不同,写过一版脚本当时没报错,但后来发现抽取数据一直为空,排查半天是退出码不对。后面我会把这类问题写进排查清单。
不管用哪种方式,核心原则是:注入一定要轻,抽取一定要全。注入太多会挤占本来就有限的上下文空间,模型反而抓不住重点;抽取太少则会漏掉关键信息。
4. 日常使用实录:保存、搜索、复用
配置好之后,真正决定工具价值的,是你每天怎么用它。这部分我记录了一些真实操作过程和细节,你可以直接照着试。
4.1 手动记和自动记的配合节奏
刚装好的头两天,我把重点放在手动记忆上。和 Claude 讨论完一个问题,我会在对话结束前问自己一句:如果一周后重新开这个会话,我最想让它记得什么?把答案用remember命令存下来。比如:“用户维表和订单维表在 dwd 层合并,上游数据每小时更新一次”。
用了一个星期,库里攒了两百多条手动记忆之后,我才打开auto_capture。原因很朴素:自动抽取的质量,很大程度上依赖抽取模型对上下文的理解,而理解能力又依赖对话本身的清晰度。如果前期你连“什么值得存”都没感觉,自动抽取出来的东西大概率也是乱的。
打开自动抽取之后,你会看到每次会话结束,后台会调用一次抽取模型,把整段对话提炼成几条记忆。这个过程不应该是阻塞的,我在设计自己的工具流时也刻意保证了它会异步运行,否则每次退出会话还要等它几秒,体验很差。
运行了几天之后,我发现自动抽取有个通病:它特别爱记录“过程性”内容。比如对话里详细讨论了一个 bug 怎么定位,它会抽出一条“逐步分析并定位到某某函数”的记忆。这类信息对以后新会话没有用,因为 bug 已经修完了。
解决办法是在抽取之后加一个过滤环节。我写了一段简单的正则规则,凡是正文里包含“调试”、“堆栈”、“临时修复”这类词的记忆,自动打入discard,并且在每天的清理里定期删除。过滤规则放在 claude-mem 的规则文件里,格式大致如下:
[[filter_rules]] pattern = "堆栈|调试|临时修复" action = "discard"这个动作看起来小,但很大程度上决定了记忆库的整洁度。
4.2 搜索和注入的实际效果
先说搜索结果。有一次我想查“之前定过的重试次数上限”,具体数字我完全忘了,只记得在讨论接口稳定性时提过。我用关键字搜:
claude-mem search "重试上限"返回了几条相关记忆,其中一条写着“对账接口失败重试 5 次,每次间隔 5 秒”,来源是三个星期前的会话。那一刻我第一次觉得这个工具不是自嗨,是真的能把过去的自己“找回来”。
如果你要搜的是一个表达上的近似意思,比如“账号体系怎么分环境的”,但库里存的是“dev 环境单独建了一套用户服务”,关键字不一定能撞上。这时候用语义搜索更靠谱:
claude-mem search --semantic "不同环境下的用户体系怎么隔离"它会丢开字面匹配,返回“语义距离”最近的内容。实测下来,对中文支持还算理想,但语义搜索的准确度取决于向量模型的质量,不是所有环境表现一致。我建议你默认先用关键字搜索,关键字实在不行再开语义。
再说注入。我用的 wrapper 方案注入之后,新会话的第一条消息会包含一段记忆摘要。最直接的感受就是,不用再写“请你记住,我们是一个管理后台项目,前端是 Vue,后端是 Go,Redis key 前缀是 admin:”。这些信息已经被注入了,我可以直接问“帮我看一下登录接口的缓存设计问题”。
但要注意控制注入量。我有一次把inject_max_tokens调到 2000,以为给的信息越全越好,结果模型反而变得啰嗦,开始复述注入内容。后来我把上限压到 800,并且把注入分成两层:系统消息只放“项目事实”和“当前目录相关约定”,用户消息再放“与最近某个主题相关的记忆”。两个层次各有侧重,效果好很多。
4.3 让自动抽取更准的几个调优方向
如果你发现自动抽取出来的记忆质量不行,别急着怪工具,先把这几件事查一遍。
第一,检查会话文本的质量。如果对话里大量是“帮我看看这个报错”“再解释一下”这类指令,抽取模块根本没有足够的信息密度去提炼。让抽取准的前提是,对话本身有结论、有参数、有上下文。所以我现在会在对话结束时,主动问一句“把结论用三句话总结一下”,这一句话出来,抽取质量立刻上一个台阶。
第二,检查抽取的触发频率。默认可能是“会话结束才抽一次”,但对超长会话来说,一次对话可能横跨多个主题,最后只抽一次会丢掉前面的大半内容。我后来改成了“每 10 轮对话触发一次增量抽取”,效果立刻均衡了。不过这增加了模型调用次数,如果你用的是远端模型,需要考虑调用成本。
第三,给记忆补充标签。手动保存时,除了--type,我还会用#项目名的形式打标签。这样注入的时候可以只挑“当前项目”的记忆,避免上次开发的仓库约定跑到这次来。标签不是必须的,但你项目多了之后,它就成了救命的过滤条件。
5. 常见问题与排查实录
这部分是从真实使用里踩坑踩出来的记录,按我的经验按出现频率排序。
5.1 搜索不到内容,先查这三处
如果你明明记得存过某条记忆,但搜不到,不要急着给工具下结论,按照下面的顺序排查。
第一,确认数据库路径一致。多个 shell 窗口可能加载了不同的环境变量,导致 claude-mem 连到了不同的数据库文件。我吃过一次亏:在项目 A 里通过配置文件指定了一个路径,后来又在一个通用环境里跑了一次,两条路径不一样,结果怎么搜都缺数据。先跑claude-mem stats,看它实际使用的是哪个库文件。
第二,确认全文索引真的建立了。新版本升级之后,有时候旧数据库的 FTS5 表没有自动重建,导致关键字搜索只返回部分结果。处理办法是导出一份备份,重新init再导回,或者手动执行一次重建索引的维护命令。
第三,确认你搜的是不是“字面匹配”。语义搜索和关键字搜索的索引结构不同,如果你只用关键字搜一个口语化的表述,而库里存的是严谨书面语,很可能就漏了。反过来也一样。我的习惯是同一个问题用两种模式各搜一次,再下结论。
5.2 数据库越来越大,记到后面越来越慢
记忆库不是只增不减的。几千条带全文索引的记忆,加上向量字段,体积会膨胀,查询也可能变慢。这不是 bug,是数据量的自然增长。
我的处理节奏是每月一次大清理。先用stats看总量,再按类型看分布,把todos里已完成的清掉,把discard里被过滤的彻底删除,再用prune --older 90d把超过 90 天的“低价值记忆”清掉。
备份这件事别等出事再做。SQLite 单文件备份非常方便:
cp ~/.claude-mem/memory.db ~/.claude-mem/backup/memory-$(date +%Y%m%d).db万一清理错了,还有退路。
5.3 隐私、冲突与记忆污染
这是用记忆系统最容易被忽视的问题。说白了,你存下来的每一条都是敏感数据的候选。我的原则很简单:凡是包含密码、密钥、真实地址、完整登录态的对话,不手动记入库;自动抽取之后,定期扫一遍有没有意外落库的敏感字段。
记忆冲突比隐私泄露更像慢性病。假设会话 A 里定了“重试次数 5 次”,会话 B 里脑子一热说“改成 3 次”,新旧记忆同时存在,注入的时候就可能给模型混入混乱的上下文。我处理冲突的办法是:一旦意识到某个决策要变更,先手动删掉旧记忆,再写入新记忆,不给模型判断的机会。
我还会给不同的业务领域建独立的配置文件。比如一个做后端服务的项目,跟一个写个人脚本的项目,记忆类型和注入偏好完全不同。分开之后,注入的准确率比共用一个库高得多。
6. 顺着这个思路还能往哪些方向扩展
claude-mem 本身是一套方案,但真正让我觉得有价值的是它的思路:把一次性的 AI 对话变成可持续积累的知识资产。沿着这个思路,你可以把它往三个方向延伸。
第一个方向是团队共享。SQLite 本质是一个文件,那也天然可以被共享。把数据库文件放到团队的公共盘,或者用同步工具分发,成员各自的 CLI 都可以读到同一个记忆库。这样做之后,新人接入项目时的背景知识不再是“人肉传递”,而是直接注入。
第二个方向是和其他工具联动。我现在会在执行git commit之后,把提交信息里涉及的关键改动同步成一条decisions记忆。以后反过来搜“为什么这里要加锁”,可以直接定位到具体的代码提交记录。这相当于给代码库配了关联的记忆侧写。
第三个方向是把它当成一个本地知识检索底座。由于 claude-mem 的数据是结构化的,你可以写脚本把记忆导出成 Markdown、JSON,或者喂给任何支持 RAG 的上下游工具。它就不只是一个 CLI 的插件,而是你个人的小规模知识中台。
最后分享一个我自己离不开的细节:我把claude-mem search和fzf做了绑定,输入关键词之后用模糊选择器在结果里挑,选中的记忆直接复制进粘贴板。这个小习惯把“查记忆”从命令行操作变成了几乎无感知的动作。
我个人在实际操作里的最大体会是:记忆系统不是装上就生效的,它需要在前几周持续喂养。手动存,观察自动抽取的输出,定期清理和调整类型,之后它会越来越准,最终变成一个你完全离不开的延伸脑容量。每次新项目开始,看着模型开口就知道项目约定的时候,我就觉得这不只是省时间,更像是把团队中最容易丢失的“经验”真正留在了系统里。