大模型长期记忆增强:技术路线与最小可用系统实践 Augmenting Long-Term Memory——给大模型增强长期记忆。这个标题听起来像一篇认知科学论文但这两年真正让开发者头疼的其实是同一个词的另一张脸我用 AI 助手讨论了一下午的需求把背景、约束、技术选型全部说了一遍第二天新建一个对话它认认真真问我请问您的需求是什么 不是模型变笨了而是大模型本质上没有“长期记忆”。它的每一次回答都只发生在当前上下文窗口里。这个窗口再大也只是“这次谈话的临时便签”不是“下次还能想起来的长期笔记”。很多人会想那我直接把历史记录全塞进去不就行了结论是窗口大并不等于记得住更不等于用得起来。真正有效的方法是把记忆从模型内部搬到模型外部设计成一套可存储、可检索、可取舍的记忆系统。这篇文章我会围绕“增强长期记忆”这个话题讲清楚为什么上下文窗口不能替代长期记忆主流的技术路线有哪几条以及怎么从零搭一套最小可用的记忆增强系统。里面会有可运行的思路、参数解释和排查路径适合已经写过一段时间 Prompt 或接过大模型 API、准备把应用做得更像“真人助手”的开发者。1. 为什么上下文窗口再大也不是长期记忆1.1 窗口是工作台不是仓库大模型的每次推理输入都被限制在一个上下文窗口内。这个窗口可以装下几万字甚至几十万字但它解决的是“单次对话能容纳多少内容”而不是“跨会话记得什么”。你可以把上下文窗口理解成一张工作台工作台再大你下班前不收进柜子里第二天依然是空的。模型本身没有“保存后下次还能加载”的机制。每个新会话都是一次全新的推理从零开始读你的输入。所以“把历史全量塞进上下文”这种思路本质上只是把仓库里的东西全部搬到工作台上每次干活前搬一次。这种方式在小规模、短周期场景里可行但一旦历史数据累积问题就会成倍放大。1.2 长上下文的三个隐性代价第一个代价是成本。Token 是按输入输出的总量计费的历史记录每多一天每次请求都要把当天之前的内容再付一次钱。用户如果每天产生几千 Token 的对话一个月后每次请求光历史输入就可能几万 Token成本不再是线性增长而是用户越用越贵。第二个代价是延迟。输入越长模型需要处理的内容就越多首字返回时间通常会明显变长。如果是一个交互型助手用户问一句“我上次那个方案改个标题”你要等十几秒才开始出字这个体验很难接受。第三个代价是注意力稀释。模型虽然能处理长输入但并不是对每个位置都一视同仁。大量不相关历史混在 prompt 里会让真正关键的信息被淹没。很多研究发现模型在长上下文场景下容易“迷失在中间”也就是开头和结尾的内容更容易被利用中间一大段反而容易被忽略。所以与其说“窗口不够大”不如说“把记忆全塞进窗口”这个思路本身就不是可持续的方案。1.3 记忆应该外置而不是硬塞给模型增强长期记忆的核心思路不是让模型自己记住而是让应用替你记住在需要的时候按需取用。这和人的认知机制其实很像。我们不会在每次说话前把从小到大的所有经历全部回忆一遍而是根据当前问题主动从长期记忆里检索“相关的片段”再放到工作记忆里处理。大模型应用也应该采用同样的策略长期记忆存在外部系统里可以是数据库、向量库、文件甚至是一份结构化的 JSON。工作记忆当前对话的上下文窗口只放“这次要用的内容”。检索器在每次回答前根据用户当前的问题从长期记忆里找回相关度高的信息再注入上下文。只有把记忆外置才能做到“该记的记下来该用的调出来该忘的删得掉”。2. 增强长期记忆的三条技术路线2.1 路线一全量历史拼进 Prompt最朴素的做法是把用户的所有历史记录直接拼进 system prompt 或对话前缀里。适合的场景是用户量很小、历史很短、查询频率低比如一个内部知识库问答机器人每天只有几个人问几次问题。这种情况下全量输入确实最简单不需要额外的存储和检索写几十行代码就能跑通。但它的上限非常明显。一旦对话轮次变多或用户数量变大Token 成本和延迟都会迅速上升。更重要的是它没有“选择性”历史里真正有用的可能只有几行但你不得不把所有内容都喂给模型让它自己找。我给这类场景的建议是选一个时间窗口比如只保留最近 7 天的记录超过的直接归档。如果归档后还需要用到更早的信息再升级到检索方案。2.2 路线二向量检索式记忆这是目前最主流、也最容易落地的方案通常被归入 RAG检索增强生成的范畴。基本流程是把对话记录切分成“记忆块”。用 Embedding 模型把每个记忆块转成向量。将向量和原文一起存入向量数据库。用户发起新问题时把问题也转成向量在向量库里做相似度检索。取出 top_k 个相关记忆拼进 prompt再让模型回答。这套方案解决了一个关键问题不用再把所有历史都搬进上下文而是按需检索。存储和检索都是可扩展的历史再多也不怕因为每次只取最相关的几条。但它也有自己的问题检索到的内容未必是用户真正关心的。比如用户半年前提过一个“想去云南旅行”的话题今天问的是“帮我写一个周报”系统可能因为“旅行”和“放松”有语义相似而把旅行记忆检索出来结果反而干扰了周报回答。所以向量检索还需要配合阈值、排序规则和过滤条件。2.3 路线三分层记忆子系统如果你想让助手像真人一样记住用户的偏好、未完成的任务、重要事实同时又能自动遗忘过时信息只靠简单的向量检索还不够。这时候需要引入分层记忆。所谓分层记忆是参考了认知科学里的工作记忆和长期记忆区分把记忆系统拆成几层工作记忆当前对话上下文负责“正在处理的事”。情景记忆最近几次对话的原始记录或摘要负责“最近发生过什么”。语义记忆从对话中提炼出的长期事实、偏好、目标负责“用户是谁、想要什么”。遗忘机制定期把不再重要的记忆下沉、合并或删除。这个思路在 MemGPT、Mem0 等开源项目中有比较典型的体现。它们通常会在对话结束后启动一个“记忆更新”流程让模型自己判断哪些信息值得保存并生成结构化的记忆条目在新的对话开始时把相关记忆注入系统。分层记忆的优点是上限最高缺点是实现复杂度也最高。它不是简单的“搜索 拼装”而是需要一套完整的记忆生命周期管理提取、存储、检索、更新、冲突处理、遗忘。2.4 三条路线怎么选维度全量拼入向量检索式分层记忆子系统实现成本极低中等较高需要的基础设施无Embedding 模型 向量库向量库 任务调度 LLM 提炼流程适合场景少量历史、低频查询知识问答、历史对话检索个人助手、智能体、陪伴类产品主要局限成本高、易淹没关键信息相关性未必准确缺少提炼工程复杂需要长期维护可扩展性差好最好我的判断是这三条路线不是互斥的成熟方案通常是组合使用。刚开始做 MVP可以先走向量检索式跑通之后再在对话结束时加上一层记忆提炼进入分层记忆的范畴。不必一上来就造一个庞大的记忆系统。3. 最小可用闭环三步给你的应用装上长期记忆下面这套流程是我认为一个“最小可用”的长期记忆增强方案。它不依赖特定平台也不绑定某个框架核心思路是用三次调用来完成“记忆生成、记忆存储、记忆检索”的闭环。3.1 第一步把对话提炼成记忆条目原始对话不能直接作为长期记忆。一方面它太长存起来占空间、检索时噪声大另一方面它是零散的缺少提炼。更好的做法是让模型在对话结束后把值得保留的信息抽取成结构化条目。这里可以写一个非常简单的函数import json def extract_memory(dialog): prompt f 请从一段对话中提取值得长期保留的信息输出 JSON字段如下 {{ key_facts: [重要事实列表], preferences: [用户偏好列表], unfinished: [未完成任务列表], summary: 这段对话的一句话摘要 }} 注意 - 只保留对后续对话有价值的长期信息。 - 临时性话题、寒暄、重复内容不要保存。 - 如果某个字段没有信息就返回空列表。 对话内容 {dialog} # 调用你选定的 LLM拿到 json_str resp llm_call(prompt) return json.loads(resp)这一步是整个系统的核心。因为长期记忆的质量很大程度上取决于“提炼”这一步的质量。如果模型从对话里提炼出来的都是废话后面检索得再好也没有意义。我一般会要求模型遵循一个原则宁可少存不要乱存。少存几条至少不会污染后续对话乱存几条会让助手在关键时刻给出错误前提。3.2 第二步记忆向量化并写入向量库得到结构化记忆后需要把它转成向量方便后续按语义检索。def save_memory(memory, user_id): # 把结构化记忆拼成一段可检索的文本 text memory[summary] text 重要事实 .join(memory[key_facts]) text 用户偏好 .join(memory[preferences]) text 未完成 .join(memory[unfinished]) vector embedding_model.encode(text) # 生成 embedding vector_db.insert( collectionfmemory_{user_id}, vectorvector, payloadmemory # 原始结构化数据一起保存 )这里有两个容易踩坑的地方。第一Collection 一定要按用户隔离。如果你把所有人的记忆全放在一个集合里先不说语义检索可能跨用户串内容更重要的是用户隐私完全没法保证。后面讲工程化时我会再展开。第二Embedding 模型必须全局统一。生成存储向量和生成查询向量时必须用同一个模型而且要固定版本。很多项目出问题就是因为中途换了 Embedding 模型新老向量分布不一致导致相似度计算失真。3.3 第三步回答前先检索召回再组装 Prompt用户发起新问题时系统不要直接调用大模型而是先做一次记忆检索把相关的长期记忆取出来再交给模型。def answer_with_memory(user_id, question, llm): query_vector embedding_model.encode(question) candidates vector_db.search( collectionfmemory_{user_id}, vectorquery_vector, top_k5 ) memory_block format_memories(candidates) system_prompt ( 你是一个有长期记忆的助手。\n 以下是关于该用户的历史记忆如果与当前问题相关请合理使用\n f{memory_block}\n 如果记忆与当前问题无关请忽略这部分内容。 ) return llm.chat( systemsystem_prompt, userquestion )这里有一个容易被忽略的点记忆检索出来不代表模型一定会用。如果 prompt 里写入位置太靠后或者被后面的指令覆盖模型可能完全无视这些记忆。我建议把记忆放在 system prompt 的开头并用明确的话术告诉模型“这是历史记忆必要时使用不需要时忽略”。3.4 判断 MVP 是否生效的标准一个简单的验收方法让用户在第一轮对话里说“我最喜欢简洁的回答”结束对话然后新开一个会话问“你对我的回答风格有什么建议吗”。如果助手能回忆起偏好说明这套最小闭环已经跑通了如果它完全不知道说明某个环节断了需要进入排查流程。记住一个判断标准一条记忆如果不能在下一次对话中被主动取出来用它就只是“存储”不是“记忆”。4. 真正要调的四个参数和一个机制4.1 记忆条目的粒度记忆不一定要按固定字符数切块更好的方式是按“事件”切块。比如用户在一次对话里聊了三件事一是最近在学 Python二是下个月要去出差三是喜欢喝美式咖啡。这三件事就应该分别成为三条独立记忆而不是揉在同一段文本里。粒度太粗检索时容易带回大量无关细节粒度太细又会把同一件事拆得七零八落召回时缺少上下文。我建议在提炼阶段就把“一件事”作为最小单位一条记忆只承载一个事实、一个偏好或一个任务。4.2 top_k 与相似度阈值top_k 决定每次检索会带回多少条记忆。太小可能漏掉关键信息太大则会引入大量噪声还会占用上下文窗口、增加模型负担。一般建议从 3 到 5 开始根据效果调整。相似度阈值同样重要。向量检索会给每条候选记忆打一个相似度分数。如果阈值设得太低无关记忆也会被当成相关记忆注入设得太高又可能一条都召不回来。建议先看一批真实查询的相似度分数分布找到“相关记忆通常高于某个分数、无关记忆通常低于这个分数”的分界线再设定阈值。参数建议初始值作用过高/过低的影响top_k3 ~ 5控制召回数量过少漏信息过多有噪声相似度阈值0.7视模型而定过滤无关记忆过高漏召回过低污染回答记忆条数上限500 ~ 1000控制存储成本过大检索慢过小记忆不完整注意不同 Embedding 模型输出的分数含义不同不要直接照搬别人的阈值一定要结合自己的数据做实验。4.3 摘要压缩的触发时机随着对话变多记忆条目也在膨胀。如果不做压缩十年后一个用户的记忆可能存了十几万条检索效果会明显下降。常见的做法是分层压缩当某个主题的记忆条数超过一定数量时触发一次更高层次的摘要把旧记忆合并成一段总结原始细节则进入更冷的存储层或直接删除。触发时机可以有三个维度条数阈值比如同一主题超过 50 条就压缩一次。时间维度比如超过 30 天的零散记忆自动合并成月度摘要。重要性评分模型在记忆提炼时给每条记忆打重要性分数低分记忆优先被合并或删除。4.4 记忆排序相关性、时效性、重要性最简单的是只按相似度排序取前几条。但在真实场景里这个方案往往不够用。比如用户昨天说“这周要去北京出差”今天问“帮我推荐一下北京的咖啡馆”。这时除了“北京咖啡馆”的语义相关事件的时效性也很关键。昨天的出差信息是近因记忆应该优先被提取。更合理的排序策略是综合三路分数语义相关分来自向量检索的相似度。时效分越新的记忆权重越高。重要分来自记忆提炼时模型打的标签。最终得分可以是这三者的加权组合。具体权重没有通用答案需要根据产品场景回归调整。比如知识问答类应用语义相关分的权重应该最高个人助手类应用时效和重要性的权重则需要提上来。4.5 遗忘机制记太多不等于记更好很多人在做记忆系统时只关心“怎么记住”不关心“怎么忘”。但在实际使用中遗忘机制是长期记忆系统里最难、也最容易被忽视的部分。记忆如果不及时清理会出现三类问题过期信息误导模型比如用户已经离职了系统还保留着他原公司的项目记忆。冲突信息污染回答比如用户上个月说“不喜欢喝咖啡”这个月说“开始迷上手冲咖啡”旧记忆会因为相似度高而被优先召回。检索效率下降记忆越多每次检索的计算量和噪声也越大。遗忘机制可以做成显式的也可以做成隐式的。显式方式是有“记忆管理”入口用户可以查看系统记住了什么并手动删除。隐式方式是系统根据某个规则自动降权或删除比如一段时间没有被检索到的记忆自动降级。我的建议是MVP 阶段先做手动遗忘让用户能直接删除记忆条目等产品稳定后再考虑自动遗忘规则。自动遗忘一旦做错用户会明显感觉助手“失忆”这种体验比“记太多”更糟糕。5. 记忆就像没生效先别怀疑模型按链路查很多人在搭建完记忆系统后发现模型还是“不记得”。第一反应往往是换更强的模型或者调 Prompt。但实际上问题往往出在系统链路的某个环节。5.1 一条可复用的排查链路把记忆流程拆成五个环节按顺序排查输入侧用户提问是否传到了检索模块有没有因为拦截、超时、路由问题根本没触发检索记忆生成对话结束后提炼模型有没有真正生成结构化记忆返回的 JSON 是不是空的存储记忆是否成功写入了向量库写入的 Collection 是否和检索时用的是同一个检索当前查询是否召回了记忆召回数量是否为零召回的条目是否和问题真正相关注入召回的记忆有没有被拼进最终 Prompt在 Prompt 里的位置是否会被后面的指令覆盖5.2 常见失败场景及确认方式现象根因确认方式完全不记得任何历史存储环节断了或 Collection 名不匹配打印向量库中的条数确认写入成功偶尔记得偶尔不记得top_k 太小或相似度阈值太高打印每次检索的相似度分数看相关记忆是否低于阈值记得但回答里不用注入位置不对或 Prompt 里明确说“忽略历史”打印完整 Prompt看记忆是否真的在模型可读范围内检索到的记忆完全不相关Embedding 模型换了版本或 Collection 没有按用户隔离检查 Embedding 模型版本检查检索范围记忆有但被后续指令覆盖system prompt 太长模型只跟随后半段指令把记忆放到 system prompt 开头并简化 prompt5.3 最容易被忽略的三个坑第一个坑是“只存不检”。我见过不少项目历史对话确实存进了数据库但用户新提问时根本没有触发检索逻辑代码直接走的是普通 LLM 调用。这时候系统自然“失忆”但问题根本不在模型。第二个坑是“检索到了但没打印日志”。排查记忆问题最重要的工具是日志。我建议在开发阶段至少把以下信息打印出来用户问题、召回的每条记忆、相似度分数、最终注入 Prompt 的文本。没有这些日志排查基本上靠猜。第三个坑是“忽略了 Prompt 长度的截断”。如果相关记忆很多系统可能为了省 Token 做了截断把真正关键的几条截掉了。要检查最终发送给模型的 Prompt 是否完整而不是只看检索返回了几条。6. 从演示到生产长期记忆还差三块拼图6.1 权限与隐私记忆必须按用户隔离这一点怎么强调都不过分。一个长期记忆系统本质上就是用户的数字化个人档案。如果权限控制没做好轻则用户 A 的偏好被用户 B 看到重则敏感信息泄露。生产环境至少要满足几个要求每个用户的记忆集合严格隔离Collection 或表中必须带 user_id 维度。检索时强制带上用户 ID 过滤而不是只靠 Collection 命名。记忆内容在入库前要做敏感信息识别和脱敏尤其是手机号、地址、身份证这类数据。提供用户可见的“记忆管理”能力让用户能查看、删除自己的记忆。有些场景下用户只是临时问一个问题并不希望这个信息被长期记录。这时候可以加一个“本次对话不记录”的开关从源头中断记忆提炼链路。6.2 记忆冲突与更新新记忆怎么覆盖旧记忆用户是会变化的。上个月他不喜欢喝咖啡这个月他迷上了手冲咖啡。如果不处理冲突检索系统很可能把两条矛盾记忆都召回模型就会输出“用户不喜欢咖啡但最近迷上手冲咖啡”这种让用户困惑的回答。处理冲突的基本思路是给记忆加上“更新时间”和“冲突分组”。当新记忆与旧记忆属于同一分组时标记旧记忆为“失效”或“降权”而不是保留两条并行的矛盾记录。这个机制在 MVP 阶段可以做得简化当模型提炼出一条新的偏好记忆时如果旧记忆和新记忆语义高度相似但内容不一致则把旧记忆的权重降低权重降到一定阈值以下就不再参与检索。6.3 评估与回归记忆系统也会让回答变差长期记忆系统不是加了就一定变好。检索错误、记忆提炼错误、过期信息都可能让回答比没有记忆时更差。所以在迭代记忆系统时要建立一套回归测试集。做法是准备 20 到 50 个真实用户问题。对每个问题标注“理想情况下需要用到哪几条记忆”。修改记忆逻辑后跑一遍测试集检查召回率有没有下降最终回答是否比上一版更好注意评测对象不能只看检索准确率还要看最终回答质量。因为有些记忆即使召回正确也会因为 Prompt 组织不当而被模型忽略。6.4 落地路径不要一上来就做复杂记忆系统最后给一个务实的路径建议。第一阶段先做 session 级记忆。也就是说在单次对话里把之前几轮的对话摘要注入上下文。这一步几乎不需要额外基础设施很快就能看到效果。第二阶段再做用户级记忆。引入向量检索让用户跨会话能被记住核心事实和偏好。这就是前文讲的最小可用闭环。第三阶段才考虑项目级或团队级记忆引入分层记忆、遗忘机制、多角色权限以及自动评估系统。很多团队直接跳到第三阶段结果做出来的记忆系统既能存储、也能检索但因为缺少最基本的“按用户隔离”和“冲突处理”上线后反而让用户体验变得更差。增强长期记忆这件事真正的难点不在技术选型而在“边界意识”记住什么、忘掉什么、什么时候用、什么时候不用。这是参数调不出来的产品层面的判断。下一次当你发现助手“不记得”时别急着骂模型。先去看看你的记忆链路是不是已经从“存储”走到了“提取”再从“提取”走到了“使用”。大多数人做的其实只是第一个词而已。