3.2 上下文压缩与窗口管理 一句话带走:上下文窗口不是「越长越好」,而是「越省越好」。用滑动窗口保近期、用摘要保长程、用 RAG 保外部知识,再给每轮对话设一个「上下文预算」,你就能在显存不爆、推理不慢的前提下,让多轮长对话依然答得准。 为什么必须管窗口 很多人以为「上下文窗口越大越省事」。但在百万级 QPS 下,这是个危险的错觉。原因有三: 成本随 token 线性增长:上下文越长,每次 prefill 和 KV 占用越大,算力账单线性上升。 长上下文会「稀释注意力」:模型在超长上下文里未必能稳定抓住关键指令,反而更容易答偏——这不是玄学,是长上下文场景反复被验证的现象。 显存是硬约束:KV Cache 随上下文长度增长,海量并发长对话会直接把显存打满,导致排队甚至 OOM。
一句话带走:上下文窗口不是「越长越好」,而是「越省越好」。用滑动窗口保近期、用摘要保长程、用 RAG 保外部知识,再给每轮对话设一个「上下文预算」,你就能在显存不爆、推理不慢的前提下,让多轮长对话依然答得准。
很多人以为「上下文窗口越大越省事」。但在百万级 QPS 下,这是个危险的错觉。原因有三:
所以正确姿态是:用最少的 token,保住回答所需的全部信息。这就是上下文压缩的精髓。
最简单的压缩:只保留最近 N 轮对话,更早的丢弃。实现成本最低,适合「对话大多只关心最近几轮」的场景(闲聊、短期任务)。
坑点 · 长程依赖断裂:纯滑动窗口会丢掉「前文定下的偏好/事实」。比如用户第 1 轮说「我姓王,用尊称」,第 20 轮还在对话,滑动窗口早把这轮滑掉了,模型突然忘了叫你「王先生」——这就是经典的「记忆断片」。所以滑动窗口不能单独扛所有场景,必须配摘要。
解决滑动窗口「断片」的标准解法:把超出窗口的早期对话,周期性地提炼成一段摘要,再用「摘要 + 近期原文」拼成上下文。这样模型既记得「你说过的关键偏好」,又看得清「最近在聊什么」。
两种触发时机:
摘要的质量决定天花板:摘要本质是「再生成一次」,可能丢细节。对强事实场景(如「订单号 12345」)要保留关键实体清单,不要只留泛泛的「用户问了订单」。
当对话依赖「长文档 / 知识库」(合同、手册、代码库)时,把全文塞进窗口既不经济也不现实。正确做法是 RAG:先根据用户问题检索出最相关的几个片段,只把片段拼进窗口。
和压缩的关系:RAG 把「全量上下文」换成「按需取用的精华上下文」,本身就是最强的上下文裁剪。它和滑动窗口/摘要并不冲突,反而互补——近期对话用窗口+摘要,外部知识用 RAG 片段。
要点:检索的相关性直接决定回答质量。检索太宽(塞一堆无关片段)会稀释注意力、浪费 token;太窄会漏关键信息。检索结果要做去重和重排,并控制拼入窗口的片段数量上限。
我强烈建议给每轮对话设一个上下文预算(token budget):比如「单轮上下文不超过 8000 token」。然后按优先级分配这块预算:
当预算不够时,先砍 RAG 片段数量,再压缩摘要,最后才动近期窗口——优先级低的先退让。这一套分配逻辑,应当做成可观测、可配置的策略,而不是硬编码在业务代码里。
本章两节其实是一套组合拳:3.1 的缓存让「算过的不重算」,3.2 的压缩让「要算的更少」。二者叠加效果最好——压缩后的上下文更短,缓存命中率反而更高(因为相似的短上下文更容易完全一致)。所以在工程落地时,我建议先做好上下文压缩(把噪声砍掉),再叠缓存(把干净结果留下来),顺序别反。
把这节做透,你的引擎就能在不堆显存的前提下,稳稳撑住超长多轮对话——这是「算得起」和「答得准」之间真正的解法。
再强调一次工程落地的顺序:先压缩、再缓存。理由很硬——压缩把噪声和冗余砍掉后,每条上下文更「纯净且趋同」,相同意图的请求更容易落到字节级一致,结果缓存命中率随之升高;反之若先缓存原始长上下文,不仅每条占的空间大、淘汰快,而且相似请求因冗余差异难以精确命中。所以把「上下文预算裁剪」放在「写回缓存」之前,是事半功倍的顺序。
上下文压缩不是免费午餐。摘要要调用模型再生成一次,RAG 检索要跑向量库查询,这些都有成本和延迟。所以压缩策略必须算「净收益」:
换句话说,压缩策略本身要「自适应」:上下文越膨胀,压缩越激进;上下文本就精简,就少动它。这比「无差别全量压缩」更省、更稳。
压缩做得好不好,不能只看「省了多少 token」,更要看「答得准不准」。建议埋两组指标:
把隐性指标接进监控,压缩策略才有「刹车」,不会为了省 token 把回答质量拖垮。
我见过太多团队在这块踩坑,列几条最典型的:
为了可运维,建议把以下项做成配置,而非写死在业务代码:
这套配置接上灰度发布,你就能边放量边调参,而不是上线后被动救火。
把这节和上一节的缓存组合拳打顺,你会在「显存不爆、推理不慢、账单可控」的前提下,稳稳撑住超长多轮对话——这就是「算得起」和「答得准」之间真正的解法。
要正确对待摘要,得先接受它是有损的。模型把十轮对话压成一段话,必然丢掉细节。工程上能做的,是把「损」控制在不影响后续回答的范围内:
RAG 不是「检索到就全塞进去」。拼装阶段有几个细节决定回答质量:
假设单轮预算 8000 token,一个典型分配(可按业务调):
当近期窗口变长、逼近预算时,先压缩摘要(保留实体,砍掉描述),再减少 RAG 片段数;只有极端情况才裁剪近期窗口。这个「优先级退让」逻辑写成配置,比硬编码灵活得多。
如果对话涉及图片或超长文档(代码仓库、PDF),上下文管理还要升级:
延伸方向很多,但万变不离其宗:用最少 token 保住回答所需信息。这一条是上下文管理的第一性原理。
本章两节合起来,给出了「让进来该算的请求算得更少」的完整打法:3.1 用三级缓存复用已算结果,3.2 用压缩让每次要算的上下文更短。二者叠加,是百万级对话引擎「算得起」的真正杠杆,远比换更快的框架来得实在。落地时记住三句话:缓存先精确后泛化、压缩先裁后存、配置要可灰度可观测。