资讯动态

Agent上下文压缩与记忆策略:5种压缩手段与3类记忆形态选型指南

发布时间:2026/10/22 19:39:12 来源:尧图企业网站定制
1. 为什么上下文管理成了Agent落地的第一道坎做过Agent项目的朋友大概率都经历过这种崩溃瞬间明明逻辑写得没问题工具调用也通了结果跑到第十几轮对话的时候模型开始胡言乱语要么把前面确认过的参数忘了要么把已经排除的方案又捡回来重新执行一遍。你去看日志发现上下文窗口已经塞满了模型在“记忆过载”的状态下做出的决策质量断崖式下跌。这个问题的本质不是模型不行而是上下文窗口是一种稀缺资源。当前主流模型的上下文窗口从8K到200K不等看起来很大但Agent场景下的消耗速度远超想象。一轮完整的Agent交互包含系统提示词、工具定义、历史对话、工具返回结果、中间推理过程轻松就能吃掉几千token。如果Agent需要执行一个多步骤任务比如“帮我分析这份销售数据并生成报告”中间涉及读文件、跑脚本、查数据库、调API十几轮下来上下文直接爆掉。所以上下文压缩与记忆策略不是一个“锦上添花”的优化项而是Agent能不能跑通长任务的生死线。这篇文章我会把我在实际项目中踩过的坑、试过的方案、以及最终沉淀下来的选型逻辑全部摊开讲。核心围绕三个东西5种压缩手段、3类记忆形态、4条选型红线中间会穿插对MemGPT、ACON、Context Editing这几个代表性方案的拆解分析。适合谁看如果你正在做Agent产品、正在被上下文溢出折磨、或者正在选型记忆方案这篇内容应该能帮你省掉至少两周的试错时间。如果你还没开始做Agent但准备入坑那更要看因为这些东西等你踩坑了再补成本会高很多。2. 五种上下文压缩手段的底层逻辑与实操细节压缩这个词听起来简单但落到工程实现上不同方案的差异巨大。有的压缩是有损的压完信息就没了有的是无损的但压缩比有限有的压缩需要额外调模型成本上去了有的压缩是结构化的需要你提前设计好数据格式。下面这五种是我实际用过或者深度评估过的按压缩力度从轻到重排列。2.1 滑动窗口裁剪最简单但也最容易翻车滑动窗口的逻辑非常直白只保留最近N轮对话超出的直接丢掉。实现上就是一个队列新消息进来旧消息出队。代码大概长这样class SlidingWindowMemory: def __init__(self, max_turns10): self.max_turns max_turns self.history [] def add(self, role, content): self.history.append({role: role, content: content}) if len(self.history) self.max_turns * 2: self.history self.history[-(self.max_turns * 2):] def get_context(self): return self.history这个方案的优势是零成本、零延迟、实现简单。但问题也很致命它假设最近的信息最重要但Agent场景下这个假设经常不成立。比如用户在第三轮说了一句“我的预算上限是5000”到第十五轮你让他推荐方案滑动窗口早就把预算信息冲掉了模型只能瞎猜。我踩过的坑早期做一个客服Agent用滑动窗口保留最近8轮结果用户在前面提到的订单号被冲掉了Agent反复问“请提供您的订单号”用户体验极差。后来改成保留最近8轮加上一个“关键信息槽位”才勉强能用。注意滑动窗口不是不能用但一定要配合一个“关键信息提取”机制把用户明确给出的约束条件、ID、参数单独存起来不参与窗口淘汰。2.2 摘要压缩用一次模型调用换长期记忆摘要压缩的思路是当上下文快满的时候调一次模型把前面的对话压缩成一段摘要然后用摘要替换原始对话。这样既保留了核心信息又大幅降低了token占用。实现上有两种粒度全量摘要把整个历史对话一次性压缩成一段话。优点是简单缺点是每次压缩都要调模型而且随着对话变长摘要本身也会变长。增量摘要只压缩最老的那部分对话保留最近的原始对话。比如保留最近5轮原文把5轮之前的压缩成摘要。这样摘要可以累积但每次只需要压缩一小段。def summarize_history(client, history, keep_recent5): if len(history) keep_recent * 2: return history old_messages history[:-keep_recent * 2] recent_messages history[-keep_recent * 2:] summary_prompt 请将以下对话压缩成一段简洁的摘要保留所有关键信息、决策和约束条件\n for msg in old_messages: summary_prompt f{msg[role]}: {msg[content]}\n response client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: summary_prompt}] ) summary response.choices[0].message.content return [{role: system, content: f历史对话摘要{summary}}] recent_messages摘要压缩的关键在于摘要质量。我试过用不同模型做摘要发现小模型比如7B级别的经常丢关键信息尤其是数字、ID、否定条件“不要用XX方案”这种。后来固定用GPT-4o-mini做摘要质量稳定很多成本也可控。实操心得摘要prompt里一定要明确要求“保留所有数字、ID、否定条件和用户明确表达的偏好”否则模型很容易把这些当成“细节”丢掉。2.3 结构化提取把对话变成数据库结构化提取的思路和摘要不同它不是把对话压成一段话而是把对话中的关键信息抽取成结构化字段。比如{ user_constraints: [预算不超过5000, 必须支持中文], confirmed_decisions: [方案A已确认, 方案B已排除], pending_items: [等待用户提供订单号], key_entities: {product: X200, order_id: ORD-12345} }这个方案的优点是信息密度极高一个JSON对象可能只占100token但包含了十几轮对话的核心信息。而且结构化数据可以被程序直接读取和操作比如你可以写逻辑判断“如果pending_items不为空就优先处理”。缺点是提取本身需要模型调用而且提取的准确性依赖prompt设计。我试过用function calling来做结构化提取效果比纯文本prompt好很多因为function calling会强制模型按照schema输出。extract_function { name: extract_context, parameters: { type: object, properties: { user_constraints: {type: array, items: {type: string}}, confirmed_decisions: {type: array, items: {type: string}}, pending_items: {type: array, items: {type: string}}, key_entities: {type: object} } } }注意结构化提取的schema设计很关键。字段太多提取成本高且容易出错字段太少信息丢失严重。我的经验是控制在5-8个字段每个字段的语义要清晰无歧义。2.4 向量检索压缩按需召回而非全量保留向量检索的思路是把所有历史对话存入向量数据库每次需要的时候根据当前query检索最相关的几条而不是把所有历史都塞进上下文。这个方案在知识型Agent里特别好用。比如一个技术文档问答Agent用户可能问了20个问题但当前问题只和其中3个相关那就只检索那3个相关的对话片段其他的不占上下文。import chromadb client chromadb.Client() collection client.create_collection(agent_memory) def store_message(msg_id, content, metadata): collection.add( documents[content], metadatas[metadata], ids[msg_id] ) def retrieve_relevant(query, top_k3): results collection.query( query_texts[query], n_resultstop_k ) return results[documents][0]但向量检索有个坑它依赖语义相似度但Agent场景下很多关键信息是“约束型”的语义相似度不一定高。比如用户说“不要用红色”当前query是“推荐一个配色方案”语义上“不要用红色”和“配色方案”的相似度可能不高但这条约束必须被召回。我的解法是混合检索向量检索 关键词检索 规则召回。规则召回就是硬编码一些模式比如检测到“不要”“必须”“只能”这类词就强制把相关消息召回。2.5 分层压缩把不同信息放在不同层级分层压缩是我目前最推荐的方案它的核心思想是不是所有信息都同等重要所以不应该用同一种压缩策略。我通常把上下文分成三层热层最近3-5轮对话保留原文不压缩。温层5-20轮之前的对话用摘要压缩保留核心信息。冷层20轮之前的对话用结构化提取只保留关键字段。这样既保证了最近交互的完整性又保证了长期记忆不丢失同时token占用可控。class LayeredMemory: def __init__(self, hot_turns5, warm_turns20): self.hot [] self.warm_summary self.cold_facts {} self.hot_turns hot_turns self.warm_turns warm_turns def add(self, role, content): self.hot.append({role: role, content: content}) if len(self.hot) self.hot_turns * 2: self._demote_to_warm() def _demote_to_warm(self): old self.hot[:2] self.hot self.hot[2:] # 调模型更新warm_summary self.warm_summary update_summary(self.warm_summary, old) def get_context(self): context [] if self.cold_facts: context.append({role: system, content: f已知事实{self.cold_facts}}) if self.warm_summary: context.append({role: system, content: f历史摘要{self.warm_summary}}) context.extend(self.hot) return context分层压缩的难点在于层与层之间的信息传递。热层降到温层的时候摘要要准确温层降到冷层的时候结构化提取要完整。我一般会在降层的时候多调一次模型做校验确保关键信息没丢。3. 三类记忆形态从瞬时到长期的完整拼图压缩解决的是“上下文太满”的问题但记忆解决的是“信息该存哪里、存多久、怎么取”的问题。这两个是互补的不是替代关系。我习惯把Agent记忆分成三类每类的存储介质、生命周期、访问方式都不一样。3.1 工作记忆当前任务的临时黑板工作记忆就是当前任务执行过程中的临时状态任务结束就清掉。比如Agent正在执行“订机票”任务工作记忆里存的是出发地、目的地、日期、乘客信息、当前执行到哪一步了。工作记忆的特点是读写频繁、生命周期短、结构灵活。我一般用一个字典或者JSON对象来存不涉及数据库。working_memory { task: book_flight, step: select_seat, params: { from: 北京, to: 上海, date: 2025-03-15, passenger: 张三 }, intermediate_results: { available_flights: [CA1234, MU5678], selected_flight: CA1234 } }工作记忆的关键是step字段。Agent每执行一步就更新step这样即使上下文被压缩了Agent也能通过读取工作记忆知道当前进度。我见过很多Agent项目把进度信息只放在对话历史里结果上下文一压缩Agent就忘了自己执行到哪了开始重复执行。实操心得工作记忆一定要有一个明确的“当前步骤”字段并且每一步操作前都先读这个字段确认自己该做什么。这个习惯能避免90%的重复执行问题。3.2 会话记忆跨轮次但限于单次会话会话记忆是同一个会话内跨轮次保留的信息会话结束就清掉。比如用户在一次会话中先问了产品价格又问了售后政策再问“那这个价格包含运费吗”这里的“这个价格”就需要会话记忆来解析。会话记忆的实现方式就是我前面讲的压缩策略的组合。我一般用分层压缩来管理会话记忆热层保留最近几轮温层用摘要冷层用结构化提取。会话记忆的一个关键设计是会话边界。什么时候算一次会话结束我的经验是用户明确说“再见”“谢谢”算结束用户超过30分钟没说话算结束用户主动点击“新会话”按钮算结束。会话结束后会话记忆可以选择清空也可以选择沉淀到长期记忆。3.3 长期记忆跨会话的知识沉淀长期记忆是跨会话保留的比如用户的偏好、历史订单、常用地址。长期记忆的存储介质一般是数据库或向量库访问方式是检索。长期记忆的设计要考虑三个问题存什么不是所有信息都值得长期存。我一般只存三类用户明确表达的偏好“我喜欢靠窗座位”、高频使用的实体常用地址、常用联系人、历史决策结果上次选了方案A。怎么存结构化字段存关系型数据库非结构化文本存向量库。我一般用混合存储结构化字段用于精确查询向量库用于语义检索。怎么取长期记忆的检索要和当前query相关。我一般用“向量检索 规则过滤”的方式先向量召回top-10再用规则过滤掉过期的、不相关的。def retrieve_long_term_memory(user_id, query): # 向量召回 candidates vector_store.query(query, top_k10, filter{user_id: user_id}) # 规则过滤 filtered [] for c in candidates: if c[timestamp] time.time() - 90 * 24 * 3600: # 90天内 if c[confidence] 0.7: # 置信度阈值 filtered.append(c) return filtered[:3]长期记忆最大的坑是信息过期。用户三个月前说“我住在北京”现在可能已经搬到上海了。我的解法是给每条长期记忆加一个时间戳和置信度检索的时候优先返回时间近的、置信度高的。如果新旧信息冲突以新的为准。4. 四个代表性方案的拆解MemGPT、ACON、Context Editing及自研方案前面讲的是通用方法论这一节我拿四个具体方案来拆看看它们分别是怎么解决上下文和记忆问题的以及各自的适用场景和坑点。4.1 MemGPT把操作系统那套搬到了Agent上MemGPT的核心思想非常有意思它把LLM的上下文窗口类比成计算机的内存把外部存储类比成硬盘然后设计了一套“虚拟上下文管理”机制让Agent可以在“内存”和“硬盘”之间换页。具体来说MemGPT把上下文分成两部分主上下文就是实际送给LLM的prompt包含系统指令、工作上下文、最近消息。外部上下文存在数据库里的历史消息、知识库、归档数据。当主上下文快满的时候MemGPT会触发一个“换页”操作把不重要的消息从主上下文移到外部上下文或者从外部上下文检索相关消息加载到主上下文。这个换页操作是通过function calling实现的Agent自己决定什么时候换页、换什么。# MemGPT风格的换页函数定义 functions [ { name: archive_messages, description: 将消息归档到外部存储, parameters: { type: object, properties: { message_ids: {type: array, items: {type: string}}, summary: {type: string} } } }, { name: retrieve_messages, description: 从外部存储检索消息, parameters: { type: object, properties: { query: {type: string}, top_k: {type: integer} } } } ]MemGPT的优点是架构清晰、扩展性强理论上可以支持无限长的对话。但缺点也很明显换页决策依赖模型判断模型判断不准的时候会频繁换页或者该换不换。我实测下来MemGPT在对话型Agent上表现不错但在任务型Agent上模型经常在关键步骤把重要信息归档了导致后续步骤找不到参数。注意用MemGPT风格方案的时候一定要给“归档”操作加约束比如“当前任务未完成时不允许归档与当前任务相关的消息”。4.2 ACON用压缩模型做端到端上下文压缩ACONAdaptive Context Compression的思路和MemGPT不同它不搞换页而是训练一个专门的压缩模型把长上下文压缩成短上下文。这个压缩模型是端到端训练的目标是“压缩后的上下文能让下游任务模型表现最好”。ACON的核心创新是自适应压缩率不是所有上下文都压到同一个长度而是根据内容的重要性动态决定压缩率。重要的内容少压不重要的内容多压。这个方案的优势是压缩质量高因为是专门训练的模型比通用摘要模型更懂“什么该保留”。但劣势也很明显需要训练、需要额外部署一个模型、推理成本增加。对于大多数团队来说训练一个压缩模型的门槛太高了不太现实。我的建议是如果你是大厂或者有充足算力可以考虑ACON思路自己训一个压缩模型。如果是中小团队直接用GPT-4o-mini做摘要效果已经够用了没必要为了10%的提升去搞一套训练 pipeline。4.3 Context Editing让模型自己编辑上下文Context Editing是最近比较火的一个方向核心思路是让模型自己决定保留哪些上下文、删除哪些上下文。具体实现上一般是在prompt里加一个指令让模型在生成回复之前先输出一个“上下文编辑”操作比如“删除第3-5轮对话”“保留第7轮的关键信息”。editing_prompt 在回复用户之前请先分析当前上下文决定是否需要编辑。 如果需要编辑输出以下格式 edit delete消息ID列表/delete keep消息ID列表/keep summarize需要摘要的消息ID列表/summarize /edit 然后输出你的回复。 Context Editing的优点是灵活、不需要额外模型缺点是依赖模型的元认知能力模型得知道自己“记得什么”“忘了什么”。我实测下来GPT-4级别的模型做Context Editing效果还行但小模型经常编辑错误把该留的删了该删的留了。实操心得用Context Editing的时候一定要加一个“安全网”编辑操作执行后如果模型在后续回复中引用了被删除的信息就自动回滚这次编辑。这个安全网能避免大部分编辑错误。4.4 自研混合方案我的最终选择试过上面这些方案之后我最终选择的是自研混合方案核心思路是工作记忆用结构化字典不参与压缩。会话记忆用分层压缩热层原文、温层摘要、冷层结构化。长期记忆用向量库 关系型数据库混合存储。压缩触发用规则 模型判断结合token超过阈值触发但具体压什么由模型决定。安全网每次压缩后用一个小模型做校验检查关键信息是否丢失。这个方案的好处是可控、可调试、成本可控。每个环节我都能看到中间结果出了问题能定位。MemGPT和Context Editing太“黑盒”了出了问题很难排查。ACON又太重了不适合快速迭代。class HybridMemory: def __init__(self, token_threshold3000): self.working {} self.hot [] self.warm self.cold {} self.long_term VectorStore() self.token_threshold token_threshold def before_llm_call(self, query): # 检查token数 if self._count_tokens() self.token_threshold: self._compress() # 检索长期记忆 long_term_context self.long_term.retrieve(query) return self._build_context(long_term_context) def _compress(self): # 热层降到温层 if len(self.hot) 10: old self.hot[:4] self.hot self.hot[4:] self.warm update_summary(self.warm, old) # 温层降到冷层 if len(self.warm) 2000: self.cold extract_facts(self.warm, self.cold) self.warm # 校验 self._validate_compression()5. 四条选型红线这些坑我替你踩过了选型的时候很多人只看“效果好不好”但实际落地的时候成本、延迟、可控性、可调试性往往比效果更重要。下面这四条红线是我用真金白银的token费和无数个debug夜晚换来的。5.1 红线一压缩不能引入不可接受的延迟压缩是要调模型的调模型就有延迟。如果你的Agent要求响应时间在2秒以内那摘要压缩基本不可行因为一次摘要调用就要1-2秒。这种情况下只能用滑动窗口或者规则压缩。我做过一个实时客服Agent要求响应时间1.5秒以内。一开始用摘要压缩结果每次压缩都超时。后来改成“滑动窗口 关键信息槽位”响应时间降到800ms虽然记忆能力弱了一点但用户体验好很多。实操心得如果你的场景对延迟敏感压缩策略一定要选“零模型调用”的方案比如滑动窗口、规则提取、结构化槽位。摘要和向量检索可以作为异步操作在后台慢慢做。5.2 红线二压缩不能丢失关键约束Agent场景下有些信息是“硬约束”丢了就出大问题。比如“预算不超过5000”“不要用红色”“必须支持中文”。这些约束一旦在压缩中丢失Agent就会给出错误建议。我的解法是约束信息单独存储不参与压缩。具体来说在对话过程中一旦检测到约束型语句包含“不要”“必须”“只能”“不超过”等关键词就立即提取出来存到一个专门的constraints列表里这个列表永远保留在上下文中不压缩。CONSTRAINT_KEYWORDS [不要, 必须, 只能, 不超过, 至少, 禁止, 务必] def extract_constraints(message): constraints [] for sentence in message.split(。): if any(kw in sentence for kw in CONSTRAINT_KEYWORDS): constraints.append(sentence.strip()) return constraints5.3 红线三压缩策略要可调试、可回滚压缩是个“有损操作”有损操作就必须可调试。我见过太多项目压缩逻辑写在一个黑盒函数里出了问题根本不知道是哪一步压错了。我的做法是每次压缩都记录日志压缩前的token数、压缩后的token数、压缩掉了哪些消息、压缩后的摘要内容。这样出了问题可以回溯。def compress_with_logging(history): before_tokens count_tokens(history) compressed do_compress(history) after_tokens count_tokens(compressed) log { timestamp: time.time(), before_tokens: before_tokens, after_tokens: after_tokens, removed_messages: [m[id] for m in history if m not in compressed], summary: compressed[0][content] if compressed else } logger.info(json.dumps(log)) return compressed另外压缩一定要支持回滚。如果压缩后模型表现异常要能一键恢复到压缩前的状态。我一般会在压缩前存一个快照压缩后如果连续两轮模型输出质量下降就自动回滚。5.4 红线四不要为了压缩而压缩最后这条红线最重要压缩不是目的让Agent表现好才是目的。我见过一些项目为了追求“上下文利用率”或者“压缩比”把压缩做得特别激进结果Agent表现反而下降了。压缩的收益是降低token成本、避免上下文溢出但代价是信息损失、额外延迟、实现复杂度。如果当前上下文还没满Agent表现也正常那就不要压缩。压缩应该是“按需触发”的不是“每轮都做”的。我的触发策略是token数超过模型窗口的70%才触发压缩。70%这个阈值是我试出来的低于70%压缩收益不明显高于70%容易溢出。当然不同模型不一样你可以根据实际情况调整。6. 常见问题排查与实战避坑速查这一节我整理了一些实际项目中高频出现的问题和排查思路做成速查表方便你遇到问题的时候快速定位。问题现象可能原因排查方法解决方案Agent重复执行已完成的步骤工作记忆的step字段没更新或压缩时把step信息丢了检查工作记忆的step字段是否每步都更新确保step字段不参与压缩每步操作前先读stepAgent忘记用户约束约束信息被压缩掉了检查压缩日志看约束消息是否被删除约束信息单独存储不参与压缩压缩后Agent表现下降摘要质量差关键信息丢失对比压缩前后的模型输出定位丢失的信息类型优化摘要prompt或改用结构化提取压缩延迟太高摘要调用太频繁或模型太大统计每次压缩的耗时降低压缩频率或换更小的模型做摘要长期记忆检索不准向量检索语义相似度不够检查检索结果和query的相关性改用混合检索向量关键词规则上下文还是溢出压缩比不够或压缩触发太晚检查压缩前后的token数提高压缩力度或提前触发阈值模型在压缩后胡言乱语压缩后的上下文格式混乱检查压缩后的上下文结构确保压缩后的上下文有清晰的角色标记和分隔符除了表格里的问题还有几个我踩过的坑值得单独说坑一摘要prompt太笼统。一开始我用“请总结以下对话”做摘要结果模型把很多关键细节都省了。后来改成“请保留所有数字、ID、否定条件、用户偏好和已确认的决策”摘要质量明显提升。坑二向量检索的top_k设太大。top_k设成10检索回来一堆不相关的反而干扰模型。后来改成top_k3配合规则过滤效果好很多。坑三压缩后没有重新计算token。有一次压缩完忘了重新计算token数结果以为压缩成功了实际上还是超的。后来在压缩函数里强制加了一步token重算。坑四长期记忆没有过期机制。用户三个月前说的偏好三个月后还在用结果推荐错了。后来加了时间戳和置信度超过90天的记忆自动降权。7. 我个人的选型建议与落地路线如果你正在选型我的建议是按这个顺序来先上滑动窗口 关键信息槽位。这是成本最低、实现最快的方案能解决80%的上下文溢出问题。关键信息槽位用来存约束、ID、参数这些不能丢的东西。再加分层压缩。热层原文、温层摘要、冷层结构化。这个方案能支撑大部分中长对话场景成本也可控。最后加长期记忆。用向量库 关系型数据库混合存储配合时间戳和置信度做检索过滤。长期记忆不是必须的如果你的Agent不需要跨会话记忆可以跳过。MemGPT和Context Editing可以作为参考但不建议直接上。这两个方案的黑盒程度太高出了问题不好排查。除非你的团队有足够的工程能力做深度定制否则自研混合方案更稳妥。落地路线的话我一般分三步走第一步跑通基础流程。用滑动窗口 工作记忆确保Agent能完成单轮和多轮任务。第二步加压缩和会话记忆。用分层压缩管理会话记忆确保长对话不溢出。第三步加长期记忆和优化。根据实际需求加长期记忆然后根据线上数据持续优化压缩策略和检索策略。最后分享一个小技巧压缩策略一定要做A/B测试。我一般会同时跑两套压缩策略一套激进一套保守然后对比Agent的任务完成率和token成本。数据说话比拍脑袋靠谱。这个方向后续还可以扩展的地方很多比如用更小的模型做压缩、用强化学习优化压缩策略、做跨会话的记忆迁移等等。但核心逻辑不变压缩是为了让Agent在有限的上下文里做更多的事记忆是为了让Agent在更长的时间跨度里保持一致性。把这两个东西搞明白Agent的上下文管理就通了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑