想象你即将出发去远方旅行,但背包只有45升——你从纪念品店抱回一大堆东西:朋友强力推荐的漂亮石头、重复买了三张的明信片、永远用不上的大帽子。如果全塞进去,背包不仅拉不上拉链,到了酒店你还得把整包倒出来才能找到护照。RAG系统面临的正是这个困境:知识库检索回来的文档太多太长,而大模型的“上下文窗口”就像那个45升的背包,空间有限、按字收费。所谓“上下文压缩”,就是一门打包的艺术——扔掉没用的、合并重复的、折叠冗长的,让有限的空间装下最有价值的信息。本文从必要性、压缩方式、重排配合、开源工具、比例平衡到质量评估,逐步拆解这门手艺。
对AI从业者而言,掌握上下文压缩等于直接手里握住“降本增效”的开关:它是RAG应用上线后性价比最高、收益最直观的优化方向之一,也是区分普通RAG工程师与资深RAG工程师的关键能力。
可视化演示
展示RAG上下文压缩管道流程、去重消除冗余策略、重排模型协同筛选机制,实时显示Token压缩比和片段数量。
本节配有交互动效(进入视口后自动加载,约 3KB)。
压缩管道流程 检索返回20个候选片段 去重消除冗余后剩14个 压缩+重排精选3个送入LLM
压缩必要性
想象一下,旅行背包只有45升,而你从纪念品店抱回了一堆东西:朋友推荐的石头、重复的明信片、占地方的大帽子。全塞进去,背包不仅拉不上拉链,到了目的地你还得把整包翻个底朝天才能找到护照。RAG系统的处境如出一辙——一次检索往往返回5到20篇文档,加起来动辄几千个token,而大模型的上下文窗口有限,每多喂一个token就要多付一份钱。
把超量内容一股脑塞给大模型,代价有三层:第一层是真金白银的token费用,检索结果越长,单次调用的成本越高;第二层是“迷路效应”,研究发现模型对长上下文中间位置的内容关注度明显下降,关键信息被淹没在冗长文本里;第三层是噪音干扰,检索结果中与问题无关的段落会像旅行箱里的大帽子一样挤压空间,诱导模型给出偏离事实的答案。这三层代价叠加,让“给背包减负”成为RAG上线前必须解决的第一件事。
我们可以用一个简单的量——信息密度——来理解这件事。信息密度指单位长度的文本承载的有用信息量,密度越低,说明“水分”越多,越需要压缩。
出发前必须学会做“减负清单”:先认清背包为什么塞不下——不是包太小,而是你没筛选就什么都往里装。RAG应用的上下文压缩,正是从认清“信息密度太低”这一天开始的。
- 信息量 I文本中真正对回答有帮助的有效信息点数量,比如关键实体、结论性陈述。
- 文本长度 L文本的token数或字符数,是大模型计费的基本单位。
- 信息密度 ρ用信息量除以长度得到的比值,数值越高代表文本越“干”,越低代表越需要压缩。
压缩方式
收拾背包有三招:用真空袋把羽绒服压扁(摘要)、只挑百搭的基础款T恤(提取)、把同款袜子合并成一双(去重)。上下文压缩的手段与之巧妙对应,掌握了这三招,就等于拿到了打包的入场券。
第一招是提取式压缩,它不改变原文,直接从检索结果里挑出最高价值的句子,像只带百搭款衣服一样,原汁原味地保留关键信息。第二招是摘要式压缩,让语言模型把几段话浓缩成一小段全新文字,像真空袋一样把体积压到最小。第三招是去重合并,用相似度算法判断多个片段是否在说同一件事,重复的内容只保留一份,就像同款袜子只带一双。此外还有关键词过滤作为辅助手段,按领域关键词做一次粗筛,快速排除明显无关的内容。
衡量压缩做得好不好,最直观的指标是压缩率。压缩率描述“节省了多少空间”,数值越高代表背包越轻。
这三招可以灵活组合,就像打包时先折叠衣服再分类摆放再塞进缝隙。实际项目中,通常先用关键词过滤做粗筛,再用提取或摘要做精压缩,最后用去重清理重复内容,层层递进地把背包空间用到极致。
- 原始大小 S_o压缩前检索结果的总token数或字符数,相当于打包前的行李体积。
- 压缩后大小 S_c经过压缩处理后保留的token数或字符数,相当于真空压缩后的行李体积。
- 压缩率 C_r1减去两者之比,0表示没压缩,0.6表示省掉了六成体积,越接近1压缩越狠。
重排配合
一个人打包容易犯糊涂,分不清哪件衣服才是这次旅行的必需品。聪明的做法是请一位熟悉行程的朋友帮你看一遍行李——他在每件物品上贴标签:“必带”、“可带可不带”、“别带”。重排模型(Rerank)正是这位朋友,它帮你判断每个检索片段到底有多值得进背包。
Rerank的核心原理是对检索阶段返回的候选片段做二次打分:先由粗排(如BM25或向量检索)快速召回一批候选,再由重排模型逐一评估片段与用户问题的相关度,给出精细分数。相比粗排,重排模型特别是交叉编码器(Cross-Encoder)能让问题和片段在每个token层面对齐交互,打分更准确,相当于朋友不是只看标签,而是把每件衣服都拿起来仔细掂量。
拿到分数后,通常用两步筛选:先取Top-K,只保留分数最高的前K个片段;再设一个最低分数线(阈值),低于阈值的片段即使排在前K位也照样丢弃。这个筛选逻辑用数学语言表达就是:
经朋友把关之后,背包里的每样东西都对得上前方的路。重排和压缩配合的价值正在于此:重排解决“哪些值得留”,压缩解决“留下的怎么装”,两道工序一前一后,行李才能又准又轻。
- 片段 s_i检索返回的第i个文本片段,相当于背包里的一件物品。
- 重排得分 score(s_i)重排模型给出的相关度评分,数值越大代表与问题越相关,相当于朋友贴的“必带”标签。
- 阈值 τ预先设定的最低保留分,低于这个分数的一律不放进背包。
- 指示函数 1方括号内条件成立取1、不成立取0,数学上把“留/不留”变成了一步干净的判断。
开源工具
自己从零缝一个背包太费劲,市面上已经有成熟的“收纳神器”了。上下文压缩领域同样如此,开源生态提供了开箱即用的工具和框架,你只需要学会挑选和组装。
其中最有名的是两套框架。LlamaIndex提供了Node Postprocessor(节点后处理)机制,允许你在检索之后、送进大模型之前插入处理步骤,比如相似度压缩、关键词过滤、去重合并,都能以插件形式挂上去。LangChain则提供了ContextualCompressionRetriever(上下文压缩检索器),它把“检索”和“压缩”组合成一条流水线:先召回文档,再对每个文档独立执行压缩器逻辑,最后只返回精简后的内容。
除了框架,还有一些值得关注的压缩算法:sentence-window(句子窗口)把文本切成句子粒度,只保留与问题最相关的句子及其邻近上下文;auto-merging(自动合并)先把文档拆成小节点,再根据相关性自动合并相邻节点,减少冗余的同时保留结构。选型时主要看三件事——延迟是否可接受、与现有技术栈是否兼容、压缩逻辑能否灵活定制。请记住:工具只是锦上添花,理解原理才知道什么时候该用哪一个,就像登山前要分清“防水背包”和“日常双肩包”适用场景完全不同。
比例平衡
背包塞太满,走在路上拉链崩开;塞太少,到了目的地发现必需品没带。压缩比例的拿捏是同样的哲学——不存在“越瘦越好”或“越全越好”,只存在“刚好够用且不超重”的平衡点。
压缩率与信息保留之间存在天然的拉扯:压缩率越高,token越省,但信息丢失的风险越大;压缩率越低,信息越完整,但成本和噪音又反弹回来。这种权衡关系可以用一个最优化目标来刻画——在质量收益和成本开销之间找到最优解。
实际工程中很少死板地求同一个压缩率。更常见的做法是动态调整:问题简单时压缩率高一些,问题复杂时需要保留更多细节;检索结果很长时加大压缩力度,检索结果本就精炼时少压甚至不压。好比登山时路况好就多背点物资,路况险就轻装上阵——没有绝对正确的比例,只有最适合当前场景的比例。
- 候选压缩率 c在0到1之间尝试的每个压缩比例,相当于每次调整背包的打包力度。
- 质量收益 R(c)采用压缩率c后的信息保留程度,可以理解为回答质量的保持率。
- 成本开销 C_cost(c)压缩后剩余token的部署费用,以及与压缩本身相关的计算开销。
- 权重参数 α 和 β它们决定你更看重质量还是更看重成本,调整这两个系数就能得到不同取向的压缩策略。
- 取最大值的比率在所有候选c里挑选使括号内表达式最大的那个,就是当前场景下的最优压缩率。
质量评估
旅行结束回到家的第一件事是什么?复盘——护照还在吗?照片拍了吗?下次同类旅行哪些该带、哪些不该带?上下文压缩同样需要这样的“复盘机制”,否则你永远不知道自己扔掉的是“垃圾”还是“宝贝”。
压缩后质量评估主要看三个维度。一是忠实度(Faithfulness),检查压缩后生成的答案是否仍然忠于检索到的原始事实,有没有因为丢细节而出现幻觉;二是答案相关性(Answer Relevance),看回答是否仍然命中用户的真实问题;三是覆盖度(ROUGE等指标),衡量压缩结果在多大程度上保留了原文的关键内容。这些指标共同构成一把尺子,量出“压缩后的回答”和“理想回答”之间的距离。
要判断压缩是否导致退化,最直接的办法是对比检测:用同一组测试问题分别跑压缩前和压缩后两条管线,把两边的输出质量得分拿来对比。退化度的计算如下:
除了离线指标,工程上还常用A/B测试做线上验证:随机把流量分给压缩版和原版,观察真实用户对回答的点赞/踩率。定期抽取一批案例做人工标注,则是兜底的安全网。没有评估就没有优化——评估是这一切“减负”决策的坐标,也是让你下一次打包时越来越有把握的旅行笔记。
- 基线得分 F_b未做压缩时,原版管线的生成质量评估得分,它是退化检测的“参考标准”。
- 压缩后得分 F_c采用上下文压缩后,管线输出的质量评估得分。
- 退化度 D用1减去两者比值得到退化程度,0表示无损,正数表示有损,负值表示压缩后反而更好。
AI 应用场景
智能客服历史工单检索压缩
阶段:检索后处理 / 上下文优化
解决的问题:智能客服系统每轮对话都要从历史工单库检索相似案例,一次返回十几条工单摘要,token消耗巨大且大量内容是重复的退换货说明。传统做法是直接限制检索数量,但这样会漏掉真正有效的处理方案。通过重排模型对工单按相关度打分后再做去重合并,既保留了最有效的处理经验,又把token成本砍掉近一半。
具体案例:某电商平台客服助手接入上下文压缩后,历史工单的注入量从平均3500 token降到1800 token,单轮费用下降48%,而问题解决率反而提升了3%,原因是模型不再被重复和无关工单干扰。
企业知识库多文档问答去重
阶段:检索增强生成 / 知识库优化
解决的问题:大型企业的知识库中,同一制度条款往往散落在多个版本的文档里,检索时反复返回相似内容,浪费token且容易造成答案前后矛盾。采用“去重合并 + 摘要式压缩”组合拳,把多个来源的重复条款合并为一段权威性摘要,再配合阈值过滤去掉低相关段落,让回答稳定且精炼。
具体案例:某法律咨询平台的知识库中,同一合同条款在数百份历史合同里重复出现,传统的RAG每条都返回导致回答冗长。加入上下文压缩后,系统自动识别重复段落并只保留最新版本的条款描述,答案长度缩短60%,引用来源也更清晰可信。
长文档研报摘要压缩
阶段:上下文构建 / 提示词优化
解决的问题:投研或法律场景常需分析上百页的PDF,直接把全文塞进模型既不现实又昂贵。通过摘要式压缩把每个章节浓缩成一段结构化要点,再按问题相关性筛选章节,让模型在有限的上下文窗口中看到最核心的信息。
具体案例:某证券公司用RAG分析年度研报,原始PDF共120页约15万token,先按章节做摘要式压缩至1.5万token,再针对具体问题重排筛选最相关的3-5段,单次分析成本从十几元降到不足两元,关键数据点无遗漏。
Python 演示
import re class ContextCompressor: def __init__(self, compress_ratio=0.5, sim_threshold=0.75): self.compress_ratio = compress_ratio self.sim_threshold = sim_threshold def sentence_split(self, text): parts = re.split(r"([。!?.!?])", text) sentences = [] for i in range(0, len(parts) - 1, 2): s = (parts[i] + parts[i + 1]).strip() if s: sentences.append(s) if parts and parts[-1].strip(): sentences.append(parts[-1].strip()) return sentences def jaccard_sim(self, s1, s2): a, b = set(s1), set(s2) if not a or not b: return 0.0 union = a | b if not union: return 0.0 return len(a & b) / len(union) def keyword_score(self, sent, keywords): s = sent.lower() return sum(1 for k in keywords if k.lower() in s) def compress(self, text, keywords, max_len=None): sentences = self.sentence_split(text) if not sentences: return "", [] n = len(sentences) scored = [] for i, sent in enumerate(sentences): kw = self.keyword_score(sent, keywords) pos = max(0.0, 1.0 - i / n) score = 1.5 * kw + pos scored.append((score, i, sent)) keep_n = max(1, int(n * self.compress_ratio)) selected = sorted(scored, key=lambda x: -x[0])[:keep_n] selected.sort(key=lambda x: x[1]) deduped, chosen = [], [] for _, _, sent in selected: if any(self.jaccard_sim(sent, c) > self.sim_threshold for c in chosen): continue deduped.append(sent) chosen.append(sent) result = "".join(deduped) if max_len and len(result) > max_len: result = result[:max_len] return result, deduped if __name__ == "__main__": doc = ("RAG检索增强生成是一种结合检索与生成的技术。" "它先从知识库中检索相关文档。" "然后将检索结果注入提示词。" "大模型基于注入的上下文生成答案。" "检索结果往往包含大量冗余信息。" "冗余信息会增加token消耗。" "冗余信息也会干扰模型生成的准确性。" "上下文压缩可以保留精华去除噪音。" "这是一种低成本高收益的优化技巧。") c = ContextCompressor(compress_ratio=0.5, sim_threshold=0.7) out, kept = c.compress(doc, keywords=["检索", "压缩", "冗余"]) print("原始长度:", len(doc)) print("压缩后长度:", len(out)) print("压缩率: {:.2%}".format(1 - len(out) / len(doc))) print("保留句子数:", len(kept)) print("压缩结果:", out)
class ContextCompressor定义一个压缩器类,把压缩比例和相似度阈值作为可调参数,相当于给背包设定“打包规格”。re.split(r"([。!?.!?])", text)按中英文句号按句切分文本,切好的句子就是待挑选的“行李物品”。self.jaccard_sim(s1, s2)用交集除以并集计算两个句子的相似度,值越大表示越可能在说同一件事。score = 1.5 * kw + pos综合打分公式:命中关键词的句子权重更高,位置靠前的句子也有保底加分。selected = sorted(scored, key=lambda x: -x[0])[:keep_n]按分数从高到低排序后截取前K句,相当于重排模型的Top-K筛选。if any(...)若新句子与已选句子相似度超过阈值就跳过,这是“同款袜子只带一双”的去重逻辑。
常见误区
误区“压缩率调到90%以上,token省到极致,一定是最优方案。反正大模型能力强,给点关键信息它也能自己推理。”
正解压缩率过高会把关键细节一并压掉,导致回答空洞、幻觉增多。实践表明60%-70%的压缩率通常性价比最高,就像背包塞到极限,省了空间却把护照压丢了——省下的钱还不够弥补回答质量下降带来的损失。
为何常见:token费用的下降是立竿见影的,而回答质量的隐性损失不容易被直观感知,所以人们容易高估高压缩率的收益。
误区“既然检索结果太长,那我在检索数据库时就少返回一些文档不就行了?直接限制检索数量就等于压缩了。”
正解检索前限制数量是“盲减”,可能把分散但高价值的片段丢掉。正确做法是先召回尽量全面的候选,再由重排和压缩在检索后精细筛选。就像出发前不确定哪些物品有用,等朋友帮你打包时再决定取舍更稳妥。
为何常见:控制检索返回数量操作最简单直观,看起来和压缩目的一致,容易被误认为同一件事。
误区“重排模型已经按相关度排好序了,直接取前几个片段就行,没必要再做压缩这一步。”
正解重排解决的是“哪些更相关”,不解决“重复”和“冗余”。排序靠前的片段依然可能互相重复,依然可能有一大段铺垫句子。重排与压缩是互补的两道工序:先排序后精简。相当于朋友帮你标好了“必带”,你仍然需要把一件件衣服叠好才能装进背包。
为何常见:重排和压缩都在减少“不相关内容”,功能边界看起来模糊,容易让人误以为存在其一即可。
误区“压缩本质上是丢信息,丢了信息回答质量一定会下降,所以干脆别压缩了。”
正解好的压缩是有损但“去噪”的过程。去掉的是噪音和冗余,留下的信息信噪比更高,模型注意力更集中,回答质量往往不降反升。关键是控制比例并配合质量评估,而不是因噎废食。
为何常见:人对“有损”二字有天然抵触,忽略了噪音对模型同样是一种干扰,去噪本身就在提升信息质量。
面试题
Q1. 上下文压缩的核心理念是什么?
- A.压缩词汇数量越多越好
- B.保留核心信息去除噪音← 正确答案
- C.删除文本中较长句子
- D.只保留文本开头内容
答案:B。上下文压缩的核心是在有限上下文窗口里保留最有价值的信息,同时丢掉噪音和冗余,B正确。A只追求数量不看质量,C和D都是简单粗暴的截断策略,无法保证保留关键信息。
Q2. 下列哪种压缩方式会生成全新文本?
- A.提取式压缩
- B.摘要式压缩← 正确答案
- C.去重合并压缩
- D.关键词过滤
答案:B。摘要式压缩让大模型把原文浓缩成一段全新文字,会生成原文中没有的新表述。提取式是原句摘抄,去重和关键词过滤都不产生新文本,所以选B。
Q3. 重排模型在上下文压缩中的作用是什么?
- A.直接删除低分片段
- B.将文档翻译成他语
- C.按相关度给片段打分← 正确答案
- D.替代大模型生成答案
答案:C。重排模型的核心职责是对检索片段与问题的相关度做精细打分,C正确。它给出分数但不直接删除(由后续阈值决定),A错误;B和D与重排完全无关。
Q4. 压缩率极高时可能出现什么问题?
- A.信息丢失使得答案变差← 正确答案
- B.token成本反而不断增加
- C.检索速度明显提升更快
- D.模型生成变得更加稳定
答案:A。压缩率越高,被丢弃的文本越多,关键细节越容易丢失,导致答案空洞甚至幻觉增加,A正确。token成本随压缩率升高而降低而非升高,B错误;C和D不是极端压缩的典型后果。
Q5. Jaccard相似度衡量什么?
- A.两个文本的长度差异
- B.交集与并集的大小比值← 正确答案
- C.两个句子出现的频率
- D.单词在语料库的权重
答案:B。Jaccard相似度计算两个集合交集元素数与并集元素数的比值,B正确。它不关心长度差、频率或权重,A、C、D都是对其他相似度或统计概念的错误描述。
Q6. 以下哪些属于常用的上下文压缩手段?
- A.提取式压缩← 正确答案
- B.摘要式压缩← 正确答案
- C.去重合并压缩← 正确答案
- D.修改模型参数
答案:ABC。提取式、摘要式、去重合并是三种主流压缩手段,A、B、C正确。修改模型参数属于模型微调范畴,与上下文压缩是两条技术路线,D错误。
Q7. 关于压缩比例与回答质量的关系,哪种说法正确?
- A.压缩比例越高质量线性上升
- B.压缩比例与质量完全无关
- C.存在最佳压缩比需权衡取舍← 正确答案
- D.压缩比例越低效果一定越好
答案:C。压缩比例与质量之间存在倒U形关系:过低压不住噪音,过高丢失关键信息,中间存在最佳区间,C正确。质量不会随比例线性上升,A错误;二者强相关而非无关,B错误;比例低未必好,还要考虑成本和噪音,D错误。
Q8. 如何评估上下文压缩后质量是否退化?
- A.对比压缩前后生成结果质量← 正确答案
- B.做A/B测试用指标验证← 正确答案
- C.人工抽查压缩的效果← 正确答案
- D.直接删除所有评估环节
答案:ABC。对比压缩前后质量、做A/B测试、人工抽查都是有效的评估手段,A、B、C正确。删除评估环节会让压缩退化无法被发现,D错误。
Q9. LlamaIndex实现上下文压缩的主要机制是什么?
- A.后处理节点对文档再次加工← 正确答案
- B.直接修改大模型权重
- C.增加检索文档的数量
- D.把文本文档重新分词
答案:A。LlamaIndex通过Node Postprocessor后处理节点在检索后对文档做二次加工,实现压缩和过滤,A正确。修改模型权重是微调的事,增加检索数量与压缩相反,重新分词只是文本预处理,B、C、D均错误。
Q10. 压缩上下文中降低token成本的方式有哪些?
- A.用提取式保留关键句← 正确答案
- B.对重复段落做去重← 正确答案
- C.用摘要式压缩长文← 正确答案
- D.增加检索返回数量
答案:ABC。提取式保关键句、去重合并重复段、摘要式压缩长文都能直接减少喂给模型的token量,A、B、C正确。增加检索返回数量只会让上下文更长、成本更高,D错误。