想象你是一位全家旅行的“首席规划师”。出发前,你从书架上翻出十几本攻略书,每本都塞满了路线、美食和景点介绍。可行李箱容量固定,你还得把换洗衣服、相机和证件都塞进去——这些“随身物品”就像大语言模型要预留的指令和输出空间。你不可能把每本攻略都装进箱子,更不可能把整本书都撕下来带着走。聪明的做法是:先给攻略书排个优先级(重排序),再在笔记本上记下最核心的路线和票价(上下文压缩),最后只把笔记放进行李箱。这正是 RAG(检索增强生成)系统中“上下文压缩与重排序”要解决的问题:检索器捞回来的文档太多太长,而上下文窗口这只“行李箱”装不下全部。本文我们沿着这条整理行李箱的线索,理解为什么要压缩与重排序、怎么做窗口裁剪与段落提取、如何用重排序模型把关,以及怎样评估压缩好坏并找到压缩比与信息保留的黄金平衡点。
对 AI 从业者而言,掌握上下文压缩与重排序,等于给 RAG 系统装上一个“信息漏斗”,既能控制 token 成本,又能守住生成质量和事实准确性这条底线。
目录
可视化演示
演示检索上下文压缩裁剪、交叉编码器重排序及压缩-重排序融合的RAG全流程优化策略。
本节配有交互动效(进入视口后自动加载,约 3KB)。
上下文压缩 窗口裁剪去除冗余 关键段落提取保留核心信息 compress(C)=top_k(sim(q,c_i)) 通过相关性评分裁剪上下文窗口降低token消耗
问题背景:行李箱装不下全部攻略
假设你是全家旅行的“首席规划师”。出发前一天,你从书架上翻出十几本攻略书,每本都密密麻麻地记着路线、美食与景点。问题是行李箱容量固定,而你还得把换洗衣服、相机和证件都塞进去——这些“随身物品”就像模型预留的指令和输出空间。纠结之下,你只能掂量着取舍:不是每一本攻略都值得带,也不是每一页都有用。
在 RAG 系统中,同样的问题每天发生:检索器返回的是大量候选文档,而大语言模型的上下文窗口无法容纳全文。更糟的是,和某一句关键词沾边的噪声内容也会被捞回来,这些噪声不仅挤占空间,还会把最终生成的内容带偏。如果不加处理,模型可能被迫在超长输入上运行,甚至直接截断丢失关键事实。
我们把每个检索文档看作信息包,设第 i 篇文档长度为 |d_i|。当 k 篇文档被拼接进上下文时,总长度必须满足上限约束:
1. 候选文档长度:每个检索返回的文档 d_i 都是一个待装载的信息包裹,其长度用 |d_i| 表示,单位通常是 token 数。
2. 上下文总长求和:把 k 个候选文档的长度累加起来,得到上下文总长度 L_ctx。
3. 长度上限约束:总长度必须小于等于模型支持的上下文窗口上限 L_limit,这是所有压缩与重排操作的出发点。
回到行李箱的比喻,这一步相当于“先量尺寸再装箱”。知道了箱子容量,你才能判断何时需要压缩、何时只需重排。下一节将从最朴素的“撕书页”方案开始,看窗口裁剪到底有多大的吸引力与风险。
窗口裁剪:撕几页书来应急
最省事的办法是“撕书页”:从攻略书中间撕下若干页塞进包里。放在 RAG 里,这就是上下文窗口裁剪——在文档序列上取一个连续片段,其余部分毫不留情地丢弃。它非常快速,几乎不需要额外计算,尤其适合那种“答案恰好出现在文档开头”的简单问答。
但问题同样明显:如果答案落在被扔掉的部分,模型就“两眼一抹黑”,只能靠猜来生成,事实错误也由此而来。窗口裁剪本质上是一把没有语义感知的剪刀,它不知道哪一页是地图,哪一页是广告。
形式化地看,对第 i 篇文档 D,我们选取从位置 a 到位置 b 的连续片段,其余位置的内容统统丢弃。这个操作几乎不消耗额外计算,却会把关键信息暴露在风险之下——如果答案片段恰好在区间之外,模型将无从回答:
1. 起点与终点:a 是片段的起始下标,b 是结束下标,两个下标共同圈定一段连续的文本区间。
2. 片段长度限制:裁剪后的片段长度 b - a 必须小于等于 K 个 token,避免单个片段就占据过大的上下文空间。
3. 多文档拼接:所有被裁剪后的片段拼接起来作为模型输入,总长度仍需满足上一节中的全局上限约束。
撕书页虽然快,但你可能撕下的是“停车场指南”而不是“山顶路线”。窗口裁剪给我们的教训是:简单暴力的方案会把关键信息置于险境,于是我们接着要看更聪明的做法——先读一遍,再做笔记。
关键段落提取:读书记笔记
一个经验丰富的旅行者不会撕书,而是带一支荧光笔,把每个景点的开放时间、门票价格、交通方式记在小本子上。RAG 中的关键段落提取正是这种“荧光笔思维”:先把文档切成若干段落,再选出与用户问题最相关的段落保留下来。
怎么判断相关?现代做法是使用嵌入模型(embedding model)把查询和每个段落都变成向量,再计算余弦相似度。与查询方向一致的段落,就好比被荧光笔重点标记的句子,会被优先选中。
设查询 q 的嵌入向量为 e_q,段落 p 的嵌入向量为 e_p,相似度公式为点积除以两向量模长的乘积:
1. 查询向量化:用嵌入模型将用户查询编码成一个向量,表示问题的语义中心。
2. 段落向量化:把候选文档切分成段落后逐一编码,得到每个段落的向量 e_p。
3. 余弦相似度:对两个向量做点积并分别除以模长,得到语义相似度分数,这也决定段落是否被保留。
笔记比整本书小得多,却保留了真正的精华。段落提取的本质就是让语义来决定哪一段上桌,而不是让页码来决定。但这里还有一个问题:如果候选段落很多,我们仍然需要先确定“哪些文档更值得优先分段”,这正是重排序的用武之地。
重排序策略:出发前按价值排队
现在你面前摆着十几本攻略书,而你只打算带最值得的几本。怎么判断哪本最值得?你快速翻阅目录、看评价、对照目的地——这个“快速把关”的动作,在 RAG 中就叫重排序。
检索器通常用双编码器模型把查询和文档分别编码,效率高但交互不足,适合做第一轮粗召回。重排序模型则会让查询和文档进行深层交互,例如交叉编码器直接把两者拼在一起输入打分器,输出更精准的相关性分数。近年来,LLM 本身也能充当重排序器,让模型对每个候选文档直接给出相关度评分。
设重排序模型为 f_rerank,它对查询 q 与第 i 篇文档 d_i 打出一个相关度分数 s_i:
1. 成对打分:重排模型接收“查询 + 文档”的配对输入,输出一个相关性分数 s_i,分数越高表示越匹配。
2. 降序排序:对所有候选文档按 s_i 从高到低重新排列,形成新的优先级序列。
3. Top-K 截取:只保留排名最靠前的 K 篇文档,交给后续压缩器处理,避免无关文档进入上下文。
就像把最靠谱的攻略放在背包最上层,重排序把真正有用的文档顶到前排。但注意,重排序只改变顺序和筛选范围,并不能让文档变短。真正让文档“瘦身”的,还是前面提到的段落提取与压缩步骤。
质量评估:旅行复盘的尺子
旅行结束后,你会对着地图复盘:计划中的十个景点,你去了几个?有没有因为信息记错而绕远路?RAG 同样需要“复盘”,也就是质量评估——它要回答两个问题:生成的内容质量高不高?压缩后的事实信息有没有丢失?
生成质量通常通过文本连贯性、相关性、人工评分等指标来衡量;事实准确性则更严格:把答案拆成一个一个原子事实,再判断每个事实是否被压缩后的上下文证据所支持。如果压缩把关键日期、金额或限定词删掉了,答案看起来通顺,但事实根基已经没了。
一个常见的事实准确率指标 FactScore 可以表示为:先拆解出 N 个原子事实,再对每个事实做证据支持判断,最后取平均:
1. 事实拆解:把模型生成的回答拆成 N 个原子事实,每个事实都是一个可独立验证的短句。
2. 支持判断:逐一判断每个事实 f_i 是否能在压缩后的上下文中找到证据,输出布尔指示值。
3. 准确率平均:把所有事实的支持指示值求平均,得到 0 到 1 之间的整体事实准确率。
旅行复盘能让你下次打包更聪明;同样,评估结果会反过来指导我们调整压缩比,在信息保留和成本之间寻找更好的平衡点。
最优平衡:背包的黄金分割
压缩能省钱,但压缩太狠会丢信息。那么到底该压缩到什么程度?这就像打包:包太小什么都装不下,包太满翻东西费劲还容易超重。真正的最优方案,是在信息价值与上下文预算之间做一次权衡。
我们定义压缩比 R=1-|C|/|O|,它表示从原始上下文 O 中删减了多少比例的内容。R 越高,保留的子集 C 越小,token 成本越低,但信息量与事实覆盖率也可能随之下降。为了自动找到平衡点,可以引入一个可调参数 λ 来控制对长上下文的惩罚强度。
一个经典的平衡优化形式是:从原始集合 O 中选出一个子集 C,用 Info(C) 衡量信息收益,再用 λ·|C|/B 惩罚长度超支,目标是最大化两者之差:
1. 候选子集:C 是从原始文档集合 O 中选出的一个子集,它将被最终放进上下文窗口。
2. 信息收益:Info(C) 对子集 C 中各片段的信息权重求和,代表这部分内容对回答问题的贡献总量。
3. 长度惩罚与平衡:λ 是惩罚系数,B 是 token 预算;上下文越长,惩罚越大,从而鼓励系统选择小而关键的子集。
行李箱的容量永远有限,但我们可以通过重排、压缩、评估和调参,让有限的背包发挥最大的效用。这也是“LLM 时代搜索”中,RAG 与搜索技术融合最核心的艺术:不是背更多书,而是选对书、做对笔记。
AI 应用场景
企业客服知识库问答系统
阶段:检索后处理(上下文压缩与重排)
解决的问题:客服场景中,用户问题往往很短,而知识库文档却有数百篇、上千字。传统做法是把召回的全部文档一股脑塞给 LLM,既浪费 token,又让答案被无关细节带偏。引入重排序选出 Top-5 知识点,再压缩成摘要,回应既快又准,还守得住事实底线。
具体案例:某手机公司的售后助手收到“屏幕失灵怎么办”,检索器召回 30 篇文档;重排模型把维修手册和常见问题置顶,压缩器只保留“强制重启”“检查贴膜”等关键片段,最终在 3 秒内生成可执行的排查步骤,token 成本降低约 60%。
法律文档检索辅助
阶段:知识增强生成(检索后精排与压缩)
解决的问题:法律条文检索需要把数千字的判例压缩成“当事人责任判定”的关键要点。简单截取容易遗漏法条依据,导致回答缺乏可信度。上下文压缩与重排序流水线能将最相关的判例段落放到前置位,并保证援引法条的准确性。
具体案例:律师提问“合同违约金的司法标准”,重排模型优先展示《民法典》相关条文与相似判例,压缩器提取违约金计算要点,生成的咨询意见引用了真实有效条款,避免了大模型“一本正经地胡说八道”。
研究论文长文档总结
阶段:长文档处理(上下文压缩)
解决的问题:论文动辄几十页,直接把全文喂进模型会超出窗口限制,且生成效果不佳。压缩器把论文各章节切成段落,依据用户关注度提取“方法”和“结论”段落,同时保留与问题最相关的实验数据。
具体案例:科研助手帮助研究者总结 50 页的 arxiv 论文,系统重排后保留摘要、方法、结果三个核心段,压缩比达到 0.75,用户评价“结论依然完整,细节足够支撑判断”。
Python 演示
# -*- coding: utf-8 -*-# 演示 RAG 上下文压缩与重排序的简化过程import re def tokenize(text): """将文本切分为词项集合(模拟向量化)。""" return set(re.findall(r'[\w\u4e00-\u9fff]+', text.lower())) def keyword_overlap(q_terms, doc_terms): """用词项重叠度作为相关度分数(模拟重排序模型打分)。""" if not doc_terms: return 0.0 return len(q_terms & doc_terms) / len(q_terms) def rerank(query, docs): """按查询相关度对文档做重排序,返回 (文档, 分数) 列表。""" q_terms = tokenize(query) scored = [(doc, keyword_overlap(q_terms, tokenize(doc))) for doc in docs] scored.sort(key=lambda x: -x[1]) return scored def compress(query, docs, top_k=3): """压缩:只保留与 query 最相关的 top_k 篇文档。""" ranked = rerank(query, docs) return [doc for doc, _ in ranked[:top_k]] def main(): query = "什么是上下文压缩?" docs = [ "RAG 系统检索到的文档数量往往很多,上下文过长。", "上下文压缩能减少冗余信息,提升生成质量。", "重排序模型可以按相关性重新排列候选文档的顺序。", "上下文窗口大小限制了输入内容的长度,必须裁剪。", "关键段落提取关注与查询最相关的内容片段。", "压缩比过高会导致重要信息丢失,影响事实准确性。", "embedding 相似度可以衡量查询与段落的语义相关性。", ] print("原始文档数:", len(docs)) ranked = rerank(query, docs) print("重排序后 Top-3:") for doc, score in ranked[:3]: print(f" [{score:.2f}] {doc}") selected = compress(query, docs, top_k=3) compressed_len = sum(len(tokenize(d)) for d in selected) original_len = sum(len(tokenize(d)) for d in docs) ratio = 1 - compressed_len / original_len print(f"压缩比: {ratio:.2%}") print("压缩后上下文:") for doc in selected: print(" -", doc) if __name__ == "__main__": main()
return set(re.findall(r'[\w\u4e00-\u9fff]+', text.lower()))同时提取中文和英文词项,生成无重复词集合,作为文档的轻量“向量”return len(q_terms & doc_terms) / len(q_terms)用重叠词比例模拟重排序模型的相关度打分scored.sort(key=lambda x: -x[1])按分数降序排列,让高相关文档排到最前ranked = rerank(query, docs)执行重排序,得到按相关度排列的文档列表selected = compress(query, docs, top_k=3)压缩器只保留最相关的 3 篇文档ratio = 1 - compressed_len / original_len计算压缩比,即删减了多少比例的原始信息
常见误区
误区刚接触 RAG 时,我以为上下文压缩就是简单的截断,比如把超长的文档从中间剪掉一段,只要不超窗口限制就行。
正解截断(窗口裁剪)只是压缩的一种朴素形态。好的压缩是信息导向的:先用重排序筛掉不相关内容,再通过段落提取保留语义最相关的片段,让信息密度变高。就像整理行李,不是把衣服剪一半,而是只带真正会穿的那几件。
为何常见:因为在最简单的小例子中,截断碰巧也能回答正确,很容易让人忽略它对复杂场景的破坏力。
误区我一直以为重排序已经把不重要的文档排到后面,所以取 Top-K 之后肯定能塞进上下文窗口,不需要再压缩。
正解重排序解决的是“顺序”而不是“体积”。Top-K 文档的总 token 数仍可能远超窗口限制。实际流水线通常是“重排序 → 压缩/提取 → 送入生成”,两者相互配合而非相互替代。就像选出最值得带的三本书,并不意味着三本书就能装进包里。
为何常见:人们容易混淆“相关度排序”和“长度控制”这两个不同目标,以为选出的就是能放下的。
误区我看到压缩能显著降低 token 成本,于是认为只要把压缩比调高,生成既省钱又省力,效果一定更好。
正解压缩比过高的直接后果是关键支撑信息被大幅削减,模型缺乏事实依据,反而更容易产生幻觉。文中的平衡优化公式在信息收益与长度惩罚之间做权衡,就是提醒我们:最优压缩比需要实验确定,绝不是越高越好。行李箱塞到爆的结果往往是把必需品压在最底层拿不出来。
为何常见:成本指标很容易量化,而信息丢失很难直观感受,所以大家容易在成本优化上用力过猛。
误区我觉得查询与文档的余弦相似度已经能反映相关度排序,再跑一遍重排序模型纯属多余。
正解双编码器 embedding 适合粗召回,因为查询和文档是分开编码的,交互不足;重排序模型(如交叉编码器)让查询与文档联合编码,能捕捉到否定词、条件句等细粒度匹配信号。因此“语义相似”不等于“回答了问题”。就像两篇文章话题相同,但不一定都回答了你的疑问。
为何常见:相似度是接近人类直觉的概念,而重排序模型的内部交互机制对初学者并不直观,容易低估它的价值。
误区我一直觉得压缩顶多让回答更啰嗦或更简短,但事实本身不会改变。
正解压缩可能删掉数字、日期、限定词等关键约束,而这些正是事实推理的地基。FactScore 等指标专门用于检测这种事实缺口。当压缩比过高时,事实准确率会显著下降,这已是检索增强生成研究中的常见结论。就像菜谱里把“3 克盐”删掉,菜的味道就完全变了。
为何常见:因为压缩后的文本仍然通顺,读起来没问题,于是人们误以为内容的正确性也保住了。
面试题
Q1. 在 RAG 系统中,当检索到的文档总长度远超上下文窗口时,以下哪个方案最能从根本上解决问题?
- A.换一个更大的模型
- B.压缩和重排序流水线← 正确答案
- C.降低生成时的温度
- D.故意增大窗口参数
答案:B。RAG 首先要想办法让输入“塞得下且有营养”,压缩和重排序流水线正是为此而生。换更大的模型只能扩展容量,不解决噪声相关性问题;调低温度会影响生成随机性;强行增大窗口参数可能超出模型真正支持的长度,反而引发更多错误。
Q2. 上下文窗口裁剪最主要的缺点是?
- A.计算开销显著更高
- B.关键信息容易被丢弃← 正确答案
- C.生成文本流畅度变差
- D.检索文档数量会减少
答案:B。窗口裁剪直接在文档上取一段连续区间,如果答案落在区间之外,模型就看不到关键内容,这是它最大的风险。计算开销通常不高,流畅度下降也不是裁剪的直接体现,检索数量更不会因此减少。
Q3. 下列哪些属于关键段落提取的常用技术?
- A.文档片段切分← 正确答案
- B.嵌入模型编码← 正确答案
- C.查询段落相似度← 正确答案
- D.调低采样温度
答案:ABC。关键段落提取的核心步骤是:把文档切成片段、用嵌入模型对查询和段落编码、再计算相似度选出最相关段落。调低采样温度是生成阶段的参数调节,与段落提取无关。
Q4. 双编码器检索与交叉编码器重排的核心区别是?
- A.查询与文档联合编码← 正确答案
- B.使用更大的模型打分
- C.完全不需要模型调用
- D.输入与输出完全相同
答案:A。交叉编码器把查询和文档拼接后输入同一个模型,能捕捉细粒度交互信息;双编码器则把两者分开编码,适合高效召回的粗排阶段。选项 B、C、D 都不符合两者在架构上的本质差异。
Q5. 在本文中,压缩比 R=1-|C|/|O| 表示什么?
- A.压缩后的剩余比例
- B.压缩后删除的比例← 正确答案
- C.原上下文的总长度
- D.文档被压缩的次数
答案:B。R = 1 - |C|/|O| 衡量的是删减比例:原始信息被排除了多少。A 给出的是保留比例 |C|/|O|,C 只是长度本身,D 是次数概念,均不符合定义。
Q6. 下列哪些属于合理压缩带来的收益?
- A.提高生成相关性← 正确答案
- B.避免上下文超长← 正确答案
- C.减少 token 消耗← 正确答案
- D.消除所有事实错误
答案:ABC。合理压缩能去除噪声、适配窗口限制并降低成本,从而改善生成相关性。但压缩无法自动“消除所有事实错误”,过度压缩反而可能引入事实缺口,因此 D 不正确。
Q7. 在上下文平衡优化 C\*=argmax(Info(C)-λ·|C|/B) 中,参数 λ 的主要作用是?
- A.调节长度惩罚权重← 正确答案
- B.增加模型隐藏维度
- C.提升检索召回数量
- D.降低词表大小限制
答案:A。λ 是长度惩罚项 λ·|C|/B 的系数,λ 越大,系统越倾向于选择更短的上下文子集。它与模型结构、检索数量和词表大小完全无关。
Q8. LLM-based 重排序相比传统关键词匹配,最大优势是?
- A.完全绕开模型调用
- B.捕捉深层语义关联← 正确答案
- C.大幅降低推理耗时
- D.无需输入源文档
答案:B。LLM 能理解同义表述、复杂句式和隐含意图,从而在语义层面判断相关性;关键词匹配只能做字面命中。LLM 重排通常需要额外计算而不是降低耗时,也不能完全绕开模型调用。
Q9. 评估压缩后上下文质量时,可以采用哪些手段?
- A.事实准确性打分← 正确答案
- B.信息遗漏检测← 正确答案
- C.人工评测对比← 正确答案
- D.随机猜测答案
答案:ABC。事实准确性打分(如 FactScore)检测生成内容是否有证据支撑,信息遗漏检测关注关键内容是否被压缩掉,人工评测则对生成质量做整体判断。随机猜测不具备任何可比性,自然排除。
Q10. 压缩比过高最容易引发的后果是?
- A.模型推理速度提高
- B.输出内容变长
- C.关键事实信息丢失← 正确答案
- D.检索文档数量增加
答案:C。压缩比越高,删掉的信息越多,关键事实(数字、日期、限定词)越容易丢失,进而导致生成内容偏离真实依据。推理速度可能提升,但那是次要影响;输出长度与检索文档数量与压缩比没有直接因果关系。