想象一场开卷考试:你翻遍课本(检索),抱回二十段“可能相关”的资料,但答题卡只有五百个格子。直接整段抄上去?写不下,老师还看得一头雾水。你会怎么做?先做笔记——把每段话提炼成一两句要点(压缩);再排顺序——把最贴近题目、最像得分点的要点写在最前面(重排)。大模型就是那位阅卷老师,RAG 系统就是你的备考流程:检索负责“翻书”,压缩负责“摘要点”,重排负责“定优先级”。三个环节配合好,答案又快又准;配合不好,要么 token 爆表,要么大模型被无关信息带偏。这篇文章带你用“备考思维”看懂 RAG 上下文压缩与重排的全部套路。
对 AI 从业者而言,掌握压缩与重排协同调优,是优化 RAG 回答质量与 token 成本的核心手艺,也是从“能跑通”走向“跑得好”的关键一步。
可视化演示
动态演示RAG检索上下文压缩与重排策的核心机制,通过基础/核心/应用/扩展四种模式展示关键概念与交互流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
RAG检索上下文压缩与重排策:压缩与重 压缩与重排的协同工作架构 LLM压缩与抽取式压缩的算法选 核心机制可视化 RAG检索上下文压缩与重排策整体架构与关键组件
压缩重排协作
回到开卷考试。你从课本里翻出来二十段资料,第一件事是判断“答题卡装不装得下”——这就是上下文窗口瓶颈:大模型的注意力窗口就像答题卡,格子有限。第二件事是判断“哪些话最可能得分”——这就是噪声干扰:资料里大段背景介绍、重复描述,直接抄上去会让阅卷老师(大模型)抓不住重点。第三件事是省钱:每多写一个字,就多花一分 token 费用。
所以 RAG 系统的标准动作是:检索器先召回 top-50 相关段落(翻书),压缩模块把每段提炼成要点(做笔记),重排模块按相关度从高到低排序(排优先级),最后只把排序靠前的 top-10 送入大模型。压缩解决“装不装得下”,重排解决“先看哪一个”,两者配合才能达到又快又准的效果。
在重排打分的工程实践中,我们还希望重排时兼顾 token 成本,避免让一个又长又不太相关的“大块头”文档霸占前排。因此可以用一个带成本惩罚的排序公式:
这就像你整理笔记时,既要看内容靠不靠题(相关度),也要看篇幅值不值得写(成本)。两者平衡好了,答题卡才用得最值。
- 重排相关度分重排模型计算问题 \(q\) 与文档 \(d_i\) 的相关度得分,分越高代表这段资料越贴近题目。
- 长度占比惩罚文档 token 数占总 token 数的比例,越长、占比越大,惩罚越狠,防止长而无物的“注水段落”占据靠前位置。
- 权衡系数\(\alpha\) 控制惩罚力度。\(\alpha\) 大则更看重省 token,\(\alpha\) 小则更看重相关度,工程上通常设为 0.1 到 0.3。
- 最终排序按分数降序排列,从而让“既相关又精炼”的文档排在最前面。
压缩算法选型
做笔记有三种风格。第一种是请学霸帮你概括:他读完整段话,用自己的话说出核心观点——这是 LLM 生成式压缩,理解语义、能针对问题提炼,但“请学霸”要花时间(延迟高),也可能偶尔自由发挥过头(幻觉)。第二种是你拿荧光笔划重点:从原文里挑出最重要的一两句,一字不改——这是抽取式压缩,速度快、绝对保真(内容都来自原文),但划出来的句子可能缺上下文、读起来不连贯。第三种是混合策略:先用荧光笔划出候选句,再请学霸把几句合并润色成通顺的要点。
三种方式各有用武之地。LLM 压缩适合复杂推理问题,比如“这两份合同有哪些冲突条款”;抽取式压缩适合延迟敏感的场景,比如实时语音助手;混合压缩则适合中等预算的常规问答。压缩率(压缩后 token 与压缩前 token 的比值)一般控制在 20% 到 50% 之间——压得太狠会把关键信息弄丢,压得太轻又省不下多少 token。
选压缩算法就像选笔记工具:学霸(LLM)贵但质量高,荧光笔(抽取)便宜但效果糙,聪明人往往口袋里两种都备着,按情况取用。
- 压缩前 token 数原始检索段落被分词器切分后的 token 总数,这是压缩的“原料量”。
- 压缩后 token 数压缩完成后保留的 token 数,是“成品量”。
- 比值换算百分比两者相除再乘以 100%,得到压缩率。工程经验是控制在 20% 到 50%,过低会损伤信息完整性。
重排模型选型
想象考场上有两位判卷老师。第一位是“速读型”老师:考前把所有课本内容背成了知识卡片(向量索引),考试时看到你的答案关键词和卡片对得上,就快速给分——这就是 bi-encoder:问题和文档分别编码成向量,用余弦相似度打分。好处是文档向量可以提前离线算好,在线只需编码问题一次,速度快得惊人;坏处是它只看“关键词重合”,容易把“苹果”和“苹果公司”混为一谈。
第二位是“精读型”老师:他拿着题目原文,逐字逐句跟你的答案比对——这就是 cross-encoder:把问题和文档拼接成一个整体输入模型,让模型在 token 级别上充分交互,精度明显更高。代价是每篇文档都得从头到尾过一遍模型,无法预计算,候选越多越慢。
精读老师判得准但判得慢,速读老师判得快但会看走眼。工程上的标准做法是:召回阶段用 bi-encoder 快筛出 top-100,重排阶段用 cross-encoder 对 top-20 精排。这也解释了为什么两者不是替代关系,而是上下游关系。
而 cross-encoder 没有这么简洁的公式,它把 \(q\) 和 \(d\) 拼成 [CLS] q [SEP] d 输入 Transformer,直接输出一个 0 到 1 的相关分——就像精读老师直接在试卷上写下“这题 0.92 分”。
- 问题向量bi-encoder 编码器把用户问题 \(q\) 映射成一个稠密向量,这一步在线实时计算。
- 文档向量文档 \(d\) 经过同一个编码器得到向量,可以在离线阶段预计算并存入向量库。
- 点积与模长分子是两向量的点积,衡量逐维乘积之和;分母是两个向量模长的乘积,起到归一化作用。
- 余弦相似度结果结果在 \([-1, 1]\) 之间,越接近 1 表示方向和语义越一致。
效果延迟平衡
考试时间总是有限的。如果每一道题都让精读老师逐字比对,时间根本不够用;如果全用速读老师,又容易漏掉得分点。聪明的考生会分层处理:先把明显不相关的答案扔掉(bi-encoder 粗排淘汰 80%),再对剩下的精读判卷(cross-encoder 精排 top-20),最后摘出最稳的几条写进答题卡。这就是级联排序策略。
延迟预算是这样拆解的:检索耗时通常在几十毫秒,bi-encoder 粗排每篇约 1 毫秒,cross-encoder 精排每篇约 20 到 50 毫秒,LLM 生成则要 1 到 3 秒——生成才是大头。压缩耗时需要特别留意:LLM 压缩一次可能就要 0.5 到 2 秒,如果对 50 篇文档全做 LLM 压缩,延迟直接爆炸。因此压缩要“量力而行”:先抽取式压掉一大半,再对真正要送入模型的 top-10 做精细压缩。
另外还有动态路由:简单问题(比如“公司邮箱是什么”)走快速路径——bi-encoder + 抽取压缩;复杂问题(比如“这三份合同有哪些风险”)走完整路径——LLM 压缩 + cross-encoder 精排。就像考生先扫一眼题目难度,简单题直接写,难题才祭出精读策略。
考场上最忌讳“平均用力”。把精读留给最关键的内容,把速读用在海量筛选中,整体时间才够用——RAG 的延迟平衡,考的就是这道时间分配题。
- 检索耗时向量索引查询耗时,通常 20 到 80 毫秒。
- 压缩耗时压缩阶段耗时,抽取式约 5 毫秒,LLM 生成式可能高达 1 到 2 秒。
- 重排耗时与候选数成正比,cross-encoder 重排 20 篇约 0.4 到 1 秒。
- 生成耗时大模型生成答案的耗时,压缩上下文能直接降低这部分成本。
端到端评估
考试最终看总分,但要想知道哪科拖后腿,还得看分项成绩。RAG 评估也是一样:检索质量指标看召回率(Recall@k),衡量“该翻到的书页翻到了没”;重排排序指标看 MRR 和 NDCG@k,衡量“翻到的书页排得准不准”;答案质量评估看准确率、ROUGE 分数和忠实度,衡量“最终写的答案对不对、有没有瞎编”。最后还要算账:token 节省了多少、延迟涨了多少。
其中 NDCG@k 是重排效果的核心指标。它的思想是:排在第 1 位的相关文档比排在第 5 位的更有价值。公式里每个位置的收益会除以排名的对数折扣,越靠前权重越大,再除以理想排序的得分做归一化,得到 0 到 1 的分值。
实操时还要做消融实验:固定同一个评估集,分别跑“全量上下文”“只压缩不重排”“只重排不压缩”“压缩+重排”四组配置,对比回答得分、token 消耗和延迟。就像每次只改一种备考习惯,看总分到底涨在哪——这样才能精准定位瓶颈,而不是瞎调一通。
- 相关性标签第 \(i\) 个位置的文档与问题的相关性得分,通常取 0(不相关)或 1(相关),也可用 0 到 3 的多级标签。
- 位置折扣\(\log_2(i+1)\) 让排名越靠后的文档收益被压缩得越狠,体现“前置优先”。
- 累计收益 DCG分子把所有位置上的折扣收益累加,得到当前排序的总收益。
- 归一化基准 IDCG分母是理想排序(把相关文档全排最前)下的最大收益,用当前收益除以它,得到 0 到 1 的 NDCG 值。
工程落地实践
把备考流程变成自动流水线,还需要几样“后勤工具”。第一样是缓存复用机制:常考的重点题(热门问题)直接查错题本,不用重新做笔记和排序。第二样是批处理加速:重排模型一次处理一批候选文档,用 GPU 批量推理,比逐篇调用省一半时间。第三样是增量更新策略:知识库新增文档时,只重算新文档的向量和缓存,不必全量重建——就像课本改版时只换新章节的笔记。
第四样是监控与回退:持续跟踪端到端延迟的 P95、压缩率、缓存命中率、重排后 top-1 答案采纳率。如果重排服务超时,就自动降级为只用 bi-encoder 的粗排结果,保证回答不会彻底失败——这就是“时间不够先答最稳的题”。
缓存能显著摊薄成本,可以用一个简单的期望成本公式来理解为什么值得做缓存:
后勤到位了,整条流水线才能稳定运转:有缓存兜底、有批量提速、有更新保鲜、有回退守底线。这样一套系统,才称得上“上了考场不慌”。
- 缓存命中率请求中有多少比例直接命中了缓存,热门问题占比越高,命中率越高。
- 命中读取成本从缓存读取结果的开销,通常只有 1 到 5 毫秒。
- 未命中计算成本需要重新做压缩和重排的开销,可能高达几百毫秒到几秒。
- 加权平均两类成本按命中率加权求和,得到每次请求的平均成本。
AI 应用场景
场景一:企业知识库智能客服
阶段:RAG 在线服务(检索后处理与生成前优化)
解决的问题:用户提问后,检索器一次召回 30 段 FAQ,直接拼接会超出上下文窗口,回答冗长且容易被无关 FAQ 带偏。传统做法是硬截断前 N 个字,结果把关键答案截没了。
具体案例:某电商售后机器人上线“抽取式压缩 + cross-encoder 重排”流水线:先把每段 FAQ 压缩成 2 句话,再重排选出 top-5 送入大模型。上线后平均 token 消耗下降 60%,答案采纳率提升 15%,P95 延迟仅增加 80 毫秒。
场景二:法律判例检索助手
阶段:离线文档索引 + 在线检索增强
解决的问题:法律判例动辄几千字,噪声多、关键信息分散。直接把原文塞进大模型,token 成本高且律师等不起。
具体案例:某法律科技公司搭建“LLM 压缩摘要 + bi-encoder 粗排 + cross-encoder 精排”三阶段流水线。每个判例先由 LLM 生成 200 字案情摘要,检索时先用 bi-encoder 快筛 top-50,再用 cross-encoder 精排 top-3。律师平均 3 秒内获得最相关的三个判例摘要,费用比全量原文方案降低七成。
场景三:智能语音助手实时问答
阶段:低延迟在线推理流水线
解决的问题:语音助手要求 500 毫秒内响应,cross-encoder 逐篇精排太慢,LLM 压缩更不可行。必须在质量可接受的前提下极限压缩延迟。
具体案例:某智能音箱团队采用动态路由:简单事实类问题(“今天天气如何”)走 bi-encoder + 抽取压缩快速路径,50 毫秒完成全部预处理;复杂推理问题(“这个药和那个药能一起吃吗”)才启用 cross-encoder 精排,且单次只精排 5 篇。整体回答延迟稳定在 350 毫秒内。
Python 演示
# -*- coding: utf-8 -*-"""RAG 上下文压缩与重排的简化 Pipeline 演示""" import re def retrieve(query, corpus, top_k=6): """模拟向量检索: 按词重叠度召回 top_k 个原始段落""" q_words = set(re.findall(r"[\w\u4e00-\u9fa5]+", query.lower())) scored = [] for idx, doc in enumerate(corpus): d_words = set(re.findall(r"[\w\u4e00-\u9fa5]+", doc.lower())) hit = len(q_words & d_words) / max(len(q_words), 1) scored.append((hit, idx, doc)) scored.sort(key=lambda x: x[0], reverse=True) return scored[:top_k] def extractive_compress(doc, max_sentences=2): """抽取式压缩: 只保留前 max_sentences 个完整句, 类似荧光笔划重点""" sents = re.split(r"[。!?!?]", doc) sents = [s.strip() for s in sents if s.strip()] return "。".join(sents[:max_sentences]) + "。" def llm_compress(doc): """LLM 压缩: 模拟生成式概括, 取最长的核心句作为摘要""" sents = re.split(r"[。!?!?]", doc) sents = [s.strip() for s in sents if s.strip()] if not sents: return "" best = max(sents, key=len) return "核心要点: " + best[:40] def rerank_cross(query, docs): """模拟 cross-encoder 重排: 用句子级 F1 重叠度打分""" q_words = set(re.findall(r"[\w\u4e00-\u9fa5]+", query.lower())) results = [] for doc in docs: d_words = set(re.findall(r"[\w\u4e00-\u9fa5]+", doc.lower())) inter = len(q_words & d_words) precision = inter / max(len(d_words), 1) recall = inter / max(len(q_words), 1) f1 = 2 * precision * recall / max(precision + recall, 1e-9) results.append((f1, doc)) results.sort(key=lambda x: x[0], reverse=True) return results def main(): query = "RAG 系统中为什么需要对上下文做压缩和重排" corpus = [ "RAG 检索返回大量相关文档, 直接拼接会占用上下文窗口并引入噪声。", "上下文压缩的目标是在保留关键信息的同时降低 token 数量。", "重排一般使用 reranker 模型, 把最相关的文档排在最前面。", "cross-encoder 需要问题和文档一起过模型, 精度高但速度慢。", "bi-encoder 可以预计算文档向量, 推理速度快但精度有限。", "压缩方法有抽取式和生成式, 生成式更灵活但延迟更高。", "重排之后再把 top-k 片段送入 LLM, 能明显提升答案质量。", "端到端评估要同时看答案正确率和 token 成本, 做消融实验。", ] raw = retrieve(query, corpus, top_k=6) # 1. 压缩: 这里选用 LLM 压缩模拟 compressed = [llm_compress(doc) for _, _, doc in raw] # 2. 重排: 用交叉打分精排 ranked = rerank_cross(query, compressed) # 3. 拼接最终上下文 final_context = "\n".join( f"[{i + 1}] (score={score:.2f}) {doc}" for i, (score, doc) in enumerate(ranked) ) print("最终上下文(压缩+重排后):") print(final_context) if __name__ == "__main__": main()
scored.sort(key=lambda x: x[0], reverse=True)检索阶段按相似度降序,排出最相关的原始段落。raw = retrieve(query, corpus, top_k=6)先召回 top-6,模拟向量检索返回的候选集。compressed = [llm_compress(doc) for _, _, doc in raw]对每个候选做生成式压缩,模拟学霸概括笔记。extractive_compress(doc, max_sentences=2)抽取式压缩的替代实现:只保留前两句,模拟荧光笔划重点。best = max(sents, key=len)LLM 压缩模拟器内部:取最长的核心句作为摘要,代表模型抓住了主要信息。ranked = rerank_cross(query, compressed)重排阶段:用 F1 重叠度对压缩后的片段重新打分排序。results.sort(key=lambda x: x[0], reverse=True)按精确分数降序排列,等价于 cross-encoder 的输出排序。final_context = "\n".join(...)把排序后的片段拼成最终上下文,带分数标注方便调试。
常见误区
误区很多人觉得上下文太长直接截断就行了,把前面 N 个 token 留下、后面的丢掉,又快又省事。反正大模型只看前面。
正解截断是最危险的压缩方式——关键信息往往藏在段落中间或结尾。就像考试让你抄重点,你只抄了试卷第一页,第二页的得分点全丢了。真正的压缩要根据问题和语义来提炼,抽取关键句或生成摘要,而不是按位置硬切。
为何常见:截断实现最简单、零延迟,且刚入门时容易忽略“信息分布不均匀”这个事实,以为开头就是重点。
误区有些人认为“反正都要做,压缩和重排先后无所谓,结果差不多”。
正解顺序会影响质量和成本。先压缩再重排是最常用的——重排模型面对的是精炼文本,打分更聚焦、耗时更短;但如果候选文档非常多,先粗排选出 top-100 再压缩,能省大量压缩开销。两者不是同一道工序的不同叫法,而是有先后依赖的流水线关系。就像考试必须先筛出可能考的章节,再逐章做笔记,顺序反了效率骤降。
为何常见:初学者把压缩和重排都理解为“检索后的简单后处理”,没有意识到它们各自的开销和对下游的影响差异。
误区既然 cross-encoder 打分准,那从召回开始就全部用它,不是效果最好吗?何必还要 bi-encoder。
正解cross-encoder 每篇文档都要和问题拼接过一遍模型,1000 篇候选就是 1000 次推理,延迟和成本直接爆炸。正确做法是分层使用:bi-encoder 负责从百万级文档中快筛到几百篇,cross-encoder 只精排最后几十篇。就像阅卷不能每次都找精读老师逐字看一千份卷子,先让速读老师筛掉明显不对的,精读老师才有时间细看。
为何常见:只在小数据集上对比过模型精度,忽视了真实场景下候选量级和延迟预算的巨大差异。
误区有人把压缩率当唯一 KPI,认为压到 5% 才是本事,token 成本降得越多越好。
正解压缩率过低会丢失关键信息和上下文关联,导致答案质量断崖式下跌。工程上 20% 到 50% 是经验区间,最终要以端到端答案得分为准。就像笔记做得越短,复习时越可能看不懂当初的上下文。压缩率要和答案质量放在一起评估,而不是单看一个数。
为何常见:token 成本最直观、最容易量化,而质量下降是间接且滞后的,容易被忽视。
误区上线后看用户反馈和回答准确率就够了,MRR、NDCG 这些中间指标没必要专门统计。
正解端到端指标只能告诉你“坏了”,不能告诉你“坏在哪”。回答变差了,到底是检索没召回、压缩丢了关键信息,还是重排把好文档排到了后面?只有分模块指标能定位瓶颈。就像学生总分下降了,不看各科成绩就不知道是数学拖后腿还是英语滑坡。消融实验加分模块指标,才是科学的调优路径。
为何常见:分模块指标需要额外埋点和评估集标注,前期投入大,容易被视为“不紧急的事”。
面试题
Q1. RAG 中为什么要对检索到的上下文做压缩?
- A.提升检索响应速度
- B.减少 token 与噪声← 正确答案
- C.扩大模型参数量
- D.替换嵌入编码模型
答案:B。压缩的核心目的是在保留关键信息的同时减少 token 占用、降低噪声干扰,让大模型在有限的上下文窗口内看到更精炼的内容。A 是检索阶段的事,C、D 与压缩无关。
Q2. 相比 LLM 生成式压缩,抽取式压缩最突出的优点是?
- A.语义理解能力更强
- B.速度快且不会幻觉← 正确答案
- C.能生成全新句子
- D.压缩率永远更高
答案:B。抽取式压缩直接挑选原文中的句子,内容百分百来自原文,因此不会产生模型编造内容(幻觉)的风险,同时不需要额外的 LLM 推理,速度更快。A、C 是生成式的特点,D 说法过于绝对。
Q3. cross-encoder 与 bi-encoder 的根本区别在于?
- A.模型参数的规模不同
- B.有无 token 级交互← 正确答案
- C.训练数据来源不同
- D.输出向量维度不同
答案:B。cross-encoder 把问题和文档拼接后输入同一模型,让 token 在注意力层充分交互,精度更高;bi-encoder 则分别编码问题和文档,互不看见对方内容。就像精读老师拿着题目逐字对照答案,而速读老师只凭印象打分。
Q4. 以下属于重排效果评估指标的有?
- A.平均倒数排名 MRR← 正确答案
- B.折损累计增益 NDCG← 正确答案
- C.文本生成相似 BLEU
- D.召回率 Recall@k
答案:AB。MRR 和 NDCG 都是衡量“相关文档是否排在前面”的排序指标,是重排质量的核心度量。BLEU 是文本生成指标,Recall@k 属于检索阶段指标,都不用于评估重排排序效果。
Q5. 级联重排策略通常指什么?
- A.先抽取再 LLM 压缩
- B.先粗排再精排重排← 正确答案
- C.先缓存再重排打分
- D.先检索再压缩整理
答案:B。级联重排指先用 bi-encoder 快速粗排,把候选从数千篇缩小到几十篇,再用 cross-encoder 精排,兼顾速度与精度。这就像先让速读老师筛一遍卷子,精读老师才有时间细看剩下的。A 是压缩策略,C 是缓存策略,D 是流程描述而非重排策略。
Q6. 工程上上下文压缩率一般控制在什么范围?
- A.5% 到 10% 区间
- B.20% 到 50% 区间← 正确答案
- C.80% 到 100% 范围
- D.完全不压缩最优
答案:B。20% 到 50% 是工程经验值:压得太狠(5%)会丢失关键信息,压得太轻(80% 以上)省不下多少 token。完全不压缩则失去了优化的意义。最优值需要通过消融实验确定。
Q7. 为平衡效果与延迟,合理的工程手段包括?
- A.热门问题使用缓存← 正确答案
- B.重排采用批量推理← 正确答案
- C.简单查询走快速路径← 正确答案
- D.只用大模型精排
答案:ABC。缓存让热点问题直接命中结果、批量推理提升 GPU 利用率、动态路由让简单查询跳过重型环节,都是成熟的降延迟手段。只用大模型精排会让所有请求都承担高延迟和高成本,属于反面做法。
Q8. bi-encoder 相对于 cross-encoder 的核心工程优势是?
- A.打分精度更高超
- B.文档向量可离线算← 正确答案
- C.支持无限长文本
- D.不需要训练数据
答案:B。bi-encoder 把文档编码为向量的过程可以在离线完成并存入向量库,在线只需编码问题一次,检索速度快。精度是它相对于 cross-encoder 的短板,长文本限制和训练数据需求两者都存在。
Q9. 消融实验发现“只压缩不重排”效果反而下降,最可能的原因是?
- A.压缩率设定过高
- B.噪声片段排到最前← 正确答案
- C.检索数量过于少
- D.压缩模型太轻量
答案:B。只压缩不重排,高噪声或低相关的片段可能仍然排在前面,大模型读到这些“先入为主”的噪声会被带偏,导致答案质量下降。这就像笔记做得很精炼但顺序混乱,得分点埋在最后,阅卷老师根本看不到。压缩率和检索数量不是直接原因,模型轻量也不必然导致变差。
Q10. 评估 RAG 回答的质量维度包括?
- A.答案准确度← 正确答案
- B.答案忠实度← 正确答案
- C.token 节省量
- D.检索召回率
答案:AB。答案准确度衡量回答是否满足问题,忠实度衡量回答是否忠于检索到的上下文(有没有瞎编),二者属于质量维度。token 节省量是成本维度,检索召回率属于检索阶段指标,都不属于“回答质量”范畴。