资讯动态

claude-mem 记忆系统拆解:采集、压缩、存储、检索与注入全链路

发布时间:2026/10/26 11:31:59 来源:尧图企业网站定制
1. 项目缘起与核心定位第一次看到 claude-mem 这个项目名我的直觉是这应该是一个围绕 Claude 生态做记忆管理的工具。实际拆解下来它的核心定位确实如此——为基于 Claude 的对话系统提供持久化记忆能力让原本“聊完即忘”的会话拥有跨会话、跨项目的上下文延续性。这个项目解决的是一个非常具体的痛点。用过 Claude 做长期项目的人都知道每次新开一个会话之前积累的偏好、决策、代码约定、项目背景全部归零。你得重新解释一遍“我们用的是哪种目录结构”“上次那个 bug 的根因是什么”“这个模块为什么不用某个方案”。这种重复劳动在长周期项目里极其消耗精力。claude-mem 要做的就是把这些散落在历史会话里的关键信息抽取出来存进一个可检索的记忆库在需要的时候自动注入到新的上下文中。适合谁来参考这个项目三类人最直接受益一是用 Claude 做长期开发协作的工程师二是需要维护多轮复杂对话状态的产品或研究人员三是想自己搭建 AI 记忆层、理解“上下文工程”落地方式的开发者。哪怕你只是好奇“AI 记忆到底怎么存、怎么取、怎么用”这个项目的设计思路也值得完整走一遍。我个人的判断是claude-mem 的价值不在于它用了多前沿的模型而在于它把“记忆”这件事拆成了采集、压缩、存储、检索、注入五个可独立优化的环节每个环节都有明确的工程取舍。下面我就按这个逻辑把整个项目的设计思路和实操细节完整拆开讲。2. 整体架构设计与选型逻辑2.1 为什么不做“全量历史拼接”最朴素的做法是把所有历史对话直接拼进新会话的上下文。这个方案我早期试过问题非常明显token 消耗爆炸、噪声比例极高、模型注意力被稀释。一个跑了三周的开发项目历史对话轻松超过几十万 token全量拼接根本不现实而且大量内容是和当前任务无关的闲聊或已废弃的决策。claude-mem 选择的是抽取式记忆路线不存原始对话存经过提炼的“记忆条目”。每条记忆是一个结构化单元包含内容、类型、时间戳、来源会话、关联标签。这个选择背后的逻辑是记忆的价值密度远比完整度重要。人脑也是这样工作的你不会记住每一次对话的每个字你记住的是结论、偏好和关键事实。提示抽取式记忆的代价是会丢失细节。所以 claude-mem 在设计上保留了“来源指针”需要时可以回溯到原始会话片段这是一个很关键的补偿机制。2.2 记忆分层模型项目把记忆分成三个层次这个分层是我认为最值得借鉴的设计层级名称内容特征生命周期典型示例L1会话记忆当前会话内的临时上下文会话结束即归档本轮讨论的临时变量名L2项目记忆与特定项目绑定的稳定事实项目周期内长期有效代码规范、架构决策L3全局记忆跨项目的用户偏好永久语言偏好、输出格式习惯L1 不落库只在内存里维护避免污染长期记忆。L2 是核心按项目隔离检索时优先命中。L3 量最小但影响面最大比如“用户偏好简洁回答”这种偏好一旦写入全局记忆所有会话都会受影响。这个分层的工程意义在于检索范围可控。如果所有记忆混在一起检索时噪声会很大。分层之后检索可以按“先 L2 后 L3、L1 不参与”的策略执行命中率和相关性都明显提升。2.3 存储选型为什么是本地文件加向量索引claude-mem 没有一上来就上重型数据库而是采用本地 JSON 文件存记忆条目 轻量向量索引做语义检索的组合。这个选择我觉得非常务实。记忆条目的数量级通常在几百到几千条这个量级用 JSON 完全扛得住读写简单、可读性强、方便手动检查和修正。向量索引只存 embedding 和条目 ID 的映射不存原文索引文件很小。检索时先走向量召回 Top-K再回表读原文整个链路清晰。如果换成关系型数据库你会引入 schema 迁移、连接管理、部署依赖一堆问题而收益在这个量级下几乎为零。如果换成纯向量数据库又失去了记忆条目的结构化查询能力比如“查某个项目的所有架构决策”。所以这个混合方案是权衡后的合理选择。注意本地文件方案在多进程并发写入时会有竞争问题。claude-mem 的做法是写入走单进程队列读取可以并发。如果你要改成多进程必须加文件锁否则记忆条目会丢。3. 核心模块拆解与实操要点3.1 记忆采集从对话流里捞出值得记的东西采集环节的核心问题是哪些内容值得变成记忆如果什么都记记忆库很快变成垃圾场如果记太少又起不到延续上下文的作用。claude-mem 的采集策略是规则触发 模型判定双通道。规则通道负责捕获明确信号比如用户说“记住”“以后都这样”“这个项目的规范是”这类句式直接触发采集。模型通道负责处理模糊情况把最近几轮对话丢给一个轻量模型让它判断“这里面有没有值得长期保留的事实”。实操中我发现规则通道的准确率接近百分之百但召回率低模型通道召回率高但会引入误判。两者结合的效果最好。具体配置上模型判定的阈值可以调默认是 0.7低于这个置信度的候选记忆会被丢弃。采集到的原始候选还要经过一次去重和合并。比如用户在不同会话里三次提到“这个项目用四空格缩进”应该合并成一条记忆而不是存三条。去重用的是语义相似度阈值设在 0.85 左右超过就认为是同一条。3.2 记忆压缩把一段话压成一句话采集到的候选记忆往往是一大段对话直接存进去检索效率低。压缩环节要把它提炼成自包含的一句话或几句话。这里有个关键原则压缩后的记忆必须脱离原始上下文也能被理解。比如原始对话是“对就用那个方案吧之前试过另一个不行”压缩后应该是“项目 X 的认证模块采用方案 A方案 B 已验证不可行”。代词要还原隐含信息要补全。压缩用的提示词设计很讲究。我实测下来让模型输出“主语 动作 对象 原因”的结构化格式比让它自由发挥效果好很多。自由发挥容易丢信息或者加戏结构化格式稳定得多。# 压缩提示词的核心结构示意 compress_prompt 将以下对话片段压缩为一条自包含的记忆条目。 要求 1. 补全所有代词指代 2. 保留决策原因 3. 输出格式主体 动作/状态 对象 [原因] 4. 不超过 50 字 对话片段 {dialog_chunk} 3.3 记忆存储结构化条目的字段设计每条记忆存下来长什么样直接决定了后续检索和注入的质量。claude-mem 的条目字段设计如下字段类型说明是否必填idstring唯一标识时间戳加随机后缀是contentstring压缩后的记忆正文是typeenumfact/preference/decision/constraint是scopeenumsession/project/global是project_idstring所属项目global 时为空条件必填embeddingfloat[]语义向量是source_refstring来源会话和轮次指针否created_attimestamp创建时间是last_hit_attimestamp最近一次被检索命中的时间是hit_countint累计命中次数是type字段是我觉得设计得最巧的地方。不同类型的记忆在注入时优先级不同constraint约束优先级最高必须注入preference偏好次之fact和decision按相关性注入。这样避免了所有记忆平等对待导致的上下文浪费。last_hit_at和hit_count用于记忆的热度衰减。长期没被命中的记忆会被降权甚至归档。这个机制模拟了人类记忆的遗忘曲线让记忆库保持精简。3.4 记忆检索怎么在正确的时候捞出正确的记忆检索是记忆系统里最难的部分。捞多了噪声大捞少了没作用。claude-mem 用的是多路召回 重排的策略。第一路是向量召回用当前对话的 embedding 去索引里找语义相近的记忆取 Top-20。第二路是结构化召回按当前会话的 project_id 拉取该项目的所有 constraint 类型记忆这部分不走向量直接全取。第三路是全局偏好召回拉取所有 global scope 的 preference。三路结果合并后进入重排环节。重排的打分公式大致是score 0.5 * 语义相似度 0.2 * 类型权重 0.15 * 热度分 0.15 * 时间新鲜度类型权重里 constraint 是 1.0preference 是 0.8decision 是 0.6fact 是 0.5。热度分由 hit_count 归一化得到。时间新鲜度是创建时间的指数衰减。最终取 Top-8 注入上下文。这个数量是我反复调过的再多会挤占正常对话空间再少会漏掉关键约束。提示检索阈值不要设太死。我见过有人把相似度阈值设到 0.9结果几乎什么都召不回。0.6 到 0.7 是比较舒服的区间宁可多召回一点靠重排去筛。3.5 记忆注入以什么形式塞进上下文注入形式直接影响模型对记忆的利用效率。claude-mem 把记忆组织成一个独立的 system 段落格式如下[项目记忆] - (约束) 该项目使用四空格缩进 - (决策) 认证模块采用方案 A因方案 B 在高并发下不稳定 - (偏好) 用户偏好简洁回答不需要冗长解释用列表而不是自然段是因为模型对结构化列表的遵循度更高。每条记忆前面标注类型让模型知道这条的强制程度。约束类记忆模型会严格遵守偏好类会尽量遵循事实类作为背景参考。注入位置也有讲究。放在 system prompt 的末尾、用户消息之前效果最好。放太前面会被后续指令稀释放用户消息后面又太晚。4. 完整实操流程与关键配置4.1 环境准备与依赖安装先把基础环境搭起来。claude-mem 本身是个轻量工具依赖不多但有几个版本要求需要注意。# 创建独立环境避免污染全局 python -m venv claude-mem-env source claude-mem-env/bin/activate # 核心依赖 pip install anthropic pip install numpy pip install sentence-transformers # 如果要用本地向量索引 pip install faiss-cpusentence-transformers用来生成 embedding默认用的是轻量多语言模型本地跑不需要 GPU几百条记忆的编码几秒钟就完成。如果你记忆量特别大可以换成更小的模型或者走 API 编码但本地模型在隐私和成本上更可控。4.2 初始化记忆库第一次使用需要初始化目录结构和配置文件。import os import json MEMORY_ROOT os.path.expanduser(~/.claude-mem) def init_memory_store(): os.makedirs(f{MEMORY_ROOT}/projects, exist_okTrue) os.makedirs(f{MEMORY_ROOT}/index, exist_okTrue) config { embedding_model: paraphrase-multilingual-MiniLM-L12-v2, retrieval_top_k: 20, inject_top_n: 8, similarity_threshold: 0.65, dedup_threshold: 0.85, decay_days: 90 } with open(f{MEMORY_ROOT}/config.json, w) as f: json.dump(config, f, indent2) # 全局记忆文件 global_mem f{MEMORY_ROOT}/global.json if not os.path.exists(global_mem): with open(global_mem, w) as f: json.dump([], f) init_memory_store()配置里几个参数值得说明。similarity_threshold是召回的最低相似度低于这个值的候选直接丢弃。dedup_threshold是去重阈值两条记忆相似度超过这个值就合并。decay_days是热度衰减周期超过这个天数没被命中的记忆权重会明显下降。4.3 采集与压缩的串联实现采集和压缩在实际运行中是串联的采集负责捞候选压缩负责提炼。下面是一个简化但可运行的实现。def collect_candidates(dialog_turns): 从对话轮次中采集候选记忆 candidates [] # 规则通道捕获明确信号 trigger_phrases [记住, 以后都, 这个项目的规范, 不要再, 必须] for turn in dialog_turns: if any(p in turn[content] for p in trigger_phrases): candidates.append({ raw: turn[content], source: turn[id], channel: rule }) # 模型通道判定模糊情况 # 这里省略模型调用细节核心是把最近几轮丢给模型判定 model_candidates judge_by_model(dialog_turns) candidates.extend(model_candidates) return candidates def compress_candidate(candidate): 把候选压缩成自包含记忆 # 调用模型压缩提示词见 3.2 节 compressed call_model_for_compression(candidate[raw]) return { content: compressed[text], type: compressed[type], scope: infer_scope(candidate), source_ref: candidate[source] }infer_scope这个函数负责判断记忆的作用域。如果候选里提到了具体项目名或项目相关术语归为 project如果是通用偏好归为 global其余归为 session。这个判断也可以交给模型做但规则判断更快更稳。4.4 检索与注入的完整链路检索链路是整个系统里调用最频繁的部分性能要求高。下面把多路召回和重排串起来。def retrieve_memories(query, project_idNone): 多路召回 重排 query_emb encode(query) # 第一路向量召回 vector_hits vector_search(query_emb, top_k20) # 第二路项目约束全取 constraint_hits [] if project_id: constraint_hits load_project_constraints(project_id) # 第三路全局偏好 global_hits load_global_preferences() # 合并去重 all_hits merge_and_dedup(vector_hits, constraint_hits, global_hits) # 重排 scored [] for mem in all_hits: score ( 0.5 * cosine_sim(query_emb, mem[embedding]) 0.2 * type_weight(mem[type]) 0.15 * heat_score(mem) 0.15 * freshness_score(mem) ) scored.append((score, mem)) scored.sort(keylambda x: x[0], reverseTrue) return [m for _, m in scored[:8]] def build_injection_block(memories): 把记忆组织成注入块 lines [[项目记忆]] for mem in memories: lines.append(f- ({mem[type]}) {mem[content]}) return \n.join(lines)重排公式里的四个权重是我调了很多次的结果。语义相似度占一半因为相关性是根本。类型权重占两成保证约束类记忆不会被漏掉。热度和新鲜度各占一成半让常用和新近的记忆有优势。4.5 记忆热度更新与归档每次检索命中后要更新记忆的热度字段。这个动作看似小但长期运行下来对记忆库质量影响很大。def update_hit_stats(memory_ids): 更新命中统计 now time.time() for mid in memory_ids: mem load_memory(mid) mem[hit_count] 1 mem[last_hit_at] now save_memory(mem) def archive_cold_memories(): 归档冷记忆 threshold time.time() - 90 * 86400 for mem in load_all_memories(): if mem[last_hit_at] threshold and mem[hit_count] 3: move_to_archive(mem)归档不是删除是移到单独的归档文件。归档记忆不参与常规检索但可以通过显式查询找回。这个设计避免了误删有价值但暂时不活跃的记忆。5. 常见问题与排查技巧实录5.1 记忆污染错误记忆被反复注入这是最常见也最头疼的问题。某条错误记忆一旦写入每次检索都可能命中然后被注入然后模型基于错误信息回答用户纠正但错误记忆还在库里。排查思路是先定位再修正。定位靠source_ref字段回溯到原始会话确认这条记忆是怎么产生的。修正有两种方式直接编辑记忆条目内容或者删除后重新采集。预防措施比修正更重要。我的经验是给采集环节加一道确认门槛模型判定的候选记忆置信度低于 0.8 的先不直接入库而是标记为“待确认”下次相关会话时再确认一次两次都命中才正式入库。这样能过滤掉大部分误判。5.2 检索召回率低该记的没被捞出来有时候明明库里有相关记忆但检索就是没命中。原因通常有三个embedding 模型不适合当前语言、相似度阈值设太高、查询本身太短缺乏语义信息。排查时先把阈值临时调到 0.3看能不能召回。如果能召回说明是阈值问题逐步往上调找到平衡点。如果还是召不回检查 embedding 模型是否匹配你的内容语言。中文内容用多语言模型纯英文可以用更小的英文模型。查询太短的问题比较隐蔽。比如用户只发了“继续”这种查询几乎没有语义信息向量召回基本失效。解决办法是对短查询做查询扩展把最近几轮对话拼进来一起编码用扩展后的向量去检索。5.3 注入过多导致回答质量下降记忆注入不是越多越好。我实测过注入 15 条和注入 8 条的对比15 条的时候模型明显开始忽略部分记忆而且回答变得啰嗦因为它试图照顾所有注入的信息。控制注入量的关键是重排要狠。Top-8 是上限实际很多时候只注入 3 到 5 条就够了。如果发现注入后回答质量下降先看注入的记忆里有多少是真正相关的。不相关的记忆哪怕只有两三条也会明显干扰。另一个技巧是按类型分配名额。约束类最多 3 条偏好类最多 2 条事实和决策类共享剩余名额。这样避免某一类记忆挤占全部空间。5.4 多项目记忆串味如果你同时维护多个项目记忆串味是个大问题。项目 A 的架构决策被注入到项目 B 的会话里会导致模型给出完全错误的建议。根本解决办法是严格的作用域隔离。检索时 project_id 必须精确匹配不能模糊匹配。全局记忆要严格控制数量只放真正跨项目的偏好比如语言和输出格式绝不放技术决策。我踩过的一个坑是早期为了省事把一些“通用技术决策”放进了全局记忆结果这些决策在不适用的项目里被注入造成了误导。后来我把全局记忆的准入标准提高到“必须与具体技术栈无关”问题才解决。5.5 常见问题速查表现象可能原因排查动作解决方向错误记忆反复出现采集误判且未确认查 source_ref 回溯加确认门槛修正条目相关记忆召不回阈值高或查询太短降阈值测试调阈值做查询扩展回答变啰嗦注入过多统计注入条数收紧重排限名额项目间记忆串味作用域隔离不严检查 project_id 匹配精确匹配严控全局记忆库膨胀无归档机制统计条目总数开热度衰减和归档写入冲突丢数据多进程并发写查写入日志单进程队列或加锁6. 记忆系统的扩展方向与个人体会claude-mem 这套设计跑通之后能扩展的方向其实不少。我试过其中一个记忆的主动遗忘。不是靠热度衰减被动归档而是定期让模型审视记忆库主动判断哪些记忆已经过时或矛盾标记出来让人确认。这个机制在长周期项目里很有用因为项目演进过程中早期决策会被推翻如果不清理新旧记忆会打架。另一个方向是记忆的跨会话关联。现在每条记忆是独立的但实际上记忆之间有依赖关系。比如“采用方案 A”和“方案 A 的配置参数是 X”是关联的。如果能把关联记忆打包注入模型的理解会更完整。这个我还在摸索初步想法是在存储时记录记忆之间的引用关系检索时做一跳扩展。最后分享一个实操中总结的小技巧记忆条目的 content 字段尽量用陈述句不要用疑问句或条件句。陈述句在注入时最稳定模型直接当事实用。疑问句会让模型产生“需要回答这个问题”的倾向条件句则可能因为条件不满足而被忽略。这个细节看起来小但对注入效果的影响比想象中大。我在实际使用中最大的体会是记忆系统的难点从来不在存储和检索的技术实现而在判断什么值得记。这个判断标准会随着使用场景不断调整没有一劳永逸的配置。定期回顾记忆库看看哪些记忆从来没被命中过哪些被命中了但没起作用根据这些反馈去调采集和重排策略才是让系统越用越好用的关键。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑