给AI Agent装上长期记忆:从失忆到“记住你”的实现指南

发布时间:2026/9/11 9:09:50
给AI Agent装上长期记忆:从失忆到“记住你”的实现指南 你和一个AI Agent聊了整整一个下午把你的工作习惯、养猫经历、最近在琢磨的副业计划全讲了。第二天你打开新会话轻描淡写地问一句“你还记得我昨天说的那个方案吗”它礼貌地回你一句抱歉我们还没有聊过这个话题。没错它把你忘得干干净净。这不是产品bug而是当下绝大多数AI Agent的底层宿命。大模型本身是“无状态”的这次对话发生在哪个会话、昨天聊过什么对它来说完全是黑历史。这也是我在这个系列前面两篇聊完Agent的运行逻辑、手把手搭出一个最小可用Agent之后专门拿一篇出来写“记忆”的原因没有记忆的Agent只能算高级问答机有了记忆它才开始像一个真正“认识你”的助手。这篇我打算按这个顺序讲先拆解Agent为什么记不住你再说清Agent记忆在工程上到底是什么接着对比三种主流落地方式然后用一套可以直接跑起来的代码演示怎么给Agent装长期记忆最后把我实际踩过的坑和记忆管理里最容易被忽略的遗忘、隐私问题一起交代清楚。无论你是想给个人助手加记忆还是在公司产品里做用户画像与多轮服务这篇都适合你。1. 失忆的Agent为什么大模型天生记不住你1.1 症状比你想的更普遍先说一个很容易被忽略的事实现在市面上大部分Agent产品所谓的“记忆”其实都是假的。你关闭页面再打开它不认识你你换一台设备登录它不认识你甚至你在同一个对话里聊了五十轮它也可能把第二十轮你明确说过的话给“忘”了。很多人以为是产品经理没做好其实根子出在模型架构上。大模型的推理过程是这样的你输入一段文本模型根据这段文本预测下一个token再一个token一个token地生成回复。整个过程里模型能看到的只有你这次请求里塞进去的内容对话之外的世界对它来说是不存在的。换句话说模型在每次推理时都“失忆”了。这就像你请了一位非常有经验的家政顾问他脑子里装着大量专业知识但每接一次活就把上一家的文件全部清空不留下任何档案。你指望他记住“你家猫叫煤球”他做不到因为“煤球”这个名字根本不在他大脑里除非你每次都写在纸条上递给他。1.2 根因模型权重是“冻住”的这里有个更容易混淆的点。很多人以为“AI会学习”所以它应该能记住我。但“学习”和“记忆”在工程上是两码事。大模型的“知识”沉淀在模型权重里是在训练阶段用海量数据迭代出来的。训练一结束权重就被“冻住”了日常对话无论进行多少轮都不会修改这些权重。你每一次输入对模型来说都是一次全新的推理它不会因为昨天陪你聊了三个小时权重里就多出一条“用户喜欢美式咖啡”的记录。所以要让Agent记住你只有一条路把记忆放在模型“大脑”之外然后在每次推理前把相关的记忆找出来塞进提示词里给它看。这就像给那个家政顾问配了一本随身的客户档案夹每次上门前助理先把和你有关的页面翻出来放在他桌上。搞明白了这一点后面所有的方案都围绕同一个核心问题展开记忆放哪里、怎么存、每次怎么取。1.3 为什么说记忆是Agent的分水岭没有记忆的Agent本质上就是一个“带工具和任务分解能力的聊天机器人”它能答好单次问题但很难做好持续性的工作。真正让Agent从“工具”变成“搭档”的恰恰是记忆这层能力。个性化需要记忆。同一个健康管理Agent对糖尿病患者和马拉松爱好者说的话应该完全不同但它怎么知道用户是谁靠的就是历史交互里沉淀下来的用户画像。多轮任务的闭环也需要记忆。比如让Agent帮你推进一个项目它需要记得前几轮已经确认过的约束条件、已经联系过的接口人、已经跳过哪些坑否则每一轮都从零开始任务永远推不动。我个人的判断是记忆能力决定了Agent的上限。推理能力决定它“聪不聪明”记忆能力决定它“可不可靠”。一个聪明的Agent如果每次都不记得你你很快就会失去耐心一个记忆系统完善的Agent哪怕能力平平也会因为“越来越懂你”而让你离不开。这也是为什么各大Agent框架都把Memory模块当成核心组件在设计。2. 记忆不是“聊天记录”Agent记忆的四种类型与分级2.1 从认知科学借来的分类法直接映射到工程“记忆”这个词在人类身上是一整套复杂系统Agent要做的不是复刻人脑而是借它的分类思路来设计结构。业界最常用的框架是把Agent记忆分成四种类型每一类对应不同的技术实现。记忆类型人脑里的含义Agent里的含义工程实现工作记忆短期此刻正在想的东西当前对话上下文、正在执行的任务状态Prompt上下文窗口、Session状态情景记忆经历过的事用户说过的话、偏好、历史交互事件向量库、记忆条目语义记忆对世界的知识领域知识、产品文档、FAQ知识库、RAG检索程序性记忆怎么做事工具调用方式、工作流、决策规则System Prompt、Tool Schema四类记忆里最容易被人忽略的是“程序性记忆”。它记的不是“你是谁”而是“这个Agent自己该怎么干活”。比如一个客服Agent应该先安抚情绪再问订单号应该用哪种语气回复投诉这些写在System Prompt里的规则本质上就是它的“肌肉记忆”。我之前见过一个Agent历史问答都接得挺好但连“遇到差评要先道歉”这种基本规则都没写进Prompt结果把用户气到投诉这就是程序性记忆缺失。2.2 为什么不能“全塞进上下文”就完事看到这里你可能会想那我把所有聊天记录一股脑全塞进Prompt里不就行了很遗憾这条路走不通而且有三个硬伤。第一个是token窗口的上限。主流模型的上下文窗口从几万到几十万token不等但真实业务里持续对话一两个月哪怕每天只聊50轮累计文本量很容易突破百万级。窗口再大也装不下完整历史。第二个是成本。Prompt里的每一个token都要算钱把大量历史文本反复喂给模型推理成本和延迟都会显著上涨。而且这些历史里九成是“今天天气不错”“好的”“谢谢”这类毫无价值的噪音纯属浪费。第三个最隐蔽叫“注意力稀释”。研究已经发现模型对Prompt中间部分的注意力会衰减术语叫“lost in the middle”。当上下文长到一定程度哪怕是关键信息模型也很容易漏看。你把整本聊天记录塞给它它反而记不住最重要那句“我猫叫煤球”。所以工程上真正要做的是“挑选记忆”而不是“堆砌记忆”——从海量历史里把和当前问题最相关的那几条找出来精准地放到模型面前。2.3 记忆分级哪些必须记死哪些按需召回设计记忆系统时我建议先做分级不要把所有信息一视同仁。我的经验是用一个“冰箱模型”来类比贴在冰箱门上的便利贴用户的核心事实比如名字、偏好、绝对不能搞错的约束。这类记忆必须每次对话都加载优先级最高。冰箱里的保鲜盒发生过的事件比如某次订单投诉的处理过程、上周聊过的项目细节。这类记忆不用每次加载但在相关话题出现时需要能快速找回来。垃圾桶里的碎纸片闲聊内容、临时状态、一次性信息。这类记忆应该当天清理不进入长期存储。我实际搭建时会把第一类放进固定的System Prompt常量里第二类放进向量数据库按需召回第三类干脆只留在当前Session的上下文里Session一结束就消失。这个分级能帮你省掉大量冤枉成本也能避免Agent被琐碎信息干扰。3. 主流记忆方案上下文拼接、向量检索、分层记忆怎么选3.1 方案A把所有相关历史塞进上下文窗口最朴素的做法是把用户最近N轮对话、或者全量历史摘要直接拼进Prompt。很多早期的客服机器人就是这个逻辑把会话数据从数据库里读出来拼在System Prompt后面。这个方案的好处是简单直接几乎不需要额外的架构设计查一下数据库就能用。但它有几个明显的天花板其一跨会话记忆很难做因为“相关历史”到底取哪些得你自己写规则去筛其二无差别拼接很快会撞上token窗口和成本上限其三历史里噪声太多时模型的回复质量会被明显拉低。结论是这个方案只适合做Demo、做极短会话周期的工具类Agent。如果你目标是一个长期陪伴型或个人助理型Agent趁早换方案B。3.2 方案B向量检索式记忆目前的主流选择向量检索是目前给Agent加记忆的主流方案思路一句话就能讲清把历史记录和当前问题都转换成高维向量然后用相似度计算找到“意思上最接近”的历史片段再把这些片段带进Prompt。举个例子。“我养了一只猫叫煤球”和“我家宠物叫什么”字面上没有重合的词但语义上高度相关。传统的关键词搜索根本匹配不上而向量检索可以轻松把它们关联起来因为这两句话在向量空间里的距离足够近。实际落地时这套方案要跑通四个环节Embedding模型负责把文本变成向量向量数据库负责存储和检索相似度算法负责排序召回Prompt组装负责把结果拼给模型。这四个环节里Embedding模型和向量数据库的选择弹性很大从OpenAI的Embedding API到本地开源的BGE系列都有人在用数据库从轻量的Chroma到企业级的Milvus、pgvector也各有适配场景。方案B最大的优点是“跨会话”和“按语义召回”能让Agent真正拥有长期记忆。缺点也比较直白检索质量直接决定记忆效果如果召回的东西不对Agent记了也白记。这个话题我放到第5节详细说。3.3 方案C分层记忆架构更接近人脑的进阶版如果你关注Agent学术圈应该听过MemGPT或者Generative Agents那篇论文它们做的是“分层记忆”。核心思想是模仿操作系统的内存与磁盘管理把重要性高、正在用的记忆放进“内存”不常用但可能用到的记忆放进“磁盘”由Agent自己决定什么时候把某段记忆换入、什么时候换出。这个思路其实很性感。它让Agent不再被动地每轮检索而是拥有一个“记忆管理器”像操作系统调度进程一样调度记忆块。当对话进入某个主题时Agent主动从长期记忆里“换入”相关内容当对话结束时它又把当前讨论“换出”到长期存储里。但我不建议新手一上来就搞这套。因为它引入了一个非常棘手的问题Agent如何判断“什么重要”这需要额外的触发策略和调度逻辑实现复杂度成倍增加。我自己的看法是分层的思路值得借鉴但现阶段大部分业务场景用方案B就能打穿没必要为了炫技而上复杂度。3.4 选型建议汇总场景推荐方案理由单轮问答工具、30分钟内短会话A上下文拼接零依赖够用就好个人助手、客服、知识库型AgentB向量检索跨会话成本可控长生命周期复杂任务AgentC分层或B摘要归档需要主动记忆调度实体关系密集场景CRM、教育向量图谱混合关系查询更精准一句话总结没有银弹按你的交互时长和数据复杂度来选。绝大多数读者的第一套Agent记忆系统从方案B起步是最稳的。4. 实战30分钟给Agent装一套能“想起你”的长期记忆4.1 技术选型与准备工作下面进入动手环节。我选的技术栈是Python OpenAI接口 Chroma向量库原因很直接Chroma是本地嵌入式向量数据库零配置、跑起来快适合学习和验证OpenAI生态普及度高照着改也方便。需要说明如果你不想用云API把OpenAI调用换成Ollama本地模型也完全可行Embedding用nomic-embed-text这样的开源模型就好逻辑一模一样。我不依赖LangChain这类重量级框架就是为了让你看清底层逻辑而不是只会调封装好的类。安装依赖只要两行命令pip install openai chromadb同时把OPENAI_API_KEY配置到环境变量里。代码逻辑分三个部分记忆存储类、记忆写入器、对话主循环。4.2 核心代码能把记忆读出来、写进去的Agentimport os import time import chromadb from openai import OpenAI client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) chroma_client chromadb.PersistentClient(path./agent_memory) collection chroma_client.get_or_create_collection(memories) def save_memory(text: str, metadata: dict None): 把一条记忆文本向量化后写入Chroma embedding client.embeddings.create( modeltext-embedding-3-small, input[text] ).data[0].embedding collection.add( ids[fmem_{int(time.time() * 1000)}], embeddings[embedding], documents[text], metadatas[metadata or {timestamp: time.time()}] ) def recall_memory(query: str, top_k: int 3): 根据当前问题召回最相关的历史记忆 embedding client.embeddings.create( modeltext-embedding-3-small, input[query] ).data[0].embedding results collection.query(query_embeddings[embedding], n_resultstop_k) return results[documents][0] def extract_memorable_facts(conversation: str) - list: 用LLM从对话中提炼值得记住的用户事实 prompt f下面是用户与助手的对话。请找出其中值得长期记住的用户事实 比如用户的宠物、偏好、身份、计划等。输出为简洁的一句话列表每行一条。 不要输出推测内容。对话 {conversation} resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: prompt}], temperature0 ) lines [l.strip(- ).strip() for l in resp.choices[0].message.content.strip().split(\n) if l.strip()] return lines def chat_with_agent(): session_history [] while True: user_input input(你) if user_input.strip() in (exit, quit): break # 1. 根据当前问题召回相关历史记忆 related_memories recall_memory(user_input) memory_block \n.join(related_memories) if related_memories else 暂无 system_prompt f你是一个能记住用户的助手。 关于用户的历史记忆 {memory_block} 请结合记忆回应用户记忆里的信息比用户当前的说辞旧时以用户最新表达为准。 messages [{role: system, content: system_prompt}] messages session_history messages [{role: user, content: user_input}] resp client.chat.completions.create( modelgpt-4o-mini, messagesmessages ) answer resp.choices[0].message.content print(Agent, answer) session_history.append({role: user, content: user_input}) session_history.append({role: assistant, content: answer}) # 2. 达到一定轮次后从会话里提炼新事实并写入记忆库 if len(session_history) 4: dialogue \n.join([m[content] for m in session_history[-4:]]) facts extract_memorable_facts(dialogue) for fact in facts: save_memory(fact, {timestamp: time.time()}) session_history [] # 已提炼过的对话可以清掉减少重复计算 if __name__ __main__: chat_with_agent()这段代码的核心思路很简单每一轮对话前先从向量库里召回相关的历史记忆拼进System Prompt让模型“带着记忆”回答每攒够几轮对话就让大模型把其中有长期价值的事实提炼出来向量化后存进Chroma。4.3 代码背后的三个设计决策为什么我特意在存之前加了一步“LLM提炼”而不是把对话原文直接扔进向量库因为用户聊天内容里绝大多数是“嗯”“好的”“今天忙死了”这类噪音直接存会让向量库被无意义文本淹没导致检索出来的全是客套话。先用LLM提炼成“用户养了一只叫煤球的猫”“用户最近在学吉他”这种干净的事实条目检索效率会高一个量级存储成本也更低。为什么把记忆拼在System Prompt而不是User消息里因为模型对“背景信息”的注意力分配更好System Prompt被当作“任务设定”来理解而User消息是“当前要回复的内容”。把记忆放在System里相当于提前告诉模型“你是带着这些资料来接待这位用户的”回复会更自然地参考记忆而不是显得生硬。为什么每条记忆都尽量短因为embedding对长文本的语义捕捉会稀释一条200字的事件记录和一条30字的事实后者在检索时往往更精准。所以我让提炼指令输出“简洁的一句话”就是为了控制记忆条目的粒度。4.4 验收让它记住你然后“忘掉你”你可以按下面步骤验证这套系统是否真的实现了跨会话记忆第一次运行脚本输入“我养了一只猫叫煤球”再随便聊几句让它触发保存逻辑然后输入exit退出。第二次重新启动脚本数据库加载的还是同一个agent_memory目录直接问“我养了什么宠物”。如果一切正常Agent会从向量库里召回“用户养了一只叫煤球的猫”这条记忆然后告诉你“你养了只猫叫煤球”。这个验证里最关键的一点是第二次启动时进程内存里的session_history完全是空的Agent能答上来靠的纯粹是外部存储里的长期记忆。这说明它不是“记住”了而是“被提醒”了——但这恰恰是工程上最可靠的做法。5. 记忆管理三件难事检索质量、遗忘机制与隐私边界5.1 检索质量召回什么比存了什么更重要很多人的记忆系统跑不起来问题不在存储而在检索。向量库里明明存着关键事实但提问时召不回来等于白存。影响检索质量的因素主要有三个。第一个是Embedding模型的语义对齐能力通用模型在垂直领域的效果往往一般中文场景尤其要选在中文语料上训练充分的模型。第二个是记忆条目的分块粒度我建议单条记忆控制在50到200字之间太短没有上下文太长语义会被稀释。第三个是召回数量top_k设太小容易漏设太大又会被不相关内容干扰我自己的经验值通常取3到8再结合一个相似度阈值过滤低分结果。如果你的召回到位了但排序还不理想可以在召回后加一层“重排序”用交叉编码器对候选结果重新打分。这个操作成本稍高但在答案一致性要求严格的场景里非常值。5.2 遗忘机制好的记忆系统要会“忘记”记忆系统最反直觉的一点是它必须会遗忘。没有遗忘的记忆库随时间累积会有两个大麻烦其一是过时信息误导Agent比如用户上个月说“喜欢喝美式”这个月已经改喝手冲了旧记忆还在发挥作用Agent就会给错建议其二是存储膨胀数据越多检索越慢噪音也越多。工程上实现遗忘有三招我建议组合使用。第一招是时间衰减给每条记忆存时间戳计算相似度时叠加一个衰减系数越久远的记忆权重越低第二招是冲突覆盖对同一个主体记录“最后更新时间”如果出现语义冲突以新记忆为准并给旧记忆打上“已过期”标记第三招是定期摘要化归档把大量零散的事件记忆在后台压缩成几段摘要既保留信息又降低存储量。这也解释了为什么我在第4节代码里给每条记忆都保存了timestamp元数据——那是为后两步预留的钩子不提前留好后面想加衰减策略就得回炉改造。5.3 隐私边界记住你但不该记住什么“让Agent记住你”和“让Agent偷窥你”之间有一条必须主动划清的线。技术上有能力把所有对话都存下来但责任上不应该。我给自己定了几条底线。第一敏感信息一律不存密码、卡号、身份证号这类内容不仅不能进记忆库最好连对话日志都做脱敏处理第二数据最小化只存“完成任务需要的事实”不存完整对话流水没用的坚决不存第三用户必须有控制权提供“查看我的记忆”和“删除我的记忆”入口这在产品层面上不是加分项而是必需品第四能本地就别上云尤其行业场景数据留在自己的存储里能少一层风险。在代码层面你可以非常简单地做到第一点在extract_memorable_facts的提示词里明确加上“不要提取密码、账号、卡号、身份证号等任何敏感信息”再在写入前用正则做一道过滤兜底。别嫌多此一举这个动作能避免绝大多数事故。6. 我踩过的三个坑给Agent加记忆的真实复盘6.1 坑1直接存对话原文召回的全是客套话我第一次实现记忆时偷了个懒把每轮完整对话原文直接塞进向量库心想“反正检索是按语义来的原文信息量最大”。真跑起来就翻车了用户问“上次你说的那个方案细节发我一下”Agent召回回来的记忆列表里全是“好的”“没问题”“稍等”这种高频客套话关键信息反而沉底了。排查时我打印了召回结果发现那些客套话在向量空间里挤成一团几乎任何问题都能和它们算出一个不低的相似度而真正包含关键事实的长句和查询的相似度反而不如客套话高。这就是典型的“高频噪声淹没低频信号”。解决方式就是我前面反复强调的先让LLM把对话提炼成简洁事实再去embedding和存储。这一步过滤掉的噪音量惊人存储量能砍掉80%召回质量反而成倍提升。6.2 坑2旧记忆权重太高把Agent带偏了第二坑发生在用户偏好变化时。用户半年前说“我喜欢美式咖啡”最近一次说“我戒咖啡了”但Agent还在推荐咖啡机。我一开始以为是检索没召回到新记忆翻日志发现新旧两条都召回了只是旧记忆被反复引用过在向量空间里的位置更“中心”相似度排序总排前面。这个问题的本质是记忆系统没有时效概念。我后来做了两处修补一是在计算相似度时叠加基于时间戳的衰减因子越新的记忆权重越高二是在Prompt里明确告诉模型“记忆里的信息比用户当前说辞旧时以用户最新表达为准”这条指令简单有效能立刻纠正模型对旧记忆的盲从。顺带说一句这类冲突处理千万别指望模型自己“悟”你不明说它就更容易被System Prompt里的旧信息带节奏。6.3 坑3换了个环境Agent就当我是陌生人第三个坑最隐蔽。本地开发时一切正常Chat历史记得好好的一旦部署到服务器Agent立刻失忆表现得像个从没见过用户的陌生人。我当时花了很长时间以为是代码逻辑问题来回检查Session管理。后来把目光放到环境差异上才发现真相开发环境的Chroma数据库是本地某个目录而部署环境的容器没把那个目录挂载成持久化存储容器每次重启数据库文件都被重置成空的记忆自然全没了。这一点给所有做Agent的人提了个醒记忆系统本质上是“数据资产”不是缓存必须用数据库的运维标准来对待。持久化路径要挂载好数据要有备份记忆的Schema版本要做迁移规划甚至Embedding模型的版本也要固定否则哪天模型一换新老向量不在同一个语义空间里检索质量会断崖式下跌。最后说点实际体会。给Agent加记忆我一开始以为是存储问题觉得找个向量库把东西塞进去就完事真正做下来才发现难点全在“怎么存”和“怎么取”存的时候要会提炼取的时候要会取舍还要让它会忘、敢忘。你不需要一上来就上MemGPT那套复杂的分层管理先按这篇文章的思路把核心事实存好、检索路径跑通就已经能做出一个“记得你”的Agent了。我下一步准备重点研究多模态记忆和跨Agent记忆共享这两块等有进展了再来填坑。