
每次跟 Claude 把复杂项目推演到最后一步心里总会咯噔一下如果这时候把会话窗口关掉整个推演过程是不是就彻底消失了这是 API 用户的日常烦恼上下文从来都不是长久记忆。痛定思痛我给自己手搓了一个叫claude-mem的本地记忆组件专门解决 Claude 跨会话失去记忆的问题。它拦下了会话中的数据流按对话片段存档用的时候直接检索这样它就能自己带着上一次的结论往下走而不是每次都在空白状态里裸奔。尤其受用的场景是 Claude API 和 Claude Code 的重度使用——凡是被上下文长度限制逼疯过的开发者都应该试试给 Claude 装上这套记忆外挂。1. 整体设计与思路拆解1.1 为什么大模型必须外挂记忆很多第一次接触claude-mem的人都会问Claude 不是有 20 万 token 上下文吗为什么还要额外存一份记忆现实是API 调用基本都是无状态的。你发出请求模型处理完返回结果这段交互就算结束了。下一次对话它对你的项目背景、偏好写法、此前结论一概不知。Token 窗口看着很大但真实业务里根本不够用。长文档分析、跨天迭代、多轮 API 串联任何一个场景都会把窗口顶满。就算你有办法在每次请求时把历史消息全部重发一遍成本和延迟也跟着翻倍最终还是会撞上长度上限。与其依赖这种笨办法不如把真正的“长期记忆”放在一个独立存储里每次只把相关的片段抽出来扔进上下文。这正是claude-mem存在的意义给会话分层。当前对话负责短暂的注意力本地仓库负责长久的沉淀。1.2 为什么用检索而不是暴力重放给 Claude 做记忆最原始的思路是把所有历史记录连原文带结论一股脑拼进 Prompt。这听起来最直接但实际跑起来全是坑。一是 token 成本炸裂存了几十万条日志之后每次请求的输入长度都变成了灾难。二是无关信息会严重干扰模型的注意力检索出 30 条记录里面只有 2 条跟当前问题相关Claude 很可能会被无关内容带偏。claude-mem采用了检索增强的思路平时只负责把对话按结构存好等需要记忆的时候先从本地库里把与当前问题最相关的若干片段捞出来注入到新一轮对话中。检索源可以关键词匹配、全文索引也可以做简单的向量相似度计算。我在最初版本里没有引入重型向量库因为早期最大的难点根本不在算法而在于把“存储-索引-召回”这个链路跑顺。先解决有没有记忆再追求记忆得准不准。1.3 存储层技术选型SQLite 反而最合适设计阶段我对比过几种方案。上 PostgreSQL 太重还得维护一个常驻服务。用 Redis 做纯内存存储则不够持久重启一次就全没了。最终选了 SQLite。这个决定看起来朴素但对个人级工具来说收益极高单文件即数据库备份只需要复制一个文件几乎没有运维负担并发读写虽然不如 PostgreSQL 凶猛但独立使用完全够用。项目的数据目录长这样~/.claude-mem/ ├── db.sqlite # 核心数据库会话、消息、事件 ├── index/ # 倒排索引与分词缓存 ├── logs/ # 原始交互 JSONL 日志 └── config.json # 配置模型、检索参数、最大召回条数SQLite 在意外的进程崩溃场景下表现也很稳。Write Ahead Log 模式打开之后即使写入中途断电也不会出现整个库损坏的灾难现场。如果你的场景是自用、小团队共享、或者部署在单台服务器上这套组合比任何分布式数据库都省心。2. 核心细节解析与实操要点2.1 数据模型会话、消息、标签记忆系统最怕存成一团浆糊。我见过有人把整段对话 JSON 原封不动丢进数据表检索的时候只能靠 LIKE 模糊匹配效果奇差。claude-mem在存储层强制成型用三张核心表组织数据。Session会话表记录一次完整的交互过程字段包含会话 ID、时间戳、来源API 或 Claude Code、摘要标签。Message消息表每一条用户问题、模型回复、工具调用结果都单独入库挂在对应的 Session 下。Tag标签表给关键业务实体打标例如Bug、性能、支付、登录方便快速筛选。这里面最容易被忽略的是消息的元数据存档。光存字符串不够还得把模型名称、token 用量、生成时的参数快照也记下来。因为不同模型处理同样问题的结论可能不同后续做数据回溯、审计时这些元数据能省去你大量连蒙带猜的时间。消息表的字段设计参考CREATE TABLE messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, role TEXT NOT NULL CHECK (role IN (user, assistant, system, tool)), content TEXT NOT NULL, token_count INTEGER DEFAULT 0, model TEXT DEFAULT , created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );2.2 全文索引与检索召回策略核心难点在于“怎么把刚存的信息捞回来”。我用的是轻量级分词加倒排索引。中文环境下直接按字拆分会导致召回结果太碎按词拆分又依赖词典质量。claude-mem默认采取组合策略英文按空格拆分中文按二元词组切分再统一构建倒排索引。这套方案不依赖外部词库工程实现简单召回效果实测足够稳健。检索链路分三步走。先把当前 Prompt 做同样切分得到一组查询词再到索引表里面找命中的消息 ID最后按 BM25 相关度从高到低排序取前 N 条注入上下文。def search(query: str, limit: int 5): tokens tokenize(query) scores {} for token in tokens: for msg_id, weight in inverted_index.get(token, []): scores[msg_id] scores.get(msg_id, 0) weight ranked sorted(scores.items(), keylambda x: x[1], reverseTrue) return fetch_messages([msg_id for msg_id, _ in ranked[:limit]])如果你对准确率要求更高可以在这个基础上叠加向量召回。常见做法是用一个小型 embedding 模型把文本切块、向量化近似匹配再把向量召回与关键词召回做 merge。但要特别注意向量化非常吃算力本地跑一次 embedding 平均要几十毫秒如果只是每天写代码时用完全没有必要引入这个复杂度。2.3 记忆注入如何优雅地塞进上下文存得再好最终都需要把召回结果交给 Claude。最简洁的方式是拼在 System Prompt 中让模型把检索内容当作参考资料。在 API 场景下我习惯留出一个独立的memory字段把检索结果格式化后拼到对话开头。[memory_context] 你之前在处理一个用户反馈分类项目结论是优先使用规则引擎处理常见类别仅将模糊样本交给 LLM 判断。 在这个项目里你使用过 Python FastAPI SQLite 的技术栈。 相关记录日期2025-05-12 [/memory_context]格式化的关键点是保持信息密度低不要丢一大段日志进去。Claude 里上下文里放的是“结论”和“背景提要”不是原始流水账。一条好的记忆消息应该像便利贴简短一两句让模型知道“事情是什么”而具体的处理细节如果没被命中就说明它根本不重要。3. 实操过程与核心环节实现3.1 安装与初始化claude-mem本质上是一个本地命令行工具加一个 MCP 服务端。安装直接走 Python 包管理pip install claude-mem claude-mem initinit 命令会做三件事创建目录结构、初始化 SQLite 数据库、生成一份默认配置文件。配置文件里的核心参数是retrieval_k默认 5和session_ttl_days默认 30。前者决定每次从记忆里捞多少条相关片段后者控制数据自动清理的周期避免库无限膨胀。3.2 核心命令实操record 与 recall用命令行把一次对话存档最简单的就是直接传文本claude-mem record --session proj-123 --role user 登录接口超时怀疑是数据库连接池满了 claude-mem record --session proj-123 --role assistant 已排查连接池最大连接数设置为 20建议改为 50 并增加监控也可以直接把整个 JSONL 文件批处理导入。批量导入适合从 API 日志做回放归档能让历史对话一夜之间变成可检索记忆。要调用记忆时使用 recallclaude-mem recall 数据库连接池超时问题排查结论命令会返回与这个问题最相关的那几个片段包括原始消息、关联会话 ID、时间戳。这样你就可以在下一步 Prompt 里引用这些片段Claude 会合理地把它当作自己的“过往经验”。3.3 与 Claude Code 的无缝集成Claude Code 本身就是为代理式编码设计的claude-mem本质上补的正是它的记忆短板。集成方式有两种。第一种通过 MCP 协议挂载记忆工具。在.claude-code/config.json中注册 MCP 服务器{ mcpServers: { claude-mem: { command: claude-mem, args: [server], env: { CLAUDE_MEM_HOME: ~/.claude-mem } } } }这样 Claude Code 在会话过程中可以主动调用claude_mem_record和claude_mem_recall这两个工具接口。它能在每次完成任务后自动做一次头脑存档也能在需要过往决策时主动查询。第二种在系统 Prompt 里注入触发指令。比如这样写在开始任何修改前如果项目历史库中存在相关记忆请先调用 claude_mem_recall 查询。 在完成阶段性任务后请主动调用 claude_mem_record 保存关键结论。两种方式可以同时使用。实际跑下来第二种更容易控制记忆的“颗粒度”Claude 会按你的指令决定什么值得记什么不必记不会把每行日志都存下来造成污染。4. 常见问题与排查技巧实录4.1 检索不到想要的内容怎么办这个问题几乎人人都遇到过。顺手排查三步走第一步确认消息是否真的入库直接看日志文件有没有写入第二步检查分词结果有时候是词表没覆盖导致索引缺失第三步测试不同查询词——中文口语和书面语的索引特征完全不一样。踩过几次坑之后我发现claude-mem对关键词抽取比较敏感用“登录接口超时排查结论”检索效果远比问一句“之前那个 bug 查得怎么样了”好得多。如果你想提高召回率最简单有效的方法是记录时多写两个标签给每条消息主动打业务实体标记不依赖裸文本匹配。4.2 并发写入导致的数据库锁SQLite 在同时有大批量写入时容易报database is locked。常见于批量导入历史日志或者 Claude Code 多个任务并行执行时。解决方法是在连接层启用 WAL 模式并把 busy_timeout 调大到 5000 毫秒import sqlite3 conn sqlite3.connect(db.sqlite) conn.execute(PRAGMA journal_modeWAL) conn.execute(PRAGMA busy_timeout5000)WAL 模式让读写并行批量导入过程里其他读取操作基本不会感知到锁的存在。如果你的并发量更大就需要在上层加一个简单的写队列保证同一时间只有一个写任务接入数据库。4.3 内存库被无关数据污染长期使用的库会被大量无效记忆淹没这是所有记忆工具的宿命。我见过有人存了三个月日志库里有几万条消息召回的内容却越来越像噪声。原因很简单模型记录时不克制什么过程都往库里塞。给记忆做净化是必不可少的一环。claude-mem提供了forget命令可以直接按 session、标签甚至内容关键词删除记录。但我建议更主动一点记录时只存“事实型结论”和“可复用的经验”过程型数据、临时排错信息主动过滤掉。还可以结合定期自动化清理把超过 30 天且从未被召回过的低价值消息批量清理掉。4.4 记忆污染导致的模型误判当召回结果显示“登录接口超时”时Claude 可能会把过去某个项目里支付回调也超时的结论混进来。这类问题是跨主题误关联。排查思路是打好隔离层在格式化注入内容时附加会话标签和项目名让模型理解这条记忆属于哪个上下文。我在格式化模板里加入过明确的project_scope字段模型在回答时如果发现记忆属于无关项目就会主动忽略而不是强行套用。4.5 记忆工具本身被 Claude Code 反复误调用模型偶尔会莫名其妙触发一堆claude_mem_recall调用导致工具输出占据大量上下文。规避办法是给工具描述写清楚触发条件。我试过最好的描述模板是{ name: claude_mem_recall, description: 仅在需要结合历史结论、排查历史 bug、引用既有架构决策时调用。对于一般性的编程问题不要主动调用。 }只有让模型明确知道什么时候不应该用才不会每次都背着整个历史库工作。5. 进阶玩法与实际体验心得5.1 用 claude-mem 做项目交接文档最大的惊喜来自“项目交接”场景。团队里换人维护代码时传统做法是写一坨 README 或者口头交代费力且容易遗漏。现在我会把之前的开发记录通过recall拉出来把关键决策脉络直接拼到交接文档里。新人读到的不再是“为什么当初这么设计”的二手转述而是 Claude 在开发过程中记录下来的第一手结论接手的效率高了很多。5.2 给记忆加上业务分层个人工具和业务级工具的记忆诉求差异很大。个人使用可以只存结论但业务级场景我会强行把记忆库按模块隔离。比较实用的做法是通过 Session 元数据里的project_name区分不同业务线每个项目独立建库而不是所有东西混在一个库里。检索层面也要加上强制过滤比如当前在改支付模块就不要把订单模块的历史记录捞出来。5.3 记忆库的备份与迁移既然数据都存在单个 SQLite 文件里备份就变得异常简单。我写了一个小的 cron 任务每天把~/.claude-mem/db.sqlite和index/目录一起打包推送到对象存储。迁移环境时只要把目录恢复回去老记忆就全部可以继续用。有一点要注意如果你启用了向量召回索引目录里的 embedding 向量也必须同步备份否则索引与库内容可能对不上。5.4 沉淀高质量记忆的通用规则最终分享一下我个人实操沉淀下来的三条规则。第一条只记录“不再变的事实”写死的结论才值得存探索过程的临时路径不要留。第二条每条记忆都要考虑“未来如何被找到”尽量写清楚业务实体、场景关键词、结论例如“日志脱敏方案使用正则替换手机号保留前三位后四位”就比“脱敏处理完成”更容易被检索。第三条定期做记忆清理把那些从未被召回过、已经过时的条目清掉保持库的新鲜度。这套工具跑下来我最大的体会是给模型外挂记忆本质上是给项目外挂团队记忆。它让对话真正沉淀成资产让每一次开发过程都不再从零开始。现在我打开任何项目第一件事就是claude-mem init然后让 Claude 带着过去所有决策往前走这种感觉踏实多了。