资讯动态

Agent上下文压缩实战:三种方法与一整套可落地流水线

发布时间:2026/10/14 22:28:18 来源:尧图企业网站定制
做Agent开发的人迟早会撞上同一堵墙上下文窗口又双叒叕不够用了。我最早是在一个模拟项目X里做客服助手系统提示词、用户历史对话、工具返回结果堆在一起还没聊几轮上下文就顶到了窗口上限。后来开始做上下文工程才慢慢理解压缩不是简单“删掉一些字”而是要让Agent在有限的内存里尽量记住真正重要的事情。这篇文章聚焦上下文压缩方法分享我实际用过的提取式、摘要式、结构式三条路线以及一整套可以直接抄进代码里的压缩流水线。适合正在做Agent应用、被token长度和成本问题困扰的开发者也适合刚入坑想了解上下文工程是怎么回事的新手。1. 为什么上下文压缩成了Agent绕不开的坎1.1 一个真实场景系统提示词历史对话工具返回先看一个最常见的Agent运行循环接收用户问题带上系统提示词和历史消息调用大模型模型觉得需要查数据就发起工具调用工具返回一段长长的JSON或数据库结果模型再根据这些内容组织回答。问题就出在这个“长长”上面。我遇到过这样一种情况某个查询工具返回了3000多字的商品详情包含规格参数、库存状态、物流配置、优惠策略。模型真正用来回答用户问题的只有“库存是否充足”和“价格区间”这两点其余2900字全是干扰项。结果模型有时候会被无关字段带偏回答里混进过时信息甚至因为上下文里有大量重复的字段名反而忽略了用户真正关心的参数。这种情况下上下文压缩要解决的不只是“塞不下”还有“注意力被稀释”。窗口是有物理上限的就算窗口够大模型的注意力也会在超长上下文里衰减。你去做长文本问答就会明显感觉等到中间位置的内容时回答质量明显下降。这就是为什么压缩不是可选项而是Agent工程里的必需品。1.2 token账本成本、延迟、准确率的三角关系每个token都对应真金白银。我常用的一个模型输入价格折算下来1万token大概要几块钱。看起来不贵但如果Agent每天处理几万次请求历史对话平均3000token那光重复传递历史消息的支出就很可观。更麻烦的是每次请求都要把全部上下文从头处理一遍上下文越长首字延迟越高。我实测过一个长对话场景上下文从2000token涨到8000token首字响应时间从0.8秒涨到2.3秒用户体验一下子就不一样了。所以压缩是在同时优化三件事省钱、降延迟、保准确率。但三件事之间存在拉扯。压缩太狠token省了关键信息丢了准确率崩了压缩太轻准确率保住了但延迟和成本没降下来。这个平衡点不是拍脑袋定的需要根据具体场景去调。我自己的习惯是先设定一个预算这条上下文的token上限是多少超过上限就必须压缩。比如把历史对话压缩到2000token以内工具返回结果裁剪到800token以内系统提示词压缩到500token以内。有了预算压缩的目标就清晰了不是“尽量压”而是“在预算内保留最大信息量”。1.3 压缩不等于傻删与检索、缓存的关系刚开始做压缩时我误以为压缩就是原始的截断——只留最近几轮对话。结果用户问起三天前提到的一个需求模型完全失忆。后来才意识到压缩应该和检索、缓存配合起来。我的理解是缓存解决“重复计算”的问题常用的系统提示词和工具定义可以缓存不用每次都重新编码检索解决“大海捞针”的问题长历史对话里真正相关的片段通过向量检索捞出来压缩解决“内存过载”的问题把检索结果和不得不保留的信息压缩得更紧凑。三者是递进的关系不是互相替代。举个例子用户的历史对话有200轮我不会全部压缩成一篇摘要因为摘要会损失细节。我会先用检索找出与当前问题最相关的10轮对话再对这10轮做压缩这样既保住了关键细节又把长度压了下来。这个思路在后面的压缩流水线里会反复出现。2. 三种主流压缩路线到底该怎么选2.1 提取式黑白名单加重要性排序提取式压缩是最容易理解的方式从原始文本里挑出重要的句子、字段、片段丢掉其余的。它的核心是“判断什么重要”。实际操作起来有两类做法。一类是规则式如果上下文里包含JSON就只保留特定的key如果包含日志就只保留ERROR级别以上的行如果包含对话就只保留用户消息和最后的助手回复。这个做法稳定、可解释、不消耗额外的大模型调用适合结构化的工具返回结果。另一类是基于模型的抽取把原始文本切成长度为100到200字的块让模型给每个块打分按分数从高到低取前面的块拼成压缩后的上下文。这种方式适合非结构化的对话文本。提取式的优点是不会凭空生成新信息所以保真度高。缺点是碎片化严重句子之间原有的逻辑关系会丢。比如两个连续的句子前一句是“库存不足”后一句是“预计三天后补货”抽取式可能只留下前一句后一句被丢掉模型就会给出错误结论。2.2 摘要式用生成换空间摘要式压缩让模型把大段文本重新组织成简短摘要。这是最常用也是风险最大的方式。好处是压缩率极高。一段5000字的对话可以压成200字的要点列表信息密度大幅提升。但风险在于模型可能“脑补”也就是摘要里出现原文没有的内容。我见过最离谱的一次模型把“用户希望收到红色包装”摘要成“用户偏好红色包装且喜欢礼盒”这个“礼盒”就是凭空出现的直接导致后续推荐策略出错。所以我用摘要式压缩有个前提原文本身就是可容忍部分失真的场景比如闲聊类的历史对话、固化的产品背景介绍。一旦涉及精确数字、状态标志、时间点这些关键字段摘要式必须和提取式配合使用或者直接改用结构化的键值对输出。还有一个技巧不要一次压一整篇而是分层摘要。先把每一轮对话压成一句再把若干句合并成一段摘要。这样能减少模型在超长输入下的幻觉概率也方便后续做局部更新。2.3 结构式把上下文拆成可管理的块结构式压缩是我后来重点研究的方向。思路是把上下文按照功能拆成独立的块然后对每个块用不同的策略处理。比如一个Agent的完整上下文可以拆成四块系统提示词、工具说明、历史对话、当前任务的临时数据。系统提示词是高度稳定的适合用固定模板加少量变量几乎不需要压缩工具说明可以用一句话概括加参数列表历史对话则分成“最近几轮完整保留”和“更早的压缩成摘要”临时数据只保留当前任务真正需要的字段。这样做的好处是灵活。我可以在同一个压缩流程里对系统提示词不做任何压缩对工具返回结果做提取式压缩对老对话做摘要式压缩。每一块都用最适合自己的方式而不是整个上下文一刀切。结构式压缩的难点在于“拆”这个动作本身需要逻辑清晰。我通常会在Agent初始化阶段就把上下文组织成字典结构每个key对应一个块压缩器按key分别处理最后再拼接起来。这比处理一大段纯文本要可维护得多。3. 一整套可落地的压缩流水线3.1 第一层规则裁剪先做减法我的压缩流程不是一上来就调大模型而是先用规则把明显没用的东西剪掉。规则裁剪速度快、零成本、不引入幻觉是必须做的基础层。具体来说我会做这几件事去掉多余的空白字符、重复的分隔符、空字段JSON和工具返回值只保留预先定义的必要key其他key全部删除日志只保留ERROR、WARN级别INFO和DEBUG直接丢弃对话消息里只保留文本内容去掉多余的元数据超过一定长度且不包含关键字的代码块直接截断。规则裁剪的核心是维护一份“保留清单”。我会花时间去看工具返回的真实结构搞清楚哪些字段会在后续决策里被真正用到把它们列入白名单。这个工作很枯燥但收益很大。我做过一个数据查询Agent工具返回的JSON原本平均1200token通过白名单裁剪后压到300token左右准确率不但没降反而因为去掉了干扰字段小幅上升。3.2 第二层用一个小模型做关键信息抽取规则裁剪处理了结构化的部分但对话文本和自然语言描述还需要更聪明的处理。这里我会用一个轻量级的小模型来做抽取比如7B到14B量级的开源模型或者API里更便宜的模型。抽取任务我一般设计成这样给模型一段对话和当前用户问题让它输出一个JSON包含三个字段用户意图、关键实体、已确认的事实。用户意图用于保留对话中真正重要的需求关键实体包括人名、订单号、型号、数量等必须一字不差地保留已确认的事实是对话里双方明确达成的结论。举个例子一段2000字的客服对话用户先问了A产品后来改主意要B产品还问到了发票问题。抽取模型会输出{ 用户意图: 购买B产品并咨询开发票流程, 关键实体: [A产品, B产品, 发票], 已确认的事实: [用户最终选择B产品, 发票需在订单完成后申请] }这样原始2000字就压缩成了大概150字的结构化信息而且保留了核心语义。我实测小模型做这个任务效果还不错因为任务足够聚焦不需要太多推理能力只要忠实抽取即可。3.3 第三层按对话轮次做动态摘要对于更早的历史对话抽取出来的信息可能还不够因为这些对话里可能藏着用户没有明说的偏好比如语气、情绪、多次提到的需求。这些用抽取很难量化更适合做摘要。我会把历史对话按时间分成两个区域最近N轮通常N3到5完整保留这部分是当前任务的直接上下文压缩它反而会丢细节更早的部分按会话session做摘要每个会话输出一个结构化摘要包含会话目标、关键事件、遗留问题。动态摘要的做法是当历史消息数量超过阈值时触发一次摘要。摘要由大模型生成生成后替换掉原始消息而不是简单附加在后面。这个操作属于主动压缩必须放在一个独立的方法里执行不能在每次请求的路径上同步跑否则会影响延迟。这里要特别注意摘要不是一劳永逸的。如果之后用户又问起一件早期对话中的具体事情摘要里可能没有那就需要回到原始日志里搜索。所以我在实现时会保留一份未经压缩的完整日志只有Agent运行时才使用压缩后的版本。3.4 压缩率与保真度的平衡经验压缩率不是越高越好。我自己的经验是把整体压缩率控制在60%到75%之间也就是压缩后token数是原来的25%到40%。这个区间内保真度基本可控模型能力下降不明显。一旦压缩率超过80%也就是只剩原文20%以下信息丢失的风险就急剧上升。保真度怎么衡量我通常用三类指标实体保持率也就是原文里的关键实体有多少还出现在压缩结果里事件一致性压缩后的摘要是否还保留原文的因果关系任务完成率跑一批固定测试问题看Agent在压缩上下文下的成功率。前两个指标可以用脚本半自动对比第三个必须人工跑。我踩过的坑是单一追求token下降。有一次我把历史对话压成了总共不到500字的摘要token节省了非常多但用户后续问到一个之前提过的收货地址Agent直接给出了一个编造的地址。原因就是摘要里没有保留地址这个实体模型只能猜。从那以后我把实体保持率作为不可突破的底线一旦低于90%就调整压缩策略。4. 手写一个压缩器的核心实现4.1 压缩Prompt模板直接抄下面这个模板是我在项目里反复迭代后定下来的专门用于把一段长对话压缩成结构化摘要。它的特点是明确要求保留实体明确要求禁止脑补明确输出格式。你是一名上下文压缩器。以下是一段用户与助手的历史对话以及当前需要回答的问题。 请把历史对话压缩成一份摘要要求 1. 只保留与当前问题相关的信息 2. 关键实体人名、订单号、型号、日期、金额、地址必须原样保留不能改写 3. 不要补充原文不存在的信息 4. 保留事实之间的因果关系例如“因为...所以...” 5. 输出格式为JSON包含三个字段summary概述entities实体列表facts已确认事实列表。 原始对话 {{history}} 当前问题 {{question}} 请直接输出JSON不要输出其他文字。这个模板有三个关键设计。第一加入“当前问题”字段压缩就有了方向不会把无关紧要的寒暄也留进来。第二单独列出entities字段方便后面做实体保持率检查。第三加了“不要补充原文不存在的信息”这条虽然模型不一定百分百遵守但能明显降低幻觉概率。4.2 用函数调用把压缩器接进Agent我建议把压缩器封装成一个独立的工具函数由Agent在适当时机主动调用而不是放在主链路里强制每次执行。这样能减少不必要的处理也更灵活。实现上有两种接法。一种是在Agent循环里加一个前置检查在构造请求前计算当前上下文token数如果超过阈值就调用压缩函数把历史消息列表传进去得到压缩后的摘要替换掉旧消息。这种方式适合逻辑可控的Agent框架。另一种是利用模型自身的函数调用能力在系统提示词里告诉模型“如果上下文即将超过限制调用compress_context函数。”模型会自行决定是否压缩。这种方式更智能但调试成本高模型有时候会在不必要的时候触发压缩或者在信息很重要的时候反而选择压缩导致后续失败。我目前更推荐第一种因为它可控、可测试。关键代码如下伪代码def maybe_compress(messages, max_tokens2000): if count_tokens(messages) max_tokens: return messages history messages[:-1] # 保留当前用户消息 compressed compress_dialogue(history, current_questionmessages[-1].content) return [{role: system, content: 以下是压缩后的历史对话摘要 compressed}] messages[-1:]注意压缩后的消息我用了system角色而不是直接塞进user消息这样避免污染用户原始输入。同时原来的历史消息被替换成一条摘要消息后续轮次继续累加。4.3 压缩质量怎么评估别只看token评估压缩质量是我花了不少时间才建立体系的。我最开始只看压缩后token数觉得压到很低就是成功后来发现完全不是这么回事。一套可用的评估方法至少包含三部分。第一是自动指标。准备一个测试集包含原始对话和对应的关键信息列表跑压缩器然后用脚本检查关键信息覆盖率。重点检查实体是否原样保留日期、金额、编号这类信息最容易出错。我写了一个简单的校验函数把实体列表里每个词在压缩结果里做精确匹配匹配率低于90%就报警。第二是任务指标。在压缩后的上下文上运行Agent跑真实任务并记录成功率。比如一个查询Agent给它压缩后的上下文看它能否正确回答5个问题全对才算通过。这个指标反映的是压缩对最终结果的影响比单纯的文本相似度更可信。第三是人工抽检。每月抽几十条压缩结果人工对比原文看是否有明显的信息扭曲。我遇到过实体都在但事件关系被改反的情况比如原文是“用户先退款后重新下单”摘要变成“用户先下单后退款”这样的事只能靠人工发现。评估不是一次性工作而是每次调整压缩策略后都要跑的回归测试。我把评估脚本做成命令行工具改完Prompt直接跑一遍很快就能看到效果。5. 实战中踩过的坑和排查思路5.1 实体丢失与人称混乱实体丢失是最常见的问题。某个订单号在原文里出现了三次摘要里一次都没有Agent后续就编造了一个。排查这类问题时我会把原始对话和压缩结果并排对比一个个查实体是否都在。另一个很容易被忽略的问题是代词歧义。有些压缩器为了简洁会把“用户要求明天发货”缩成“要求明天发货”结果到了模型那里这个“要求”到底是谁提的就变得模棱两可。所以在Prompt里我会明确要求“所有动作必须保留主语不得省略发出者。”如果你发现压缩结果经常丢实体建议不要指望模型自觉。更可靠的办法是在提取层单独把实体列表拎出来拼到压缩结果的最前面。即便摘要本身有缺失实体列表也能兜底。5.2 摘要幻觉比不压缩更危险摘要幻觉是压缩带来的特有风险。原文说“库存暂时缺货”摘要变成“库存准备中”看似意思接近但在一个下单Agent里这个差异会导致完全不同的动作。更极端的情况是模型把两个不同事件拼在一起制造出一个原文根本没有的结论。应对幻觉我总结了几条经验不要一次性让模型压缩超过3000token的内容分段压缩摘要输出必须基于原文可以在Prompt里要求模型逐句对照原文检查在Agent流程中如果后续操作依赖某个关键事实尽可能保留原文片段而不是依赖摘要。还有一招是给摘要内容带上置信度。我做过一个扩展让模型在输出摘要时对每一条事实给出“原文可见”或“推测”的标记。对于标记为推测的内容Agent在决策时降低权重需要求证时再回查原始日志。5.3 这四种场景千万别强行压缩不是所有上下文都适合压缩。我权衡之后认为以下场景宁愿让上下文长一点也不要压缩。涉及金额、日期、地址、订单号等精确信息的任务必须保留原始字段压缩导致数字错一位就是事故。多步推理任务中中间结果被摘要掉后面的步骤就没法衔接这类任务应该保留完整的推理链。处理用户情绪或敏感沟通时压缩容易去掉语气信息导致Agent显得冷漠或冒犯。当前正在执行的工具调用结果不能压因为下一步马上要用一压就废。遇到这些场景我的策略不是压缩而是“隔离”——把精确信息存成单独的变量决策时直接读取变量不放进上下文文本里把推理链拆成多步每步使用独立的小上下文工具调用结果做成结构化对象需要哪个字段取哪个字段。这些方法能避开压缩风险又不会撑爆窗口。5.4 一些经验数据与使用建议最后分享一组我在模拟项目X里实测的数据。有一个用户支持Agent原来历史对话平均8500token通过分层压缩后平均降到2100token压缩率75%。首字响应时间从1.9秒降到1.1秒单次请求成本降低约70%。任务成功率从94.3%下降到92.8%但在实体保持率被专项优化后回升到96.1%对比未压缩版本反而略高。这组数据说明一件事压缩带来的准确率下降不一定是必然的因为上下文变短后模型注意力更集中反而能表现更好。关键在于压缩要保得住真正重要的信息。给正在做上下文工程的朋友一个建议起步阶段不要一上来就追求极致压缩。先把规则裁剪做好把白名单列清楚把保存原始日志的机制搭好再逐步引入模型摘要。每一步都加评估确认没有明显回退再进入下一步。上下文压缩不是一个独立模块它是Agent整个记忆系统的一部分需要和缓存、检索、日志一起设计。我个人在实际操作中的体会是压缩器的核心不是技巧而是取舍。你得清楚每个token背后对应的业务价值才能决定什么该留、什么该丢。多跑几轮真实业务场景多看几次压缩结果和原始对话的差异比埋头调Prompt参数有用得多。最后再分享一个小技巧给压缩器单独建一个日志记录每次压缩前后的内容片段。刚开始觉得麻烦后来排查问题全靠它能省下大量猜测的时间。

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

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

免费获取报价 →
↑