前几天凌晨两点我盯着屏幕里Claude的回复有点发愣它把我下午千叮咛万嘱咐的项目背景忘得一干二净把“预算只有五万”理解成了“预算大概五十万”还一本正经地给出一个没三十万根本落不了地的方案。这已经不是第一次了问题也不在模型本身——Claude确实没有能力记住上一次会话的内容。真正的问题在于我一直拿它当个有脑子的搭档用却忘了给它配一个“脑子之外能长期存东西的地方”。后来我花了两周时间倒腾出一个实验性项目代号就叫claude-mem。名字很直白给 Claude 加一层外部记忆。它不是哪个大厂的产品也不是什么高深理论就是我在自己的工作流里反复试错后沉淀下来的一套做法——把对话里值得留下的信息抽出来、存好在下次对话开始之前把相关的记忆重新塞回上下文。这篇文章就把 claude-mem 的来龙去脉一次讲清楚为什么模型天生没记忆、记忆层到底该存什么、怎么存取最稳以及我在实测里踩过的那些坑。如果你也受够了每次和 AI 对话都得从头交代背景想给自己的自动化流程或私人助手加一套能落地的记忆方案这文章应该能帮你省不少返工时间。1. 痛点背后的真相模型天生没有“长期记忆”这回事很多人第一次感觉到 Claude“记性差”的时候第一反应是骂模型笨。但作为深度用过一段时间的从业者我得说句公道话这根本不是笨是架构决定的。1.1 上下文窗口再大也解决不了“记一辈子”的问题大语言模型的工作原理可以粗略理解为你给它一段文本它在内部把这些内容全部编码成注意力权重然后基于这段内容生成下一个词。整个过程是一次性的——对话结束这些上下文就被丢弃模型本身不会因为这段对话发生任何持久性的改变。Claude 的上下文窗口已经做得很大了几万甚至几十万 token 都支持。但窗口再宽也只是一个“临时工作台”工作台上的东西一旦会话结束就会被清理掉工作台本身有物理尺寸上限塞太多旧信息新信息就放不进去就算窗口没满大量无关历史也会稀释注意力让回复质量肉眼可见地下降。所以你把所有历史一股脑塞进提示词里表面上“记住了”实际上是在跟上下文窗口的物理上限对抗。短时间几次没问题时间一长要么爆长度要么答非所问。1.2 我理解中的记忆应该分两层而不是一层人脑的记忆不是单块的至少分“工作记忆”和“长期记忆”。工作记忆对应模型的上下文窗口——你正想着的事、正在处理的数据都在这层。但人之所以能记住老朋友的名字、知道自己上次把钥匙放在哪靠的是长期记忆这层在一个完全不同的地方需要的时候才被调取出来。claude-mem 做的事情就是给模型外挂这样一层“长期记忆”。它的核心思路不是把每句话都存下来而是从每轮对话里提取值得记的东西——用户偏好、项目约束、重要结论、决策理由等把这些内容写到外部存储里——本地文件、数据库、向量库都行下次发起新对话之前按当前话题检索相关记忆拼进提示词开头。这样一来模型无需真正“记住”任何东西它每次看到的都是精心挑选过的历史片段表现得好像全都记得。这个思路不是我发明的很多团队都这么搞但 claude-mem 把整个过程收敛成了一条比较好上手的工作流。2. claude-mem 的骨架拆解从对话到档案的四层流水线如果你也想自己搭一套不用复制我的实现但下面这几层结构基本绕不开。我把 claude-mem 分成四层每一层解决一个具体问题。2.1 采集层不是所有对话都值得进记忆很多人的第一版记忆方案就是“全存下来”。真这么干了你会发现存三个月之后整个记忆库变成了一堆垃圾检索什么都检出无数条低质量记录。我后来给 claude-mem 定了一条规则只有满足下面至少一条的信息才允许进入记忆层。用户明确表达的偏好“我更喜欢简洁的回答”“这个项目的部署环境是内网”关键事实与决策“服务器迁移到 10.20.30.40”“最后选了方案 B 因为成本低”待办与目标“下周要交付客户演示”“记得跟进供应商报价”长期有效的规则“所有脚本必须加上超时处理”“输出必须是中文”怎么实现这层过滤最简单的方式是让 Claude 自己来判断。我写过一个提取提示词模板让模型在每轮对话结束后输出一个结构化的记忆提案像下面这样请从这段对话中提取值得长期记忆的内容。 要求 - 只输出事实、偏好、决策、待办不输出寒暄和过程性描述 - 如果没有任何值得记忆的内容输出 none - 格式为 JSON { memory_type: fact | preference | decision | task, content: 具体的记忆内容, importance: 1-5 }这一层不追求把信息完整复述重点是降低后续几层的噪声。从实际效果看对话里大约六成的话最后都会被滤掉真正留下来的只有那些关键决策和偏好。2.2 存储层用什么载体取决于你打算怎么检索记忆提取出来之后下一步是落盘。根据我的实测体验存储方案没有绝对最好只有合不合适。我按重到轻列几种方案你可以参考存储方式适合场景优点缺点SQLite单机使用、个人工作流结构化好、支持复杂查询语义检索需另配向量JSON/Markdown 文件原型验证、小规模记忆简单透明、可直接查看规模大了查询很慢向量数据库大规模、语义检索为主支持相似度检索、扩展性好部署成本高、需要维护索引混合存储生产级方案事实用关系型、语义用向量实现复杂度翻倍我自己的 claude-mem 开发期用的就是 Markdown 文件每个记忆条目一个标题加正文放在memory/目录下。好处是出 bug 的时候我能直接打开文件看内容排查效率高。等条目数超过一千条之后我换成了 SQLite 存结构化字段、同时存一个文本嵌入向量用于语义检索这样速度和准确率才平衡下来。2.3 索引层没有索引的记忆库等于一堆杂物间存储只是把东西放进去真正决定记忆能不能派上用场的是索引。每一条记忆写入时我会让 Claude 给它打三个标签话题领域比如“预算”“技术架构”“客户偏好”、实体名比如项目代号、人名、系统名、时间戳。这一步的作用是让后续检索从“翻遍整个仓库”变成“只查相关的那个抽屉”。这里有个实操细节实体名尽量统一大小写和叫法。我自己踩过坑——“mysql”和“MySQL”两条记录在标签系统里如果不统一检索时会漏掉大约三成相关条目。后来我加了一个简单的归一化步骤所有实体名统一小写缩写全大写标签用固定词表这个问题基本就消失了。2.4 注入层怎么把记忆塞回对话还不破坏上下文记忆存在的意义是影响未来的对话所以最后一步是把检索到的记忆注入到新的对话里。注入的位置我试过几种放系统提示词里、放用户第一条消息前面、放对话历史最前面。实测下来放系统提示词里效果最干净因为模型会把它当作背景设定来处理而不是对话内容的一部分。格式上我用一个记忆档案块包裹所有检索到的记忆# 记忆档案来自 claude-mem - 项目客户演示系统预算上限 5 万元部署环境为内网 - 用户偏好输出内容以中文为主方案需要附上成本估算 - 待办下周交付演示版本优先级最高 - 长期规则所有执行脚本必须带有超时处理与错误日志这种方式有几个好处模型会自动把档案当作事实背景不容易和当前对话冲突同时这些条目在逻辑上独立后面如果要调整或者截断不会影响其他指令。3. 从零搭一条最小可用的记忆回路实操记录下面这部分是真正可以“抄作业”的环节。我把 claude-mem 的最小可运行版本拆成三步每一步附上关键构造思路。你不用完全照抄我的但照着这个流程走一遍基本能跑通。3.1 第一步定义记忆条目模型先别急着写代码把记忆长什么样定下来。我用的数据结构很简单# memory_entry.py from dataclasses import dataclass, field from datetime import datetime dataclass class MemoryEntry: entry_id: str # 唯一编号我用 uuid4 的短版本 memory_type: str # fact / preference / decision / task content: str # 记忆内容文本 entities: list[str] # 实体标签如 [mysql, 预算] topic: str # 话题领域如 预算 importance: int # 1-5越高越重要 created_at: datetime # 写入时间 last_access_at: datetime # 最近被检索到的时间 access_count: int # 被检索次数 expires_at: datetime | None # 过期时间任务型记忆用这里access_count和last_access_at不是摆设它们支撑着后面的“遗忘机制”回头我会展开说。3.2 第二步写提取和检索两个核心函数提取函数负责把一段对话交给模型拿回结构化记忆。我用的是单次调用的方式# extract_memory.py import json from memory_entry import MemoryEntry EXTRACT_PROMPT 请从以下对话中提取值得长期记忆的内容。 只输出事实、偏好、决策、待办不输出寒暄和过程性描述。 如果没有任何值得记忆的内容输出none 对话开始 {conversation_text} 对话结束 请以 JSON 数组格式输出字段包括 memory_type, content, entities, topic, importance def extract_memories(claude_client, conversation_text: str) - list[MemoryEntry]: prompt EXTRACT_PROMPT.format(conversation_textconversation_text) response claude_client.chat(prompt) # 实际调用按你的 SDK 来 if response.strip() none: return [] raw json.loads(response) entries [] for item in raw: entries.append(MemoryEntry( entry_idgenerate_short_uuid(), memory_typeitem[memory_type], contentitem[content], entities[e.lower() for e in item[entities]], topicitem[topic], importanceitem[importance], created_atdatetime.now(), last_access_atdatetime.now(), access_count0, expires_atNone )) return entries检索函数则是整个记忆系统的心脏。我给 claude-mem 用了两路召回——关键词匹配 向量相似度算完分数之后合并排序# retrieve_memory.py def retrieve_relevant_memories( sqlite_conn, embedder, query_text: str, top_k: int 6 ) - list[MemoryEntry]: # 1. 关键词匹配从 entities 和 topic 字段里做精确匹配 keyed_memories find_by_keywords(sqlite_conn, query_text) # 2. 向量相似度把 query 向量化后检索相近记忆 query_vec embedder.encode(query_text) vec_memories find_by_similarity(sqlite_conn, query_vec, top_k) # 3. 合并去重按 importance 和新鲜度加权排序 merged merge_and_rank(keyed_memories, vec_memories) return merged[:top_k]3.3 第三步把记忆注入会话的时机与位置注入不是简单把检索结果堆在开头就完事。我试过几个版本最后稳定下来的一套规则是会话开始前注入拿到用户第一条消息后先检索再把记忆档案插进系统提示词里会话中途按需注入如果对话超过一定轮次每轮都重新检索一次但只把新增的、真正相关的记忆追加进档案而不是全部替换对话结束后异步提取整个会话结束后把完整对话文本送去提取记忆写入存储。这套时序保证了“开始前有背景、过程中跟得上、结束后能沉淀”。我踩过的典型错误是在对话结束时不提取结果聊了两小时的方案全部白费下次还得从头说。4. 检索与沉淀的微调为什么你的记忆库越用越顺手又会越用越乱这一步是最看细节的地方。检索做得好了记忆会越用越精准做得糙了记忆库会快速变成乱坟岗。4.1 相关度排序单纯“相似”还不够很多初版检索只看相似度分数谁分高就取谁。但实际场景里一条记忆对于当下对话的价值不只是语义相似。举个例子用户问“预算明天能批下来吗”语义最相似的一条记忆可能是“明天要交预算申请材料”但实际上此刻更该优先看到的是“预算上限五万、客户要求周三前提交最终报价”这种带任务约束的条目。claude-mem 在后面的一版里把排序分数改成了一个加权和final_score 0.5 * semantic_similarity 0.3 * importance_score 0.2 * recency_scoreimportance_score就是存储层里那个 1-5 的重要性评分recency_score和最近访问时间挂钩。这样高重要度的硬约束不会被普通的语义近义内容顶掉。4.2 遗忘机制记忆不是越多越好而是越准越好这是 claude-mem 里容易被忽略、但我觉得最值得花心思的部分。记忆库只增不减条目越来越多。但模型上下文窗口是有限的每一次能塞进去的记忆就那几条。所以忘掉什么比记住什么更重要。我的遗忘策略是三个字用进废退。定期扫描记忆库超过 90 天没被检索到、且 importance 小于等于 2 的条目直接删除类型是task、expires_at已经过期的条目删除access_count高但 importance 很低的条目降权在排序里几乎不会再出现。这套机制特别像人的记忆曲线常用的知识不断被强化用不上的细节慢慢淡出。我在团队里演示过一次效果使用遗忘机制前检索经常捞上来一堆三周前的琐碎对话开启之后前十条结果的精准度明显高了很多。4.3 记忆写入的频率控制别在每轮对话后都执行提取一个很容易犯的错误是每轮对话结束都跑一次提取。虽然单次调用不贵但对话里很多信息是过程性的、阶段性的提取出来的记忆大量重复或没营养。我给 claude-mem 加了一条触发规则只在一段语义完整的片段结束时提取比如用户明确说“就这么定了”“可以”话题出现了明显切换连续对话超过 10 轮手动触发这样提取次数大概是原来的三分之一但有效记忆的占比高了很多。少跑几次模型调用不光省时间更重要的是减少重复信息对记忆库的污染。5. 实测中的边界与问题记忆系统在真实场景里会炸掉的几个地方记忆系统看起来简单真正跑起来你就会发现各种边界问题。以下是我在 claude-mem 实测中摔过跟头的地方每个都对应一个具体的修复经验。5.1 记忆漂移模型会把提取和总结混为一谈这个坑很隐蔽。你以为提取只是“摘原文”实际上模型在提取时经常顺手对内容做一层改写和总结。改写的方向多数时候是对的但偶尔会加入它自己的推断导致记忆内容和原始事实对不上。举个真实例子对话里用户说“这台服务器我们有三个环境在共用”提取出来的记忆却变成了“用户有开发、测试、生产三个环境每个环境独立”。原文里没提环境分几个也没提独立部署是模型自己脑补的。这种记忆一旦被注入后续对话用户会以为模型从哪拿到了确切信息实际上是被记忆污染了。我的修复方案是给提取环节增加一个校验步骤让模型输出记忆时必须附带原文摘录作为依据。如果某条记忆在原对话里找不到明确对应的句子这条记忆就直接打回。实测下来“无依据记忆”的比例从原来的大约两成降到了很低。5.2 检索噪声导致的上下文污染一条垃圾记忆毁掉整轮对话记忆注入是双刃剑。你塞进去的信息如果有一条和当前话题没太大关系模型就可能被带偏。我遇到过一次最典型的翻车准备给一个用 MySQL 的项目生成迁移脚本但记忆库里有一条“用户之前提到想从 MySQL 换到 PostgreSQL”的旧决策。检索时语义相似度把这条也召回了结果 Claude 在回复里花了一大段论证为什么该换 PostgreSQL完全没接我当下的需求。这个问题的根源不是记忆内容错了而是检索到的内容没有足够的话题隔离。后来我在检索阶段增加了一个硬性过滤如果当前对话里出现了明确的话题标签那么只检索该话题下的记忆其他话题的记忆即使语义相近也不能进来。这个白名单机制上线之后类似的带偏问题基本绝迹。5.3 多用户共用一套记忆库身份隔离不能省claude-mem 最初是给自己用的所有记忆都混在一个库存里没有任何用户维度。后来我把它接到一个多人场景测试立刻发现麻烦A 用户的偏好会出现在 B 用户的对话里甚至有些决策性记忆会互相打架。这事让我意识到记忆库从设计第一天就得有 owner 维度。我在每个记忆条目上加上owner_id字段检索时先按 owner 过滤再谈相关性。如果将来要支持团队共享某个项目知识库那就另建一个scopeproject的共享空间和个人的记忆完全分开。5.4 隐私与数据安全的心理底线说到这里也得提一句记忆层存的是用户真实说过的话、做过的决策有些可能是敏感的工作信息。claude-mem 目前默认只支持本地存储所有记忆文件都在自己机器上模型调用只发生在提取和检索阶段记忆原文不会上传到任何第三方服务。如果是产品化场景我建议至少要加三点控制记忆写入前做敏感信息脱敏比如手机号、地址、内部代号检索接口的访问权限单独管控不跟业务接口混在一起提供“一键清空记忆”的入口不能只靠手动删文件。这一点如果你打算在公司内部推广务必提前规划不然后面改起来成本高得多。6. 如果从零再做一次 claude-mem我会这么设计整个方案跑到现在我其实走过不少弯路。假如今后有新项目需要重新搭一套记忆系统我会从一开始就做好这三件事。6.1 先定义“这份记忆的价值已经失效”的判定标准很多记忆刚存下来的时候有价值过一阵子就变成了噪音。但如果你只在“存入”的时候设想过它的价值没有设计“失效”的机制记忆库迟早会烂掉。我现在的做法是每条记忆写入时都要回答三个问题这条记忆能服务于未来哪一类问题如果它失效了会有什么可观察的信号它最长的有效周期是多久想不清这三个问题的记忆宁可当时不存。6.2 用“记忆测评集”来验证检索效果而不是靠感觉调参记忆系统最大的难点是检索效果不容易量化。你觉得这次调参效果好可能只是运气好碰上了合适的测试问题。后来我给 claude-mem 建了一个小的“记忆测评集”大概三四十条问答场景每条场景包含一个用户提问应该被召回的记忆条目列表不应该被召回的记忆条目列表每次改动检索逻辑我就拿这个测评集跑一遍算一个召回率和误召回率。有了这个基线调参时才不会东拉西扯。这是整个项目里我觉得最值得推荐的习惯哪怕你的记忆库只有几十条也建议随手攒一份这样的测试集。6.3 把记忆能力做成独立服务而不是耦合在业务代码里最终版的 claude-mem 我把它拆成了一个独立的小服务对外提供三个接口写入记忆、检索记忆、清理记忆。业务代码只通过 API 调用不直接读写存储。这样做的好处很实际以后想换存储后端、想改遗忘策略、想加新的记忆类型都不需要动业务代码不同的对话流程可以共用同一套记忆能力。拆开之后维护成本明显下降了。最后再分享一个个人体会别一上来就追求“全能记忆助手”那种宏大设计。先用最小方案跑通一套记忆回路——一次提取、一次检索、一次注入——感受一下记忆层带来的变化遇到的问题会比纸上推演真实得多。claude-mem 这个名字听起来像个产品实际上就是一次持续两个多星期的动手实验。如果你的核心诉求是让 AI 助手不再“翻脸不认人”这套思路已经足够起步了。
阅读完成 · 觉得有帮助?