☰
给Claude装个‘记性‘:claude-mem外部记忆实战
2026/10/10 7:44:35 网站建设 项目流程

要不要给 Claude 装个"记性"?聊聊我实际用 claude-mem 的经历

如果你天天用 Claude 做开发、写文档、处理长流程任务,大概早就发现一个烦心事:对话一长,它就开始"选择性失忆"——昨天讨论过的接口设计,今天问起来像第一次听说;上一轮刚确认的命名规则,下一轮它又按自己的理解来。Claude 本身不提供长对话记忆能力,这是模型机制决定的,抱怨没意义,但活总得接着干。

我用的解决方案是一个叫 claude-mem 的小工具,名字很直白:给 Claude 补一记"记忆"。它不是模型本身的升级,而是通过命令行在本地持久化保存每一轮对话的关键信息,让后续会话能按需检索、回填,相当于给 Claude 配了个外部硬盘。这篇文章不聊什么玄乎的 AI 技巧,就是我把这个工具从安装、配置到实际跑项目踩过的坑、总结出的用法,全部分享出来。不管你是想给自己的 Claude 工作流加点缓存,还是想看看 CLI 工具怎么跟大模型会话结合,这篇都能给你一个能直接照做的参考。

1. 需求拆解:对话"失忆"的真正痛点

1.1 大模型会话为什么记不住事

先说个基本概念:Claude 这类模型本身有上下文窗口,比如一次能容纳几万 token,超过这个长度就必须丢前面的内容。但实际用下来,真正的瓶颈不是 token 数量,而是"信息价值密度"。一个项目对话进行两小时后,前面积累的决策、偏好、命名习惯,只占很少的 token,可一旦被挤出窗口,后面所有回答都会失去这些上下文约束。

更麻烦的是,每次新开会话,Claude 面对的是一个空白的上下文。它知道的知识是通用的,但不知道你这个项目特有的约定。这就像来了个能力很强的新同事,你每次都要重新交代项目背景、命名风格、已知结论,他还未必记住。

我早期试过的手动方案是:每次对话快结束时,让 Claude 自己总结要点,然后复制到笔记里。下次开头再粘贴回去。能用,但太笨了——总结质量参差不齐,复制粘贴容易出错,而且多轮之后越来越敷衍,到后面干脆只写两行"页面组件已完成"。这就是 claude-mem 这类工具存在的真正意义:把记忆变成自动化流程,而不是靠人去维护。

1.2 开发者真正需要的是"上下文快照"

我梳理自己实际使用场景,发现对记忆的需求分三层:

  • 事实层:做过哪些决定,选了什么技术方案,改了哪些文件
  • 偏好层:代码风格偏好、命名习惯、回答问题时希望用什么形式
  • 复用层:已经验证过的解决方案,后续可以直接调用

三层里,第一层最紧急,第二层影响长期协作质量,第三层是锦上添花。 claude-mem 的定位就是解决好第一层,兼顾第二层。它把每次对话的关键信息提取出来,按会话归档,提供检索能力,并把最相关的历史记忆作为前缀注入下一次对话。

这样做的好处很明显:不需要模型原生支持持久记忆,也不需要改模型调用方式,主动权完全在你手里。需要它就拉取,不需要就忽略,记忆的增删完全可控。

1.3 把记忆做成"外部器官"而不是"内部补丁"

市面上也有给 Claude 加记忆的轮子,但大多是在系统提示词里塞一长串历史,告诉模型"记住这些"。这本质上是把记忆塞进上下文窗口,一旦对话拉长,仍然会被挤掉。 claude-mem 的思路不太一样:它是把记忆存储在你的本地文件系统里,到需要的时候再检索出来,注入当前会话。相当于从"让模型自带记忆"变成了"让工具替你记忆"。

这种"外部器官"式设计的核心价值是解耦:记忆的保存、检索、删除、统计,全部和模型能力无关。你可以随时换模型平台,记忆数据仍然是你的。这也是为什么我后来坚持用这类工具而不是依赖任何平台的官方记忆功能——数据归属权在自己手里,才真正踏实。

2. 总体设计:记忆到底装成什么样子

2.1 核心数据模型:消息、会话、记忆

claude-mem 的底层数据模型很朴素,几乎没有学习成本。它把记忆分为三个层次:

  • 消息层:每条用户消息和 Claude 的回复,按时间戳和角色存储
  • 会话层:以对话开始时间或会话 ID 为维度,把一组消息归入同一会话
  • 记忆条目层:从消息中提取出的、值得长期保留的信息片段

记忆条目是核心。它不是原始消息的全文,而是经过压缩的要点型文本。比如你在一轮讨论里说"用户认证用 JWT,过期时间设 60 分钟",这条完整消息可能很长,但沉淀出的记忆条目只需一句:"认证方案采用 JWT,过期时间为 60 分钟"。

存储格式我用的是 JSONL,每行一条 JSON。选这个格式的原因很实际:追加方便,就算文件很大也不需要全部加载;逐行读取速度快;出问题的时候能准确定位到某一行。如果你考虑自己实现,建议不要用单个大 JSON 文件,追加和并发写都会很痛苦。

每条记忆我加了几个字段:ID、所属会话ID、内容、来源消息ID、创建时间、标签。标签是手工斟酌的,后面可以按标签过滤,这在实际检索时帮了大忙。

2.2 会话恢复:让新对话继承旧记忆

会话恢复是这个工具最有实用价值的部分。假设你昨天讨论了一个组件设计方案,今天新开对话想继续,不带任何上下文直接问,Claude 大概率会重新发挥。 claude-mem 的做法是:新对话开始时,指定一个会话 ID,它会把该会话下按时间倒序排列的记忆条目取出来,作为上下文前缀注入系统提示词。

注入量要控制。我一般取最近 20 条,或者按标签过滤后取最相关的 10 条。这里有个关键细节:注入的是记忆要点,不是原始对话全文。原始对话可能几百条消息,但提炼成记忆后可能只有 30 条。上下文占用大幅减少,信息密度反而更高。

这个"压缩"动作本身,其实才是记忆系统的价值所在。 Claude 原生的上下文窗口再大,也经不起无限塞原文;但只要提炼成要点,几百轮对话的记忆也能压缩进一个可控的 prompt 空间。

2.3 记忆聚合:跨会话的主题聚类

除了单会话恢复, claude-mem 还支持跨会话的记忆聚合。比如你过去几周和 Claude 聊了好几个主题,但都给同一个会话 ID,这时可以按标签分组统计,看看哪些主题密度最高。

这个功能一开始我觉得是锦上添花,直到有次想复盘一周工作,发现能一条命令拉出"本周讨论过的所有与数据模型相关的记忆",才意识到它的价值:它让你能以主题维度审视与 Claude 的协作历史,而不是一条条翻原始记录。

聚合的触发方式目前比较基础,就是按标签或关键词过滤后分组。但基础够用就行,实操中真正高频的场景是"我记得我们聊过 XX,但忘了细节"——这时一次关键词检索比肉眼翻对话快太多。

3. 核心实现:一个能跑的 claude-mem 最小版本

3.1 环境准备与目录规划

在讲实现之前,先说明:我实际用的是 claude-mem 社区版命令行工具,但为了弄明白原理,我自己也写过一个最小实现。下面说的流程和代码,是我"抄作业 + 改作业"后总结出的经验,可以直接照做,也可以按需修改。

跑通这个工具需要三个基础依赖:

# Python 3.9+ python3 --version # 可选:用 uv 做依赖管理,比 pip 快很多 curl -LsSf https://astral.sh/uv/install.sh | sh # 创建项目目录 mkdir -p ~/claude-mem && cd ~/claude-mem

目录规划上,我建议把数据文件单独放一个目录,别和代码混在一起。用环境变量指定数据路径,这样升级代码不会影响已有记忆数据:

export CLAUDE_MEM_HOME="$HOME/.claude-mem" mkdir -p "$CLAUDE_MEM_HOME/sessions" "$CLAUDE_MEM_HOME/memories"

会话原始归档和记忆提炼文件我分开存,原因后面在"踩坑"部分会细说。

3.2 CLI 命令行设计

claude-mem 本质上是一个 CLI 工具,核心命令就四五个。我优化的命令设计是这样的:

claude-mem record --session demo --role user # 记录一条用户消息 claude-mem record --session demo --role assistant # 记录 Claude 回复 claude-mem recall --session demo # 恢复会话记忆 claude-mem search --query "JWT" # 全局搜索 claude-mem stats --session demo # 统计

这个设计有一个核心思路:record负责写入,recall负责读取,search负责跨会话检索。职责清晰,没有复杂的子命令嵌套。

用 Python 里的 argparse 实现很简单:

import argparse def main(): parser = argparse.ArgumentParser(description="Claude memory tool") subparsers = parser.add_subparsers(dest="command") rec = subparsers.add_parser("record") rec.add_argument("--session", required=True) rec.add_argument("--role", choices=["user", "assistant"], required=True) recall = subparsers.add_parser("recall") recall.add_argument("--session", required=True) recall.add_argument("--limit", type=int, default=20) search = subparsers.add_parser("search") search.add_argument("--query", required=True) args = parser.parse_args() # ... dispatch

3.3 消息记录与记忆提炼

record命令是写入侧的核心。它做两件事:把原始消息追加到会话归档;如果这条消息包含值得保留的信息,提炼成记忆条目写入记忆库。

原始消息追加很直接,JSONL 一行一条:

import json from datetime import datetime from pathlib import Path def save_message(session_id, role, content): path = Path(os.environ["CLAUDE_MEM_HOME"]) / "sessions" / f"{session_id}.jsonl" path.parent.mkdir(parents=True, exist_ok=True) record = { "id": uuid4().hex, "ts": datetime.utcnow().isoformat(), "role": role, "content": content, "session": session_id } with open(path, "a") as f: f.write(json.dumps(record) + "\n")

记忆提炼稍微麻烦一点。最直接的办法是调用 Claude 的 API,让它把消息压缩成要点。但如果你不想在每次记录时都调 API(费钱也费时间),可以做一个简化版:基于长度和内容特征自动截取。

我当时的判断是:超过 120 字的消息才值得提炼,标准句式的决策型内容(包含"选择""决定""确定""用"等动词)直接提取。这个规则简单,但有效覆盖了大部分场景。完整版可以后续接模型调用,把质量再提一档。

3.4 记忆恢复的注入逻辑

recall命令是读取侧的核心。它从记忆库中读取指定会话的条目,按创建时间倒序,取前 N 条,然后转为文本块,作为上下文前缀。

def recall_memories(session_id, limit=20): mem_path = Path(os.environ["CLAUDE_MEM_HOME"]) / "memories" / f"{session_id}.jsonl" if not mem_path.exists(): return [] lines = mem_path.read_text().strip().split("\n") records = [json.loads(x) for x in lines[-limit:]] return records

注入到 Claude 的 prompt 时,我用一个明确的标记块:

<archive_memory> (此处为 claude-mem 检索到的历史记忆) - 认证采用 JWT,过期 60 分钟 - 用户表增加 email 唯一索引 - 前端路由已完成 3 个页面 </archive_memory>

这个格式起了很大作用。Claude 能明确区分"记忆背景"和"当前真实提问",不会把历史内容错当成当前指令。有次我没加标记,直接把记忆文本和问题拼在一起,结果 Claude 把一段历史结论当成了当前待办,回答跑偏。加了清晰的分隔标签之后,这个情况再没出现过。

3.5 模糊搜索实现

search --query "关键词"这个命令,单靠简单字符串匹配太弱。我用了 sqlite3 的 FTS5 全文检索,把 JSONL 数据导入一个内存数据库,建全文索引。速度很快,几万条记忆毫秒级返回。

import sqlite3 def search_memories(query, limit=10): conn = sqlite3.connect(":memory:") conn.execute("CREATE VIRTUAL TABLE mem USING fts5(content)") # load memories ... res = conn.execute( "SELECT content, session, ts FROM mem WHERE content MATCH ? LIMIT ?", (query, limit) ).fetchall() return res

这个命令的价值在于跨会话检索。比如你三个月前和 Claude 讨论过"数据同步方案",但当时在哪个会话里已经忘了,直接 search 就能捞回来。这个场景单靠会话恢复解决不了,必须有一个全局索引。建议从一开始就做好 search 能力,别等记忆攒多了再补,那时 JSONL 已经很大,遍历会很慢。

4. 实际使用:我的一周工作流实录

4.1 一次完整的会话流程

我用一个实际场景说明整套工作流怎么跑。

周一早上,我准备继续开发"某跨平台系统的订单模块"。直接新开一个 Claude 对话,然后先执行两件事:

claude-mem recall --session order-module --limit 25

输出大概是这样:

[记忆恢复] order-module 会话,最近 25 条: - 订单状态机:created -> paid -> shipped -> done - 支付回调需做幂等处理,增加 request_id - 库存扣减用乐观锁,版本号字段已加 - 订单列表接口分页参数已确定:page/page_size - ...

我把这些内容复制进 Claude 对话,作为"背景资料"。然后才开始问问题。这条命令让我省去了至少 10 分钟的背景复述时间,更关键的是,Claude 的回答从一开始就落在之前确定的方案框架内,而不是另起炉灶。

会话过程中,我关键的操作是:每当一个重要决定确认下来,就手动执行一次:

claude-mem record --session order-module --role user \ --content "确认:订单详情页展示物流轨迹,接口返回轨迹数组,按时序排列"

这里有一个实操要点:record记录的内容,不一定是用户提问的原文,而是提炼后的结论。也就是说,如果你在对话中让 Claude 确认了某件事,可以自己把结论整理成一句简洁的话再写入。这个动作看似繁琐,但换来的是后续记忆恢复时的高质量上下文。用"确认:""决定:""注意:"这类前缀开头,效果更好。

一天的开发结束后,整个会话过程的核心决定都沉淀在记忆文件里了。第二天继续时,一行 recall 就能接上进度。

4.2 记忆摘要的自动提炼 vs 手动提炼

用了一段时间后,我对"记忆是怎么来的"这个问题有了更深体会。理论上最理想的状态应该是全自动:每条用户-Claude 对话结束后,自动提炼成要点。这也是 claude-mem 完整版的设计方向。

但实际执行时,全自动有一个隐蔽问题:提炼出来的要点质量不稳定。因为提炼动作如果依赖模型 API,那每次对话后都要多一次调用;如果依赖规则,就会有大量冗余或漏提。

我的折中方案是:自动记录原始消息,手动提炼关键结论。具体操作是每周五用一次 search 拉出本周所有记忆,发现有重复或失效的条目,批量清理一下。这样既保证了记忆的完整覆盖,又维持了要点条目质量。

4.3 何时该让 Claude"失忆"

这一点可能和直觉相悖,但我认为和记忆工具打交道久了,"选择性失忆"反而比"全记住"更重要。

有次我往某会话里塞了一个已经废弃的接口方案,后续每次 recall 它都会出现在前缀里,Claude 也会时不时把旧方案引入新讨论,造成混乱。排查了半天才定位到是记忆污染。从那以后,我开始定期清理记忆:凡是已经明确废弃的设计、完成并无需回顾的任务、错误过的中间结论,一律删除。

claude-mem 里删除记忆的方式是直接编辑对应 JSONL 文件,删掉不需要的行。我建议每两周固定清理一次,别等记忆库膨胀到影响检索时才动手。记忆的价值不在于多,而在于"需要时找得到,不需要时不碍事"。

另外注意:不要把密码、密钥、内网地址这类敏感信息写入记忆库。因为记忆文件是纯文本 JSONL,一旦泄露就是所有上下文同时泄露。我一般规定:涉及密钥的信息,在记忆条目里只写"密钥已配置于某环境管理工具",不放具体值。

5. 踩坑记录与排查手记

5.1 JSONL 并发写入问题

第一个坑出现在同时开多个终端窗口操作的时候。两个终端几乎同时执行claude-mem record --session order-module,结果出现文件内容交叉,JSONL 中间一行变成了半截 JSON。

排查后发现是追加写入没有加锁。解决办法很简单:写操作用文件锁包起来。Python 里可以用fcntl.flock:

import fcntl def append_with_lock(path, line): with open(path, "a") as f: fcntl.flock(f, fcntl.LOCK_EX) f.write(line + "\n") fcntl.flock(f, fcntl.LOCK_UN)

如果你在 Windows 上跑,fcntl不可用,可以用threading.Lock加进程内锁,或者退一步接受偶尔的冲突——毕竟 claude-mem 本身就是单用户工具,并发场景不算高频。但这个坑让我意识到,任何"追加写入"型工具,只要有可能被并发调用,锁就是必需品。

5.2 模糊搜索命中过多

search "订单"返回 300 条结果,这不算搜索失败,但也几乎等于没用。第一次遇到时我以为是搜索实现有问题,后来发现症结在于:FTS5 默认把 "订单" 匹配到所有包含这两个字的记忆条目,而这些条目里很多只是顺带提到,并非核心主题。

解决办法是给搜索命令加一个排序权重:关键词出现在开头比出现在结尾分值更高,包含关键词的记忆条目比引用了关键词的条目更靠前。另外可以在 query 里用引号做短语匹配,比如search --query "\"订单状态机\"",能显著提升精确度。

实际使用中,我发现对记忆条目做"标签 + 关键词"的组合检索最有效。比如给记忆条目加decision、bugfix、api这些标签,然后 search 时同时过滤标签和关键词,噪声会大幅下降。

5.3 角色标识混乱

--role user和--role assistant如果填反了,记忆库里所有对话方向就反了。这个问题听着低级,但恰恰是高频事故——因为很多时候你是手动复制粘贴对话内容,复制着复制着就分不清哪条是谁说的了。

我的解决方案是:在 record 前先看一眼消息原文的角色标识,不要凭感觉判断。如果批量记录,建议把所有 user 消息放在一起先记,再记 assistant 消息,而不是一条一条交替来,因为交替时最容易贴错。

另一个更隐蔽的问题是:如果你记录的是提炼后的结论,角色其实已经没有意义了。结论是双方讨论共同得出的,不是某一方的发言。这种情况下我干脆用--role summary这类额外角色来标记,检索时看到 summary 就直接知道这是一条结论性记忆,不是对话原文。

5.4 记忆恢复导致上下文暴增

recall --limit 20每条约 50 字,换算下来约 1000 字上下文,完全没有压力。但如果你把 limit 改成 100 或者 200,很快会发现一个问题:记忆本身占用的 token 越来越多,而留给真实对话的空间越来越少。更麻烦的是,久远的低价值记忆会淹没近期的重要决策,Claude 会对"当前重要的事"失去判断。

所以务必控制恢复数量。我实测下来,15 到 25 条是最佳区间。如果真的要回顾很多条,用 search 去定向检索,而不是把几百条一股脑塞进 prefix。

另外,恢复记忆时要按时间倒序取"--记忆越新鲜越重要",这个原则在几乎所有场景下都成立。早期我把升序和倒序搞反过,结果 Claude 的意识还停留在三个月前的旧方案上,白白浪费了半天排查。

5.5 特情:升级工具的迁移问题

claude-mem 更新版本后,JSONL 的字段可能变化,比如新版增加了tags字段,旧数据没有。我的建议是:升级前先备份数据目录,然后用一个一次性脚本把旧文件补成新格式。如果迁移中有复杂的数据转换,不要迷信工具自带迁移命令,先跑一次diff,确认数据没丢。

数据是这整个工具唯一真正不可再生的资产。代码可以重写,模型可以换,但三个月的记忆沉淀一旦丢,就真的是从头来过。我对它的态度一直是:定期留备份,操作前先想清楚再删。

6. 一点个人体会

用 claude-mem 这段时间,我的一个直接感受是,它解决的不是一个高大上的技术问题,而是一个很日常但切肤的问题:你跟 AI 的协作,能不能像跟同事协作一样有连续性和沉淀。以前每开一个新对话,之前的上下文就断了,所有方案、决定、偏好都归零。现在有了记忆层,至少昨天讨论的东西今天还在,上周做过的决定下个月还能查到。

如果你打算自己动手实现一个类似的记忆工具,我的建议是:先做一个最小闭环——只做 record、recall、search 三个命令,存储用 JSONL,外加一个 sqlite 索引。不用一上来就加各种复杂功能。用一段时间,积累真实的使用场景后,再往里面加自动摘要、多会话聚合这些进阶能力。工具的价值是解决问题,不是堆功能,claude-mem 本身的气质也是这样。

再补一个小技巧:把 claude-mem 的命令封装成 shell 别名,能省很多事。比如:

alias cmr='claude-mem record --session' alias cml='claude-mem recall --session' alias cms='claude-mem search --query'

这样实际操作时,手都不需要离开键盘太多,效率提升明显。用顺了之后,你会感觉跟 Claude 的合作方式不知不觉变了——不是每次对话都是"初次见面",而是越来越像一个有积累、有共同记忆的老搭档。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询