Agent上下文压缩实战:四大流派与增量摘要落地指南

发布时间:2026/10/14 5:14:10
Agent上下文压缩实战:四大流派与增量摘要落地指南
先说我遇到过的真实状况。有一次跑一个带联网搜索和代码执行的 Agent刚开始单轮请求也就一万多 token任务也还算正常。结果迭代了几轮之后上下文越滚越大单轮请求的输入量直接涨到三四万更要命的是任务正确率跟着往下掉。我一开始以为是工具调用姿势不对又是调超时又是换模型折腾了一圈最后把历史消息打印出来才发现问题就出在“什么都往上下文里塞”这件事上。那之后我开始系统整理上下文压缩的方案也踩了不少坑这篇就把我的实践经验和踩坑记录整理出来。先说清楚这篇聊什么。它不聊 prompt 优化、不聊 RAG 检索专门聊 Agent 运行过程中如何处理已经产生的历史消息、中间推理、工具返回值让模型在有限的上下文窗口里尽可能“无损”地继续干活。适合已经被长会话搞到崩溃的 Agent 开发者也适合刚接触上下文工程、想完整了解压缩方法体系的朋友。1. 为什么 Agent 比聊天机器人更需要压缩1.1 上下文增长的四个来源很多人会觉得上下文压缩不就是把历史对话总结一下嘛。这个理解在纯聊天场景里勉强成立但放到 Agent 场景里就远远不够了。因为 Agent 的上下文膨胀速度远快于普通聊天来源也更复杂。多轮对话历史是最基础的一部分用户和系统你来我往每轮都会产生新消息。真正让上下文失控的是后面三类中间推理过程、工具调用及返回值、以及系统指令与格式残留。中间推理过程是 Agent 特有的模型在思考时会生成一大段推理轨迹这些轨迹往往很长而且是逐步堆叠的每一步都基于前面的推理继续展开。工具调用更是重量级搜索、查库、执行代码每一次工具调用的入参和返回结果都会追加进上下文一次失败的代码执行可能产生几千 token 的报错信息一段网页抓取返回上万 token 的 HTML 也不稀奇。系统指令和格式残留则容易被忽略一些框架会把重复的工具描述、格式说明反复注入这些固定文本在长会话中不断叠加同样吞噬上下文空间。实际跑起来两三轮工具调用之后历史就可能占据窗口的一半以上如果中间再出现几段超长报错窗口直接顶爆。1.2 上下文压缩要解决的三个核心问题压缩到底解决什么问题总结下来就是三个成本、窗口、质量。成本层面最直接。输入 token 按量计费历史越长每次请求的单价越高。一个持续运行的 Agent 一天可能发起成千上万次请求历史如果永远不清理累计消耗会相当可观。压缩就是为了避免对历史内容反复重复计费。窗口层面即使模型支持很长的上下文全塞进去也不一定行得通。很多模型标称支持 128K但有效上下文其实要打个折扣超过一定长度后注意力会分散模型会“记不住”开头的关键信息。压缩就是要把内容控制在一个既能放入窗口、又能被模型有效利用的范围。质量层面是最容易被忽略的。长上下文模型表面上看“能记住”但真实效果会随着长度明显衰减尤其是塞入大量工具返回值之后。模型可能丢掉早期的重要结论也可能被后来的噪声信息带偏。压缩不是简单删东西它是通过重构信息结构帮模型把注意力集中在关键内容上。1.3 压缩不是截断这里必须强调一个特别容易犯的错把压缩理解成截断。截断是简单从头部、尾部或中间丢弃一部分 token比如只保留最近十条消息。这种方式实现简单、速度快、零成本但破坏性极大。Agent 场景里用户最初的需求描述、早期约定好的规则、中间某次关键的工具返回都可能在被丢弃的区域里一旦丢了后续任务基本只能靠猜。上下文压缩应该是有损的重构不是无脑的删除。它的目标是保留核心信息的前提下把冗余表达转化为紧凑表达。真正可用的压缩方法必须能感知哪些信息对后续任务仍然有用、哪些已经不再需要这个感知能力正是不同压缩方法拉开差距的地方。2. 四大压缩方法流派与选型思路根据我自己实践和社区讨论的总结我把上下文压缩方法归纳成四个流派裁剪式压缩、摘要式压缩、结构式压缩、上下文外置。每个流派都有自己的适用场景和代价先把底层逻辑搞明白再谈具体实现才有意义。2.1 裁剪式压缩基于规则与启发式丢弃裁剪式压缩是最务实、也是门槛最低的一类。它不改变原有文本的语义结构只是按规则把部分内容移除。常见的裁剪规则有三种。时间衰减裁剪只保留最近 N 轮会话更早的直接删除实现最简单但对 Agent 可能是灾难因为用户最初的需求描述往往在最前面。重要性评分裁剪给每条消息或片段打“重要性分”低于阈值删掉评分可以基于关键词命中、消息类型、长度等信号。事件生命周期裁剪根据任务生命周期判断哪些环节已经结束比如某次工具调用已经成功返回并且后续没有再被引用这个调用记录就可以清理。裁剪式压缩的优点就是便宜、快代价是信息丢失不可控删掉就恢复不了。所以我的实践经验是只把它用在两类场景。一类是对历史日志做粗粒度清洗把格式化的重复内容删掉另一类是作为其他压缩方法的前置步骤先把明显垃圾信息去掉再交给摘要模型做精细压缩。直接拿裁剪当唯一的压缩方案在 Agent 场景里翻车概率极高。2.2 摘要式压缩用模型重写历史摘要式压缩是目前效果上限最高、也最常被采用的方法。核心思路是让一个语言模型把原始历史信息进行归纳总结生成紧凑摘要后续轮次用摘要替代原始历史。摘要式压缩可以细分成三种模式。整体摘要把全部历史一次性总结成一段话。优点是信息覆盖全面缺点是一次性处理长文本消耗的 token 较多而且摘要粒度不好控制历史特别长时一次摘要本身的成本就不低。增量摘要这是我在实际项目中用得最多的模式。每轮或每隔几轮把最新交互内容合并到上一轮已有的摘要中形成持续更新的摘要。好处是不需要每次重新总结全部历史成本稳定适合长时间运行的 Agent。分层摘要先对每个时间段或任务块做局部摘要再对局部摘要做二次摘要形成摘要树需要细节时回溯到对应层级获取原始信息。整体像个金字塔结构适合任务跨度大、需要分阶段管理的场景。摘要式压缩的关键在于几个细节用什么模型做摘要、摘要 prompt 怎么写、以及摘要之后保留哪些原文。这些实操细节在第 4 节展开说。2.3 结构式压缩维护可检索的记忆结构结构式压缩比摘要更进一步它把历史信息组织成结构化的记忆而不再是一段连续文本。常见的做法有两种。键值记忆库把工具调用结果、用户明确表达的需求、已确认的结论等抽取成键值对或结构化记录比如“用户偏好 中文”、“订单状态 已完成”、“库存查询结果 缺货”。后续需要时通过检索获取对应内容。时间线索引把发生过的关键事件按时间线建立索引每条索引附上原始内容的引用或摘要。当模型需要回答“之前是不是做过某个操作”时可以直接检索时间线获取上下文。结构式压缩的本质是“把连续文本转换成可查询的结构”。优势是信息的定位和复用效率高缺点是额外增加了解析和存储逻辑实现复杂度明显上升。在 Agent 场景里我通常这样处理不追求把所有信息都转成结构化而是针对关键结论、任务进度、用户需求、工具状态这几个维度做结构化提取其余内容用摘要兜底。既保留结构化检索的高效又避免过度工程化。2.4 上下文外置让历史“不在上下文中却可读取”这个方法严格说不算压缩但它解决同样的问题历史太长放不进 Prompt。所谓外置是把历史信息存到外部存储模型不直接看到但需要的时候检索拉回来。最常见的实现是 RAG 风格的外部记忆将历史交互按 chunk 切分、向量化后存入向量数据库每轮生成前检索相关历史片段拼进 Prompt。也可以把历史存为文件或数据库记录通过工具调用去查询。上下文外置的优势是理论上不设上限不受窗口约束代价是检索质量决定内容还原度检索不到关键信息模型就只能盲猜。还有容易被忽略的一点频繁的外部检索会增加链路时延要在响应速度和完整度之间做取舍。实践中我把它作为压缩体系的兜底层处理那些很少用到、但又不完全丢得掉的历史数据。经常需要访问的核心上下文还是留在 Prompt 中保证模型即时可见。3. Agent 循环中的压缩时机与编排策略压缩方法选好之后下一个问题是什么时候压缩这一步处理不好再好的压缩算法也会造成损失。3.1 触发式压缩与阈值设计我建议采用触发式压缩而不是固定轮次压缩。所谓触发式是当上下文规模达到某个预设阈值时才启动压缩过程。阈值可以从两个维度设置。token 数量阈值最直观比如当前上下文达到窗口上限的 70% 就触发压缩。轮次阈值是兜底机制比如每经过 10 轮交互即使 token 没到阈值也做一次轻量整理。为什么要设在 70% 左右而不是等窗口快满再动手因为压缩本身需要时间模型要基于已有历史做总结这是个多请求过程。如果等到窗口快满才压缩压缩过程中的临时输出可能直接把窗口撑爆。预留 30% 的空间是给压缩操作本身留安全余量。不同模型的窗口规格不一样这个比例也需要动态微调。3.2 压缩点上的依赖检查压缩不能随便在任意节点执行。Agent 循环里有些环节对历史信息依赖很强贸然压缩会打断任务。需要特别小心的有三类内容未完成的工具调用链、被模型引用或承诺过的信息、以及后续步骤依赖的操作结果。在每个压缩点执行前应该先做依赖检查确认这些内容都被纳入了压缩后的摘要或结构化存储。我在项目里维护了一个“最近活动引用”列表记录最近几轮中被模型明确引用过的历史内容。发现某段内容还在被引用就标记为不可压缩跳过压缩流程或者单独保留在上下文中等引用结束再压缩。3.3 压缩操作本身的瞬时开销还有个容易忽略的点压缩本身是昂贵的。用摘要模型压缩 10 万 token 的历史可能消耗数千 token 的输出而且这个过程中 Agent 无法处理其他请求。如果压缩发生在用户交互中间用户会明显感觉到卡顿。合理做法是把压缩放在任务间隙。在一次完整任务结束后、第二次任务开始前Agent 没有正在执行的步骤这时候压缩对用户体验影响最小。也可以考虑异步压缩先继续跑任务压缩完再替换摘要。异步方案实现复杂度高一些但对长时间运行的 Agent 价值很大。4. 实操搭建一套可用的增量压缩链理论说了一大堆这节上点能直接用的东西。4.1 增量摘要的落地方案增量摘要是摘要式压缩里最实用的一种落地时我会把流程拆成四步。第一步维护一个全局摘要变量保存当前会话的浓缩信息初始为空随着对话推进不断更新。第二步在每轮或每几轮交互后把新增的交互内容用户消息、模型回复、工具结果和已有摘要拼在一起作为摘要模型的输入。第三步让摘要模型输出新版本摘要。这里要特别控制输出长度我会在 prompt 里明确要求输出不超过 500 字否则摘要会越滚越大失去压缩意义。第四步用新摘要替换旧摘要同时有选择地保留部分原始消息。最近一轮的完整原始消息通常需要保留因为模型对最近信息的精度要求比较高。4.2 一个可复用的摘要 Prompt 模板直接放一个我调出来的模板做参考这个模板适配通用场景如果你的业务有特殊要求可以在此基础上加约束。你是一个对话总结助手。请将以下对话历史压缩为一段中文摘要要求保留所有对后续任务有影响的信息包括用户明确表达的需求、关键决定、已确认的结论、当前任务进度、未完成的事项。保留关键数字、时间、实体名称。删除寒暄、重复表达、与任务无关的讨论。如果历史中已包含旧摘要请将新内容合并进旧摘要不要只写新增内容也不要丢弃旧摘要中的信息。摘要控制在 500 字以内以段落形式输出。这里有个典型坑模型压缩时为了求简会把一些看似不重要但其实关键的信息丢掉。比如用户前几轮提过一个后续要做的需求摘要模型可能觉得“这个还没发生不重要”就省掉了。针对这种情况我会在压缩请求里显式注入“当前任务”和“待办事项”字段让模型知道哪些信息是后续可能用到的。4.3 压缩执行的完整伪代码下面这段伪代码是增量压缩的核心逻辑可以直接翻译成 Python 或 TypeScript 实现。def maybe_compress(agent_state, threshold_ratio0.7): current_len get_context_length(agent_state) window_limit get_window_limit(agent_state) if current_len / window_limit threshold_ratio: return agent_state # 1. 依赖检查找出最近被引用、不可压缩的内容 protected_ids get_recently_referenced_ids(agent_state) # 2. 把可压缩的历史和已有摘要打包 compressible [ msg for msg in agent_state.history if msg.id not in protected_ids and msg.id not in reserved_recent_ids ] summary agent_state.global_summary or # 3. 调用摘要模型 new_summary summarize( old_summarysummary, new_contentformat_messages(compressible), max_len500, ) # 4. 替换留下受保护内容和最近一两条原文其余换成摘要 agent_state.global_summary new_summary agent_state.history [ msg for msg in agent_state.history if msg.id in protected_ids or msg.id in reserved_recent_ids ] return agent_state这一段实现有几个关键点。首先是threshold_ratio的设定。我会根据模型窗口规格动态计算而不是写死。窗口 128K 的模型触发点可以设在 70%窗口 32K 的小窗口模型我会把触发点提到 80%。小窗口模型本身需要更多空间才能正常工作压缩太早反而影响性能。其次是get_recently_referenced_ids的实现。最简单的做法是让模型在输出中明确声明“本次引用了哪些历史片段”把引用 ID 记录进状态。也可以通过正则匹配识别显式的引用标记。初始成本低而且对后续追踪很有帮助。第三点是保留原始消息的策略。我一般保留最近 1 到 2 条完整的原始消息模型对最近信息精度要求最高更早的消息即使重要也已经被摘要覆盖再保留原文就是浪费空间。4.4 工具返回值的压缩策略工具返回值往往占据上下文的大头也是最容易被低估的一块。一次完整网页抓取可能返回几千行 HTML一次数据库查询可能返回上万行结果。对工具返回值我的策略分三层。第一层工具调用前就对请求做约束比如搜索 API 上加max_results限制从源头上控制返回总量。第二层返回后立刻预处理截断或抽取关键字段去掉格式冗余。第三层对历史中已经使用过的工具返回确认后续不再需要就直接删除只保留工具类型和最终结论。实测下来这三层策略能把工具相关的上下文占用削减一半以上对任务质量几乎无感。模型真正需要的是工具返回中的有效结论不是完整原文。堆原文进去反而会因为超长无关信息干扰模型判断。5. 评估压缩质量比“看着还行”更靠谱的量化方法评估压缩效果是很多人跳过的一步。但压缩是有损操作如果不量化评估你根本不知道每次调整是在优化还是在破坏。5.1 三个实用评估指标我习惯从三个维度评估信息召回率、任务完成率、成本变化。不是每个 Agent 都要同时跑三个如果是商城客服最关心任务完成率如果是数据分析助手信息召回就更重要。第一个是信息召回率指压缩后的摘要中保留了多少关键信息。评估方法是随机抽取历史中的关键信息比如用户需求、工具结论、已确认事实构成待召回清单再看这些信息有多少能从压缩后的上下文中还原出来。第二个是任务完成率这是最终衡量标准。把同一套任务集在压缩开启和关闭的情况下各跑一遍对比完成率。如果开启压缩后完成率下降超过 3 个百分点说明压缩策略有信息丢失问题需要调整。第三个是成本变化。压缩增加了摘要模型的输出消耗但减少了后续每轮请求的输入 token。差值决定了压缩的净收益。大致公式是压缩净收益 压缩减少的后续输入 token 数 - 压缩过程的输出 token 消耗。5.2 一次实测案例的量化对比这里分享一组来自我项目的实测数据展示压缩前后的量化对比。场景是某个查询类 Agent原始上下文中包含用户模糊描述、多次搜索过程和若干次工具返回。未压缩时每轮平均输入 token 是 3.2 万任务完成率 79%。开启增量摘要压缩后平均输入 token 降到 1.1 万任务完成率 80%。压缩让输入成本下降了三分之二任务完成率没有明显下降这说明压缩策略达到了预期效果。另一个对照实验是纯裁剪方案。同样任务集只保留最近 5 轮消息其余直接删除。输入 token 降到 0.9 万但任务完成率直接掉到 64%差距非常明显。这个对比能直观看出裁剪和摘要的差距不在压缩率而在信息保留的有效性上。5.3 压缩率怎么选压缩率不是越高越好。压缩率过高意味着大量信息被强行归并摘要只剩骨架没有细节模型没法基于这样的上下文做准确判断。压缩率过低则失去了压缩的意义只是把摘要模型当做一个转述工具。实践里我推荐的压缩率区间是 40% 到 70%也就是压缩后的内容量大约是原始内容的 30% 到 60%。具体数值取决于任务复杂度任务越复杂需要保留的细节越多压缩率越保守任务流程简单、历史废话较多时可以放开压。还有一个可以微调的参数摘要最大长度。需要高精度执行的 Agent我通常把摘要限制放宽到 800 字普通对话助手压缩到 200 字以内就够。6. 实践中的典型问题与排查压缩逻辑写完真正跑起来才会遇到各种问题。这里把我在实际项目中踩过的坑整理成一张排查表。症状可能原因排查思路压缩后 Agent 忘记用户原始需求早期需求描述不在受保护列表摘要模型没纳入摘要在压缩任务中强制注入用户初始需求字段要求摘要模型无论如何都要保留摘要越滚越长失去压缩效果摘要模型每次迭代自动追加内容没控制输出长度在摘要 prompt 中加长度上限并代码层面对摘要做长度截断压缩后任务步骤错乱中间某环节工具调用链被删掉模型看不到前序步骤引入工具调用链保护机制未完成的调用链上的消息禁止压缩频繁触发压缩性能下降阈值设太低或大量临时标记不可压缩内容调高触发阈值检查依赖检查逻辑排除不必要保护压缩后出现幻觉信息摘要内容与原始历史不一致摘要模型归纳时补充了不存在的内容为摘要素材引入校验关键实体与原文比对必要时改用更高保真的压缩策略用户反馈响应变慢压缩操作耗时长且没有异步压缩压缩放到任务间隙或改用异步压缩流程除了表格里的问题还有两个细节值得单独说。一个是我会在每次压缩后的摘要里打时间戳记录“这份摘要是什么时候生成的”。后续如果模型需要访问更早的详细历史可以根据时间戳去外部存储或原始日志里找回对应的原始片段。没有时间戳的摘要就像一个没有出处的引用追踪起来特别痛苦。另一个是我在调试压缩逻辑时的心得不要只看压缩后结果要把“压缩前片段”和“压缩后摘要”并排放在一起人工抽查。这个动作最好定期做尤其是调整了摘要 prompt 之后。不要相信“模型说它记得”要相信“你能在摘要里看到它”。我吃过不少亏之后才养成这个习惯之前有段时间压缩后任务完成率看起来正常但抽查发现摘要模型把一批时间敏感的过期数据也归纳进去了要不是后来对召回了清单这个问题还在继续潜伏。压缩方法没有放之四海而皆准的方案核心思路其实就一套感知哪些信息重要在合适的时机把关键内容保留下来然后用最经济的方式替换掉冗余内容。这篇把分类、选型、触发时机、实现细节、评估和排查串了一遍希望能给正被长上下文困扰的人一个清晰的起点。如果有更极端的场景比如百万级 token 的长时间运行 Agent后续我可以再单独写一篇聊聊层次化记忆与外置存储的组合玩法。