
很多做AI Agent的人一开始可能都有这种感觉单轮对话的时候它像个专家逻辑清晰、条理分明。可一旦进入多轮对话或者复杂任务它就开始“犯糊涂”前面刚确认过的信息转头就忘你说过的偏好它完全不记得。这时候的问题大概率不是模型不够聪明而是Agent没有记忆管理。这个主题我放在了AI Agent系列教程的第四篇原因很简单前三篇解决了Agent的骨架和工具调用能力相当于让它有了手和脚而记忆管理是让Agent真正拥有“大脑持续性”的核心模块。没有记忆的Agent本质上就是一条只有七秒记忆的金鱼每次对话都是一次彻底的重来。这篇教程里我会把这个相对抽象的概念拆开揉碎从记忆的底层分类到工程级别的实现方案再到我实操中踩过的坑尽量一次讲清楚。适合正在搭建Agent应用、或者准备优化多轮对话体验的开发者参考无论你用的是现成的Agent框架还是自己从零卷一套逻辑这篇文章里的思路都能直接套用。1. 没有记忆的Agent等于每次都和陌生人聊天1.1 从一次失败的客服Demo说起我最早尝试做一个带记忆的Agent起因特别朴素一个做电商客服的场景。当时我搭了个Agent模型用的某主流大模型工具调用也接好了能查订单、能查物流看起来像模像样。结果测试的时候翻车了——用户在对话里说出了自己的订单编号Agent查询成功了但下一轮用户直接说“那这个订单什么时候发货”Agent立刻懵了。它不知道“这个订单”指的是哪个订单因为上一轮的上下文在它眼里可能就是一堆毫无关联的历史消息。那是我第一次意识到上下文窗口和记忆是两回事。上下文窗口只是给模型看的当前文字段落但模型并不会从中提取出“用户的订单编号是A12345”这种结构化的事实留给后续使用。这个问题迅速从“怎么改Prompt”演变成了“怎么设计一套记忆系统”。那次之后我重新梳理了整个Agent的架构发现记忆管理不是某一段代码能解决的它是一个需要贯穿始终的横切模块。你在哪个环节设计它的存取逻辑、用什么格式存、什么时候该检索、什么时候该遗忘全都会直接影响Agent的表现。1.2 记忆管理到底管的是什么通俗地说Agent的记忆管理要解决三个问题记什么、存多久、怎么用。记什么指的是从用户对话里提取出哪些信息是有长期价值的。比如用户的身份信息、偏好设置、项目目标、历史决策这些值得沉淀而“今天天气怎么样”这种一次性寒暄过去了就该翻篇。存多久是区分短期记忆和长期记忆的核心。短期记忆服务于当前对话上下文窗口满了就该主动做摘要压缩长期记忆则需要跨对话持久化在下次会话开始时重新拉取让Agent知道“我记得你是谁、你上次聊到哪了”。怎么用则是检索和注入的工程问题。你存的记忆如果不经过筛选全部塞进上下文很快就把Token预算撑爆了只有按相关性、按时间权重把最合适的那部分记忆注入当前会话才是有效利用。把这几个问题想明白之后你才会意识到记忆管理本质上是Agent系统设计里的“数据库层”加“缓存层”加“预处理层”的组合体而不是一个简单的数组变量。2. 记忆的四种形态像人脑一样分层存储2.1 工作记忆、情景记忆、语义记忆、程序记忆我习惯把人脑的记忆机制映射到Agent设计上这样跟团队里其他人聊起来沟通成本低很多。人脑可粗略分为四种记忆工作记忆、情景记忆、语义记忆、程序记忆。工作记忆对应Agent的上下文窗口和对话历史容量有限用完即走情景记忆对应“某次会话中发生了什么”比如上次和用户敲定了哪些需求细节语义记忆对应“用户是谁、世界是什么样的”比如用户偏好简洁回复、用户所在行业、常识性规则等程序记忆则对应Agent能力的“肌肉记忆”比如它学会了如何调用某个工具、某类任务的标准处理流程。在工程实现上工作记忆基本靠上下文管理解决情景记忆和语义记忆靠向量数据库和键值存储配合而程序记忆通常需要固化到系统提示词或者独立的规则引擎里。2.2 长期记忆该怎么落库拿我现在做的一个项目举例某知识助手Agent用户会跟它持续讨论多个主题跨天甚至跨周。当时我设计了双轨存储一条轨是结构化键值存储用的是Redis专门存用户信息、用户偏好、会话摘要之类的高确定性数据key按用户ID和业务键拼接读取速度快另一条轨是语义向量存储用的某开源向量数据库存的是非结构化的对话片段、知识点、决策记录靠Embedding做相似度召回。这么拆的原因是键值存储查起来准但没法做模糊匹配向量存储能找“意思相近”的记忆但偶尔会召回不相关的垃圾。两条轨各管一段互补性很强。比如用户偏好这种明确的信息用Redis存每次对话开头精确取出来而用户上次提过的一个模糊需求描述用向量检索去捞又准又快。如果你只是在做轻量级原型前期完全可以用简单的JSON文件加内存做一个简化版记忆系统但是一旦跑起来你就会发现查询路径混乱、数据没清理、扩展性为零所以我还是建议尽早把正式的存储层立起来。2.3 短期记忆的滚动窗口策略短期记忆部分最直接粗暴的做法是只保留最近N轮对话超过部分直接丢弃。试过几天就会发现两个问题一是关键信息刚好被挤出窗口二是模型对前面内容的“遗忘”是硬切断的对话连贯性很差。后来我换了滚动摘要策略维护一个对话摘要变量每当窗口快满时调用模型把当前对话压缩成一段结构化摘要再把摘要与最近几轮完整对话一起送入上下文。这个方案实测下来效果不错基本相当于一个“自动任务交接笔记”让旧信息以浓缩的方式持续存活。这个策略在工程实现上不算复杂但有一个细节需要处理好压缩频率不能太高否则调用模型的成本会增加不少而且连续压缩会导致信息层层失真。我的经验是当对话轮数超过窗口容量的一半时触发一次压缩压缩后保留摘要加最近五轮完整对话作为新的上下文基础效果比较均衡。3. 向量化记忆库的实操从Embedding到召回3.1 选Embedding模型的几个思路向量记忆库的核心是把文本变成向量存起来再做近似检索。选Embedding模型时不同场景的侧重完全不同。如果你做的是中文场景尽量选对中文支持好的模型别拿通用英文向量模型硬跑中文文本如果是多语言混合内容可以考虑多语言向量模型避免语言割裂造成的召回不准确。这里我的建议是在正式选型前用你自己业务里的真实语料跑一个简单的召回测试看top10结果跟预期的匹配程度这比看公开榜单分数有用得多。另一个容易忽略的点是文本切片长度。Embedding模型通常对输入长度有上限比如512个token如果你的记忆片段超过这个长度直接编码会丢失信息或者被模型截断。我一般把单条记忆控制在200-300字左右超出就做切分按语义边界拆成多条。3.2 元数据过滤记忆检索的精髓很多人做向量检索只传“查询文本”进去然后topK召回一堆看着好像还行但实际效果极不稳定。我给记忆检索加了几个维度的元数据过滤效果立刻稳多了。第一个维度是时间。时效性强的记忆比如“用户上周提到项目X”需要限定时间范围而长期偏好不需要时间限制。所以在存储时我给每条记忆打了时间戳检索时根据业务需求选择是否加时间过滤。第二个维度是会话来源。跨会话的记忆检索容易造成上下文污染我习惯把记忆的会话ID一起存进去查询时可以指定“只看当前会话”或者“全局检索”。第三个维度是记忆类型。我会给每条记忆打上类型标签用户偏好、项目事实、对话摘要等检索时按需过滤。比如开场只注入用户偏好类记忆而任务执行过程中再检索项目事实类记忆避免一上来就把Agent“带偏”。这三个过滤条件组合起来基本能挡住大部分不相关召回。你可以理解为单纯靠向量相似度是“大海捞针靠手感”加上元数据过滤相当于直接换了个池子。3.3 记忆写入与读取的完整链路实操层面我拆成两段写入链路和读取链路。写入链路发生在每轮对话结束后。Agent先把当轮抽取的“关键记忆点”格式化成一个结构化对象包含内容、类型、时间戳、来源会话ID然后同步写入两个地方——高确定性信息写Redis需要语义检索的信息生成Embedding后写向量库。这一段如果想做得更精细还可以做“记忆合并”。比如用户重复表达了同一个偏好如果每次都存一条新纪录库里很快会出现大量冗余向量召回时翻来覆去就是那几句。我自己的方案是写入之前先做一次相似度检索如果发现已存在高度相似的旧记忆就触发更新合并逻辑而不是新增一条。读取链路发生在每轮对话开始前。系统先并行执行几个查询从Redis取用户偏好与历史摘要用当前轮次的用户问题做向量检索结合元数据过滤召回TopK相关记忆把召回结果按照“时间新近度加相关性”做一个重排然后注入系统提示词。这一步的重排很关键。直接按相似度分数排序注入可能会出现三条几乎同样的旧记忆占据名额的情况。我调试时加了简单的加权公式最终分 相似度分 * 0.7 时间衰减系数 * 0.3跑了几轮测试召回内容的多样性明显提升了。下面给一段我用了很久的伪代码框架能帮你把上面的逻辑串起来def fetch_relevant_memories(user_id, query, session_id, top_k5): # 1. 读结构化记忆 profile redis_client.get(fagent:profile:{user_id}) # 2. 向量检索 元数据过滤 filters { user_id: user_id, memory_type: [preference, project_fact], # 可选: session_id: session_id 限定当前会话 } candidates vector_db.search( embeddingembed(query), filtersfilters, top_ktop_k * 2 ) # 3. 重排结合时间衰减 now time.time() for mem in candidates: age_hours (now - mem.timestamp) / 3600 mem.final_score mem.score * 0.7 (1 / (1 age_hours)) * 0.3 candidates.sort(keylambda m: m.final_score, reverseTrue) return { profile: profile, memories: candidates[:top_k] }这段代码的核心思想就是“先精确捞再模糊捞最后混排”它兼顾了短标签型信息的确定性和长文本型信息的语义灵活性。4. 上下文窗口的压榨艺术让每一分Token都花在刀刃上4.1 动态分配Token预算模型的上下文窗口是有限的哪怕是最新一代模型把几十万字全塞进去也不现实成本和延迟都会失控。我建议的做法是给Agent建立一套Token预算分配体系。假设你用的是8K上下文窗口预留1K给系统提示词和工具定义再预留1K给用户当前的输入剩下6K分配给记忆注入。在6K里我习惯按比例切分40%给最近对话轮次短期记忆、30%给向量检索出的历史相关记忆长期记忆、20%给结构化用户画像剩余10%作为缓冲。每次写入前先把内容Token化量一量实际长度超了就触发压缩或裁剪逻辑。这套分配方案帮我减少了很多“上下文溢出”的尴尬报错。而且你会发现给不同内容分配固定配额后模型的回复质量反而更稳定了——因为关键信息不会被海量的历史对话稀释掉。4.2 记忆更新与遗忘机制定期做“断舍离”记忆库如果只进不出跑一个月之后里面堆积的记忆碎片能把检索质量拖垮。这里我推荐三个机制配合使用。第一过期清理。给每条记忆设置TTL生存时间比如临时性记忆保存24小时项目事实类保存30天。到期后由定时任务统一删除或归档。第二重要性分级。每次写入记忆时附上重要性评分可以从对话中提取情绪强度、用户重复提及次数等信息辅助判断。高重要性记忆延长TTL低重要性记忆优先清理。第三重复合并。前面提过的写入前相似度检查既控制了冗余也变相实现了一个“记忆强化”效果——被频繁匹配到的记忆可以人工调高持久化权重。这三个机制配合起来记忆库才不会是“垃圾堆”而更像是真正经过筛选的笔记系统。4.3 跨会话记忆恢复开场白的设计技术上有了长期记忆接下来要考虑的是“什么时候把记忆用起来”。我的经验是跨会话记忆恢复最适合放在对话开场也就是模型第一次生成回复之前。具体做法是在每轮对话的预处理阶段把“用户画像摘要”和“最近一次会话的核心结论”作为固定的前导注入背景信息。比如Agent看到用户上线系统已经在提示词里注入了“这个用户是某电商公司的运营上次对话定下了A/B测试方案优先级较高”这类信息它就能在开场主动提及上次的讨论而不是当作新用户一样从头问起。这个设计对用户感知的提升非常明显。我做了个小范围用户测试有记忆恢复的版本比无记忆版本的用户满意度高了很多核心原因就是“被记住的感觉”带来了一种明显的基础信任感。当然注意不要把记忆恢复做成“查户口”注入时只挑关键信息别把用户所有旧历史都轰炸出来。5. 常见问题与排查技巧那些年我踩过的坑5.1 检索结果不相关向量检索的结果越来越离谱这个问题我排查了挺久。最后发现大概率不是向量库的问题而是文本切片方式不对。比如你把一段包含多个主题的对话存成一条长记忆查询的时候只命中其中一部分整条召回回来就带着大量无关内容。解决办法是优化切片逻辑尽量按照主题语义边界切而不是简单按字符数硬切。我后来还加了“主题标签”字段切出来的每片内容在入库前都被打上一个主题标签查询时如果知道当前对话的主题就优先过滤这个标签。5.2 记忆互相“污染”记忆污染指的是用户本次聊的是项目A的方案结果Agent翻出了用户上次讨论项目B的记忆并且混在回复里造成了牛头不对马嘴的效果。这类问题主要靠元数据过滤来解。我在检索时必须加“当前主题”或“当前项目ID”的条件必要时直接限定只在当前会话的上下文中检索。如果你把记忆体系做到足够精细甚至可以给每条记忆建立一个“所属项目”的归属链每次切换项目时只加载对应项目的记忆子集。5.3 上下文爆炸记忆层叠导致Token超限当记忆体系运转顺畅之后新的麻烦来了每次对话开头都要注入那么多记忆量变叠加后token直接爆了。这个问题我会用两段式压缩来解决。第一次压缩发生在把记忆提取出来之后我会先把召回的记忆片段丢给一个小型模型做“合并摘要”把重复的、低价值的部分合并成几句话第二次压缩发生在生成用户回复之前把最终要注入上下文的所有内容做一个预检超过预算就继续裁剪。实测下来两段式压缩能把单轮调用成本压下来约三成同时对回复质量的负面影响非常小。你可以把这个过程理解成做PPT不是把全部资料贴在每一页上而是每页只提炼出最核心的结论和依据。Agent的上下文也一样信息量越大越需要提炼。5.4 隐私和合规记忆里存了什么得心里有数做记忆管理还必须留一条底线用户数据的隐私和合规。有些信息不应该进记忆库或者至少不应该完整地存下来——比如密码、卡号、身份证号这类敏感字段。我通常在写入链路里接一个简单的脱敏过滤器用正则匹配和实体识别把敏感信息替换成占位符再从服务端下发时落库。另外要设计“忘记”能力用户要求清除记忆时系统能按用户ID把键值库和向量库里对应的记忆全部删除。这件事在商品化部署时几乎一定会被问到留着不做后面验收会很麻烦。常见问题排查优先级推荐解法召回内容不相关高检查切片方式和Embedding模型选型跨主题记忆污染高检查元数据过滤与主题隔离上下文Token溢出中引入两段式压缩与预算分配记忆重复冗余中写入前相似度检查与合并敏感信息入库存留高写入链路脱敏过滤与“忘记”机制6. 在项目里落地记忆管理我的最后三条建议第一不要一上来就追求记忆系统的完美形态。先把最基础的双轨存储搭起来跑通“写入→检索→注入”这个闭环比纠结用什么向量数据库更重要。我见过不少人在选型上花了两三周最后跑起来的Demo还是用JSON文件临时顶着的。第二记忆管理和业务场景强相关通用方案只能作为起点。电商客服、知识助手、个人助理它们的记忆维度差异非常大。电商客服要记住订单和物流知识助手要记住用户的知识短板个人助理要记住日程和偏好你必须根据具体业务定制记忆类型和检索策略。第三时刻关注“记忆的负面效应”——也就是记忆注入过多导致的上下文污染和回复僵硬。记忆的目的是辅助Agent而不是主导Agent。当你发现Agent回复越来越像“复读机”大概率是记忆权重设置得太高了适当减少注入量把更多生成空间还给模型本身。我在实际项目中见过太多团队费劲把记忆库做出来了结果线上效果反而比没记忆的时候差。最后排查下来几乎都有一个共同特征过度依赖检索出来的历史记忆而忽略了模型的实时推理能力。记忆管理要做的是给模型提供精准的线索而不是把模型变成一个“只会翻旧账的记录员”。这个系列进行到这里Agent算是有了比较完整的“大脑回路”感知输入、规划动作、调用工具、保留记忆。下一篇我打算聊一聊Agent的自我反思与纠正机制那也是让Agent从“能干活”到“能越干越好”的关键一步。如果这一篇里某个环节你在实操中遇到了问题欢迎交流我踩过的坑大概率能帮你省点时间。