☰
RAG上下文软压缩:OSCAR方案精读与落地实践
2026/10/16 9:37:38 网站建设 项目流程

上周我复盘手头的检索增强问答系统,发现一个很典型的怪相:检索器明明把最相关的文档都捞回来了,拼给模型之后,回答质量反而往下掉。后来我统计了一下拼接进上下文的token分布,10篇文档平均8000多token,真正对最终回答有用的信息量大概不到三成,剩下全是背景介绍、页面导航文案、重复口径、无关评论这类“长尾噪声”。也是从这时候开始,我集中翻RAG上下文压缩方向的论文,这周精读的OSCAR就是其中一篇,标题全称是OSCAR: Online Soft Compression for RAG。

这篇文章名很有意思,三个关键词每个都值得拆开琢磨:Online(在线)、Soft Compression(软压缩)、RAG(检索增强生成)。我这篇精读笔记不想做成那种“标题+摘要复述”的水文,而是想把它放在一个完整的问题链路里看:RAG为什么要做压缩?现有的硬压缩方案到底差在哪?OSCAR这个方案里的Online、Soft、Compression分别解决什么问题?如果要自己动手复现,代码上每一步怎么走、评测怎么做、最容易翻车的地方在哪儿?最后再聊聊这类思路落地到业务时,哪些场景是真香、哪些场景千万别硬上。

1. 先聊清楚:RAG上下文里到底藏了多少“无效token”

在讨论压缩方案之前,得先把问题本身量化清楚。RAG的流程不复杂:query进来,检索器从知识库里取回一批文档,拼到prompt里,交给大模型统一阅读并生成答案。听起来顺畅,但工程落地时,这条链路有四个叠加在一起的问题。

第一是上下文窗口的预算问题。模型窗口虽然越来越大,但窗口大不等于可以随意挥霍,因为输入长度直接影响计算量和成本,尤其是按token计费的生产环境。对大多数团队来说,单次请求的token预算通常是有限额的,检索结果一旦把预算吃光,后面想塞历史对话、想塞few-shot示例都没有空间了。

第二是检索结果里的信息密度极低。真实知识库里的文档不是论文,它往往是产品介绍、FAQ、新闻稿、论坛帖子的混合体。我查“某设备保修政策是多少年”,检索器最可能返回的是含关键词最多的长页面,里面有公司介绍、产品卖点、用户评价,真正回答保修期限的句子可能就一两句,而被一起拼进上下文的无关内容可能有上千token。信息密度这么低,大模型阅读起来既慢又容易跑偏。

第三是所谓的“Lost in the Middle”效应。大模型对长上下文中间位置的内容注意力最弱,而检索结果拼接时中间往往塞满了次要内容,真正关键的证据链被淹没在中间地带。检索做不好是召回问题,检索做好了上下文排布不当,照样答不好。

第四是时序和一致性问题。多轮对话场景里,每一轮都带着同样的背景文档反复拼接,token消耗成倍叠加;几轮之后上下文里充斥着重复内容,模型既没有记住之前说过什么,又因为重复信息分散了注意力。

这几个问题合在一起,指向同一个矛盾:我们需要包含足够证据的上下文,又不能毫无节制地把原始文档全塞进去。解决办法自然是压缩——在把检索结果喂给生成模型之前,先做一轮“取舍”。

2. 硬压缩的三种常用做法,以及它们各自付出的代价

我见过很多RAG系统在做上下文瘦身时,直接使用简化的启发式方案。最粗糙的是截断,文档取头部或尾部N个token,或者按检索得分从高到低只取前几篇。截断的问题很明显:答案出现在“恰好被截掉的那一段”是家常便饭,尤其是很多文档的关键结论写在结尾部分,头部全是铺垫。

稍微讲究一点的是抽取式摘要,用句子重要性打分把文档里的句子挑出top-k。这比无脑截断好不少,但它破坏了跨句子的指代关系和逻辑衔接。比如文档先说“该设备提供三年标准保修”,后面句子用“这项政策”指代前文,抽取式摘要很可能会丢掉前一句,留下孤零零的“这项政策”,模型看完一头雾水。

更高成本的做法是生成式摘要,让一个摘要模型先把文档改写压缩一遍,再送去给下游阅读理解。这个方案信息压缩率高,但有两个硬伤:第一是额外增加了一次生成过程,端到端延迟翻倍甚至更多;第二个也是更致命的,摘要模型压缩时容易产生幻觉,会脑补原文没有的细节,而下游大模型无法分辨哪些是原文真实信息,哪些是摘要器脑补出来的,最后可能把幻觉当事实输出。

我发现上面这三种方案有个共同特征:它们做的都是二值化决策——保留或丢弃,没有中间状态。token或者句子要么完整进入上下文,要么直接消失,不存在“这部分内容不太确定有没有用,我给它一个中等权重保留下来”的可能。这个“非黑即白”的取舍,就是硬压缩和软压缩最本质的分界点。

3. OSCAR三个词拆开看:Online、Soft、Compression各解决什么

3.1 Online:压缩发生在什么时间点?

OSCAR标题里的Online,我先从字面理解:压缩是“在线”发生的。

这里要对比一个常见的做法——离线预压缩。很多团队会把知识库里的文档提前做一遍摘要,把摘要向量化建索引,线上检索时直接检摘要,文档原文根本不出场。这种做法的优势是线上零额外开销,但有一个绕不开的问题:压缩目标与用户query无关。不管用户问的是保修期、安装方法还是退换货政策,同一篇文档永远只有一份摘要。一份摘要很难同时满足多个角度的查询,为了“通用”它最终会写成四平八稳的概述,而概述对具体问题往往信息量不足。

OSCAR走的是另一条路:检索已经拿到top-k文档之后,压缩器带着用户原始query,对每篇文档重新计算“哪些内容对这个具体问题重要”,然后进行压缩。这样压缩是面向问题的,方向明确:不是把文档压缩成“好摘要”,而是压缩成“能回答当前这个问题的上下文”。

在线当然有代价,最直接的就是压缩步骤插在了检索和生成之间,变成了端到端时延的一部分。所以OSCAR这类方案在工程上有个隐含约束:压缩器模型本身必须足够轻量,否则节省下来的token成本会被额外延迟吃回去。这个约束,我后面会专门讲工程踩坑时细说。

3.2 Soft:软压缩的“软”到底是怎么个软法?

Soft是我觉得最值得展开的词,它对应的是前文说的二值化取舍问题的解法。

硬压缩的“硬”在于决策是离散的,每个句子或者每个token只有保留和丢弃两种状态。而软压缩的思路是:让压缩器给上下文里的每个token或者每个语义单元输出一个0到1之间的连续重要度分数,在拼接阶段按分数加权,甚至采用“有一定概率被保留,但保留下来的证据会得到相应加权”的方式。

为什么这样设计有价值?核心是可微性。在训练阶段,如果我们能用下游生成任务的loss来反向传播,压缩器的打分网络就能被优化成“生成效果好就强化这个打分模式,生成效果差就调整打分方向”。这就是“为了让答案更好而保留内容”,而不是“因为内容长得像摘要所以保留”。

另一个隐蔽的好处是软压缩天然带容错能力。硬压缩一旦误删了关键证据,信息就永久丢失了;软压缩给的是概率化的保留,重要度不高的内容只是被削弱,不是被彻底抹掉。对需要多段证据综合推断的问题,这种“柔和”的保留方式容错空间明显更大。

3.3 Compression:压缩不是一个距离度量,而是一个任务目标

Compression这个词看似直白,但在RAG场景里,“压缩成什么样子才叫好”其实是个容易跑偏的问题。如果只追求压缩率,那最优解是什么都不保留;如果只追求信息保全,那又把问题绕回了原始全文拼接。关键不是压缩率本身,而是在给定压缩率的前提下,最大程度保留“下游回答所需的信息”。

我觉得理解压缩目标最好的一个思维实验是“干扰敏感性分析”:把原始文档中的某一段内容删掉,喂给模型,观察回答质量的变化。如果删掉这一段,模型回答得分掉得特别厉害,说明这段是回答问题所必需的关键证据,压缩器必须优先保留它;如果删掉之后回答得分几乎没有变化,说明这段在这个问题语境下就是冗余的,可以被软化掉。按这个标准训练出来的压缩器,不是在学“文档哪些句子写得好”,而是在学“文档哪些内容与回答当前问题强相关”。

这个视角也解释了为什么不能用“压缩结果与原文的语义相似度”来做评价——一段全是客套话的文字和原文语义相似度可能很高,但删掉它对回答质量毫无影响,留着它纯属浪费token。压缩得好不好的最终标尺只有一个:下游回答的质量是否保住了。

3.4 我推测的OSCAR整体工作流程

下面这部分是我从标题和RAG压缩领域常见技术路线做的合理推测拆解,如果你手上拿到了论文原文或者公开代码,以原作者给出的方案为准,我这里主要提供一种“如果我也要这么设计,会怎么搭骨架”的思路。

整体流程文字描述是这样:query进来,检索器先取回一排候选文档;压缩器作为一个轻量模块,接收“query + 单篇文档”,逐token输出重要度分数;随后压缩模块根据分数与预算约束,对上下文做软加权拼接;最终拼好的紧凑上下文交给大模型生成答案。压缩器内部大致分四步:

第一步:用一个小体量的编码模型把query和文档拼在一起,做交叉编码。为什么用交叉编码而不是双编码?因为“某个词重不重要”往往取决于它与query的交互,单独看文档词频是看不出重要性的,交叉编码能捕捉query和文档词之间的细粒度attention。

第二步:在编码结果上接一个打分头,输出每个token的重要性分数。打分头的设计可以用线性层加sigmoid,也可以套用token-level的分类头加温度系数。

第三步:按照压缩预算约束筛选token。比如预算要求压缩率30%,也就是原始1000个token最后只能保留300个,那么压缩器就在全局分数约束下做选择。如果采样保留,那就让保留概率与分数成正比,被保留的token还可以按其重要性度做一个加权,这就是“软”的体现。

第四步:训练时用下游生成loss端到端回传,同时加一个压缩率正则项,保证模型不会为了追求效果而无节制地保留token。

这套设计最大的优势在于:压缩器不是一个独立训练的摘要模型,而是与下游生成能力绑定的一个组件。这也是它在概念上区别于生成式摘要方案的关键——生成式摘要的优化目标是“摘要与原文接近”,OSCAR的优化目标是“最终答案答对”,这两种目标在数学上完全不是一回事。

4. 从标题到代码:一份可执行的OSCAR复现路线

光聊概念不过瘾,我把自己在模拟项目X上复现软压缩思路的路线整理出来,这条路线不一定与论文实现细节一致,但每一步都是可落地、可验证的。

4.1 环境选型,以及为什么我不推荐上来就上大模型

先说选型逻辑。在线压缩器要承受线上实时延迟压力,所以不能选大参数模型;同时它又要做token级语义交互,不能太弱。我的选择是:压缩器用一个小体量的开源编码模型,也就是一般被称为“轻量级Encoder”的那一类,参数量在几十M到一两百M之间;阅读器用开源基座模型,参数量级按团队现有推理能力决定。如果想先快速验证效果,甚至可以直接用对话模型做reader,先把链路跑通。

软件依赖不需要很多,我用的核心组件大概是这些:

torch>=2.0 transformers>=4.40 datasets>=2.10 faiss-cpu # 检索用,小语料CPU版够用 einops accelerate

需要提醒的是,与其纠结压路机版本,不如先把评测集定好。因为没有稳定的评测,后面做的所有优化都无法判断是好是坏。

4.2 数据集与评测指标:先定义“什么才算压得好”

我构造了一个“长文档答案”型评测集:每个样本给一个问题,附带一大篇混合文档。文档里故意掺入几段与问题无关的干扰文本,保证数据难度是真实的RAG场景难度。

评测维度我固定死四个:

评测维度测量方式说明
答案准确率模型生成答案与标准答案的F1核心指标,必须看
压缩率压缩后token数 / 原始token数越低代表节省越多预算
端到端延迟从query发出到拿到答案的p50耗时直接决定生产可接受度
关键信息保留率文档中预设的关键实体/论证点是否出现在压缩结果中辅助诊断用

这四张表一起看,才能把“压得多”和“压得好”分开。如果只看压缩率,容易做出一个把所有内容都删光的模型;如果只看答案准确率,又容易得到“压了个寂寞”的结果。

4.3 训练流程:我用两阶段法,先让压缩器学会“看重点”

端到端直接训练压缩器和reader是可行的,但工程上我强烈建议先做两阶段。第一阶段先离线收集“关键段敏感性”软标签:对训练集里的每个文档片段,依次做删减实验,把删除后回答得分下降幅度超过阈值的片段标记为高重要性。用这些软标签先去训练压缩器的打分网络,让压缩器先学会“哪些内容与问题相关”。第二阶段再端到端微调,把压缩器接到reader上,同时优化生成loss和压缩率正则。

我用的训练目标大致长这样:

# 伪代码,核心思路是生成loss + 压缩率正则项 loss = generation_loss + lambda * max(0, current_compression_rate - target_rate) # 打分网络输出token级重要性分数 importance_scores = torch.sigmoid(scoring_head(last_hidden_state)) # 在压缩预算约束下,对token基于分数软筛选 source_mask = sample_by_importance(importance_scores, budget_ratio=0.3)

lambda参数需要反复调。我的经验是从0.01量级起步,每个实验翻倍,观察压缩率和生成loss的变化,找到一个“压缩率达标但生成质量下降不超过1-2个点”的平衡值。温度系数初始设1.0,如果训练初期打分分布过于集中,可以适当降温度促使分数平滑化。

4.4 先跑通一个“插拔式”基线,再谈优化

如果时间紧,我建议不要一上来就搞端到端训练,先把压缩器做成“可插拔”模块,评估三种基线:截断、抽取式句子摘要、生成式摘要,然后才对比OSCAR思路的软压缩。

我在模拟项目X上的初期对比结果如下(下表数值是展示对比逻辑用的示例,不是论文复现数据):

方法压缩率答案F1端到端延迟(p50)
硬截断top-k0.500.511.8s
抽取式句子摘要0.350.582.1s
生成式摘要0.250.623.9s
软压缩(OSCAR思路)0.300.662.3s

这个表的信息量很大:生成式摘要指标看着不错,但延迟是硬伤;软压缩在压缩率与F1之间取得了比硬截断好得多的平衡点。延迟比抽取式摘要只高一点点,但F1提升明显。

5. 复现OSCAR时最容易翻车的几个环节

如果只看论文标题想当然地写代码,大概率会被下面这几个坑反复折腾。我把自己的排查记录列出来,能少走不少弯路。

第一个坑是压缩器训练不稳定,具体表现是模型打了半天分数,最后输出的sigmoid分数全都集中在0.5附近。这不是模型没学到东西,是打分头缺乏区分度。我试过把温度系数调低、给打分头加一个熵正则,强制分数分布不要过度集中于中段。亲测有效的方法是先冻结reader,单独用软标签训练压缩器几百步,等损失降下去再解冻做端到端。上来就端到端,两个模型互相干扰,训练曲线通常很妖。

第二个坑是前文提过的在线时延陷阱。软压缩器再小,也是“插入”了额外的一跳推理。读者可能觉得压缩完少喂了几千token,生成更快了,时延应该降下来,但实际不是。生成少几千token省下的时间,很可能被压缩器的推理时间抵消掉。解决思路有几种:压缩器参数量压到极致;先做句子级粗筛,筛掉明显无关的句子后再做token级细选,把精细编码用在刀刃上;对高频query的检索结果加缓存,省掉重复压缩。我在系统里就是把压缩器限定在轻量级Encoder级别,实测对p50延迟的影响在可接受范围。

第三个坑是信息损失不好量化。我遇到过压缩结果看着挺通顺,但模型回答里的关键实体却对不上的情况。后来逐段排查发现,软加权会把一些规范化表达的词削弱,比如“保修期36个月”里的“36”被弱化,模型就认成了“几个月”。对这类“强保护词”,我的做法是维护一个小规则库:数字、否定词、专有名词在软压缩时不参与降权,直接强制保留。同时定期做人工抽样评估,每批抽20条,检查关键实体和论据有没有漏。

第四个坑是评测指标作弊。单一事实型问答做评测集,压缩器只需要学会“保留命名实体”就能拿高分,表面F1好看,但遇到需要跨段落推理的多跳问题立刻露馅。我在评测集里专门混合了几类问题:单点事实、多跳推理、否定问答、对比异同。只保留实体不保留推理链的压缩器,在多跳问题上的F1会掉得非常快。不要只盯着总分,把不同题型的分数分开看,才能暴露压缩器的真实能力。

6. 落到业务里:软压缩适合哪种RAG,不适合哪种

论文里的方案看着先进,落地时也得挑场景。我在团队内部做过好几轮适配评估,总结下来大概是这么个边界。

适合的场景有三个明显特征:文档长、数据噪声大、对token成本敏感。比如企业知识库问答,一篇文章动不动几千字,问的就是其中一两处细节;再比如多轮对话场景,每一轮都要重复塞背景文档,软压缩能把每一轮的上下文做得更精炼,多轮之下累积节省的token非常可观。软压缩器输出的重要度分数本身也有价值,我实际测试下来,把它当作检索结果的重排信号,能够把真正包含答案的文档往前提,这算是方案带来的额外甜点。

不适合的场景也很明确。如果检索出来的文档本身只有一两百token,压缩的收益微乎其微,纯粹增加时延,那还不如原样拼接。对需要逐字引用的法务条款、合同原文、审计记录这类场景,任何压缩都意味着风险,软压缩也不例外,改走“不压缩+引用标注”路线更稳妥。再就是交互延迟要求极高的在线服务,如果产品不允许在检索和生成之间再多一次模型调用,那当前阶段更现实的方案还是离线预压缩或者优化检索器本身。

工程上要真用起来,我给三个建议。第一,把软压缩器和缓存搭配起来,高频知识库条的压缩结果可以缓存复用,把在线成本摊薄;第二,检索时可以先多取一些候选文档,比如从top-10扩到top-30,交给压缩器按重要度分数筛到预算内,这往往比让检索器直接硬截断效果更好;第三,压缩器打分结果建议做成可观测数据,压掉的内容单独存一份日志,方便出问题时回溯。

这周精读OSCAR,我最实际的收获倒不是具体网络结构,而是把“压缩”这件事从“减少token数量”重新定义成“面向下游任务的软性信息选择”。一开始我也是条件反射地想到截断、摘要这些老路子,真正被软压缩打动是在一次对比实验里:同样的压缩率,硬截断把关键证据一刀切没了,软压缩反而靠着重要性加权保住了推理链,答案质量直接拉开了差距。这篇笔记写到这儿,我自己的下一步计划是把手头问答系统里的硬截断换掉,先用两阶段法训练一版压缩器,在模拟项目X上把压缩率、F1、延迟三个数都盯住,跑够一个评测周期后再决定是否进生产。如果你也在做RAG落地,建议别只看标题和摘要,拿这个思路去你自己的知识库上跑一轮评测,数据会告诉你它到底值不值得用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询