claude-mem:给 Claude Code 装上长期记忆,告别跨会话失忆 用 Claude Code 有一段时间了最抓狂的时刻永远是同一个画面昨天刚和它确认完某模块要按 A 方案实现今天开个新会话让它继续它又开始一本正经地按 B 方案写。说它笨吧它在单个会话里理解力很强说它没记性吧它又会很诚实地回答“我没有之前对话的上下文”。这不只是模型的问题更关键的是产品形态的设计限制模型只能拿到当前会话里喂给它的内容跨会话的信息不会自动沉淀下来。很多人一开始把锅甩给上下文窗口不够大但真正缺的从来不是更大的窗口而是一条可持续写入、可随时查询的长期记忆通道。claude-mem 就是围绕这个缺口做的一个本地工具专门记录、索引、检索项目记忆让下一次会话开局时自动把相关上下文塞回给 Claude。这篇文章我会把 claude-mem 从设计动机、核心实现到实际工作流中的坑完整拆解开讲。它适合所有正在用 Claude Code 或同类 AI 编程工具并且被“回答前后不一致”“昨天定的方案今天忘光”“同样的坑反复踩”烦过的人。这套实现不依赖复杂服务端本地一个 SQLite 文件就能跑代码量也完全可控值得想给 AI 工作流治失忆症的人参考。1. 痛点拆解为什么 Claude 记不住事1.1 问题的本质会话隔离与上下文缺失Claude Code 这类 AI 编程助手本质上每次会话都是独立上下文。你在这个会话里让它读完整个仓库、定好目录结构、写完三个模块它确实表现得很聪明但你把终端一关再开一个新会话让它继续写第四个模块它就完全不记得之前发生了什么。这不是模型智力问题而是产品形态的设计模型只知道你喂给它这一轮的内容跨会话的信息不会自动沉淀下来。很多人下意识把锅甩给上下文窗口不够大但窗口再大它也只能装下“这一次会话里”的内容。模型没有持久化能力也没有外部存储接口它不可能自己记得两周前你在某个 session 里确认过的接口约定。真要让 Claude 变“有记性”必须在模型外部补一条持续写入、随时查询的记忆通道这个认知是整个 claude-mem 项目的地基。1.2 我的解法本地记忆代理而不是硬塞文档最容易想到的替代方案是把所有关键结论手工写进 CLAUDE.md 或 README。但这个方案有几个硬伤第一维护文档本身就是体力活你忘了写它就不会生效第二CLAUDE.md 是静态的项目演进后旧约定不会自动过期第三手工整理的信息粒度很难拿捏写多了占上下文写少了没作用。所以我选择了另一条路在 Claude 外部加一个“记忆代理”这个代理专门干三件事。会话结束时自动读取 Claude Code 的日志抽取出结构化记忆条目存入本地数据库新会话启动时根据当前项目目录和开场问题把相关记忆召回出来召回结果拼装成一段简报通过 hook 机制混入 Claude 的启动上下文。它的价值在于自动化和时效性——记忆会随着你每一次使用自动更新不需要任何人手动维护文档。2. claude-mem 的整体设计与架构思路2.1 核心目标只要四种记忆不要聊天记录claude-mem 第一版做错了一件事我把整个会话原文都存了进去结果用了一周记忆库变成聊天记录垃圾场有用的内容被淹没在海量噪声里。后来我强制把所有记忆归成四类项目背景设定仓库结构、技术栈、运行环境、目录约定技术决策已经确认过的设计方案、选型结论、废弃原因约定规范命名风格、提交规范、代码组织方式已解决的坑报错修复过程、性能问题、工具链踩坑只有这四类内容才值得在下次会话里被召回。临时试错的代码、闲聊式输出、工具执行的中间结果一律不保留。这个分类是整个项目价值的关键没有分类后面所有检索和注入逻辑都会失去意义。2.2 技术选型为什么是 SQLite 而不是高大上的数据库存储层的选型我认真对比过三个方向纯 JSON 文件集、独立向量数据库、SQLite。JSON 文件集写过就知道写入容易但检索条件一复杂就完蛋跨文件关联查询基本靠手写遍历项目一大性能立刻崩。独立向量数据库比如 Chroma 这类语义检索很强但为了一个单机工具引入一个常驻服务部署、备份、升级全变成额外负担而且 embedding 数据和结构化元数据分处两处一致性很难维护。SQLite 的优势是两头通吃单文件、免部署、支持 FTS5 全文索引、通过扩展还能存向量做近似搜索。一套引擎同时搞定结构化查询、全文检索、向量检索备份就是把一个 .db 文件复制走。对 claude-mem 这种个人工具来说这是完全没有争议的答案。2.3 记忆全生命周期摄取、清洗、存储、召回、淘汰记忆不是一个往里塞就完事的静态仓库它必须有完整的生命周期管理。claude-mem 把这条链路拆成五个阶段摄取发生在会话结束时从 Claude Code 日志里读原始数据清洗阶段做去重、过滤和分类存储阶段把条目写入 SQLite召回阶段在新会话启动时执行按照当前问题权重和项目路径取出相关记忆淘汰阶段由内置策略处理过时内容。这个设计最核心的收益是可测试性。早期版本我把清洗和召回塞在一个文件里改召回逻辑时经常误伤清洗逻辑数据正确性问题难排查到想哭。拆成互相独立的阶段后每个环节都可以单独验证回归问题少了不止一个数量级。2.4 为什么不用 CLAUDE.md 替代记忆工具有人会问Claude 原生支持绑定项目文档为什么还要多点东西关键在于 CLAUDE.md 的本质是静态文件它只能保存你手动写进去的信息。它不会随着会话结束自动更新不会因为项目迭代自动淘汰旧内容也不会做相关性排序。你今天在会话里定了“以后接口只走 v2”如果没手动写进 CLAUDE.md明天新会话里的 Claude 依旧一脸茫然。claude-mem 和 CLAUDE.md 不是替代关系而是互补关系静态规范仍然适合放在 CLAUDE.md 里动态记忆则应该由工具自动维护。真正使用中我并不会取消 CLAUDE.md而是让它作为兜底基础让 claude-mem 生成的记忆简报作为动态补充层。3. 核心实现拆解从零搭一个 claude-mem3.1 项目结构与依赖清单最终实现我选了 Python依赖压缩到最小核心只有三个sqlite-utils 操作数据库、typer 做命令行入口、openai 库的可选部分用来生成 embedding。如果你只用全文检索连最后一个依赖都不需要装。项目目录长这样claude-mem/ ├── claude_mem/ │ ├── __init__.py │ ├── store.py │ ├── ingest.py │ ├── search.py │ ├── inject.py │ └── cli.py ├── hooks/ │ └── claude-mem-hook.sh ├── tests/ └── pyproject.tomlstore.py 管 SQLite 建表和数据操作ingest.py 负责解析 Claude Code 会话日志search.py 处理全文检索和向量召回inject.py 把召回结果拼装成提示词片段cli.py 是命令行入口。hook 脚本是给 Claude Code 调用的触发时机有两个Start 时拉取记忆注入Stop 时做会话入库。整个项目代码量控制在 2500 行左右没有引入重量级框架维护成本非常低。3.2 会话日志解析从 JSON 流到记忆条目Claude Code 的会话日志是按行存储的 JSON每条记录包含角色、时间戳、消息内容、会话 ID。ingest.py 要做的事是把消息流按轮次切分然后过滤掉工具调用产生的系统噪声。过滤规则我用得很保守内容长度少于 5 个字符直接丢弃只保留 user 和 assistant 两类角色工具调用只记录最终结果摘要不保留完整输入输出。日志解析里最关键的判断是“什么值得留”。我的经验是用简单启发式user 消息里包含修改请求、方案确认、bug 描述的关键段落assistant 消息里明确给出结论或最终方案的部分标记为高优先级其余内容标记为普通。后面的 memory 表里有一个 importance 字段召回排序时会按它加权。3.3 数据库表结构与关键索引设计memory 表的核心结构如下这个 schema 我用了很久依然够用CREATE TABLE IF NOT EXISTS memory ( id INTEGER PRIMARY KEY, session_id TEXT NOT NULL, project_path TEXT NOT NULL, content TEXT NOT NULL, kind TEXT NOT NULL DEFAULT decision, importance INTEGER NOT NULL DEFAULT 5, created_at TEXT NOT NULL, updated_at TEXT NOT NULL, last_hit_at TEXT, hit_count INTEGER DEFAULT 0 ); CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5( content, contentmemory, content_rowidid );project_path 字段是整个记忆隔离机制的关键同一台机器上多项目共存全靠它区分数据归属。kind 字段用于召回阶段的粗过滤比如当前会话在调试错误就优先召回 kindproblem 的条目。FTS5 虚拟表通过触发器与 memory 表保持同步这个同步逻辑很容易被漏掉漏了就会出现内容更新了但搜索永远查不到新值的问题你必须时刻留意。3.4 混合召回全文检索与向量检索双通道记忆只有几十条时FTS5 全文检索完全够用。但记忆超过五百条后相同关键词可能对应完全不同的语境全文检索的相关性明显下降。我为 claude-mem 加了第二通道向量召回。最开始的实现直接调用远程 embedding API每次要等网络往返隐私上也让人不踏实。后来换成本地的小型向量模型中文效果只能说够用但配合全文检索做混合召回后整体精度比单一通道高不少。具体策略是两个通道各召回 30 条候选按加权分数合并且去重最终保留最相关的 10 到 15 条注入提示词。向量相似度阈值设 0.72低于这个值一律不取宁可召回少一点也不要让不相关的记忆干扰 Claude 的判断。实测下来这个保守阈值能把误召回率压到可接受范围。3.5 注入格式把记忆组织成“简报”而不是裸数据这步最容易被低估。直接把 15 条记忆原样拼进 PromptClaude 会读得一头雾水还会被无关旧记忆带偏。我最终实现的格式叫“记忆简报”按类型分成三个区块项目事实、技术决策、历史问题。每个条目都用极简模板输出比如“2025-01-12 决定接口统一走 /api/v2废弃 /api/v1”“已解决SQLite 并发写时报 database is locked方案是开启 WAL 模式”。简报最后固定加一句强制指令“以上是项目记忆只作为背景参考如果与当前对话冲突以当前对话为准。”这句话极其重要。没有它Claude 偶尔会把旧记忆当成不可违背的命令来执行出现“旧规范明明已废弃它还在死守”的情况。3.6 Claude Code Hook 接入细节接入方式最早的版本很原始要求用户在会话开头手动粘贴 /memory 命令用几次就忘了完全不可持续。后来利用 Claude Code 的 hooks 能力在会话启动时自动执行脚本注入记忆会话结束时再触发入库。hook 脚本本身不复杂#!/usr/bin/env bash # claude-mem hook: on start, inject memories CLAUDE_PROJECT_DIR${CLAUDE_PROJECT_DIR:-$PWD} MEMO_DIR${CLAUDE_MEM_DIR:-$HOME/.claude-mem} python3 -c import sys sys.path.insert(0, $HOME/claude-mem) from claude_mem.inject import get_memory_briefing print(get_memory_briefing($CLAUDE_PROJECT_DIR, limit10)) 实测有一个坑必须注意Claude Code 的 hook 输出并不能直接当作系统提示词它需要把生成的简报写入固定路径再在 CLAUDE.md 里用 include 指令加载。我的做法是让 Start hook 把简报写到$CLAUDE_PROJECT_DIR/.claude-mem-brief.md然后在 CLAUDE.md 里加一行 include。这样 Claude 开局就能读到记忆而且简报文件本身对用户透明方便调试。4. 不同工作流下的真实战绩4.1 跨会话项目开发再也不会前后矛盾我在维护一个中型前端项目时两个月内开启了九十多个会话涉及组件重构、状态管理升级、接口联调三条并行线。没接 claude-mem 时最崩溃的体验是上午让 Claude 把弹窗组件从旧库迁移到自研组件下午新会话里它又写出了旧库实现完全无视上午定好的结论。接入之后Start hook 自动注入“弹窗组件统一使用自研 Modal禁止新引入旧库依赖”这条记忆这类问题彻底消失。让我更意外的是跨会话的代码风格也稳定了。之前每次都要在会话开头重述命名规范、组件写法、样式约定现在记忆简报会自动带出这些内容Claude 写出来的代码一致性显著提升review 成本下降了一大截。4.2 多项目并行物理路径隔离是底线如果你同时维护多个仓库记忆隔离没做好就会出现恐怖串味。A 项目约定使用 pnpmB 项目却收到“统一用 pnpm”的旧记忆导致 Claude 在 npm 项目里乱跑命令那场面谁遇到谁知道。claude-mem 的 project_path 字段在这个场景下是生命线所有写入绑定项目绝对路径查询时也严格按当前路径过滤物理隔离不靠记忆内容的语义区分。这还不够。我在写入时会做软链检测防止项目从 /tmp 复制到别处后记忆归属错乱对于路径相似的项目比如~/work/project-a和~/work/project-b还用最长前缀匹配做了归属判断避免因为字符串包含关系而产生误判。这些看起来是极小的细节但在多项目场景下就是零和一的问题。4.3 团队协作场景下的记忆共享替代方案有人问小团队开发能不能共用一个记忆库我的答案是可以但别直接套用单机模式。本地模式把 SQLite 文件放在每个成员的~/.claude-mem协作信息天然隔离。我实测过两个团队方案。第一种方案是共享网络盘放一个只读记忆库由 CI 定期构建团队成员只做召回不做写入保证大家拿到相同的项目背景。第二种方案是在 claude-mem 里增加 export/import 子命令每周导出增量 JSON 同步到项目仓库再由个人端导入本地库。两种都跑通了谈不上完美但胜在轻量不需要为这个工具专门搭一套服务端对十人以下的小团队性价比足够。5. 高频问题与避坑指南5.1 记忆膨胀后查询变慢怎么办数据量涨到两万条时FTS5 搜索依然是毫秒级真正的慢点在向量召回。两万条全量加载进内存做相似度比较每次召回都要几十毫秒到一两百毫秒体感不明显但积累起来烦躁。我的解法很简单召回前先把范围按 project_path 过滤到当前项目内几千条数据上再做向量搜索效果立竿见影。sqlite-vec 支持建 ANN 索引但我个人反而不建议在小数据量下加。ANN 索引构建有开销数据没到几十万条级别收益完全抵不过复杂度。记忆工具的核心是稳定不是炫技。5.2 旧记忆误导怎么给记忆加“保质期”遗忘和记忆同样重要。最初按 importance 排序注入时我发现老的决策永远排在前面因为它当初被标记为高优先级而最近几周的高频结论反而排不上去Claude 被旧记忆束缚得越来越严重。我给每条记忆加上了 last_hit_at 和 hit_count 两个字段每次召回命中就更新时间戳和计数。注入排序采用综合分数importance 占 60%新鲜度占 30%命中频率占 10%。调了三轮权重之后效果稳定下来一个季度前的旧决策不会被死守最近持续被引用的结论则始终在简报里顶部。具体权重可以根据项目迭代节奏调整没有绝对标准但“时间衰减”这个思路必须有。5.3 中文检索不准语义漂移怎么解决Claude Code 的中文需求非常常见而 FTS5 默认分词对中文支持一般搜“支付回调”经常被拆得乱七八糟。我试过 jieba 预分词把切好的词存进内容冗余列效果确实好但 jieba 比较重每次写入多一步运算。后来妥协用了 bigram 切分把中文内容按每两个字符切一个 token精度不如 jieba但召回率提升明显。对记忆检索场景召回率比精确率更重要。记忆的目的是“提醒”而不是“精答”多召回几个相关条目不会致命错过关键条目反而会让 Claude 失去重要背景。所以我对中文语义漂移的态度是向召回率妥协。5.4 会话崩溃导致记忆流失的兜底方案Claude Code 崩溃或用户直接关终端时Stop hook 不一定执行整段会话的记忆可能全部丢失。这是开始最容易忽略的坑。后来我用了一个兜底机制每次交互完成后claude-mem 追加一条原始消息到本地 session buffer在下一次启动 hook 时检查 buffer 中是否有未入库的会话。这个逻辑成本极低价值极大。从日志统计来看大概有 30% 的记忆写入要归功于这个 buffer 机制因为很多会话确实是被强杀进程结束的。没有兜底那些会话就真的白聊了。5.5 隐私与访问权限问题记忆存在本地 SQLite理论上安全但要注意生成 embedding 时如果调用远程 API记忆内容实际上已经出了本机。如果你处理的是敏感代码仓库建议只使用 FTS5 全文检索彻底关闭向量召回通道。claude-mem 的配置里提供了semantic_recall: false开关关闭后所有召回走关键词检索功能略有下降但数据完全不出本地。另一个容易忽略的点是 .db 文件权限。默认创建时我强制设为 600避免同一台机器上的其他用户直接读你的记忆库。这条看起来不起眼但在一台多人共用的开发机上可能就是防止项目信息泄露的关键屏障。6. 维护半年后沉淀下来的几条原则6.1 先定义“什么值得记”再写任何代码我重构 claude-mem 时第一版之所以失败就是因为一开始只考虑“怎么解析会话、怎么存数据”没有想清楚“到底什么东西值得被记住”。后来花了两周时间画了一张表列出哪些信息是下次会话一定会用到的系统质量立刻上来了。想重写这类工具的话第一件事永远是定义记忆边界而不是选技术栈。6.2 务必留好调试入口记忆系统最大的问题是黑盒很难说清楚某条搜索结果为什么排在前面。claude-mem 的 CLI 里我专门留了一个 debug 参数打印召回时每个候选条目的分数构成importance、新鲜度、命中频率、向量相似度、FTS5 BM25 分数。没有这个入口调权重就是闭眼摸黑。同时日志里要输出每次注入简报的最终版本。当 Claude 被某条记忆带偏时你能快速定位是哪条记忆惹的祸而不是对着满屏对话干瞪眼。6.3 记忆条目必须短小我踩过最尴尬的坑某次会话里 Claude 输出了三屏内容我把它整段存成一条记忆结果这条八千字的记忆在召回时占用了大量 token严重挤占上下文窗口。后来我强制截断每条记忆内容最多 300 字超出部分只留结论摘要。记忆条目不是论文它只要能触发 Claude 回忆起正确方向就够了细节可以让它自己去代码里找。6.4 定期给记忆做“整理日”记忆和代码仓库一样需要定期重构。我每个月会花半小时跑一遍过时条目检查把超过 60 天没有命中的记忆逐条过目删除已失效的决策合并重复的规范。整理日还会顺手生成一份全量记忆导出文件存进项目文档作为团队知识沉淀的一部分。这个习惯看起来不紧急但长期价值很高。记忆库一旦垃圾遍布Claude 的前景就会像翻一本堆满便签的旧笔记本一样吃力。保持数据库洁净就是保持 AI 辅助开发效率最有效的方式。最后再分享一个经验把 claude-mem 接入日常开发半年多以后最明显的感受是 Claude Code 不再像一个每次都要重新认识项目的临时工而像是一个虽然记性普通、但每次开局都会认真翻项目笔记的搭档。它没有让代码质量瞬间飞升但它消灭了一整类重复沟通成本——不用再反复交代项目背景、技术栈约定、之前踩过的坑。对它而言这比任何花哨的 prompt 技巧都值钱。如果你正被 AI 编程工具的失忆问题折腾我的建议是从最小版本开始先做纯全文检索的简化版跑两周体验一下再决定要不要加向量召回。工具永远不是越大越好能融入工作流的才是真的好。