给Claude装上长期记忆:外部记忆层设计与实现全解析

发布时间:2026/10/26 3:34:48
给Claude装上长期记忆:外部记忆层设计与实现全解析 去年年底我一直在折腾一个头疼的问题Claude 这类对话模型什么都好就是“聊完就忘”。今天你跟它说过项目背景、技术偏好、踩过的坑明天新开一个对话它又是一个毫无记忆的陌生人。这种体验对于偶尔聊天的人来说无所谓但对像我这种把 AI 当长期写作搭子、代码审查员、甚至半个人生管理助手来用的人简直是致命伤。所以我动手写了一个自用工具名字就叫 claude-mem用了一个多月效果超出预期。这篇博文就把整个设计和实现过程完整拆开讲一遍包括踩过的坑和最终沉淀下来的方案。claude-mem 的核心定位非常明确给 Claude 补齐一个“长期记忆层”。它不修改模型本身也不做任何训练而是通过提取对话中的重要信息、持久化存储、以及在合适的时机把相关记忆重新注入到上下文中让 Claude 在跨会话、跨时间尺度上表现得像是“记得你”。这件事听起来简单但真正落地时涉及对话信息抽取、记忆组织、检索召回、上下文占用控制、注入位置选择等多个环节。我这里把每一个环节的取舍逻辑和实操代码都记录下来给同样被 AI 失忆症困扰的人一个可以直接抄作业的参考。1. 为什么需要外部记忆层模型上下文的天花板1.1 上下文窗口的本质限制先明确一个基本认知Claude 为代表的对话模型工作方式是基于一个有限长度的上下文窗口。这个窗口里放着你当前的对话、系统提示词以及中间过程的推理内容用完之后最老的内容会被丢弃或者被截断。你用官方对话界面的时候长对话后期它“忘记”了你开头提到的需求本质上不是模型智商的问题而是上下文长度被撑爆了。打个生活化的比方你临时雇了一个记忆力超强但脑子只能装一张纸的助理这张纸上写满之后再输入的新信息就得把旧信息擦掉这个助理每次表现好不好完全取决于纸上的内容是不是恰好覆盖了你现在最关心的那部分。我们做的 claude-mem就是给这个助理配一个外置的笔记本随时把他该记住的重要信息抄到本子上下次接待你的时候提前翻好放到桌上。技术层面当前主流模型的上下文窗口虽然越来越大但依然满足不了真实的使用场景。日常使用中单纯把历史对话一股脑往前带就是灾难几万 token 很快就会放满先不说成本模型对超长上下文的注意力分配天然会衰减中间部分的信息很容易被忽略。这就引出了外部记忆层的两条路一是做压缩摘要把天量的历史对话浓缩成几条关键记忆二是做向量化检索把对话编码成语义向量存进数据库需要时按相关性召回。claude-mem 两条路都走了后面细说。1.2 我设定 claude-mem 要满足的四个硬指标写这样一个小工具之前我先给自己定了四条规定整个开发过程都围绕这四条来第一是对用户零干预。也就是我不想在每次对话之后手动告诉它“记下这句话”这不现实也是传统对话工具一直没能做大的核心原因。信息提取必须全自动完成我需要做的只是定期打开看一眼摘要是否正确。第二是跨会话可用。记忆存储之后新的对话要能自动感知到相关的旧记忆。今天聊到 Python 项目部署它应该自动想起上次我们讨论过的服务器选型和踩坑记录。这就要求实现语义检索而不是简单的关键词匹配。第三是上下文开销可控。注入记忆会占用模型上下文注入太多反而伤害模型对当下对话的理解所以必须有优先级机制每条记忆要有一个重要性分数只把真正需要的高分记忆注入进去。第四是数据本地可导出。所有记忆都保存在本地 SQLite 和向量数据库里随时可以导出、删除、备份不绑定任何云端服务这是隐私底线。事实证明这四个指标在开发过程中帮了大忙。每当我面临技术选型的纠结——比如用复杂的历史摘要算法还是简单的时间衰减评分——都会拿它们当标尺来卡有时候直接一刀切掉那些华而不实的选项。2. 整体架构拆解从对话流到持久记忆的流水线2.1 三个核心模块的职责划分claude-mem 的整体架构可以切成三个完全独立的层。第一层是摄取层负责监控对话过程、抽取和结构化对话中的关键信息第二层是存储层负责把结构化的记忆写进本地数据库同时生成向量索引第三层是召回层在新对话启动时查询相关的旧记忆并拼进系统提示词里。第一层的实现在官方 API 上很直接通过流式接口监听对话完成的消息一旦助手生成了一条完整回复就把刚刚这一轮完整的用户消息和助手回复拼起来丢给专门的信息抽取器去处理。信息抽取器的做法是单独开一个小 model call让 Claude 用一套固定的 JSON Schema 返回结构化抽取结果。第二层做“本地优先”存储。结构化信息落地到 SQLite这样我可以随时用 SQL 查询操作记忆同时把记忆文本丢给嵌入模型生成向量存入一个轻量的向量索引目录用于语义相似度检索。选 SQLite 而不是 MySQL 这类服务端数据库是为了让工具保持零配置、单文件启动的轻量状态。第三层是用户真正能感知到的部分新对话开始时先把用户当前这句话做向量化拿到记忆库里检索最相关的记忆片段按相关性阈值过滤再拼装成一段“记忆提示块”附加在系统提示词后面。听起来很顺滑是不是但这个架构第一个版本跑起来后我很快就发现了一个尴尬的事实抽取层生成的记忆质量太拉胯经常把一些毫无意义的内容当成重要信息存下来比如“用户今天喝了一杯咖啡”这种更别提后续的向量召回环节会因此被大量噪声污染。这就是我接下来要重点讲的内容信息抽取环节是整个流水线里最需要精细调教的部分。2.2 为什么选择“双层存储”而不是单个方案可能有人会问既然要检索旧记忆直接全丢进向量数据库不就行了吗何必还需要 SQLite 结构化存储这里有一个核心产品逻辑上的考量向量检索擅长召回“语义相似”的内容但它吐回来的结果是不确定的、排名式的我无法精确判断它遗漏了哪些内容也无法层次分明地管理记忆。而结构化存储的价值在于提供了确定性的索引和审计能力。举个例子。我可以用一条简单的 SQLSELECT * FROM memories WHERE importance 7 AND last_accessed_at datetime(now, -7 days)把过去一周都没访问过的次要记忆找出来自动做一次降权或者存档。这种操作在纯向量库里实现起来非常别扭因为向量库没办法直接对“上次访问时间”这种字段做条件查询。而且双层存储还让调试变得可行。当某次对话里模型没有记住应该记住的信息我可以去 SQLite 里验证这条记忆是否真的被写入了它的重要度打分是多少距离上一次被召回是什么时候。排查起来就像是“查看后端日志”问题出在抽取阶段、存储阶段还是注入阶段一目了然。如果只有向量库一个存储体排查这种问题时往往只能瞎猜。两个存储体之间的数据流是这样走的抽取器输出的结构化记忆首先写入 SQLite 生成一个全局唯一 ID然后拿同一段文本算 embedding 向量连带这个 ID 一起写进向量索引。这样在任何时候向量检索命中的结果都可以通过 ID 回查到 SQLite 里的完整记录包括置信度、来源对话 ID、时间戳等元数据。2.3 记忆类型体系怎么让记忆变得“可管理”光有存储还不够裸数据堆在那里等于没有管理。我在记忆条目上设计了类型标签体系把对话中抽出来的信息归为六个大类人设偏好类记录你的身份背景、职业、语言风格偏好等长期稳定信息适合永久保留项目状态类记录当前手头在推进的事比如某个模拟项目的技术选型、进度节点、卡点时效性中等事实知识类记录对话中出现过的具体事实比如某个库的版本号、某个 API 的调用方式这类信息要尽量精确不能做模糊转述情感关联类记录你提到过的个人情绪、担忧、期待这类信息在后续激励和沟通里用得上临时待办类记录短期的 action items比如“下周要提交那个报表”过了时间就可以降权事件回顾类记录发生过的事和结论比如上一次评审总结的几条决定。这个分类不是拍脑袋定的完全是基于我平时使用的对话场景反推出来的。分类直接决定了后续每条记忆的生命周期策略比如临时待办类的记忆重要性会随着时间快速衰减而人设偏好类的除非用户主动推翻否则一直保持高权重。数据库表设计上很简单一个 memory_type 字段就解决了但它是整个记忆管理逻辑的抓手。3. 核心实现细节三个最关键的代码块3.1 信息抽取器用严格 JSON Schema 约束输出信息抽取器是 claude-mem 的灵魂模块。我的做法是给 Claude 一段单独的抽取系统提示词附带上完整的对话文本让它按照预定义的 JSON Schema 输出一段 JSON里面包含记忆条目数组。这里最容易翻车的是模型输出不符合 JSON 规范的随机问题。我的解决方案是让模型严格按照一个枚举类型的 JSON Schema 输出并在解析侧做了多重容错先用正则把包裹 JSON 的注释性文本去掉再用 json.loads 直接解析如果失败就进入第二级容错单独调用一次修复合规接口把模型原输出和 Schema 定义都扔给它让它重新输出一串合法的 JSON。import json import re def parse_extraction_response(raw_response: str) - list[dict]: # 第一步剥离可能的 markdown 代码块包裹 cleaned re.sub(r^(?:json)?, , raw_response.strip()).strip() cleaned re.sub(r$, , cleaned).strip() try: data json.loads(cleaned) except json.JSONDecodeError: # 第二步容错修复——把原始输出交给一个专门修正 json 的调用 data repair_json_output(cleaned) return data.get(memories, [])稳定性上来之后下一步是调教抽取质量。我敢说运行起来之后最能拉开工具差距的就是提示词里“抽取什么”的定义部分。我最终沉淀下来的版本总共三百多词核心指令讲了这么几个要点区分“临时对话信息”和“值得长期保存的信息”不要抽取礼貌性对白、无关寒暄、明显一次性的事件记忆条目必须具体到可以在未来独立使用禁止出现“用户对部署方案有兴趣”这种语义残缺条目每一条记忆必须附带 importance 分数、confidence 分数和推测出的记忆类型。过了一个月的使用我发现抽取器产出的记忆条目质量已经稳定在一个很高的水平偶尔出现几条“用户提到了某个技术”之类的泛化条目时多数也是因为源对话本身就含糊。这里有个很反直觉的经验不要试图让抽取器把每条对话都榨出记忆来它越克制保留下来记忆的有效率越高。宁缺毋滥是记忆抽取的第一原则。3.2 向量化与相似检索轻量方案也能好用向量检索我最终选了轻量级的方案固定维度嵌入模型加内存向量索引直接嵌入到主进程内。索引方式用暴力余弦距离扫描没有上 HNSW 这类图索引。原因很简单我的记忆库规模在个人使用场景下最多几万条暴力扫描一个几万规模的向量集合在 CPU 上每次检索也就几十毫秒的量级完全够用但省去了维护索引构建参数和持久化策略的负担。import numpy as np class MemoryVectorIndex: def __init__(self): self.vectors: list[np.ndarray] [] self.memory_ids: list[int] [] def add(self, memory_id: int, vector: np.ndarray) - None: self.vectors.append(vector) self.memory_ids.append(memory_id) def search(self, query_vector: np.ndarray, top_k: int 8) - list[tuple[int, float]]: if not self.vectors: return [] matrix np.vstack(self.vectors) scores matrix query_vector top_indices np.argsort(scores)[::-1][:top_k] return [(self.memory_ids[i], float(scores[i])) for i in top_indices]向量维度选的是 768 维模型用的一个通用中文 embedding 方案覆盖中英文效果都不错。每次新对话开始时做一次查询向量生成然后在记忆库里召回 top_k 条结果再用相关性分数和记忆条目里的 importance 分数做一个和值排序筛出最终进入上下文的记忆。有个细节值得分享向量召回的 top_k 不要永远固定一个值。我是根据用户当前输入消息的长度动态调整的用户的输入信息量少的时候 top_k 取小一点避免背景记忆喧宾夺主输入包含明确技术名词或者任务指令时top_k 加大让模型更多参考历史记忆。这个动态策略实测效果非常好模型回答的上下文相关性提升很明显。3.3 注入机制用“记忆提示块”拼进系统提示词召回出来的记忆不能直接裸放在对话里那样格式混乱而且容易被模型忽略。我封装了一个标准化的记忆提示块在每次开启新对话时作为系统提示词的一部分拼进去让模型明确知道这些是外置记忆需要参考但不要当成当下对话的输入。def build_memory_prompt(memories: list[dict]) - str: if not memories: return lines [【长期记忆档案】以下是从历史对话中提取的相关记忆\n 可据此了解用户的习惯和项目脉络但不要直接复述或过度依赖。] for i, mem in enumerate(memories, start1): lines.append(f记忆{i}: [{mem[type]}] {mem[content]}) return \n.join(lines)注入位置放在系统提示词里紧跟在角色定义之后、正式对话内容之前。注入量严格控制在总上下文的 8% 以内超过这条线宁可不注入也不强塞这是实测下来的甜点区。一开始我堆了很多记忆进去12% 左右的占比结果模型经常在回答问题前先给记忆里的内容做个意识流回顾废话明显增加后来降到 8% 以内才恢复正常。另外要强调的是注入记忆时我在提示词里明确要求了“不要直接复述这些记忆”。因为刚开始不加这个约束的时候模型经常把记忆里的内容当成当前问题的直接答案问一个开箱即用的工具推荐它上来就复述上次对话提到的那个工具名显得非常僵硬。加了“仅供参考”的说明之后模型的回答明显更自然记忆变成了背景知识而不是模板句。4. 实操过程从零配置到跑通全流程4.1 初始化配置存储目录与模型端点整个 claude-mem 不需要复杂的服务编排初始化过程是零配置的。数据全部落在单个数据目录下结构包含三个部分SQLite 主数据库文件、向量索引目录、日志目录。mkdir -p ~/.claude_mem/{db,vectors,logs}第一次启动时工具会检查数据目录是否存在不存在就自动初始化数据库表结构。数据库里核心就两张表一张存记忆条目本体包含内容、类型、重要度、置信度、来源对话 ID、创建和最后访问时间另一张存所有历史对话的元数据包含会话 ID、起止时间、对话摘要。向量索引目录和数据文件分离方便备份的时候把数据库文件直接拷走向量做了持久化序列化存储。配置项上我最关注的是两个端点设置主对话模型端点和信息抽取模型端点。日常对话用一个快速模型信息抽取我用同一个模型但 prompt 单独走了完整长版本。实际使用下来抽取的质量和推理模型的选择关系不大关键在于提示词的设计用常规模型跑抽取反而速度快、成本低。4.2 接入对话流程三种接入方式对比跑通 claude-mem 的方式按你的使用习惯有几种我实测下来最推荐的是命令行包装器模式。所谓包装器就是一个简易的交互式脚本在这个脚本里你先输入一条消息程序内部把消息发给 Claude API拿到回复之后自动把这一轮的 message pair 送进抽取器抽取器返回的 JSON 记忆立即持久化。整个流程对用户只有一句话的感知差异就是每次对话结束后终端会闪出一行小字“已新增记忆 3 条”。第二种方式是 API Proxy 模式适合你平时已经通过脚本或者网页客户端调用模型、不想迁移既有工作流的情况。做法是把 Claude API 的 base_url 指到本地一个代理服务上代理服务在转发请求前先查询记忆库把相关记忆注入系统提示词收到回复后再异步执行记忆抽取和写入。这个方案的优点是接入面广缺点是要多维护一个常驻进程。第三种是我最不推荐的手动导出对话历史然后离线批量抽取。适合你有大量历史对话想一次性导入记忆库的场景但不适合日常使用因为延迟和割裂感太强。我试过把过去三个月的几百条历史对话文本一次性扔进去批量抽取效果只能说勉强能用因为信息抽取器对脱离对话语境的长文本理解力有限很多关键记忆被漏掉了。4.3 记忆召回实测一个跨会话检索的真实例子讲一个实际跑通的场景来直观展示效果。前几天我在和 Claude 讨论一个数据迁移脚本的设计对话里提到了“不想用现成的第三方库打算手写一个解析器”这个决策。这句话被抽取器捕获后重要度打到了 8/10记忆类型被判定为“项目状态”。两天后我开启了一个全新对话输入是“帮我看看那个迁移脚本一直报编码错误怎么办”claude-mem 在后台把这句话向量化后从记忆库里召回了三条高相关记忆其中就包含那条“想手写解析器”的决策记录。于是 Claude 在回答时非常自然地带出了这个背景建议我先确认解析器在遇到畸形编码时的异常处理逻辑有没有覆盖到而不是一上来就推荐我换个现成库。这个例子说明记忆的跨会话价值不在于直接提供答案而在于给模型补上“你为什么选择这条路”的上下文。模型基于这个上下文给出的建议贴合度和可用度完胜无记忆的冷启动状态。4.4 对话历史压缩防止 SQLite 无限膨胀用着用着你会发现对话元数据表和记忆条目表会慢慢变大。我设计了一套归档机制每完成十个会话就对历史对话摘要做一次二级压缩把超过三十天的会话摘要全部归并成一条月度总摘要原来的会话摘要如果没有任何关联记忆就直接删除。记忆条目的清理逻辑则更讲究按时间衰减和访问频率双维度计分。每个星期跑一次定期任务把所有记忆条目的综合热度分重新计算低于阈值且不是重要度 9 以上的就移入归档状态不在常规检索里出现只有当用户手动搜到才能看到。这套机制保证了工具在长期运行下响应速度不劣化。def decay_memories(conn): now int(time.time()) cursor conn.execute( UPDATE memories SET importance importance * 0.95, is_active CASE WHEN importance 4.0 AND confidence 0.4 THEN 0 ELSE is_active END WHERE strftime(%s, last_accessed_at) ? - 14 * 86400, (now,) ) conn.commit()5. 常见问题与排查技巧实录5.1 抽取器漏数据先查对话上下文最常遇到的坑是抽取器对某些记忆毫无反应。排查的第一步永远不是改提示词而是回去看原始对话里这条信息所在的上下文长什么样。如果信息本身就藏在很长的技术讨论中间比如模型输出了一整段部署建议中夹杂了一句“用户想用容器化的方式部署”这种语境下抽取器把它漏掉的概率确实高。解决方案是让抽取器在抽取时也输出信息出处也就是它在识别到候选记忆时同时产出“对应的原文片段”这样漏报时我能快速定位。如果抽取器连明显的关键句都漏了那就要检查是不是这次对话文本超出了抽取器的单次处理长度限制。我加了一个滑动窗口机制把超长对话拆分到多个处理批次里每个批次重叠保留最后几条消息作为上下文衔接很大程度上缓解了长对话尾部信息丢失的问题。5.2 召回结果不相关调整向量权重的次序召回结果不相关的核心症状是模型拿到了一些强行拼装在一起的旧记忆回答显得牛头不对马嘴。我排查后发现问题常常出在向量检索的召回环节把太多相似但无用的记忆一并返回了。比如用户问了“今天天气如何”向量库里有几条“之前聊过天气相关规划”的记忆也一并命中了相关性打分居然还挺高。这里的优化手段是给记忆检索加一个前置过滤条件当用户当前输入短于四个汉字时强制跳过向量检索只在最高重要度的记忆里寻找直接匹配项。输入明显长这个阈值时才执行完整检索。这个过滤条件生效后误召回的频率大幅降低。5.3 模型开始复述记忆剥夺“复述权”模型把记忆内容当答案直接复述出来是最容易被发现也是最破坏体验的问题。这个问题的根因一般都在注入提示词的措辞上“请参考以下记忆”这个措辞太弱了模型被引导到复述的方向。我最终固定下来一段措辞原文是“记忆仅供参考你可以使用记忆中的信息作为回答的背景知识但应当基于当前对话内容组织你的回答”。如果调整措辞后仍然出现复述行为还有一个降级方案是降低注入记忆的条数从 8 条降到 5 条甚至 3 条。因为当模型上下文里塞进了过多外置记忆时它会产生一种“必须回应这些记忆”的强迫症控制条数能直接解决这个问题。我现在的默认值就是 top 5 条只在用户主动搜索历史时才放宽到 8 条。5.4 数据膨胀导致检索变慢启动期全量扫描改为分片索引暴力扫描在记忆规模达到几万条之后单次检索耗时还是会缓慢上升从几十毫秒涨到一两百毫秒。虽然对个人场景还能接受但每隔一段时间还是要做个清理。我的做法是按月份对向量索引切片每周跑一次定时任务把超过九十天的记忆向量从活跃索引中摘除转存到归档索引。活跃索引查询永远只扫描最近三个月的记忆常用记忆和经验知识基本都在这个时间窗口内感知不到任何损失。6. 扩展方向和个人体会6.1 后续还能加入的增强能力claude-mem 现在的架构预留了两个明显的扩展空间。一个是多模态记忆目前它只处理纯文本信息但真实使用中经常会有图片、截图、文件这类非文本内容出现在对话里。提取图片里的关键信息需要额外接视觉理解模型这个扩展技术上没有壁垒只是成本会高一些。另一个重要扩展是主动记忆修正。现在记忆的更新是被动的只有用户在对话中提出新信息时抽取器才知道旧记忆要修正。理想状态是定期的“记忆复查”机制把高权重记忆拿出来让模型检查一遍结合最近对话审视是否存在过时或矛盾的条目。这个功能我下一个版本已经预定要加了。另外还可以把记忆导出为 Markdown 格式方便人直接阅读和管理也可以配合外部知识库工具做二次消费。这个数据自由度的价值在长期使用后才会真正显示出来它意味着你沉淀下来的记忆不会被困在单一工具里。6.2 关于“AI 记忆”这件事的反思工具本身的技术细节聊完了最后说点个人的真实感受。做 claude-mem 一个多月我最大的体会是外部记忆层的价值不在于让 AI 看起来更聪明而在于让 AI 的工作方式更接近一个真正的长期协作者。当你和 AI 对话时它不需要你每次重复交代背景不需要你反复解释“上次讨论过那个方案”这种体验上的提升非常巨大甚至比换一个更大的模型带来的提升还明显。但同时也必须警惕一个反向问题记忆不代表忠实记录一切。一个不筛选的记忆系统最终会变成一个陈旧信息的垃圾场反而会干扰模型对当下的判断。做记忆系统本质上是在做信息筛选和遗忘决策这件事比大多数人想象中的重要得多。好的记忆系统应该像人脑一样知道什么该记、什么该忘、什么时候该想起什么。我现在已经把 claude-mem 融入了日常几乎所有和 Claude 的交互里包括技术问题的讨论、个人计划的推演、甚至一些长期项目的推展。它带来的便利已经让我无法再回到没有记忆系统的 AI 工作流中毫无记忆的 AI 对话对我来说已经像用一个不存档的文本编辑器一样别扭。如果你也被 AI 对话失忆困扰这个方案值得花一个周末动手复刻。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询