☰
Agent上下文压缩与记忆管理实战:5种压缩手段、3类记忆形态与MemGPT选型
2026/10/14 6:54:01 网站建设 项目流程

做Agent的朋友应该都体会过这种感觉:窗口一长,模型就开始胡言乱语;日志一多,每个请求的账单都在提醒你token有多贵。我接手过的几个Agent项目,早期清一色靠堆上下文窗口解决问题,等到对话轮次过百、工具调用记录翻了几番之后,整个系统基本上就处在“能跑但随时会崩”的状态。后来我花了大半个月把上下文压缩和记忆策略重新梳理了一遍,从简单截断到分层摘要,再到引入MemGPT这样的管理框架,算是把这套东西彻底理清了。这篇文章就把我这次改造过程里的核心思路、具体做法和踩过的坑整理出来,重点讲5种压缩手段、3类记忆形态,以及我在做技术选型时总结出的4条红线,最后会拆解一下MemGPT、ACON、Context Editing这三条路到底该怎么选。

先说明一点,这篇文章不是什么教科书式科普,也不是从文档里抄出来的“官方指南”,就是我自己实际接入、对比、调参之后沉淀下来的东西。里面会涉及不少原理,但我会尽量把“为什么这么做”讲透,而不是只丢给你一堆术语。如果看完能帮你在下次做Agent记忆系统时少踩几个坑,这篇文章就没白写。

1. 上下文问题的根源,不只是“不够用”

1.1 为什么窗口再大也还是会炸

很多技术团队的第一反应是把上下文窗口从128K换到200K、甚至1M,觉得只要窗口够大,所有问题就都解决了。但真做起来你会发现,窗口变大的同时,三个问题被放大了。

首先是检索噪声问题。Token多了不代表模型都能用上,实际表现是,相关度低的内容会严重干扰模型的注意力分配。我做过一次简单对比测试,在32K窗口内,模型对指定信息的提取准确率还行,但把窗口拉到120K之后,一段夹在大量无效日志里的关键指令,准确率直接掉了将近一半。

其次是成本失控。每个请求的token消耗是实打实的,窗口拉大意味着每次调用都在烧钱,还不算多轮对话里的重复累加。我算过一笔账,如果要让一个Agent在一天之内完成几百次工具调用并将所有历史都保留在上下文中,光输入token的费用就足够让人肉疼了。

最后是延迟上升。长上下文会对每次生成的初始化阶段带来额外耗时,在用户侧体感就是“转圈圈”。如果你做的是一个实时性要求较高的交互型Agent,这种延迟几乎是不可接受的。

所以结论很简单:窗口不该用来“装下所有东西”,而应该被当作临时工作台来管理。你要做的不是让所有内容都留在窗口里,而是确保窗口里永远只放当下最需要的内容,同时把历史沉淀到外部存储里,用的时候再拉回来。

1.2 压缩的本质是“丢之前想清楚”

压缩和记忆本质上是一件事的两个方向。压缩解决的是“窗口放不下”的问题,记忆解决的是“丢了之后还能找回来”的问题。两者配合得当,才能做到既保证当前对话的流畅,又不丢失长期有用的信息。

我做改造前的状态是:所有历史消息直接拼进窗口,工具返回的JSON原样保留,日志全量塞在上下文末尾。结果就是,模型在前几轮表现得像专家,越聊越像失忆,甚至会重复调用同一个工具。后来我把架构调整成“工作记忆+长期存储”的模式,窗口里只保留最近几轮对话和高优先级的临时信息,其余内容按记忆形态分类落到外部存储里,情况立刻好转。

在做任何压缩之前,你要先想清楚一个原则:**你丢掉的不是数据,而是当下用不到、之后可能有用的信息。**所以压缩不是一个单向丢弃的动作,而是“筛选+转换+索引”的组合。接下来要讲的5种压缩方法,本质上是这几项操作的不同组合。

2. 5种压缩手段拆解:从暴力截断到语义索引

2.1 截断与丢弃,最朴素但最容易被误用

截断是所有人最早尝试的方式,就是窗口满了之后,把最早的消息直接扔掉。实现成本几乎为零,效果也非常直接。但它的问题也很明显:你丢的不只是无效信息,还可能是用户早期给出的关键约束或偏好设定。

我在一个客服类Agent上试过按轮次截断,保留最近10轮,结果用户在第8轮提到“我之前说过不要推荐XX品牌”,模型完全无感,继续推荐,用户体验直接崩盘。

截断不是不能用,而是要配合“关键信息保护机制”。我的做法是:在截断之前,先跑一轮关键词和指令识别,把包含用户明确偏好、任务目标、安全约束的句子单独抽出来,挪到一个“持久约束区”,这个区域永不参与普通截断,只受显式更新。这样窗口清理了,核心约束还在。

另外,截断的粒度也很讲究。按完整消息删是最粗的方式,容易误伤;按token数删又可能切开半句话,影响模型理解。我建议按“消息块+关联标记”的方式删,也就是一次性把一条完整消息及其附属的工具调用记录删掉,保证语义的完整性。

2.2 摘要压缩,用信息密度换空间

摘要压缩比截断高级很多,核心思路是用一小段文字概括一大段历史,让模型在不看原始内容的情况下,仍然能把握关键信息。这也是目前大多数Agent框架默认推荐的压缩方式。

做摘要压缩的时候,最忌讳的是“每次都从头总结”。如果你的Agent每分钟就要做一次摘要,每次都把这十分钟的全部对话压成一句话,那上下文里塞的就是一堆冗余的“历史小结”。更好的做法是分两层:

  • 增量摘要:每次只对新增的对话进行总结,再和之前的摘要做合并。
  • 层级摘要:把增量摘要按时间段或者按主题分组,再次压缩成更高层的摘要,形成“对话摘要树”。

我在一个数据分析Agent上用的就是层级摘要:第一层是每5轮对话生成一条短摘要;第二层是把10条短摘要合并成一段主题性总结;第三层才是跨会话的“用户习惯与偏好”描述。这样每次取用的时候,可以根据需要选择不同粒度的摘要,不必每次都从原始日志恢复。

不过摘要压缩有个天然缺陷:信息遗失是永久性的。摘要本身就会丢失细节,一旦原始内容被替换掉,后面想找回某个具体的数值或某段准确的表达就不可能了。所以摘要压缩一定要和外部记忆配合使用,摘要负责给模型的“当前理解”兜底,原始数据负责给“事后追溯”兜底。

还有个小技巧:做摘要之前,先让模型列出“关键实体”,比如人名、产品名、数字指标、截止日期,然后把实体清单加在摘要前面。这样即使摘要本身写得很干巴,关键实体也能被后续检索命中。

2.3 结构化信息提取,把对话变成可查询的数据

摘要压缩适合“保留语义概貌”,但如果你想让Agent具备“精确回答某个字段”的能力,摘要就有点力不从心了。比如用户问“我们上次说的那个报价是多少”,摘要里可能写的是“讨论了报价方案”,但具体数字已经被吞掉了。这种情况就要靠结构化信息提取。

结构化提取的思路是:让模型把自然语言对话转成预设好的结构化字段,比如意图、实体、数值、状态、截止时间等,然后存成JSON或者一张表。这样后续查询时可以直接检索字段,不必在长文本里大海捞针。

我在客户管理类Agent里维护了一张“会话状态表”,字段包括:客户ID、当前意向等级、行动项列表、下一步计划、最近一次联系时间。每次对话结束,模型会尝试更新这张表。当下次会话开始时,Agent只读取这张表,就能快速恢复“这个人现在处于什么阶段”,而不需要翻聊天记录。

说实话,结构化提取的效果很依赖字段设计。字段定得太细,模型提取时容易出错;字段定得太粗,又容不下复杂信息。我的建议是:核心对话里只需要维护5~8个关键字段,其余信息一律落到摘要和非结构化记忆里,让检索负责兜底。

2.4 语义索引与检索,让记忆可以被“问”出来

如果说摘要和结构化提取是“主动压缩”,那么语义索引和检索就是“按需解压”。它的思路是:不急着把信息压缩成摘要,而是先把所有原始内容做向量化,建立索引,等到模型回答问题或者做推理时,再去检索和当前任务最相关的片段。

这套思路本质上就是RAG(检索增强生成)在Agent记忆里的应用。和文档问答里的RAG相比,Agent的记忆检索有个明显不同:Agent的记忆不仅包含用户消息,还包括工具调用记录、系统事件、模型自身的历史推理过程。这些内容如果混在一起做向量化,检索效果会变差。

我给Agent做记忆索引时,是按消息类型分库的,至少分成三路:

  • 用户消息库(记录用户需求、偏好、反馈)
  • 工具调用库(记录每次调用了什么工具、参数是什么、结果如何)
  • 系统事件库(记录错误、超时、异常状态)

每次需要检索记忆时,根据当前任务的类型去对应的库中找。比如模型要判断“上一次信用卡扣款失败的错误码是什么”,只查工具调用库就完全够了,不用把用户闲聊的向量也拉过来干扰排序。

检索压缩的另一层含义是“检索结果的再压缩”。拉回来的Top-K条记忆加起来可能有好几千Token,直接全部塞进窗口同样会撑爆。所以我在检索之后还会加一个精排和重写的过程,把Top-K结果进一步筛选到3~5条,再让模型用一两句话概括每条的核心内容。

2.5 分层摘要与图记忆,面向长期演化

单次对话再长也有限度,Agent真正面临挑战的是多会话场景里的记忆管理。用户可能隔了两周又回来继续讨论同一个项目,这时候你不能再依赖对话摘要,而是要有一套能够长期演化的记忆结构。

我最后落地的是“对话树+实体图”的结合方案。对话树负责按时间保存每个会话的分层摘要,实体图负责记录核心实体之间的关系,比如“项目X”、“客户Y”、“约定时间Z”,每次对话更新这些实体和关系。这样用户回来之后,Agent可以先查实体图恢复背景,再查最近会话摘要恢复细节,最后才开始新一轮对话。

这套方案在落地时要注意一个问题:实体图如果维护得太细,更新成本非常高,而且容易产生矛盾(同一实体在A会话和B会话里状态不一样)。我的做法是,实体图只保存“稳定事实”,比如名字、角色、约束、长期目标,而把临时状态放到会话摘要里。对话树负责“动态演进”,实体图负责“稳定锚点”,两者互相配合。

3. 3类记忆形态:工作记忆、语义记忆、情景记忆

3.1 工作记忆:窗口里的“临时便签”

工作记忆对应的是模型当前上下文窗口中的内容,包括系统提示词、最近的对话轮次、当前的工具调用链。它的特点是读取快、容量小、不需要额外存储,但一压缩就可能丢。

管理工作记忆的标准是“够用就好”。我在实践里会动态调整窗口内容:每轮对话结束之后,识别哪些内容已经不再需要(比如已经完成的工具调用结果),哪些内容还要留着(比如用户刚提到的新约束),然后在下一次生成前重写系统提示词,把不需要的部分拆出去。

一个容易踩坑的细节是,工作记忆的更新要基于“重写”而不是“追加”。如果你直接在新消息前面堆旧消息,很快窗口就又满了;但如果你每次根据当前状态重新生成一份精简的系统提示词,就能让窗口利用率保持在较高水平。这个过程我习惯叫它“状态快照更新”,每次只留当前需要的信息。

3.2 语义记忆:关于“事实”的长期存储

语义记忆保存的是用户或系统长期稳定的事实性信息,比如用户的偏好、项目的背景、业务规则。它存在的意义是:即使相关对话早就滚出窗口,Agent仍然能给出符合用户预期的回答。

在落地时,语义记忆通常对应我前面说的“实体图”和“结构化字段表”。它的更新策略不是“每次写入”,而是“定期校验”。我见过不少团队把语义记忆做成了聊天记录的全量备份,每次有新信息就追加一条“用户说了……”的记录,结果存了几个月之后,同一事实可能以七八种不同说法存在里面,检索时也不知道信哪条。

正确的做法是:语义记忆需要“合并去重”。新对话产生了新信息之后,先尝试把它合并到已有的语义条目里,如果描述一致就更新强度,描述冲突就标记待确认。只有确认过的信息才能进入语义记忆,否则最多算临时记忆。

3.3 情景记忆:关于“经历”的可回溯存档

情景记忆保存的是特定时间和场景下的完整经历,相当于用户的“操作流水账”。它不强调抽象出事实,而是强调“当时到底发生了什么”,方便回溯和复盘。

情景记忆最适合的载体就是对话日志和事件流水。我做了一个统一的“事件流存储”,所有用户消息、工具调用、系统异常都按时间顺序写进去,每条事件带标签、时间戳、关联会话ID。查询的时候,你可以按“时间段+事件类型+关键词”组合筛选,也可以直接按时间轴回放整个会话。

情景记忆和语义记忆还有一个配合方式:语义记忆负责“概括”,情景记忆负责“佐证”。比如语义记忆里有一条“客户对价格敏感”,当模型基于这条事实做决策时不放心,可以回溯情景记忆,看看是哪次对话让模型得出了这个结论。这在调试Agent决策链的时候非常救命。

三类记忆的最终划分可以这样记:工作记忆是“当前正在思考的内容”,语义记忆是“已经确定的事实”,情景记忆是“发生过的事件流水”。三者各自的压缩方式、存储介质、更新策略都不同,混在一起管理是最常见的坑。

4. 4条技术选型红线,决定系统能不能撑住

4.1 红线一:压缩方案必须区分“模型读什么”和“系统存什么”

很多团队在做完压缩之后,直接把压缩产物当作唯一存储,原始内容删掉。这种设计刚开始看着很省空间,但一旦模型需要精确信息(某个数字、某句话说法的原文),就再也找不回来了。

我的原则是:**模型读取的上下文可以压缩,系统存储的原始记录必须保留。**压缩只影响模型的输入,不影响系统的数据完整性。所以无论用哪种压缩方法,原始数据都应该以可回溯的方式落在存储层,压缩产物只是“为模型准备的视图”。

这条红线决定了你的架构能不能长期演进。因为评估压缩质量需要对比,如果你把原始数据都丢了,后面连“压缩得好不好”都无从验证。

4.2 红线二:压缩必须可配置,禁止“一刀切”

我发现不少开箱即用的框架默认把压缩做成“固定流程”,比如窗口快满时就自动摘要,不管当前是什么任务类型。这在研发阶段没问题,上线之后就麻烦了。

不同场景对压缩的容忍度差别非常大。客服场景可以接受对话摘要丢细节,因为用户诉求通常比较明确;法律或金融场景,一个字都不能丢,压缩只能做索引,不能做摘要;代码调试场景,工具报错信息必须原样保留,因为错误码差一位,排查方向就完全不一样。

所以选型时一定要关注压缩模块是否支持策略配置。至少要能针对会话类型、当前状态、消息等级定制压缩动作。我自己的做法是在配置中心维护一张“压缩策略表”,按场景选择压缩方法、触发条件、摘要粒度、是否保留原始记录。

4.3 红线三:不要为了压缩而压缩,评估要看端到端效果

我见过一种典型的“指标游戏”:压缩率从80%提到95%,后台指标看着很漂亮,但模型回答的正确率反而降低了,因为关键信息被压掉了。

压缩方案的评估必须看端到端任务效果,而不是只看token数。我会在每次压缩改造后跑同一批真实场景用例,对比压缩前和压缩后的准确率、召回率、任务完成率、用户满意度。一套压缩方案只有在端到端效果不降级的前提下,token的节省才是有意义的。

还要注意,压缩本身也有成本。模型生成摘要是要消耗token的,检索索引的构建也需要计算资源。有时候压缩得很狠,但每次摘要生成的成本反而比直接多塞点token更贵。这类成本要算总账,不能只看单次调用。

4.4 红线四:记忆和压缩架构必须支持“可插拔”

你现在选的方案可能适合当前业务的复杂度,但Agent的业务量会增长,需求会变化。今天你只需要压缩最近50轮对话,明天可能就要处理跨周的用户关系维护。如果压缩和记忆模块耦合在业务逻辑里,到时候很难抽出来升级。

我建议把“记忆管理”和“业务逻辑”做分层。业务层只需要调用一个统一的记忆接口,比如save(conversation_id, events) 和 retrieve(conversation_id, query)。具体用什么压缩策略、存到哪、怎么检索,都是记忆模块内部的实现细节。这样你今天用简单截断,明天换MemGPT,后天自己实现一套新的压缩算法,业务层完全不需要感知。

5. 三大主流方案对比:MemGPT、ACON、Context Editing

5.1 MemGPT:把操作系统思想带进Agent记忆管理

MemGPT的核心贡献,是把传统操作系统的“分层存储”概念搬到了LLM的上下文管理上。它把上下文窗口比作“内存”,把外部存储比作“磁盘”,通过一个函数调用机制来控制数据在两者之间的换入换出。

我在实测MemGPT时的感受是:思路非常清晰,尤其适合需要“长期自主运行”的Agent。它会自动决定哪些信息要保留在上下文里(相当于进程的活跃页),哪些要换出到外部存储(相当于不活跃页),并且能在需要时主动发起检索,把相关记忆调入上下文。整个过程对上层业务透明,你只需要给它定义好“记忆的类型”和“检索的触发条件”。

不过MemGPT也有让我想吐槽的地方。一是它的记忆粒度管理比较重,对于简单的客服对话或短流程任务,引入MemGPT反而有种杀鸡用牛刀的感觉。二是它的状态管理依赖函数调用边界,一旦Agent的流程里混入了大量非标准工具调用,MemGPT的记忆调度就会变得不太稳定,需要额外适配。

如果项目需要的是一个能够长期运行、自我管理记忆的复杂Agent,MemGPT是非常值得考虑的基础框架。但如果你只想解决“对话太长导致窗口溢出”这种局部问题,用它可能有点过了。

5.2 ACON:把压缩时机和内容选择交给模型自己

ACON(Adaptive COmpactioN)这个方案的思路和MemGPT不太一样。它主张让模型动态判断“哪些内容重要、应该在什么时候压缩”,而不是靠固定的窗口大小或固定的轮数来触发压缩。

具体来说,ACON会监控对话的上下文状态,当信息密度达到一定阈值时,模型会选择性地替换掉已经不再重要的内容,而不是简单地从最早的消息开始删。这样压缩保留了“语义的重要性权重”,有效避免了截断法“误杀早期关键约束”的问题。

我实际试用ACON的时候,最明显的感受是:压缩之后的上下文更接近一个“经过整理的工作台”,而不是一段被截短的历史记录。模型自己在决策哪些内容要保留,这让后续生成的连贯性好了不少。但这也意味着你对压缩过程的控制力变弱了,如果模型判断失误,把不该丢的信息丢掉了,排查起来会比较吃力。

适用场景上,ACON更适合那些对话节奏自然、信息分布不规律的任务,比如开放域对话、研究型问答。如果你的Agent流程里充满了结构化工具调用,ACON的灵活性反而可能带来不稳定。

5.3 Context Editing:从文本编辑视角做上下文瘦身

Context Editing和前面的方案走的是完全不同的技术路线。它把上下文看作一篇需要编辑的“文章”,通过预设的编辑操作(删除、替换、重写、插入标记),在保持语义连贯的前提下精简上下文内容。

这个方案的理论基础是:很多上下文中的词语和句式对最终答案贡献很小,删除它们不会影响模型的理解。所以Context Editing更像是一个“有原则的文本压缩器”,通过一系列规则或模型判断,找出那些可以在不改变核心语义的前提下删除的片段。

它和摘要压缩的区别在于:摘要压缩是“重写一遍”,Context Editing是“删改原文”。重写会引入模型自己的表达偏差,删改则尽量保留原文结构。这一点在处理“需要引用原文”的场景里很关键,比如让Agent根据历史回复解释它当时的推理依据。

不过我实测下来,Context Editing对文本类内容效果不错,但对结构化JSON、代码块、工具返回值的处理效果一般,因为这些内容的“语义完整长度”比较刚性,删改之后很容易变得不可解析。

5.4 三者的实际选择建议与组合使用

我在最终项目里并没有只选一种方案,而是做了一套组合策略:

  • 短期会话内,我主要用“轻量级摘要+结构化提取”,保证窗口不爆,这是基础层;
  • 当Agent需要长期运行、有多会话维护需求时,引入MemGPT做记忆分层管理,这是架构层;
  • 对文本密集、且需要“原文可引用”的场景,用Context Editing做上下文瘦身,这是优化层;
  • 对开放域对话、信息分布不规律的任务,用ACON做自适应的内容替换,这是策略层。

这个组合思路的核心是:不要把一个方案当成万能药。压缩和记忆本质上是系统级的资源管理问题,不同的任务模式、不同的上下文特征、不同的业务约束,天然需要不同的处理逻辑。

6. 实操中的几个通用细节

6.1 压缩触发时机怎么定

很多团队的压缩逻辑是按“窗口占用率”触发的,比如超过80%就执行一次压缩。这个做法简单,但容易在“刚好需要完整上下文”的关键节点上做压缩,导致推理结果变差。

我的建议是:压缩触发要避开关键操作点。比如在工具调用发起前、在模型需要整合多步信息做决策前,尽量不要触发压缩。可以把压缩动作挂到对话的自然停顿点,比如一次工具调用结束后的空闲阶段。这样既不影响当前任务的执行,又能及时腾出空间。

6.2 压缩前后的状态一致性

压缩处理完之后,Agent的系统提示词中不应该还有“已被压缩掉的内容”的引用。这个听着像废话,但特别容易出问题。比如早期摘要里提到“用户要求7天内交付”,新摘要里没有这个信息,系统提示词里却还残留着“7天交付”的字样,模型就会基于不存在的新摘要做出互相矛盾的判断。

我为此加了一层“一致性检查”:每次压缩完成后,对比压缩前后的关键实体和目标指令,如果有丢失,自动从原始记录中恢复并追加到当前上下文。这个过程可以做成自动化,也可以做成半人工抽检。

6.3 记忆系统的可观测性

记忆系统最怕的是“黑盒”运行。模型说“我根据记忆库分析得出……”,但你不知道它检索了哪些记忆、哪些记忆被压缩掉了、哪些记忆处于冲突状态。出了问题根本没法排查。

所以我在记忆模块里加了完整的审计日志:每次save操作记录写入的记忆类型和来源事件ID;每次retrieve操作记录查询向量、召回候选、最终选用的记忆片段;每次压缩操作记录压缩策略、输入输出token数、被丢弃信息的摘要。有了这些日志,才能定位到具体是哪一次压缩引发了问题,否则就只能靠猜。

7. 最后的实操心得

我这套方案已经在一个客服咨询类Agent和一个数据分析Agent上跑了一段时间,老实说效果很明显,但也走了不少弯路。最深的体会是:上下文压缩不是“省token的小技巧”,它是Agent能否长期稳定工作的地基工程。如果你只把它当作一个优化项来做,大概率会在轮次变长之后被各种奇奇怪怪的问题反噬。

还有一条经验也很重要:好的压缩策略一定是随着业务演化而调整的,不是一次定死就完事。上线之后要持续观察不同会话类型下的端到端效果,不断微调压缩触发条件、摘要粒度和记忆分类。我的做法是每两周做一次“记忆质量巡检”,拉取一批真实会话,人工检查压缩后的上下文是否还能支持模型做出正确决策。

最后分享一个小技巧:在做任何压缩改造之前,先把原始对话按“用户消息、工具调用、系统事件”三类型拆开,分别统计它们的token占比和语义价值。大多数Agent的窗口爆掉,都是工具调用记录和系统日志在“灌水”,真正的对话内容占比反而不高。只要对这两类做针对性的结构化压缩,窗口压力往往能直接下降一半以上。先找到大头,再设计方案,永远比从优化对话压缩开始更高效。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询