Agent记忆管理全解析:从上下文窗口到知识图谱的工程实践 记忆管理这件事几乎每个做 Agent 的人都会在某个时刻被它绊一跤。刚开始搭 Demo 的时候把对话历史一股脑塞进上下文窗口跑得挺欢等到对话轮次上到几十轮、工具调用记录堆成山模型开始答非所问、重复劳动、甚至把三天前用户随口提的一句偏好当成当前指令执行——这时候你才意识到Agent 的记性不是把东西存下来就完事了它得会记什么、忘什么、什么时候想起来。这篇是 ai-agent 教程系列的第四篇专门聊记忆管理。我会把记忆分成几个层次来讲从最朴素的上下文窗口到向量检索式的长期记忆再到结构化的知识图谱记忆每一层都讲清楚它解决什么问题、在什么场景下会失效、以及实际落地时那些文档里不会写的坑。如果你正在做 Agent 开发或者被 RAG 的召回质量折磨过这篇应该能帮你少走一些弯路。1. 先搞清楚 Agent 到底需要记住哪几类东西很多人一上来就说我要给 Agent 加记忆但没想清楚要记的是什么。结果做出来的东西要么什么都存、检索时噪声爆炸要么只存对话历史、跨会话就失忆。我习惯把 Agent 的记忆按时间尺度和用途分成四类分类清楚了后面的技术选型才有依据。1.1 工作记忆当前任务的临时白板工作记忆就是当前这一轮任务执行过程中需要临时保存的信息。比如用户让它帮我把这份报表里的异常数据标出来然后发邮件给张三Agent 需要记住报表在哪、异常数据的判定标准是什么、邮件收件人是谁、当前执行到哪一步了。这类记忆的特点是生命周期极短任务结束就该丢掉。它通常直接放在上下文窗口里或者用一个简单的状态对象state dict在工具调用之间传递。你不需要给它上向量数据库那是杀鸡用牛刀。我见过有人把工作记忆也塞进向量库每次工具调用都去检索一遍结果延迟翻了三倍召回还经常串味。工作记忆就该老老实实待在内存里任务结束随进程一起销毁。1.2 会话记忆一次对话内的连贯性保障会话记忆是同一个会话session内的历史交互。用户前面说了什么、Agent 回复了什么、调用了哪些工具、得到了什么结果这些构成了对话的上下文。它的核心作用是保证指代消解和意图连贯——用户说把它改成红色Agent 得知道它指的是上一轮提到的那个按钮。会话记忆的典型做法是滑动窗口加摘要。窗口保留最近 N 轮完整对话更早的内容压缩成摘要。这里有个经验值大多数对话任务保留最近 10 到 15 轮完整交互加上一份滚动摘要效果和成本比较平衡。窗口开太大token 成本飙升且模型注意力被稀释开太小用户会觉得 Agent刚说过就忘。1.3 长期记忆跨会话的用户画像与事实沉淀长期记忆是跨会话保留的。用户上周说过自己偏好简洁的回复风格、所在团队用的是某个特定工具链、某个项目代号指的是什么——这些信息应该在下次对话时还能被想起来。这才是真正需要向量检索或结构化存储的地方。长期记忆的关键难点不在存而在写入时机和检索精度。什么时候判断一条信息值得长期记住检索时怎么保证召回的正好是相关的、而不是一堆语义相近但无关的噪声这两个问题后面会展开讲。1.4 领域知识记忆Agent 的专业知识底座还有一类容易被忽略的是 Agent 所服务领域的专业知识。比如一个客服 Agent 需要记住产品文档、退换货政策一个代码 Agent 需要记住项目结构、API 约定。这类记忆本质上是静态知识更新频率低但体量大、检索要求高。它和长期记忆的区别在于长期记忆是关于用户的领域知识是关于世界的。两者可以共用一套检索基础设施但写入策略和更新机制完全不同——领域知识通常是批量导入加版本管理长期记忆是增量写入加衰减淘汰。把这四类分清楚之后你会发现记忆管理这个词其实是个筐里面装的是四个技术诉求完全不同的东西。下面我按从简到繁的顺序逐个拆解实现方案。2. 上下文窗口不是记忆但它是记忆的第一道闸门在聊向量检索之前必须先说清楚上下文窗口的定位。很多人把上下文窗口大等同于记忆能力强这是个危险的误解。上下文窗口是工作台面不是仓库。台面再大你也不可能把所有家当都堆在上面干活。2.1 上下文窗口的真实成本结构上下文窗口的成本不是线性的。模型处理长上下文时注意力机制的计算量随长度增长而且中间部分的信息容易被稀释——这就是所谓的lost in the middle现象。你把一份关键信息塞在 8000 token 上下文的中间位置模型很可能视而不见放在开头或结尾召回率明显更高。实测下来同样一段关键指令放在上下文开头和放在中间任务成功率能差出 20 到 30 个百分点。所以记忆管理的第一条实操原则是最重要的信息放在上下文的两端中间放次要的、可被稀释的内容。2.2 滑动窗口加摘要的具体做法会话记忆的经典实现是滑动窗口加摘要但细节决定成败。我的做法是分三层最近 3 轮完整保留包括工具调用的原始输入输出。这几轮是当前意图最直接的上下文不能压缩。第 4 到 15 轮保留用户消息和 Agent 的最终回复工具调用的中间过程只保留结论。这样能省掉大量 token同时不丢失对话主线。15 轮之前压缩成一段滚动摘要每新增 5 轮就更新一次摘要。摘要的 prompt 也有讲究。不要让它总结对话而要让它提取仍然可能影响后续对话的事实和未完成事项。前者会产出一堆流水账后者才是真正有用的记忆。提示摘要更新不要每轮都做那样既费 token 又容易累积误差。每 5 轮更新一次或者当窗口即将溢出时触发是比较务实的节奏。2.3 什么时候该把信息下沉到长期记忆滑动窗口的边界就是长期记忆的入口。当一条信息即将滑出窗口时你需要判断它值不值得下沉到长期记忆我的判断标准是三条满足任意一条就下沉用户显式表达的偏好或约束比如我不喜欢用表格我们公司规定金额超过 5000 要审批。跨会话仍然有效的事实比如我的项目代号是 Phoenix我负责的模块是支付。未完成的待办事项比如下周再帮我看这个问题。不满足这三条的让它随窗口自然滑出就好。什么都往长期记忆里塞检索时必然噪声爆炸。3. 向量检索式长期记忆好用但别神化它向量检索是目前长期记忆最主流的实现方式。把记忆条目转成 embedding 存进向量库需要时用当前对话的语义去检索最相近的若干条。听起来很优雅但实际用起来召回质量往往达不到预期。3.1 向量检索的召回瓶颈到底出在哪向量检索的本质是语义相似度匹配它擅长的是意思相近不擅长的是精确条件。问题就出在这里。用户问我上次说的那个预算上限是多少向量检索会去找语义上和预算上限相近的记忆条目。但如果记忆库里存的是项目总预算是 50 万语义相似度可能不够高召不回来反而可能召回一堆提到预算但说的是别的事的条目。更麻烦的是否定和条件。用户说除了周三其他时间都行向量检索很可能把周三当成关键词召回完全忽略否定语义。这类问题在纯向量方案里几乎无解只能靠混合检索来缓解。3.2 混合检索向量加关键词的务实组合纯向量不够用纯关键词又太死板所以实际项目里我基本都用混合检索向量召回一批BM25 或全文索引召回一批然后做融合排序。融合排序常用 RRFReciprocal Rank Fusion逻辑很简单对每个文档把它在各路召回里的排名取倒数相加得到最终分数。排名越靠前、出现在越多路召回里分数越高。这个方法不需要调权重鲁棒性好我一般直接用它做初版。def rrf_fusion(rankings, k60): scores {} for ranking in rankings: for rank, doc_id in enumerate(ranking): scores[doc_id] scores.get(doc_id, 0) 1.0 / (k rank 1) return sorted(scores.items(), keylambda x: -x[1])k取 60 是经验值来自 RRF 原论文实测下来在多数场景都稳。融合之后取 top 5 到 top 8 条记忆注入上下文比纯向量召回的相关性明显更好。3.3 记忆条目的切分粒度这是最容易被忽视的坑向量检索的效果一半取决于切分粒度。切得太碎单条记忆没有完整语义检索出来是断章取义切得太粗一条记忆里混了多个主题检索时相关性被稀释。我的经验是一条记忆只表达一个事实或一个偏好。比如用户偏好简洁回复是一条用户所在团队用 Python是另一条不要合并成用户偏好简洁回复且团队用 Python。合并之后检索团队技术栈时这条记忆的相关性会被简洁回复稀释排名下降。写入时做一次结构化提取把一段对话里的事实拆成独立条目每条带上时间戳、来源会话 ID、置信度。这些元数据在后续检索和淘汰时都用得上。3.4 记忆衰减与淘汰不清理的记忆库会腐烂长期记忆如果不做淘汰会越来越臃肿检索质量越来越差。我一般用时间衰减加访问频率的组合策略每条记忆有一个基础权重随时间指数衰减半衰期设 30 天左右。每次被检索命中并实际使用权重加一个增量。权重低于阈值的记忆不直接删除而是标记为冷记忆检索时降权但不完全排除。这样既能让过时信息自然退场又不会因为一次误判就永久丢失可能有用的记忆。冷记忆定期归档需要时可以人工恢复。注意淘汰策略一定要可回滚。我踩过的坑是早期直接硬删除低权重记忆结果用户回头问一个三个月前的事Agent 完全失忆体验很差。改成软删除加归档之后就没这个问题了。4. 结构化记忆与知识图谱当向量检索不够用时向量检索处理不了精确条件和多跳推理这时候就轮到结构化记忆上场了。所谓结构化记忆就是把信息以实体、关系、属性的形式存起来检索时走图查询或结构化过滤而不是语义相似度。4.1 什么场景下必须上结构化记忆不是所有项目都需要知识图谱。以下场景向量检索基本无解必须上结构化多跳关系查询用户问我负责的项目里哪个的负责人和我同一个部门。这需要沿着项目-负责人-部门的关系链走向量检索做不了。精确条件过滤用户问上个月我提的那些还没关闭的问题。这需要按时间范围和状态做结构化过滤。否定和排除前面提到的除了周三这类语义结构化存储加查询能精确处理。如果只是做简单的偏好记忆和事实召回向量检索加混合排序就够了别为了用图谱而用图谱。知识图谱的构建和维护成本不低用错地方就是负资产。4.2 轻量级结构化记忆别一上来就上重型图谱很多人一听知识图谱就想到 Neo4j 加本体建模其实大可不必。多数 Agent 场景用关系型数据库加 JSON 字段就能搞定结构化记忆。比如一张记忆表字段包括主体subject、关系predicate、客体object、时间、置信度、来源。这就是最简版的三元组存储用 SQL 就能做多跳查询和条件过滤。等数据量和查询复杂度真的上来了再迁移到图数据库也不迟。我做过一个项目一开始就上了图数据库结果团队里没人熟悉 Cypher 查询维护成本高得离谱最后又退回关系型方案。教训是技术选型要匹配团队的实际能力不是越先进越好。4.3 向量与结构化的协同各管一段实际项目里向量检索和结构化记忆往往是协同工作的不是二选一。我的做法是先用结构化查询做硬过滤把候选范围缩小到满足精确条件的子集再在这个子集里做向量检索做语义排序。这样既保证了精确性又保留了语义灵活性。举个例子用户问我上个月提的关于性能的问题后来怎么解决的。结构化查询先按时间上个月和类型问题过滤得到一批候选向量检索再在这批候选里找和性能解决语义最相关的。两步下来召回质量比单用任何一种都好。5. 记忆写入的时机判断比检索更难的问题检索做得好不好用户能感知到写入时机判断得对不对用户往往感知不到但它对记忆质量的影响更大。写早了把无关紧要的话当成事实存下来污染记忆库写晚了关键信息已经滑出窗口再也找不回来。5.1 用规则做第一层过滤最省事的做法是用规则做初筛。每轮对话结束后跑一遍规则判断有没有值得写入的内容用户消息里出现记住以后都我的偏好是这类显式指令直接写入。出现明确的实体加属性描述比如我用的是 Mac我们团队有 8 个人提取后写入。出现时间相关的待办比如下周月底之前写入待办记忆。规则能覆盖大概 60% 到 70% 的写入场景剩下的靠模型判断。5.2 用模型做第二层判断规则覆盖不到的用一个轻量模型做判断。prompt 大概是以下是一段对话请判断其中是否有值得长期记住的用户信息。如果有提取成独立的事实条目如果没有返回空。这里的关键是让模型输出结构化结果而不是自由文本。用 JSON schema 约束输出每条包含事实内容、类型、置信度。置信度低于阈值的丢弃避免把模型的猜测当成事实存下来。5.3 写入去重别让同一条事实存十遍用户可能在不同会话里反复提到同一个偏好如果每次都写入记忆库里就会有大量重复条目检索时互相挤占排名。写入前必须做去重。我的做法是新条目写入前先用向量检索在已有记忆里找相似度超过阈值的条目。如果找到不新增而是更新已有条目的时间戳和置信度如果没找到才新增。这样同一条事实只会有一条记录权重随提及次数累积。提示去重阈值不要设太高0.85 左右比较合适。设到 0.95语义相近但表述不同的条目就漏过去了设到 0.7又会把相关但不同的事实误判为重复。6. 记忆管理的工程落地几个绕不开的实操问题前面讲的都是策略层面的东西真正落地时还有一堆工程细节。这部分我挑几个最常被问到的问题说说。6.1 记忆的存储选型别过度设计存储选型上我的建议是从简到繁阶段存储方案适用场景原型内存字典加 JSON 文件单机 Demo数据量小小规模SQLite 加向量扩展单机生产几千条记忆中等规模PostgreSQL 加 pgvector多实例几万到几十万条大规模专用向量库加关系库百万级以上高并发多数项目在第二阶段就够了。我见过太多项目一上来就搭分布式向量集群结果数据量还没到一万条纯属浪费。先跑起来等真的遇到瓶颈再升级这是我一贯的原则。6.2 记忆注入上下文的位置和格式检索出来的记忆怎么注入上下文也有讲究。我的做法是单独开一个区块用明确的分隔符包起来放在系统提示之后、用户消息之前。格式上每条记忆标注类型和时间比如[偏好][2024-03] 用户偏好简洁回复。类型标注帮助模型理解这条记忆的性质时间标注帮助模型判断时效性。不要把所有记忆混成一坨文本那样模型很难区分哪些是事实、哪些是偏好、哪些是待办。6.3 记忆冲突的处理用户改主意了怎么办上个月说喜欢详细回复这个月说太啰嗦了。如果两条记忆都存着检索时可能同时召回模型无所适从。处理原则是新记忆覆盖旧记忆但保留历史版本。写入新记忆时如果检测到和旧记忆冲突同一主体同一属性但值不同把旧记忆标记为已过期新记忆设为当前有效。检索时默认只召回有效记忆需要历史追溯时才查过期版本。6.4 隐私与安全边界记忆里可能包含用户的敏感信息写入前要做脱敏。我的做法是在写入管道里加一道过滤识别并屏蔽身份证号、手机号、银行卡号这类模式。同时记忆的读取要有权限控制不同用户、不同会话之间的记忆不能串。这一点在多人共用的 Agent 场景里尤其重要。我见过一个项目因为记忆库没有做用户隔离A 用户的偏好被 B 用户检索到了虽然内容不敏感但体验上很糟糕。7. 从 RAG 到 Agent 记忆几个容易混淆的概念澄清做 Agent 记忆的时候经常会和 RAG 的概念混在一起。这里澄清几个容易搞混的点。7.1 RAG 知识库和 Agent 记忆不是一回事RAG 知识库通常是静态的、领域性的比如产品文档、法规条文更新频率低写入是批量的。Agent 记忆是动态的、个性化的随对话不断增长写入是增量的。两者可以共用向量检索的基础设施但写入策略、更新机制、淘汰策略完全不同。把 Agent 记忆当成 RAG 知识库来管会导致记忆库无限膨胀把 RAG 知识库当成 Agent 记忆来管又会导致知识更新不及时。7.2 结构化知识库和向量知识库的分工结构化知识库比如知识图谱擅长精确查询和多跳推理向量知识库擅长语义模糊匹配。实际项目里两者往往是互补的。我的经验是能用结构化表达的优先结构化。比如某产品的价格是 X这种确定的事实存成结构化字段查询又快又准。只有那些难以结构化的、语义丰富的内容比如用户的一段反馈、一篇文档的段落才用向量存储。7.3 记忆管理不是越复杂越好最后说个反直觉的观点记忆管理不是越复杂越好。我见过一些项目把记忆系统做得极其复杂多层缓存、多路召回、复杂的衰减算法结果调试困难、故障点多实际效果还不如一个简单的滑动窗口加向量检索。记忆管理的目标是让 Agent 在该记住的时候记住、该忘记的时候忘记而不是堆砌技术。从最简单的方案开始遇到具体问题再针对性优化这个思路在记忆管理上尤其适用。我在实际项目里的体会是记忆系统的复杂度应该和 Agent 的任务复杂度匹配。一个只做单轮问答的 Agent根本不需要长期记忆一个做多轮任务编排的 Agent工作记忆和会话记忆是刚需长期记忆看情况只有那些真正需要跨会话、跨任务保持连贯性的 Agent才值得投入精力做完整的记忆管理。搞清楚自己的 Agent 处在哪个层次比盲目上技术更重要。