上下文蒸馏与长上下文压缩

Context Distillation / Compression

上下文压缩是 RAG 与长文本场景中,在把检索结果或历史对话喂给 LLM 之前,用轻量模型或算法把冗余信息过滤、精简、摘要,显著降低输入 Token 成本并提升答案事实性的技术。

详细解释

上下文压缩(Context Compression / Context Distillation) 是 RAG 系统最容易被忽视但性价比最高的一层。先看它解决的具体问题:假设 RAG 粗检索 ANN 召回 Top-20 条 chunk,每条 chunk 长度 1000 字符 → 喂给 LLM 约 20000 Token。这里面真正和”用户问题”相关的句子可能只有 5-8 句,剩下 15000 多 Token 全是”章节背景、表格前导文字、页脚、目录摘要、文档头部说明”等冗余内容。这些冗余不是”没用信息”,而是会带来三重致命问题:

  1. 成本浪费:20000 vs 4000 Token → 每次 RAG 请求输入费 5 倍差距;
  2. 分心效应(Distraction),也叫 Lost in the Middle(中间遗忘):2023 Stanford 论文证实,把答案放在 20 条 chunk 的第 10 条附近,LLM 的准确率从 86% 掉到 60%,因为它注意力被前后不相关的 chunk 给”晃晕了”;
  3. 幻觉概率上升:无关内容里相似但错误的事实(比如文档 A 说”张总负责销售”,文档 B 说”张总离职 2024 年”,但 B 在 ANN 召回了但和当前问题无关)混在上下文中,模型会拼出”已离职的张总仍然负责销售”这种幻觉。

上下文压缩的职责:在”RAG 粗检索”之后、“LLM 生成答案”之前,加一道”只保留和当前问题直接相关的 2% 至 20% 内容”的过滤网。

三类主流压缩器(按精度从低到高)

压缩器类型代表实现原理压缩后尺寸比例精度影响每百万 Token 压缩成本
① 轻量 Extractive 抽取(无额外 LLM 调用)LLM Lingua / LongLLMLingua用 7B 级别小模型,计算 context 中每个 Token 对于 question 的 PPL / 条件熵,保留高信息 Token;或用 Cross-Encoder 做 sentence 级打分,低于阈值删句原文的 20% 至 35%F1 下降 1% 以内约 ¥0.1(远低于省下来的输入费)
② Reranker 句子级过滤 + 摘要BGE-Reranker-v2 / bge-m3 先打分再取 TOP-N 句先把 chunk 切成句子级,Cross-Encoder 对 (question, each_sentence) 打分;只保留前 8 至 15 句;超过阈值的句子用 T5 小模型做 1 句摘要原文的 10% 至 25%F1 上升 +2%(因为噪音变少了)约 ¥0.2
③ LLM 蒸馏式压缩(让小 LLM 重写只保留问答相关信息)GPT-4o-mini / Claude 3.5 Haiku 当”压缩器”Prompt:“以下是 N 段文档片段,保留所有与用户问题 XX 直接相关的实体、数字、引用、日期、结论,其余全部删除;若某段完全不相关则返回关键字 SKIP。”原文的 5% 至 15%F1 最高(+3% 到 +5%)约 ¥0.8

2025 年真实业务主流选择是” ② + ① 组合”:先用 Cross-Encoder 把 20 条 chunk 切成 400 个句子打分,取前 15 句;再让 LLM Lingua 二次压缩,把这 15 句里 30% 冗余短语进一步去掉 → 最终上下文从 20k Token 缩到 2-3k,准确率不降反升(Lost in the Middle 效应被大幅缓解)。

唯元智创 RAG 的 Context Compression 默认流水线

唯元智创 控制台的 RAG 知识库默认开启的压缩链路如下(用户可一键开关、自定义每步阈值):

  1. Query Rewrite 前置:用 HyDE 假设文档 + Query2doc 改写用户原始 query(比如”这个月总营收多少”→ 加上”2026年8月财务报表总营收亿元”的关键词)。
  2. ANN Top-K=50 粗检索:召回 50 条 chunk(HNSW ef_search=120)。
  3. Cross-Encoder Rerank Top-6:BGE-Reranker-v2-M3 对 50 条精排。
  4. Sentence 级压缩器:把 6 条 chunk 切成句子 → 用 Reranker 对(question, each sentence)打分 → 只保留 top-12 句且 score > 0.1。
  5. 去重与拼接:对 12 句做去重(余弦相似度 0.95 以上合并),按原文顺序拼接,附带给 LLM 每条出处的 metadata(doc_title/section/page_no)。

上线客户的平均数据:Token 压缩比为 3.7:1,即 RAG 输入从 18k Token 降到 4.9k;事实准确率提升 +4.1 个百分点(人工评测 500 条 RAG 问答集),成本节省 55%。

什么时候不该做 Context Compression

有三类业务场景,过度压缩反而会害你:

  1. 法律 / 合规 / 医疗类要原文引用:你需要输出原文摘录段落 + 原文页码出处,这种场景压缩器的任何一个字改动都可能被对方律师质疑——应当”只做 chunk 级 Top-3 选择,不做句级改动”,把原文整段丢给 LLM。
  2. 代码生成 / SQL 生成:压缩器会把代码里的注释、空行、边缘条件判断删掉,LLM 拿到只剩骨架的代码后,最容易写出”核心逻辑对但少了一个 try-catch 导致线上崩”的代码。应当用 Rerank 选 3 个完整 chunk 丢进去。
  3. 长文生成(写书 / 报告 / 综述类需要 3000+ 字输出):LLM 需要用上下文里的”背景段落”做长文结构规划,被抽只剩事实句,它会写不出过渡段落,整个长文像问答题列表。这种场景建议做”层次化压缩”:保留章节标题,正文保留事实句,标题树结构完整保留。

常见问题

Context Compression 和 Rerank 的区别是什么?两个都要做吗?
职责不同:Rerank 是”从 50 条 chunk 里选最相关的 5 条”(chunk 级选段,不改动内容);Context Compression 是”在选中的 5 条 chunk 里,把每条中与 question 无关的那部分句子/词语删掉”(内容级裁剪)。最正确的顺序是:ANN 粗 → Rerank 选段 → Compression 裁句。只做 Rerank 不做 Compression,你会把”最相关的 5 条整段”都塞给 LLM,里面还是有废话。先做 Compression 再做 Rerank 会丢失信息,顺序反了。
长对话多轮历史怎么压缩?不是 RAG。
对话历史压缩走不同的套路,不推荐用上述的抽取压缩器(容易把用户意图抽没)。主流 3 种办法:(1)滚动窗口保留最近 N 轮 —— 最稳妥,保留最近 8 轮;(2)摘要压缩(Summarization Distillation):每 8 轮就让小模型把历史对话写成一段”对话至此的上下文摘要”,单独作为一条 system message 放系统提示区;(3)向量摘要:对每轮对话打 embedding 存向量库,需要历史事实时再做”语义搜索取最近 3 轮相关的”。真正上线一般是(1)+(2)组合:最近 12 轮原样保留,之前的 20 轮压成一段摘要,再之前的丢进向量库按需检索。
压缩器会不会把关键信息(尤其是数字)给压没?
早期版本的 LLM Lingua 会,所以 2025 年的压缩器都会加”数字与实体保护”规则。通用做法:在压缩前先用 spaCy / LAC / HanLP 把 chunk 中”日期 / 金额 / 百分比 / 人名 / 公司名 / ISBN / 法律条款号”这类专有实体识别出来,打一个”禁止删除”的保护标签;任何压缩器在做 Token 或句子删除时,如果该 Token 命中保护实体,强制保留;或者如果压缩后实体覆盖率 < 100%,自动回滚到不压缩该段。加了这条保护之后,数字 / 实体丢失率在真实业务数据下从 2.3% 降到 0.08%,几乎不会再犯”把 38.5% 压成 38%“这种低级错。