尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Agent分层记忆架构:从上下文窗口到向量库的完整实现指南

Agent分层记忆架构:从上下文窗口到向量库的完整实现指南 你有没有遇到过这种情况给Agent写了一份特别详细的System Prompt把用户画像、历史偏好、业务规则全都塞进去了结果跑了几天之后Agent的表现依然像第一次见面一样生硬。用户上周明明说过“我正在出差周末才有空”这周问它“我什么时候方便沟通”它完全答不上来。不是模型不行是Agent根本没记住用户说过什么。我最早做Agent应用时也被这个问题反复折磨。当时想的很简单既然模型有上下文窗口把之前的对话历史一股脑拼到下一轮请求里不就行了吗一开始确实有效但跑着跑着问题就来了——上下文越来越长响应越来越慢Token费用像开了水龙头一样往外流最关键的是当历史消息超过一定量模型反而被那些过时的旧信息干扰聊得越久、忘得越快。后来我意识到Agent的记忆不是简单的“聊天记录堆叠”它需要一套分层的结构来承接不同粒度的信息。这就是我想在这篇里展开聊的“Agent分层记忆”。我会直接把这套方案的架构思路、各层的实现路径、检索与更新的策略以及我实际踩过的坑全部拆开来讲适合正在做Agent开发、被上下文和记忆问题折磨的工程团队也适合想了解Agent架构细节的初学者。1. 先搞清楚Agent到底需要什么样的“记忆”1.1 记忆缺失引发的一连串真实问题我把Agent记忆缺失带来的问题分成三类你在实际开发中一定遇到过其中至少一个。第一类是短期语境断裂。用户在对话里刚说过“我预算控制在五千以内”Agent转身推荐了一万二的产品还一本正经地介绍“这是目前性价比最高的方案”。不是模型弱智是因为这一步的请求里根本没带上上一步的上下文。这类问题在你用纯API调用模型、又不做任何会话管理的时候几乎百分之百出现。第二类是中长期偏好无法沉淀。用户连续一周每天都会说“我不吃辣”“我早上九点开会”如果这些信息每次都只存在于那一刻的上下文窗口里窗口一滚动就彻底消失。Agent永远在“重新认识”用户永远学不会个性化。这造成的结果是用户必须反复交代同样的背景一段时间后他们会失去耐心觉得这个Agent“很笨”。第三类是知识碎片化导致的行为不一致。今天的对话里你告诉Agent“公司内部项目代号用字母T开头”明天它又用数字编号生成了一份文档。它并不是违反了指令而是那条规则从来没有被真正“记住”过。长期来看一个没有记忆能力的Agent就没有所谓的人设、规则和一致性可言。这三类问题的本质是把“对话”当成了记忆的全部载体。但现实中人的记忆本来就分很多层你记得今天早上吃了什么短期记得上周和朋友聚餐的大致内容中期也记得自己从小不吃香菜长期。Agent的记忆设计也应该遵循同样的分层逻辑而不是一个上下文窗口打天下。1.2 为什么不能只靠上下文窗口硬扛有人会说既然上下文窗口越来越大从4K到32K甚至到128K全都塞进去不就行了我实测下来的结论是不行而且随着窗口变大问题边际在加剧。首先是成本问题。你每次对话都要把所有历史塞给模型Token消耗是平方级增长的。聊到第20轮历史部分可能已经超过了新增内容的十倍。用户只是问了一个“在吗”模型却要把过去一万个Token的历史读一遍。这笔账在商业化项目里根本算不过来。其次是效果问题。模型对超长上下文的注意力天然存在“中间丢失”现象——醒目的开头和结尾信息保留得比较好夹在中间的内容容易被忽略。你历史里那一条“用户说预算五千以内”恰好处在上下文中间偏后的位置当整个上下文拉到几万字时模型极有可能直接“看漏”。然后是维护问题。历史里不只是对话还有工具调用记录、临时变量、错误日志、中间计算结果。这些内容在当前任务完成后就失去了价值但如果全部塞进上下文它们会持续占空间、干扰注意力。你需要一个机制把这些内容按价值分类能丢的丢、能压缩的压缩、能沉淀的沉淀。所以我最终的结论是Agent的记忆必须做分层处理每一层负责不同时间尺度和抽象程度的信息层与层之间有明确的读写策略。这才是解决“记不住”和“忘记该记的、记住该忘的”这两个问题的根本路径。2. 分层记忆的整体架构三个抽屉各管各的2.1 工作记忆层当前任务的“临时便签”工作记忆Working Memory对应的是Agent当前这一轮或者当前任务正在使用的信息。它存在于请求上下文之中生命周期极短任务一结束就该清空。很多人忽略了一个细节工作记忆不只是对话历史它还包括当前任务的中间状态。举个例子Agent在做一个“撰写季度报告”的任务它先检索了数据、生成了图表、写了一段开篇这些中间产物在工作记忆里都存在。如果Agent因为超出调用限制中断了重新启动时丢掉的不只是对话上下文还有这些中间产物任务就得从头再来。工作记忆层的设计目标是“够用且不越界”。它不该承担长期知识存储的责任也不该无限膨胀。在实际工程里这一层通常用上下文窗口管理、会话状态存储比如Redis中的Session数据结构来实现核心动作是“裁剪”和“滚动”。我在项目里给工作记忆层定的规矩是单轮请求限制上下文在上下文窗口的一半以内另一半留给模型输出和工具返回结果。之所以留出一半是因为如果上下文已经塞满模型就没有空间去老老实实生成结构化输出了。2.2 情景记忆层任务历史的“事件记录”情景记忆Episodic Memory存储的是Agent与用户完整交互事件的摘要性记录。它不像工作记忆那么细碎也不像长期记忆那样抽象成通用知识它保留的是“发生过什么事”的相对完整的叙事。这一层的典型例子是用户在上周二问过“如何做数据迁移”Agent当时推荐了三种方案其中用户对方案A表达了兴趣。这些信息不一定要逐字保留但“用户关注过数据迁移”“对方案A有兴趣”这些关键事件必须被记录。情景记忆在工程上的常见载体是向量数据库。每条记忆被嵌入成向量附带时间戳、事件类型、关联主体等元数据检索时通过相似度查找召回相关段落。它的核心特点是仍然保留上下文结构但经过了摘要化压缩不再逐字记录。这一层解决的核心痛点是“跨会话的连续性”。没有情景记忆Agent换个会话就像失忆有了它至少在用户说“我上次问过的那个数据迁移方案”时Agent能准确回忆起是哪个方案。2.3 语义记忆层抽象出来的“长期常识”语义记忆Semantic Memory存储的是从具体事件中抽取出的、可独立于事件存在的知识与偏好。它不再关心“什么时候、在哪一次对话里”只关心“用户有哪些长期偏好”“项目有哪些固定规则”“组件的调用约定是什么”这类稳定信息。举个例子从“用户上周二问过数据迁移方案”这条情景记忆中最终沉淀到语义记忆的内容可能是“用户所在团队倾向于先用小范围试点之后再全量迁移”。这条信息与具体时间无关但它能指导Agent在未来所有涉及变更类建议时的决策。语义记忆的工程实现通常是一张结构化的知识库可以是KV存储、图数据库甚至就是一张配置表。它的抽象程度越高复用的价值越大但同时也越难自动抽取需要设计完善的沉淀机制。我常给团队打的比方是情景记忆像日记写了日期、经过、感受语义记忆像人生信条是翻了很多页日记之后提炼出来的东西。Agent不能只写日记不提炼信条否则永远活在具体琐事里做不了高质量的抽象决策。3. 各层记忆的落地方案与实现路径3.1 工作记忆层的具体实现上下文窗口的精细管理工作记忆层的实现核心是管理好“当前上下文”。我常用的方案分三步走。第一步是会话状态的外部化。不要让Agent的会话状态只存在于模型服务的上下文里要把它迁移到外部存储。我在项目里用Redis存当前的会话状态Key是会话IDValue是一个结构体里面包含当前任务的目标、已完成的步骤、最近几轮对话的精简记录。这样即使请求中断Agent也能从Redis恢复会话状态。第二步是上下文的裁剪策略。每次组装请求前先对历史记录做一次过滤。我给每条历史记录打上标签系统指令、用户消息、Agent回复、工具调用、工具结果。其中工具调用和工具结果只保留最后一轮的因为大多工具结果在下一步之后就失去了价值。用户消息和Agent回复保留最近10轮更早的进入压缩流程。第三步是压缩流程。当历史消息超过阈值时不是直接截断而是调用摘要模型把超过10轮之前的内容合并成一段概述。这段概述在下一轮请求时作为“对话背景”注入系统消息而不是塞在对话历史里。这样一来上下文长度被压住关键背景又不丢。我用一个真实项目的参数来说话。当时项目用的是32K上下文窗口的模型我设定的工作记忆上限是16K其中系统指令加背景摘要占4K当前对话历史占8K预留4K给模型输出和工具返回。实测下来这个比例下模型回答质量最稳定。如果背景摘要膨胀到8K对话历史只剩4K用户连续聊几轮后模型就会出现“记不起刚才几步”的问题。3.2 情景记忆层的落地从对话历史到向量记忆情景记忆层的实现核心是把“对话历史”转成“可检索的事件记忆”。我这里说的不是简单的逐句存档而是有结构的摘要化记录。我在项目里的做法是在一轮对话结束后用户意图已明确、Agent已回复触发一次记忆写入流程。这个流程会做三件事整理事件摘要、生成向量、存入向量数据库。整理事件摘要时我会按固定结构抽取信息用户的目标是什么、Agent给出了什么结论、用户有没有表达偏好或异议、有没有遗留待办事项。每一条摘要控制在50到100字之间既保证信息密度又不至于太琐碎。这里我会用一次专门的模型调用做摘要抽取而不是直接把对话原文存下来因为原文太长了而且有很多寒暄和无关内容存进去只会污染向量索引。向量化用的是一个Embedding模型把摘要转换成768维或者1536维的向量。我项目里用的是bge-m3在中文场景的检索效果比较稳。存储介质用的Qdrant当然你也可以用Chroma、PgVector或者云服务提供的向量能力各有优劣后面我专门对比。还有一个容易被忽视的点情景记忆一定要带元数据。我在存储的每条记忆里都附加了会话ID、时间戳、事件类型用户偏好、任务记录、方案讨论等。这样在检索时除了相似度匹配还可以用时间范围、事件类型做过滤。如果没有元数据向量检索只能做无差别的相似度召回精确度会大打折扣。3.3 语义记忆层的构建从事件中提炼长期规则语义记忆层是所有层级里最容易做烂的。有些团队直接把它做成“把System Prompt里的硬规则存起来”但这样根本不算记忆因为规则没有来源也没有更新路径。真正有价值的语义记忆应该从情景记忆中自动沉淀出来。我的沉淀机制是一个定时/触发式的提炼流程。每次情景记忆新增到达一定规模比如累计20条新的摘要就触发一次语义提炼调用。这次调用把所有新闻情景摘要作为上下文让模型抽取其中具备长期参考价值的信息输出为结构化的语义记忆条目。我要求抽取时遵守三条标准一是不依赖特定时间背景排除“上周”“昨天”这类时间限定二是不依赖特定对话场景用户这次问A方案不能得出“用户只喜欢A方案”的结论三是具备行动指导价值能影响未来某一类决策的信息才算。凡是不满足这三条的即便在情景摘要里很醒目也不允许沉淀到语义记忆层。语义记忆的存储结构我建议用JSON格式每条记录包含主体用户/项目/组件、谓词偏好/禁止/要求/规则、对象具体内容、来源从哪条情景摘要提炼的、置信度初始为0.7后续被验证会提升。置信度这个字段特别有用当你发现某条记忆被频繁验证时可以提升其权重让它在后续检索排序中占据更靠前的位置。提取时机把语义记忆注入到Agent上下文的时机我这里给出了一个规律语义记忆要在每次请求组装时和基础System Prompt一起注入而不是检索出来临时拼接。因为语义记忆是Agent做任何决策都要参考的底层背景。但它又不能太占空间我控制语义记忆条目的总Token在2K以内超过这个体量就要做优先级裁剪只保留置信度最高的条目。4. 写入、检索与更新的完整实操策略4.1 写入时机不是所有对话都值得进记忆把什么信息写入记忆比怎么存更容易决定记忆系统的成败。我见过太多项目把所有对话全部入库结果记忆池里充满了“好的”“明白了”“哈哈哈”这类垃圾检索时把好好的向量空间搅成一锅粥。我的写入策略分两级。第一级是即时判断在每轮对话结束后用一次轻量模型调用判断这轮对话是否包含值得记录的信息。判断维度包括是否出现了用户的具体偏好、是否做出了决策或承诺、是否引入了新的事实或约束。只要命中其中任意一条就进入情景记忆的写入流程否则直接跳过。第二级是定时萃取情景记忆池每积累到一定规模就触发一次向语义记忆的提炼。这也就是我前面提到的批量抽取机制。注意这里有一个取舍——萃取跑得太频繁会耗费大量Token跑得太慢又会让情景记忆池膨胀。我试过的合理阈值是每20条新情景记忆触发一次或每24小时触发一次你先到先的为准。还有个关键点写入操作要异步。不要在用户对话的关键路径上做记忆写入否则每轮对话都要等待一次甚至两次模型调用响应延迟直接飙升。我通常的做法是把记忆写入动作丢进消息队列由后台Worker去处理用户无感知。4.2 检索策略相似度、时间衰减和重要性权重检索决定了记忆能不能在需要的时候被想起来。只做简单的Top-K相似度召回效果往往不理想因为相似的句子不一定相关相关的内容不一定字面相似。我的检索策略是“两阶段召回加精排”。第一阶段用向量相似度召回候选集比如召回30条这个阶段追求召回率不太在意精度。第二阶段做精排精排公式结合三个因素向量相似度、时间衰减因子、重要性权重。时间衰减因子我用的是指数衰减score base_score * exp(-lambda * days_since)。lambda取值在0.01到0.05之间具体看业务对时效的敏感度。比如做活动运营的Agentlambda取大值三天前的信息权重快速下降做项目知识管理的Agentlambda取小值三个月前的信息依然有价值。重要性权重直接取自元数据。我在写入情景记忆时会给每条记忆打一个重要性标签用户明确表达的偏好权重最高Agent观察到的隐含行为次之纯粹的流程记录最低。精排时权重叠加确保高价值记忆不被时间冲掉。还有一个细节检索时的查询改写。不要把用户的原始问题直接拿去向量搜索先做一次查询改写把用户的自然语言问题转成一个更适合检索的查询语句。比如用户问“你们还有什么适合新手的课程吗”改写后的查询是“面向新手的课程推荐 入门级别”召回效果会明显更好。4.3 更新与遗忘机制记忆不能只增不减在所有关于记忆的讨论里遗忘是最容易被忽略的环节。我早期做记忆系统就栽过跟头语义记忆越攒越多注入上下文的条目越来越多结果模型被一堆过时的旧偏好干扰做出很怪的判断。遗忘机制至少要做三件事。第一件是陈旧性清理定期扫描语义记忆超过有效期比如180天且未被命中的条目降权或删除。这里“未被命中”的判断依据是检索日志——如果一条记忆长期没有被召回说明它的存在价值很低。第二件是冲突消解当新记忆与既有记忆矛盾时不能直接写入覆盖。比如用户之前说“我喜欢简洁风格”今天说“这个设计太简单了我要更华丽的效果”。正确的做法是把两条记忆都保留但给新的记忆更高的置信度同时标记旧的记忆为“可能已失效”在一段时间内观察用户行为来最终裁决。第三件是记忆合并大量相似记忆会占用空间、干扰检索。我定期做一次聚类合并把同一主题的N条情景摘要合并成一条综述同时释放原始条目。这一步类似于人类把一段时期内的重复体验浓缩成一个总结性认识是记忆系统保持敏锐度的关键操作。5. 实操中的踩坑记录与排查清单5.1 向量检索结果质量差怎么排查这是最常见的坑。你兴致勃勃地做了向量记忆结果检索出来的东西驴唇不对马嘴。我的排查路径是这样的第一先确认Embedding模型选型是否匹配场景。做中文场景用通用英文模型效果大概率差我这里踩过坑后来换成了中文优化过的模型效果立竿见影。第二检查摘要质量。如果存入向量库的摘要本身就写得模糊不清检索质量一定上不去先看一眼库里存的原始摘要是不是“用户说了一些东西”这种废话。第三看检索的Top-K取值。K太小会漏掉相关记忆K太大会掺杂噪声。我的建议是先取大K召回、用精排压缩而不是一上来就取小K。还有一个经常被忽略的因素混合检索。纯向量检索在专有名词和多字面差异场景下效果不稳比如用户提到“预算方案”而库里存的是“资金计划”语义接近但向量匹配度不高。加上BM25词法检索做混合把两路结果合并再精排效果会稳很多。5.2 记忆污染和串扰记忆污染指的是低质量信息混进了记忆池然后反复被检索到、反复影响模型输出形成恶性循环。污染源主要有三个一是对话中的噪声信息被当成偏好记录用户随口说一句“今天天气真好”被记成了“用户喜欢晴天”二是摘要模型抽取时产生了幻觉写了用户根本没说过的话三是旧知识没有及时失效用户已经换了技术栈旧的偏好还在注入上下文。应对污染的办法第一是写入端把关严格按4.1的判断标准过滤第二是定期抽查我在可视化后台里做了记忆浏览功能隔几天人工扫一眼记忆池发现有问题的条目直接标记删除第三是建立“记忆反馈环”当用户明确否定Agent提到的记忆内容时触发一条“否定信号”对相关记忆降权并在上下文里标注“该记忆已被用户推翻”。5.3 存储膨胀和性能瓶颈记忆系统跑上几个月后向量库的体积会膨胀得很厉害随之而来的问题是检索延迟上升、Token费用上涨、校准流程变慢。我遇到过最极端的情况是情景记忆库存了超过百万条摘要单次检索耗时从50毫秒涨到了300毫秒。解决存储膨胀核心是分层级的归档策略热记忆最近30天、高频命中放在在线存储温记忆30到180天内做聚合压缩冷记忆超过180天直接转存到低成本存储或者干脆删除。检索时默认只查热记忆只有在热记忆召回不足时才降级查询温记忆。用这种策略我把检索延迟压回了60毫秒左右。还有一点是关于索引参数的调优。向量索引的HNSW参数里M值决定了每层最大连接数efConstruction决定了建索引时的候选集大小。M越大内存占用越高但召回质量越好efConstruction越大建索引越慢但索引质量越好。我在项目里用的M16、efConstruction200在召回质量和资源消耗之间平衡得比较好。如果你只有几千条记忆直接用暴力扫描就行建索引纯属多此一举。5.4 记忆与Agent框架协同时的几个共性问题最后这一小节想聊一聊记忆模块和整体Agent框架的配合问题。记忆系统不是独立的它要在Agent的编排流程里被正确调用才会真正发挥价值。第一个共性问题是在多Agent协作场景下记忆要不要共享。我的建议是共享基础语义记忆如用户的长期偏好、全局规则各自保留私有情景记忆如各自的执行过程。如果所有的Agent共享全部记忆会导致A Agent在某次任务中的中间错误被B Agent当作事实使用造成错误传播。更稳妥的做法是设计一份全局知识库存放可信的、经过确认的共享记忆各Agent只拥有这个知识库的读权限没有写权限只能向“记忆审核队列”提交建议写入的内容。第二个问题是一致性和刷新时机的冲突。有的框架会在每一轮Agent循环里重新注入全部记忆这在浅层任务里没问题但在长链路任务里会明显拖慢速度。我的做法是把记忆注入的时机分成三等任务开始时全量注入、工具调用之间只注入工作记忆、单轮对话结束时增量异步更新情景记忆。这样既保证Agent在整个任务里方向不偏又不在高频循环里反复重算大向量检索。第三个注意到的是安全问题。记忆内容可能包含敏感信息需要对不同层级设置不同的访问控制。语义记忆层因为它会被全局共享必须做脱敏处理比如用户手机号、地址信息只存储在受保护的存储介质中模型上下文里引用时用脱敏后的占位符。情景记忆层属于会话级数据默认只允许创建它的会话读取跨会话访问需要显式授权。这条看似是管理问题真出了问题就是安全事故我从一开始就把权限模型写进了记忆服务的访问逻辑里。还有就是在做Agent和框架选型时很多人分不清底层模型能力、Agent编排框架和记忆服务之间的关系。如果你用的是常规的对话式应用一个支持会话历史和摘要功能的应用框架加上外部向量库就能解决大部分问题但如果你做的是复杂的多步骤、跨会话Agent我建议独立出一套记忆服务别和业务逻辑耦合在一起。后面接新的Agent应用时记忆这块能直接平移复用。结尾一点真实体感分层记忆做下来我最大的体会是它不只是一个技术架构方案更是一种“取舍哲学”。每一层记忆都在时间和抽象度之间做权衡工作记忆快但是短情景记忆有细节但是散语义记忆稳定但是抽象。把这三层做扎实、做联动Agent才真正从一个“每轮都重新开始的接口调用者”变成一个“有连续经验的执行者”。如果你也在做Agent开发我建议你先别急着上很复杂的记忆系统。先把工作记忆管好清理上下文、压缩历史把最基础的连贯性保住然后加一层最简单的情景记忆用向量库存住用户的关键偏好跑两周之后再慢慢沉淀语义记忆。千万别一上来就把三层全做了Agent的分层记忆系统复杂度是递增的每一层都会给你带来新的权衡问题。先跑通再优化被问题逼到墙角你就知道该往哪一层下功夫了。
返回列表