
如果你用 Claude 做过长期项目一定被同一个问题折磨过它记不住昨天的事。今天聊的这个项目 claude-mem就是专门给 Claude 加一块“外置记忆”的 CLI 工具。它把每一次会话中值得保留的信息抽出来存进本地 SQLite下次对话启动时把相关记忆自动注入到语境里让模型真的能“想起来”你上个礼拜交代过的偏好和约束。我用了大概一周时间把 claude-mem 接进了日常开发流也顺手翻了它的源码。这个项目解决的痛点非常明确Claude 本身是无状态的每开一次新对话之前的上下文全部归零。你当然可以手动把旧内容贴回去但对话一长就复制到崩溃。claude-mem 的思路是“记忆外置”——不依赖模型自身长上下文而是用工具帮你把记忆沉淀下来按需取用。这篇文章我会从设计思路、原理拆解、实操接入一直讲到踩坑记录适合正在用 Claude API 或 Claude Code 写项目的人参考也适合所有对“LLM 记忆增强”感兴趣的人。不管你懂不懂代码看完都能明白它是怎么转的。1. 项目核心思路为什么要给 Claude 做一块外置记忆1.1 模型无状态是痛点的根源大模型本身就像一个大脑清零的天才知识丰富推理快但聊完就忘。每次会话结束刚才的上下文就被丢掉了。这在闲聊场景没事但在真实项目里非常致命。比如我在维护一个开源文档项目里面有几十个文件的命名约定、术语表、目录结构。每次我开新对话跟 Claude 说“接着改一下第三章节的措辞”它根本不知道第三章节在哪也不知道“术语表”里那几个词是什么。我试过几种土办法把整个项目背景复制粘贴进每条新消息把常用约束写进系统提示词文件甚至有人把历史对话导出成 Markdown再手动喂给模型。这些方案都能跑但维护成本极高——背景一长token 就爆文件一多模型就分不清哪些是当前任务、哪些是历史包袱。这时候就需要一个能自动沉淀、自动检索、自动注入记忆的工具。claude-mem 做的就是这个。1.2 记忆放本地不折腾云服务市面上很多“记忆增强”方案会把记忆存到云端用向量数据库、Embedding API、云函数听着高大上实际用起来配置一大堆。claude-mem 的思路是本地优先一个 SQLite 文件搞定存储FTS5 全文索引搞定检索。你不需要起任何服务不需要注册任何云账号装好直接用。SQLite 这个选择我觉得很聪明。Python 自带的 sqlite3 就能操作Go、Rust 也都有官方库部署时零依赖。FTS5 的全文检索配合 BM25 排序在几万条记忆的规模下响应速度毫秒级完全够用。很多项目一上来就上向量检索本质上是对记忆检索的复杂度误判了个人项目的记忆量级根本到不了需要向量数据库的地步关键词匹配加上时间衰减排序效果已经不错。等真到了几十万条再迁移也不是难事。1.3 整体工作流程先写库再读库claude-mem 的运转逻辑可以概括成两条链路。写入链路一次对话结束后你或一个钩子脚本把对话内容喂给 claude-mem它会对原始记录做拆解提取问题、回答、结论、约束、待办压成一条结构化的记忆记录然后写进 SQLite。读取链路新对话开始前claude-mem 会拿当前对话的开场信息去库里检索把命中条目按相关度和时间排序拼成一个“记忆上下文块”注入到系统提示词或首条用户消息里。这两条链路一个向东、一个向西互不干涉但组合起来就形成了一个完整闭环。你可能会问为什么不直接把整个历史对话塞进上下文答案是 token 预算不允许而且噪音太大。一个项目聊了 200 轮里面八成是寒暄和过程性内容真正有价值的信息可能只占两成。claude-mem 做的事情本质上是“蒸馏”把两成的价值提出来把八成的噪音扔掉。这样既保留了记忆又不会挤占当前任务的上下文空间。2. 核心机制拆解记忆是怎么被写入、检索和注入的2.1 写入链路从流水账到结构化记忆claude-mem 的写入不是简单把聊天记录整段存进去而是做了结构化处理。一条记忆记录我打开数据库看大概长这样CREATE TABLE memories ( id INTEGER PRIMARY KEY AUTOINCREMENT, project TEXT NOT NULL, -- 所属项目 session_id TEXT, -- 来源会话 created_at TEXT NOT NULL, -- 时间戳 updated_at TEXT, -- 更新时间 question TEXT, -- 触发场景 answer TEXT, -- 关键回答 summary TEXT NOT NULL, -- 蒸馏后的摘要 importance INTEGER DEFAULT 3, -- 重要度 1-5 tags TEXT DEFAULT [] -- 标签 JSON );写入时它会做三件事。第一给原始对话分段拆成“问题-回答”对第二对每一对做摘要把长度压到 200 字以内第三按规则打上重要度和标签。你可以手动调用claude-mem add添加记忆也可以在脚本里让它自动抽取。我建议在对话结束后自己快速过一遍抽取结果——AI 自动摘要偶尔会把关键约束漏掉手动纠偏值得的。2.2 检索链路SQLite FTS5 帮了大忙读取时最怕的就是找不到。claude-mem 的检索默认走 FTS5 全文索引。关键词先被分词器切碎然后走 BM25 算法打分返回一条条候选记录。我翻源码时看到查询大概长这样SELECT id, project, summary, importance, created_at, bm25(memories_fts) AS score FROM memories_fts JOIN memories ON memories_fts.rowid memories.id WHERE memories_fts MATCH ? ORDER BY score DESC LIMIT 20;这个MATCH ?支持布尔语法比如含义 AND 目录或章节 OR 目录。BM25 打分是全文检索的事实标准它会把“词频高但常见词”降权把“稀疏但精准的词”提高权重。claude-mem 在基础上还做了二次排序把importance和时间衰减算进去新近且重要的记忆排前面这样即便关键词匹配一般也能靠时间权重抬上来。2.3 记忆注入的模板设计光检索出来还不够怎么塞回模型上下文也有讲究。claude-mem 默认的注入模板长这样以下是过去关闭的项目记忆中可能与本次对话相关的部分。 请在回答时优先参考以下事实如果它们与本次对话冲突请明确指出冲突点。 memories memory time2025-01-12 projectdocs-site importance5 用户坚持目录文件命名统一使用 kebab-case不要驼峰。 /memory memory time2025-01-10 projectdocs-site importance4 术语表中“回调”统一翻译为 callback不要写“回到调用”。 /memory /memories用memories这种边界标签包起来模型能明确区分“这是历史记忆”和“这是当前对话”。我实测下来加了这个边界之后模型对历史内容的引用准确率明显提升不会把记忆当成新的用户指令执行。这个设计代价很小收益却很实在。2.4 上下文窗口的预算管理Claude 的上下文窗口虽然长但也不是无限的。claude-mem 在注入时会对结果做一步截断先设置一个总预算比如 1024 token再按排序结果逐条塞塞到预算用完为止。为什么不是一次塞 20 条因为每条记忆平均占 150~200 token20 条就是 4000 token直接吃掉整个窗口的 2%~4%。对于大批量任务这无所谓但你要是有很长的工具定义和示例要放记忆部分就该压缩。我自己的经验预算设成 512~1024 token 比较平衡。太低的话模型容易忽略记忆太高的话主任务上下文被挤压。检索结果宁可少而精不要多而杂。claude-mem 的配置里可以直接改inject_token_budget按项目需求调整。3. 实操接入从零开始跑通 claude-mem3.1 安装与初始化claude-mem 是命令行工具安装方式看项目 README 为主。一般就两种预编译二进制或者源码编译。我用的是从源码编译的方式git clone https://github.com/your-local-clone/claude-mem.git cd claude-mem make build sudo install -m 0755 build/claude-mem /usr/local/bin/装好之后先初始化配置。初始化会生成一个默认配置文件同时创建一个空的 SQLite 库。第一次跑claude-mem init claude-mem status如果初始化成功status会显示数据库路径、记忆条数、当前项目名。我建议把数据库单独放一个目录比如~/.local/state/claude-mem/memories.db这样备份和迁移都方便不会跟项目代码混在一起。3.2 配置文件修改初始化生成的配置文件是 TOML 格式我在~/.claude-mem/config.toml里改了几个关键项[storage] db_path /home/you/.local/state/claude-mem/memories.db [inject] enabled true token_budget 768 max_results 10 min_importance 2 [retrieval] match_limit 100 min_score 12.0 [project] name default auto_tag truemin_importance 2意味着重要度低于 2 的记忆不参与注入过滤掉大部分过程性记录min_score 12.0是检索分数阈值低于这个分数说明匹配度太低不值得占用预算。这两个阈值是暴力调参调出来的不同项目建议自己跑几次claude-mem search 关键词看看分数分布再定。3.3 把 claude-mem 接进 Claude Code我日常主力是用 Claude Code 写代码所以最关心怎么把 claude-mem 嵌进去。Claude Code 支持 hook 机制可以在会话启动和会话结束时执行外部命令。我在项目根目录配了一个.claude/settings.json{ hooks: { PreToolUse: [ { matcher: UserPrompt, command: claude-mem inject --format compact } ] } }这样每轮用户提问之前claude-mem inject会先把相关记忆查出来以紧凑格式追加到对话开头。格式如果选compact注入内容会短很多适合高频轮次如果选full会把完整时间戳和标签也带进去信息量更足但 token 消耗也更高。3.4 用 Python 接入 Claude API如果你不是用 Claude Code而是直接调 API那接 claude-mem 更简单。核心逻辑就是调 API 之前先claude-mem search把结果拼进 system prompt 或者首条 messages 里。我写了一个最简单的工作流示例import subprocess import anthropic def get_memories(query: str) - str: result subprocess.run( [claude-mem, search, query, --limit, 5], capture_outputTrue, textTrue, checkTrue ) return result.stdout system_prompt ( 你是一个可靠的技术文档助手。\n 以下是历史记忆请优先参考\n get_memories(术语表 目录命名) ) client anthropic.Anthropic() resp client.messages.create( modelclaude-sonnet-4-0, max_tokens2048, systemsystem_prompt, messages[{role: user, content: 帮我检查第三章节的措辞}] ) print(resp.content[0].text)这里有几个细节要注意。第一search的查询词别太长一两句话足矣第二--limit 5是取前五条配合 token 预算刚好第三最好在每次对话开始前搜不要在每轮都搜——每轮都搜会增加不少 API 延迟。3.5 常用命令速查命令作用常用参数claude-mem add 约束 --tags project,style手动添加一条记忆--importance 1-5claude-mem search 关键词检索相关记忆--limit N、--project Pclaude-mem inject将相关记忆注入上下文--format full/compactclaude-mem list --project P查看某个项目的记忆列表--sort timeclaude-mem status查看库状态无claude-mem compact压缩旧记忆合并重复项--dry-run先看效果compact这个命令我每两周跑一次。它会把重要度低、时间久、内容重复的记忆合并成几个大的“档案条目”库就不会无限膨胀。4. 进阶调整记忆分级、团队共享与防“中毒”4.1 记忆分级短期、中期、长期分开管理默认配置下所有记忆一视同仁但用久了你会发现这不够。有些记忆是要长期遵守的规范比如“项目命名统一 kebab-case”有些记忆只是临时的比如“今天下午三点要测一下接口”过俩小时就没用了。claude-mem 支持用importance字段做分级。我按自己的习惯分了三档重要度 5永久规范项目核心约束如命名规范、术语表。重要度 3中期事实如当前模块状态、负责人分工。重要度 1临时代办过几天就删。配合清理策略重要度 1 的记忆 3 天自动清理重要度 3 的 30 天归档重要度 5 的永不自动删。这个策略不是内置功能是我写了个 cron 脚本调compact和delete --old实现的效果很好。库不会泛滥注入的质量也更高。4.2 多项目隔离一个库还是多个库我一开始把所有项目的记忆都塞进同一个库结果灾难了——搜“目录”这个词文档项目和前端项目的结果混在一起注入一堆无关记忆。后来改成按项目名隔离每个项目建一个独立 SQLite 文件或者在同一库里用project字段过滤。更推荐后一种方案因为search命令天然支持--project参数claude-mem search 目录 --project docs-site同一个库里跨项目搜索偶尔也有用比如查一个技术方案的往事能搜到其他项目里的类似经验。共用库让数据可复用按项目滤噪又保证精准两全其美。4.3 防“记忆中毒”冲突与过时的处理记忆系统最大的坑是旧记忆跟新对话冲突。比如项目改版了命名规范从 kebab-case 改成了 snake_case但库里还残留旧记忆。如果 claude-mem 注入的是旧规则模型就会犯错。我的解决办法分三层。第一层检索时加时间衰减超过 90 天的记忆分数打折降低被注入的概率。第二层注入模板里明确写了“如果历史记忆与本次对话冲突请明确指出冲突点”给模型一个“抗命授权”。第三层主动更新每次改规范我会手动删掉旧记忆写一条新记忆。删旧用claude-mem delete --id写新用claude-mem add --importance 5一分钟搞定。4.4 团队共享记忆把知识沉淀下来单人用 claude-mem 解决的是“失忆”问题团队用解决的是“重复造轮子”问题。我把团队的 SQLite 库放在一个共享目录里大家读写同一个文件。SQLite 对这种并发场景天然支持 WAL 模式读写不会锁库。我实测过三个同事同时写入没有出现问题。如果是异地团队也可以定期导出、合并、再导入。claude-mem 支持 JSON 导出claude-mem export --format json --output backup.json claude-mem import --file backup.json团队共享的关键是给记忆打上“作者”标签比如--tags author:zhang这样后续看到一条陌生记忆能知道是谁写的也方便私下确认。5. 常见问题与踩坑记录我一个月里踩过的坑5.1 高频问题速查表现象原因解决方案搜不到昨天的记录分词器对中文支持弱配置 FTS5 trigram 分词或写入时额外生成英文别名标签注入后占用太多 tokentoken_budget 设太大调低inject_token_budget同时调高min_score多客户端并发写入报错默认 rollback journal 模式在 SQLite 打开时设置PRAGMA journal_modeWAL记忆注入太旧回答错误时间衰减不足打开时间衰减缩短保留期启动时注入慢数据库体量过大且无索引给project、created_at建索引定期 compact5.2 中文搜索匹配的坑这个我折腾了两天。claude-mem 默认的 FTS5 分词器是 unicode61它把中文按“整句单词”处理所以搜“目录”搜不到“目录结构”因为它们在词法上是完全不同的 token。解决方法是给记忆表单独建一个 trigram 分词索引。SQLite 从 3.34 开始支持 trigram tokenizer把每一个连续三个字符当一个 token中文搜索效果立竿见影。CREATE VIRTUAL TABLE memories_trigram USING fts5(summary, content, tokenizetrigram);配上 trigram 之后英文的精确匹配依然走原表中文的模糊匹配走 trigram 表两个结果做合并取并集后统一排序。数据量不大时这个“双通道”方案非常靠谱。5.3 记忆条目爆炸式增长人懒的时候会特别夸张我有一阵子每天加 20 条记忆两周就攒了 300 多条。里面混合着核心规范、临时任务、甚至还有心情记录。库一膨胀检索速度不明显变慢但注入质量明显下降——因为噪声多了真正有用的记忆被挤掉。这个问题的根源不是 claude-mem 设计不好而是输入质量失控。现在我给自己订了个规矩手动加记忆必须写“可执行的约束”而不是“想法记录”。比如“我觉得目录结构可以更扁平”不算一条记忆“目录层级不超过三层新增文件优先放根目录”才算。质量把关是用户自己的事工具只能帮你存不能帮你判断哪些值得存。5.4 API 报 400 错误注入内容格式问题有段时间我的 Python 脚本总是偶发报 400查日志发现 system prompt 里混入了从记忆里带过来的非法字符。原因是我导入过一条带 HTML 标签的旧记忆注入模板包上memories标签之后就变成了多级标签嵌套导致消息格式解析出问题。解决办法是在检索结果输出给模型之前做一个转义把、转成全角字符或者删掉记忆内容中的 XML 标签。claude-mem 后续版本在inject时可能已经处理了但自定义接入时一定要自己加一道过滤。6. 我的最终体会记忆工具的边界和取舍不多说最终聊点实在的用 claude-mem 跑了一个多月最大的感受是项目连续性确实改善了。以前跨会话协作基本靠我的“复制粘贴毅力”现在靠工具自动完成。这不代表它完美——检索准确性、注入质量、记忆的时效性之间始终存在张力需要根据项目实际反复调整参数。我花了一天调min_score和token_budget从最初每次对话注入 10 条、每次消耗 2000 token收敛到注入 4~5 条、消耗 600 token模型对历史规范的遵守率反而提升了。原因很简单少而精的记忆更容易被模型注意到。最后再分享一个我养成的习惯每天结束工作前跑一次claude-mem list --sort time | tail -20扫一眼今天写了哪些记忆发现写歪了就顺手改掉。这个动作花不了两分钟却能在第二天开新会话之前把“记忆库”清理干净。如果你也想把 LLM 真正用进长期项目从一件外置记忆工具开始是性价比最高的路径。