最近在 GitHub 上翻到一个项目,名字很简练——claude-mem。如果你长期用 Claude 的 API 或者 Claude Code 写代码、整理文档,大概率遇到过同一个尴尬:上午聊得好好的,下午新开一个会话,它完全不记得上午说过什么。Claude 自己是有上下文窗口的,但窗口一关,历史清零,一切从头再来。claude-mem就是给这个痛点补位的:它把每次会话自动保存下来,整理成结构化记忆,下次再聊的时候,把相关的历史内容重新注入提示词,让 Claude 表现得像"想起来了"一样。这篇文章我会从原理讲到实操,把我折腾这个工具的过程、踩过的坑、以及最后总结出来的一套用法,完整分享出来。适合正在用 Claude 做实际项目、又受不了反复交代上下文的开发者参考。
1. 这个项目到底解决什么问题?为什么我需要它?
先别急着装工具,想清楚一个问题:Claude 本身已经有很大的上下文窗口了,为什么还需要额外的记忆层?这个问题想通了,你才知道claude-mem适合放在什么位置。
1.1 Claude 的"金鱼记忆"困境
Claude 的每次 API 调用都是无状态的。也就是说,模型本身不保存你和它的聊天记录。你看到的"连续对话",其实是前端把整段聊天历史反复塞给模型,模型基于这些历史生成新的回答。这个机制有两个直接后果。
第一,上下文窗口有上限。虽然 Claude 的窗口已经做得很大,但真正常用的场景中,长文档、多次工具调用、多轮问答叠加起来,很容易逼近上限。一旦超出,要么报错,要么被静默截断,早期的信息就丢了。
第二,会话之间的记忆完全不互通。你在项目 A 里交代过的技术选型、命名偏好、代码规范,切到项目 B 的新会话时,Claude 一概不知。重复交代是一件非常消耗耐心的事情,尤其是你已经在之前某个会话里花了半小时把一个复杂的业务规则讲清楚了,结果第二天又要重讲一遍。
我试过最笨的办法:每次开场把之前的结论粘贴过去。短对话还行,对话一长就不现实。复制出来的东西本身又占 token,而且你只会粘自己记得的重要结论,那些"当时没觉得重要、后面才发现有用"的细节,就这么丢了。
1.2 claude-mem 的定位:给 Claude 加一层"长期记忆"
claude-mem做的事情,简单说就是三件事:存、取、注入。
- 存:把每次会话的原始内容落盘,形成一份可以检索的历史档案。
- 取:新会话开始前,根据当前的问题,从历史档案里找出相关的片段。
- 注入:把这些片段拼到 system prompt 或者对话开头,作为背景信息交给 Claude。
这样 Claude 在生成回答时,就相当于拥有了一份"外部记忆",不用靠上下文窗口硬扛。它不改变模型本身,只是在外面套了一层记忆管理。这个思路其实很像给一个完全没有记性的员工配一个私人助理,助理负责在开会前把以前的会议纪要和相关邮件放到桌上。
1.3 claude-mem 和 RAG 的区别
很多人看到"存下来、检索、再注入",第一反应是 RAG(检索增强生成)。本质上确实有相似之处,但定位完全不同。RAG 通常解决的是"模型不知道的知识",比如公司内部文档、产品手册,这些是静态的、公开的、多人共享的。而claude-mem解决的是"模型曾经知道但忘了的信息",这些信息是动态的、私有的、跟具体对话历史绑定的。
简单点说,RAG 是图书馆,claude-mem是你的个人聊天记录本。图书馆里的书谁都可以借,但记录本里写的是你和 Claude 之间发生过的具体事情。两者可以共存,但不是一个东西。
| 维度 | Claude 原生会话 | claude-mem |
|---|---|---|
| 记忆范围 | 单次会话内 | 跨会话、跨项目 |
| 存储形式 | 内存中临时保存 | 磁盘落盘持久化 |
| 检索能力 | 无 | 关键词/向量检索 |
| 额外成本 | 每次调用都带全量历史 | 只带最相关的片段 |
| 适用场景 | 短对话、一次性问答 | 长期项目、持续迭代 |
这张表基本就是我当时决定折腾它的原因:我需要的是跨会话的稳定记忆,而不是每次重新开始。
2. 核心机制拆解:它是怎么把"忘记"变成"记住"的
claude-mem不是一个黑盒,它背后的几个关键步骤都很值得拆开看一遍。理解了这些机制,你在配置参数时才不会抓瞎。
2.1 会话数据从哪里来
要让工具记录会话,第一步是让数据流到它手里。claude-mem的接入方式取决于你怎么用 Claude。
如果你用的是 Claude Code,最常见的方式是在配置文件里配置 hooks。Claude Code 本身支持在特定事件发生后执行外部命令,claude-mem就是靠这个接管会话记录的。每当一轮对话结束,hook 触发,把最新的消息追加到对应的会话文件里。
如果你只是用普通 API 写自己的应用,接入方式就更灵活了。可以在调用 API 的封装层里加上一段逻辑:拿到 Claude 的返回结果后,异步调用claude-mem的记录接口,把用户输入和模型输出写进去。
这里有我的一点经验:数据采集尽量放在应用层,不要在模型层做。原因是模型层拿到的只是 prompt 和 response,没有调用元信息,比如会话 ID、用户 ID、触发时间。有了这些元信息,后面的检索和过滤才能做得精准。
2.2 记忆的加工与存储
原始聊天记录不能直接用。如果每次检索都把整段对话塞回上下文,那跟手动粘贴历史没有本质区别,token 一两轮就爆了。所以claude-mem会在存储阶段做几层处理。
第一层是归档原始记录。这是最保险的做法,无论如何,原始日志留一份,后面摘要错了还能回溯。
第二层是提取核心事实。比如你告诉 Claude"这个项目的部署环境是 Ubuntu 22.04,使用 Docker Compose",这句就是一条值得单独保存的显式记忆。它会被拆出来,打上标签,比如环境、部署、项目名。
第三层是生成摘要。Claude 的每次会话往往是一大段来回,工具会定期对长对话做压缩,形成一段简洁的会话摘要。摘要的作用不是替代原文,而是为检索提供更高层的入口。比如你问"之前为什么选 PostgreSQL",匹配到的可能不是某条原始消息,而是某次会话摘要里的关键词。
存储后端我见过几种实现思路,最常见的是 SQLite 加 JSONL 文件。SQLite 存索引和元信息,JSONL 存原始消息流。这样做的好处是查询速度快,而且只需要一个文件,备份非常简单。如果你要自己实现一套同样的机制,存储结构至少要包含这几个字段:
- 会话 ID:唯一标识一次对话
- 用户 ID:区分不同使用者
- 时间戳:排序和过滤的基础
- 角色:用户、助手还是系统
- 内容:文本本身
- 标签/元数据:用于后续过滤
2.3 记忆检索与注入:不是全量回放
claude-mem最有含金量的部分是检索。工具会在新会话启动时或每次用户提问后,拿当前的输入去历史记忆里做匹配,找出最相关的若干条记录,然后拼装成一段"记忆上下文"。
检索方式通常有两种。简单的是关键词匹配,适合记忆量不大、对精度要求不高的场景。复杂一点的是向量检索,先把历史记录切成片段,用 embedding 模型转成向量,再用余弦相似度排序。向量检索的好处是语义相关也能命中,即使你这次问题的措辞和之前完全不一样,也能找回那段历史。
我自己的使用体会是:向量检索不是必须的。如果你只是个人使用,每天几十轮对话,关键词加标签过滤已经够用了。向量检索的收益要到记忆库积累到一定规模后才明显,但代价是需要引入额外的模型和计算资源。
检索完之后是注入。注入的位置一般有两个:system prompt 或者 user message 的开头。我个人更倾向于放 system prompt,因为 Claude 会把 system prompt 当作长期背景信息来处理,优先级更高,不容易被用户说的话干扰。注入的内容要严格控制长度,claude-mem里一般会有类似max_context_tokens的参数,默认可能几百到一千出头,超过的部分宁可不用也不要硬塞。
2.4 几个关键参数,到底在调什么
用这个工具时,你会碰到几个参数,我把含义说透。
top_k:检索结果的数量。设得太小,可能漏掉关键记忆;设得太大,无关内容混进来,反而干扰模型判断。similarity_threshold:相关性阈值,只有相似度高于这个值的记录才算是"相关"。这个值我建议从低往高试,先看检索结果是否准确,再逐步收紧。max_context_tokens:注入内容的最大 token 数。这是硬上限,为了控制成本必须设。session_ttl:记忆的保留时间。这个参数容易被忽略,但对精度影响很大。时间太久的记忆可能早已过期,比如某个服务的临时地址,强行注入反而误导模型。
理解这些参数背后的逻辑之后,你就不会盲目照抄别人的配置了。不同项目、不同使用频率,最优参数是完全不同的。
3. 实操:从零搭起一套可用的 claude-mem
下面这部分是完全可以照着做的。我尽量把每一步都写清楚,包括我自己实际执行时用的命令和配置文件。
3.1 安装与环境要求
claude-mem这类工具通常以 Node.js 包或 Python 包的形式分发。安装之前先确认本机环境:
- Node.js 18 以上,或者 Python 3.10 以上,具体看项目文档的要求
- 有 Anthropic API Key,并且环境变量
ANTHROPIC_API_KEY已经配置好 - 如果你用的是 Claude Code,需要安装并初始化过 Claude Code CLI
安装命令我以 npm 为例:
npm install -g claude-mem装完之后先跑一下版本检查:
claude-mem --version如果命令不存在,大概率是 npm 的全局 bin 目录没加到PATH里。Windows 上常见,Linux 上一般没事。
3.2 最小可用配置
安装完成之后,第一步先初始化配置目录。我建议把数据目录单独设到一个你容易备份的位置,不要放在系统临时目录里。
export CLAUDE_MEM_STORAGE_DIR="$HOME/.claude-mem" claude-mem init初始化之后,目录里会出现一个配置文件。最基本的配置长这样:
storage: backend: sqlite path: $HOME/.claude-mem/memory.db chatlog: format: jsonl path: $HOME/.claude-mem/chatlogs retrieval: method: keyword top_k: 5 similarity_threshold: 0.3 max_context_tokens: 800 injection: position: system enabled: true这里我特意把检索方式设成keyword,而不是向量。原因前面说过,个人使用场景下关键词检索已经能解决大部分问题,而且配置简单,不需要额外拉一个 embedding 模型。等你记忆库超过几万条,再考虑切换向量检索不迟。
设置好配置后,可以把显式记忆功能测试一下。显式记忆的意思是你主动告诉工具"这句话很重要,请记住"。我见过有些实现支持类似--remember的参数:
claude-mem remember "项目代号为 atlas,生产环境数据库不允许直连"然后在新的会话里搜索:
claude-mem search "atlas 环境约束"正常的话,刚才那条记录能搜出来。这一步通了,说明存储和检索链路是通的,后面接入 Claude 才有意义。
3.3 接入 Claude Code:用 hook 实现自动记录
Claude Code 支持通过.claude/settings.json配置 hooks。claude-mem的接入逻辑是:在一轮对话结束的 hook 里调用claude-mem的采集命令,把消息追加进记录。
在项目根目录的.claude/settings.json里添加类似这样的配置:
{ "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "claude-mem ingest --session $CLAUDE_SESSION_ID --input -" } ] } ] } }注意:Stop事件会在一次模型回答结束后触发,此时用git diff或者标准输入的变动信息,可以把当前轮次的上下文交给claude-mem处理。
配置完成后,随便在 Claude Code 里聊几句有实质内容的话,比如"把项目的端口配置改成 8080,并且以后所有回话都默认这个端口"。然后退出会话,新建一个会话,直接问"这个项目现在默认端口是多少"。如果配置生效,Claude 应该能给出准确回答。
这一步是整篇文章里最容易出问题的地方。很多人配置完成后发现没生效,原因多半是以下三个:
- hook 的命令路径不对,
claude-mem不在 Claude Code 进程的 PATH 里 - 环境变量没传到 hook 子进程,
$CLAUDE_SESSION_ID是空的 - 配置文件的 JSON 格式不对,解析失败但不会报明显错误
排查方式很简单:在命令行手动执行一次 hook 里的命令,看能不能正常输出。能输出,问题就在 hook 环境里;不能输出,问题就在你的配置参数上。
3.4 管理命令和数据备份
claude-mem一般会提供几个管理命令,用来查看和操作记忆库。常见的几个:
# 列出所有会话 claude-mem list # 查看某个会话的详情 claude-mem show <session_id> # 搜索某条记忆 claude-mem search "关键词" # 删除某条记忆或整个会话 claude-mem delete <session_id> --confirm # 导出数据 claude-mem export --format json我强烈建议你定期执行一次导出,把记忆库备份到网盘或者 Git 仓库。记忆数据是你和 Claude 反复沟通沉淀下来的,丢失了很难找回来。备份频率不用太高,每周一次足够。
3.5 在自定义 API 应用中集成
如果你不用 Claude Code,而是自己写程序调 Claude API,集成思路稍微绕一点,但原理一样。
在你的请求处理流程里,加三步:
- 调用
claude-mem search,用当前用户输入去检索历史记忆 - 把检索结果拼进 system prompt
- 请求完成后,把用户输入和模型输出写入
claude-mem
伪代码大概是这么个样子:
user_input = "这个项目的数据库密码加密方式定下来了没?" memories = claude_mem_search(user_input, user_id="zhangsan") system_prompt = base_prompt + memories.to_context() response = anthropic.messages.create( model="claude-sonnet-4-20250514", system=system_prompt, messages=[{"role": "user", "content": user_input}] ) claude_mem_ingest( user_id="zhangsan", messages=[ {"role": "user", "content": user_input}, {"role": "assistant", "content": response.content} ] )这套流程跑通之后,你的应用就拥有了跨会话记忆能力。用户今天问你一次"加密方案定了没",过三天再问,你还是可以给出当时的结论,而且不需要用户在界面上手动翻聊天记录。
3.6 多用户场景下的隔离策略
如果你的应用是给多个人用的,一定要在记忆里区分用户维度。claude-mem的检索命令通常支持指定用户 ID 或项目 ID,比如:
claude-mem search "部署环境" --user-id zhangsan不要把所有用户的记忆混在一起。我见过有人图省事,把系统里所有用户的对话都写进同一个记忆库,结果用户 A 问"我之前定的方案你记得吧",Claude 答成了用户 B 的方案。这个 bug 特别难排查,因为从代码逻辑上看完全没问题,问题出在数据隔离缺失。
4. 常见问题与排查技巧实录
这部分是我实际使用中踩过的问题汇总,不保证覆盖所有情况,但大概率能帮你省几个小时排查时间。
4.1 检索结果总是命中旧信息,怎么办?
这是记忆工具最常见的翻车场景。原因大多数是检索参数没区分时间维度。比如你一个月前用 PostgreSQL,这周切到了 MySQL,但旧记忆权重太高,每次搜索"数据库"都命中 PostgreSQL 的那条记录。
解决办法有两个层面。第一个是配置层面:把top_k调低,同时加上时间衰减逻辑。有些工具支持类似recency_weight的参数,时间越近的记录权重越高。第二个是使用层面:重要变更发生时,手动把对应的旧记忆删除或标记为过期,比如:
claude-mem delete <old_record_id> --confirm不要指望工具自动判断所有内容是否过期。机器判断不了你的业务变化,定期清理是必须的。
4.2 token 成本为什么会暴涨?
用claude-mem之后,如果发现 API 账单明显上涨,大概率是注入内容太多。每次请求都携带 2000 token 的记忆上下文,一天几千次请求,这个增量就很可观了。
我的建议是严格控制max_context_tokens。个人日常问答,600 到 1000 token 足够;代码生成场景可以稍微放宽,但也不要超过 1500。另外可以加一个规则:只在会话开始时注入记忆,会话中间不重复注入。否则每一轮都重新检索、重新注入,成本翻倍。
4.3 显式记忆和自动记忆,谁优先级更高?
我测试下来,显式记忆应该永远优先于自动摘要的内容。实现方式也很简单:给显式记忆打一个更高的标签权重,比如source: explicit,检索排序时优先展示。
如果你使用的工具不支持权重排序,那就把显式记忆直接拼在检索结果的最前面。模型对前置内容的关注度远高于后面内容,这条规则虽然有点暴力,但有效。
4.4 记忆丢失或找不到,如何排查?
先确认数据有没有写进去。执行:
claude-mem list --limit 10看看最近的会话在不在。如果在但搜不到,问题出在检索链路。检查关键词是否一致,例如你记得当时说的是"数据库密码",但配置里把重点标签设成了"数据库凭据",那就搜不到。
如果记录也没了,那就得看存储文件。SQLite 文件是否存在、是否有权限、是否在会话过程中被其他进程锁住。这类问题多半和存储路径配置有关,检查配置文件里的路径是不是在系统重启后发生变化。
我整理了一张速查表,按现象直接对照处理方法:
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 搜索无结果 | 关键词不一致或阈值太高 | 降低 similarity_threshold |
| 搜索有结果但内容混乱 | 注入顺序不对 | 把显式记忆放前面 |
| 会话记录没有写入 | hook 未触发 | 手动执行 hook 命令检查路径 |
| token 成本异常 | max_context_tokens 过大 | 限制注入长度,会话内只注入一次 |
| 多用户记忆串线 | 没有按用户过滤 | 检索时指定 user-id |
| 数据目录被清空 | 使用了临时目录 | 改用固定路径并备份 |
4.5 注入内容被模型忽略
模型不是每次都严格遵循 system prompt 里的记忆内容。有时候你明明注入了"用户偏好使用简洁回答",但模型还是啰嗦了一大堆。
这种情况不一定是注入没生效,可能是你的记忆内容太模糊。比如"用户偏好简洁"这种描述,不如改成"用户要求回答不超过 200 字,不要列多余步骤"。具体的约束比抽象的偏好更容易被模型执行。
另外,注入内容尽量用陈述句,避免疑问句。你写"用户是否喜欢简洁回答?",模型可能会把这句话当作一个问题来处理,而不是一条背景指令。
5. 我的一些使用体会和扩展想法
最后这部分不打算写太长的总结,就分享几件我在实际使用中印象比较深的事。
第一件事,记忆工具真正提高效率的阶段,是在记忆库积累了大概两周之后。刚装上的头两天,你会觉得这工具很鸡肋,搜出来的东西感觉都是废话。这是因为记忆太少、太碎片化。坚持用下去,让对话记录沉淀出规律,它才开始"好用"。
第二件事,摘要生成要给 Claude 留出专门的调用。如果你只是把历史记录原样存下来,不提炼摘要,检索效果会打折扣。但反过来,如果每一轮都让 Claude 做一次长文本摘要,成本也不低。我的做法是只在会话结束或者隔段时间做一次总结,不是每条消息都摘。
第三件事,claude-mem未来如果能和 MCP 生态打通,会方便非常多。现在的记忆工具本质上是一个独立服务,需要外部把对话数据喂给它。如果能做成标准 MCP 工具,让模型自己决定什么时候读写记忆,那记忆就不只是"注入上下文"这么简单,而是真正变成了模型可调用的外部能力。从接入体验来看,这是很自然的演进方向。
有一段时间我也试过最土的办法:直接用一个 JSON 文件,手动往里面塞关键信息,然后每次请求前手动拼到 prompt 里。这个办法在会话数量很少时确实能用,但一旦超过三五十条记录,手动维护就完全不可持续了。claude-mem这类工具的价值,恰恰在于把"存、取、注"这个流程从手工变成了自动化。
另外一个让我比较惊喜的场景是:它不只是给你当前的 Claude 会话提供记忆,还能让你跨会话检索自己之前的所有思考过程。比如整理月度复盘时,直接搜"这个月踩过哪些坑",能把散落在十几个会话里的相关内容一次性拉出来。这种能力比单纯记性好用得多,等于给自己的工作留了可检索的底稿。
如果你正在被"每次重新交代上下文"折磨,不妨把它当成一个小基础设施去搭。装好、配置好、然后把备份做起来。它在前期需要一点耐心,但磨合期过后,带记忆的 Claude 和裸用的 Claude,体验差距不亚于"有草稿箱"和"每次写完再重抄一遍"。我自己现在的新项目已经默认加上了这层记忆层,今后大概率也会一直用下去。