AI 工程基础体系 · 第 92/100 篇。内容覆盖机器学习、深度学习与生成式 AI;模型、数据、评测、权限和成本会作为同一生产系统处理。

RAG 上下文压缩:去重、摘要、证据选择与信息损失

1. 为什么 RAG 需要压缩上下文

RAG(Retrieval-Augmented Generation,检索增强生成)把外部知识检索结果提供给生成模型,而不是只依赖模型参数中的知识。经典 RAG 结构通常包含两个阶段:

  1. 检索器根据用户问题找到相关文档或文档片段;
  2. 生成器根据问题和检索结果生成答案。

Lewis 等人在 RAG 论文中将这种方法描述为:利用外部非参数记忆补充生成模型的参数化记忆,从而改善知识密集型任务中的事实性和可更新性。RAG 论文

但检索结果不能无限增加。上下文窗口存在长度上限,即使模型支持很长的上下文,输入 token 增加也会带来成本、延迟和注意力稀释问题。于是系统通常需要把原始检索结果转换为更短的上下文:

C=f(C,q,b)C' = f(C, q, b)

其中:

  • CC 是检索到的原始证据集合;
  • qq 是用户问题;
  • bb 是允许使用的 token 或字符预算;
  • ff 是上下文压缩过程;
  • CC' 是最终交给生成模型的压缩上下文。

这里的“压缩”不是单纯删除文本,而是在预算约束下保留对当前问题有用的信息。去重、摘要和证据选择都是不同的压缩操作:

  • 去重:删除表达同一事实的重复内容;
  • 摘要:用更短的新文本重述多个原文;
  • 证据选择:只保留能够支持答案的原文片段;
  • 信息损失:压缩后无法从 CC' 恢复、判断或证明的原始信息。

压缩的核心目标不是让文本尽可能短,而是让答案质量在更低成本下尽可能接近未压缩上下文。


2. 上下文压缩的形式化目标

设最终答案为 aa,理想答案依赖问题 qq 和知识集合 CC

a=Answer(q,C)a^* = \operatorname{Answer}(q, C)

压缩后生成器实际看到的是 CC'

a^=Generate(q,C)\hat{a} = \operatorname{Generate}(q, C')

一个基本优化问题可以写成:

maxCU(q,C,a^)\max_{C'} U(q, C', \hat{a})

约束为:

tokens(C)b\operatorname{tokens}(C') \leq b

其中 UU 不应只表示文本相似度,而应至少包含以下因素:

U=λ1AnswerCorrectness+λ2EvidenceSupportλ3Latencyλ4Costλ5RiskU = \lambda_1 \cdot \text{AnswerCorrectness} +\lambda_2 \cdot \text{EvidenceSupport} -\lambda_3 \cdot \text{Latency} -\lambda_4 \cdot \text{Cost} -\lambda_5 \cdot \text{Risk}

各项含义如下:

  • AnswerCorrectness:答案是否正确;
  • EvidenceSupport:答案中的关键断言是否能被压缩后的证据支持;
  • Latency:压缩与生成带来的延迟;
  • Cost:嵌入、重排、压缩模型和生成模型的调用成本;
  • Risk:权限泄露、摘要幻觉、版本混淆等风险。

这说明“token 越少越好”是错误目标。若删除了一个否定词、版本号或适用条件,文本虽然更短,答案质量却可能下降。

2.1 有损压缩与问题相关充分性

如果压缩后的上下文 CC' 保留了回答问题所需的全部信息,则可以近似认为:

P(aq,C)P(aq,C)P(a \mid q, C') \approx P(a \mid q, C)

这是一种“问题相关充分性”。它不要求 CC' 保留原文所有信息,只要求对当前问题而言,答案所需的信息没有丢失。

例如,原文是:

在生产环境中,缓存默认启用;但是当租户开启强一致模式时,缓存必须关闭。该设置仅在 3.2 及以上版本生效。

问题是:

强一致模式下能否使用缓存?

一个安全的压缩结果至少需要保留:

强一致模式下必须关闭缓存。

如果压缩结果是:

生产环境默认启用缓存。

它保留了一个一般规则,却丢失了当前问题需要的例外条件,因此对该问题不是充分的。

2.2 不可逆压缩的基本限制

摘要通常是不可逆的。设压缩函数为 ff,若存在两个不同的原始上下文 C1C_1C2C_2,使得:

f(C1)=f(C2)f(C_1) = f(C_2)

但对某个问题 qq

Answer(q,C1)Answer(q,C2)\operatorname{Answer}(q, C_1) \ne \operatorname{Answer}(q, C_2)

则仅依赖压缩结果无法保证正确回答该问题。

例如:

  • C1C_1:退款申请可在 30 天内提交;
  • C2C_2:退款申请可在 7 天内提交。

若摘要器把两者都压缩成“用户可以申请退款”,那么这个摘要无法回答“期限是多少”。这不是生成器能力不足,而是压缩阶段已经合并了两个答案不同的世界状态。

因此,生产系统不能把摘要视为原文的等价替代品。摘要是一个有条件的表示:它只在预期问题分布和指定任务范围内有效。


3. 去重:减少冗余,不等于解决冲突

3.1 什么是重复

RAG 检索结果中常见三类重复:

  1. 完全重复:同一片段被多个检索路径返回;
  2. 文本近重复:文档版本、网页模板或切块边界略有差异;
  3. 语义重复:不同措辞表达同一事实。

例如:

片段 A:服务超时后,客户端会自动重试三次。
片段 B:发生超时时,客户端默认最多进行 3 次重试。

A 和 B 可能是语义重复。

但以下内容不能因为主题相同就去重:

片段 A:客户端默认重试三次。
片段 B:批处理任务最多重试五次。

它们讨论的是不同操作范围。去重前必须识别实体、条件、时间、版本、动作和数值。

3.2 精确去重

精确去重通常对规范化后的文本计算哈希:

hi=Hash(Normalize(xi))h_i = \operatorname{Hash}(\operatorname{Normalize}(x_i))

Normalize 可以处理:

  • Unicode 规范化;
  • 空白字符合并;
  • 统一大小写;
  • 去除不影响语义的页面模板;
  • 规范化明显的标点差异。

但不能随意删除数字、否定词、版本号或单位。例如把 3.2332 处理成同一字符串,可能导致严重错误。

精确去重的优点是速度快、可解释性强、不会因为模型误判而合并不同事实。缺点是无法处理轻微改写。

3.3 近重复去重

近重复去重可以用词法相似度或向量相似度。设两个片段向量为 viv_ivjv_j,余弦相似度为:

sim(i,j)=vivjvivj\operatorname{sim}(i,j) = \frac{v_i \cdot v_j} {\|v_i\|\|v_j\|}

当相似度超过阈值 τ\tau 时,可以把它们视为候选重复:

sim(i,j)τ\operatorname{sim}(i,j) \geq \tau

但这只是候选条件,不是充分条件。高相似度可能来自:

  • 同一主题但不同版本;
  • 同一流程但不同角色;
  • 同一政策但不同地区;
  • 正常规则与例外规则;
  • 事实和对事实的否定。

更稳妥的流程是:

  1. 先按租户、权限、文档类型和版本分组;
  2. 对文本做精确去重;
  3. 对候选近重复进行相似度计算;
  4. 比较元数据中的 source_idversioneffective_atscope
  5. 只有在语义和适用范围都一致时才合并;
  6. 若存在版本或条件差异,则保留并标记为可能冲突。

3.4 去重时的代表片段选择

多个重复片段需要选择一个代表片段。代表片段不应只按检索分数最高选择,还应考虑:

  • 来源权威性;
  • 更新时间;
  • 版本是否适用于当前请求;
  • 是否包含完整条件;
  • 是否保留原始定位信息;
  • 是否可被权限策略允许使用。

可以将代表片段评分写成:

S(xi)=αRi+βAi+γFi+δPiϵOiS(x_i) = \alpha R_i +\beta A_i +\gamma F_i +\delta P_i -\epsilon O_i

其中:

  • RiR_i:检索或重排相关性;
  • AiA_i:来源权威性;
  • FiF_i:条件完整性;
  • PiP_i:权限和适用范围匹配度;
  • OiO_i:冗余程度。

必须保留被合并片段的来源列表,而不能只保留代表文本。否则生成答案无法回溯到原始证据,出现争议时也无法判断去重是否错误。


4. 摘要:用更短文本重建答案相关状态

4.1 摘要的三种基本形式

抽取式摘要

抽取式摘要直接选择原文中的句子或句子片段:

原文:
缓存默认启用。强一致模式下必须关闭缓存。该配置在 3.2 及以上版本生效。

抽取结果:
强一致模式下必须关闭缓存。

它的优点是可追溯、较少引入新事实,适合政策、接口约束、错误码和法律文本。缺点是压缩率有限,句子之间可能缺少必要上下文。

生成式摘要

生成式摘要由模型重新表述:

摘要:
从 3.2 版本开始,强一致模式不能使用缓存。

它的压缩率通常更高,但可能发生:

  • 换词后改变条件;
  • 丢失例外;
  • 合并不同版本;
  • 引入原文没有的因果关系;
  • 把不确定性写成确定性;
  • 把多个主体的规则混成一条规则。

查询感知摘要

查询感知摘要把问题 qq 作为输入:

si=Summarize(xi,q)s_i = \operatorname{Summarize}(x_i, q)

它不是总结文档“讲了什么”,而是总结“文档中哪些内容与当前问题有关”。对 RAG 来说,查询感知摘要通常比通用摘要更节省 token,但也更依赖问题理解。

例如原文同时包含安装、升级、回滚和监控信息。问题是“如何回滚”,查询感知摘要只保留回滚步骤和前置条件,而不是平均压缩所有主题。

4.2 摘要必须保留的语义槽位

对于技术文档,安全摘要通常需要显式保留以下槽位:

  • 对象:哪个服务、资源或用户;
  • 动作:允许、禁止、创建、删除、重试;
  • 条件:在什么情况下成立;
  • 例外:哪些情况下不成立;
  • 数值:上限、下限、次数、时间;
  • 单位:秒、分钟、字节、百分比;
  • 版本:从哪个版本开始或在哪个版本失效;
  • 范围:租户、地区、环境、角色;
  • 时间:生效时间和截止时间;
  • 证据来源:原文位置和文档版本。

可以把一条事实表示为:

f=(e,r,a,c,v,t,s)f = (e, r, a, c, v, t, s)

其中:

  • ee:实体;
  • rr:关系;
  • aa:属性或动作;
  • cc:条件;
  • vv:值;
  • tt:时间或版本;
  • ss:适用范围。

摘要只有在保留回答所需的槽位时才安全。例如:

原文:
管理员可以删除未绑定资源。已绑定资源必须先解除绑定,删除操作才会成功。

不安全摘要:
管理员可以删除资源。

较安全摘要:
管理员只有在资源未绑定时才能删除;已绑定资源必须先解除绑定。

第一种摘要丢失了条件,可能让模型生成越权或失败的操作建议。

4.3 摘要链的误差累积

如果系统先对文档摘要,再对摘要继续摘要,信息损失通常不是线性累积。设每一层保留关键事实的概率为 pp,经过 kk 层后,一条事实仍被保留的近似概率为:

pkp^k

p=0.98p=0.98,经过 5 层后约为:

0.9850.9040.98^5 \approx 0.904

这只是独立近似,真实情况还会受到事实之间依赖关系的影响。更严重的是,一层摘要可能把事实改写成错误事实,后续摘要会把这个错误当成输入继续传播。

因此,多级摘要应该保存:

  • 每条摘要句对应的原文片段;
  • 摘要使用的模型和版本;
  • 摘要时间;
  • 事实级校验结果;
  • 无法确定的内容,而不是强行补全。

5. 证据选择:不是选最相关文本,而是覆盖答案所需断言

5.1 相关性与证据性不同

检索相关性回答的是:

这段文本与问题主题相似吗?

证据性回答的是:

这段文本能否支持答案中的某个具体断言?

例如问题是:

如何在 Linux 上配置服务自动启动?

片段 A:

本服务支持 Linux、macOS 和 Windows。

片段 B:

在 Linux 上执行 systemctl enable myservice 可设置开机启动。

片段 A 与问题有较高主题相关性,但片段 B 才是直接证据。

因此,证据选择不能只依赖向量相似度。通常需要结合:

  • 初始向量检索;
  • 关键词或实体匹配;
  • 重排序模型;
  • 句子级或段落级相关性;
  • 对答案断言的覆盖分析。

5.2 从答案断言反推证据

设一个候选答案由断言集合组成:

A={a1,a2,,am}A = \{a_1, a_2, \ldots, a_m\}

每个证据片段 xix_i 能支持其中一部分断言。定义覆盖函数:

cover(xi,aj){0,1}\operatorname{cover}(x_i, a_j) \in \{0,1\}

选择证据可以近似成带预算的最大覆盖问题:

maxSj=1mwj1[xiS:cover(xi,aj)=1]\max_{S} \sum_{j=1}^{m} w_j \cdot \mathbf{1} \left[ \exists x_i \in S: \operatorname{cover}(x_i,a_j)=1 \right]

约束为:

xiStokens(xi)b\sum_{x_i \in S} \operatorname{tokens}(x_i) \leq b

其中 wjw_j 是断言重要性。数字、权限、删除操作和安全限制通常应该比背景介绍拥有更高权重。

实际系统未必先生成最终答案,再选择证据,因为这样会引入“先猜答案、再寻找支持”的偏差。更安全的做法是:

  1. 从问题识别需要回答的方面;
  2. 对候选片段提取可验证断言;
  3. 选择能够覆盖这些方面的证据;
  4. 让生成器只基于已选证据作答;
  5. 对生成答案进行断言级引用检查。

5.3 最大边际收益选择

如果已经选择了证据集合 SS,新片段 xx 的价值不应只看自身相关性,还应看它提供了多少新信息:

Gain(xS)=Relevance(x,q)+λNewCoverage(x,S)μRedundancy(x,S)\operatorname{Gain}(x \mid S) = \operatorname{Relevance}(x,q) + \lambda \cdot \operatorname{NewCoverage}(x,S) - \mu \cdot \operatorname{Redundancy}(x,S)

这解释了为什么一个相关性稍低、但补充了“例外条件”的片段,可能比第三个重复说明片段更值得保留。

一个简单的选择过程如下:

  1. 先选能支持核心断言且来源可靠的片段;
  2. 计算每个剩余片段新增的断言覆盖量;
  3. 在预算内反复选择边际收益最高者;
  4. 若新片段与已选片段冲突,则进入冲突处理,而不是直接丢弃;
  5. 如果预算不足,优先保留限制条件、数值、版本和反例。

5.4 证据之间的关系

证据不总是独立的。常见关系包括:

  • 支持:两个片段共同证明同一结论;
  • 补充:一个片段给出步骤,另一个给出前置条件;
  • 限定:一个片段说明一般规则,另一个片段说明例外;
  • 冲突:不同版本或来源给出不同值;
  • 依赖:片段 B 的结论只有在片段 A 的条件成立时有效。

如果系统只选择单个最高分片段,可能丢失这种关系。例如:

片段 A:API 默认启用缓存。
片段 B:对于强一致请求,必须通过请求头显式禁用缓存。

片段 B 不是片段 A 的重复,而是对它的限定。正确上下文应同时保留两者,并明确优先级和条件。


6. 去重、摘要和证据选择的组合顺序

一个常见的压缩流水线如下:

flowchart LR
    Q[用户问题] --> R[召回候选片段]
    R --> P[权限与版本过滤]
    P --> D[精确/近重复去重]
    D --> X[冲突与条件分析]
    X --> S[重排与证据选择]
    S --> C[查询感知压缩]
    C --> V[断言与引用校验]
    V --> G[生成答案]
    G --> E[答案评测与日志]

关键路径中的顺序并非绝对固定,但有几个重要约束。

6.1 权限过滤必须早于压缩

如果先把不同权限范围的文档放在一起摘要,再在摘要结果上做权限过滤,可能发生跨租户泄露:

租户 A 文档:A 的配额为 100。
租户 B 文档:B 的配额为 1000。

摘要器可能输出:

该服务的配额为 1000。

此时已经无法判断这个数字来自哪个租户。权限、租户、地域和数据敏感级别应在检索结果进入共享压缩步骤前完成过滤或隔离。

6.2 去重通常早于摘要

若先摘要再去重,两个重复片段可能被模型改写成略有差异的句子,反而增加去重难度。先去重可以:

  • 减少摘要调用;
  • 降低重复内容造成的注意力浪费;
  • 避免同一事实被模型写出多个不一致版本;
  • 保留更清晰的来源映射。

但如果文本来自不同版本,不能只因相似就合并。版本冲突应先被识别和保留,摘要阶段可以将其表达为:

3.1 版本的文档要求 7 天,3.2 版本开始改为 30 天;当前请求使用 3.2 版本。

6.3 证据选择通常早于生成式摘要

如果对所有候选片段都做摘要,系统会把成本花在最终不会使用的内容上。更合理的路径是:

  1. 先通过重排和证据选择缩小候选集;
  2. 再对选中的长片段做压缩;
  3. 保留原文引用和定位;
  4. 重新验证压缩结果是否覆盖原证据中的关键断言。

不过,句子级抽取式压缩可以在重排前使用,因为它本身不容易引入新事实。


7. 一个完整的压缩算例

假设用户问题是:

3.2 版本中,强一致模式下缓存和重试策略是什么?

检索得到四个片段:

D1:
服务默认启用缓存。客户端在网络超时后自动重试 3 次。

D2:
在强一致模式下,所有读请求必须绕过缓存,以避免读取到旧数据。

D3:
3.2 版本将网络超时重试次数从 3 次调整为 1 次。
D2 适用于 3.2 及以上版本。

D4:
对于写请求,即使启用强一致模式,也不会自动重试,因为重试可能导致重复写入。

第一步:识别事实

从四个片段提取事实:

  • f1f_1:服务默认启用缓存;
  • f2f_2:强一致模式下读请求绕过缓存;
  • f3f_3:3.2 版本网络超时重试 1 次;
  • f4f_4:3.2 之前网络超时重试 3 次;
  • f5f_5:写请求不自动重试;
  • f6f_6:强一致模式的缓存规则适用于 3.2 及以上版本。

第二步:识别问题需要的断言

问题要求两个方面:

  • 缓存策略;
  • 重试策略。

其中重试策略还包含请求类型和版本条件。因此至少需要覆盖:

A={acache,aread-retry,awrite-retry,aversion}A = \{a_{\text{cache}}, a_{\text{read-retry}}, a_{\text{write-retry}}, a_{\text{version}}\}

第三步:去重和关系分析

D1 与 D3 都提到重试次数,但不是重复:

  • D1 描述默认行为;
  • D3 描述 3.2 版本变更。

D2 和 D3 通过版本条件互相补充。D4 是写请求的例外或独立规则,不能删除。

第四步:证据选择

若预算只能保留三个片段,最合理的组合是:

  • D2:支持强一致模式下的缓存规则;
  • D3:支持 3.2 版本的重试次数和版本条件;
  • D4:支持写请求不重试的限制。

D1 可以被删除,因为它的“默认缓存”和“重试 3 次”对当前版本问题不再是最终规则;但如果答案需要解释版本变化,则可保留 D1 作为历史对照。

第五步:查询感知压缩

安全的压缩结果可以是:

[证据 D2]
强一致模式下,读请求必须绕过缓存,以避免读取旧数据。

[证据 D3]
从 3.2 版本开始,网络超时的自动重试次数调整为 1 次;该强一致模式规则适用于 3.2 及以上版本。

[证据 D4]
写请求不会自动重试,因为重试可能导致重复写入。

第六步:生成答案

基于该上下文,答案应为:

在 3.2 及以上版本,强一致模式下读请求必须绕过缓存。网络超时时,默认自动重试 1 次;写请求不会自动重试,以避免重复写入。

这个答案同时保留了:

  • 模式条件;
  • 请求类型;
  • 版本条件;
  • 重试数值;
  • 写请求例外;
  • 行为原因。

如果压缩时只保留 D3,模型可能回答“重试 1 次”,却无法知道写请求不适用自动重试,也无法回答缓存规则。


8. 一个可运行的简化证据选择示例

下面的 Python 示例不调用模型,使用词集合近似相关性和冗余,目的是展示“相关性 + 新覆盖 + token 预算”的选择机制。生产系统通常会把词法相似度替换为向量、重排模型和断言级校验。

from dataclasses import dataclass
import re

@dataclass
class Chunk:
    chunk_id: str
    text: str
    tokens: int
    facts: set[str]
    score: float

def words(text: str) -> set[str]:
    # 仅用于演示;中文生产分词应使用合适的分词器或向量模型
    return set(re.findall(r"[A-Za-z0-9_.-]+|[\u4e00-\u9fff]", text))

def select_evidence(chunks: list[Chunk], query: str, budget: int) -> list[Chunk]:
    query_terms = words(query)
    selected = []
    remaining = chunks[:]
    used = 0
    covered_facts = set()

    while remaining:
        candidates = []

        for chunk in remaining:
            if used + chunk.tokens > budget:
                continue

            relevance = len(words(chunk.text) & query_terms)
            new_facts = len(chunk.facts - covered_facts)
            redundancy = len(chunk.facts & covered_facts)

            # 权重只是示例,不代表通用生产参数
            value = (
                2.0 * relevance
                + 4.0 * new_facts
                + 1.0 * chunk.score
                - 2.0 * redundancy
            )
            candidates.append((value, chunk))

        if not candidates:
            break

        _, best = max(candidates, key=lambda item: item[0])
        selected.append(best)
        used += best.tokens
        covered_facts.update(best.facts)
        remaining.remove(best)

    return selected

chunks = [
    Chunk(
        "D1",
        "服务默认启用缓存。客户端在网络超时后自动重试 3 次。",
        tokens=18,
        facts={"默认启用缓存", "超时重试3次"},
        score=0.9,
    ),
    Chunk(
        "D2",
        "强一致模式下,读请求必须绕过缓存。",
        tokens=13,
        facts={"强一致读请求绕过缓存"},
        score=0.95,
    ),
    Chunk(
        "D3",
        "3.2 版本开始,网络超时自动重试次数调整为 1 次。",
        tokens=16,
        facts={"3.2后超时重试1次", "版本条件"},
        score=0.98,
    ),
    Chunk(
        "D4",
        "写请求不会自动重试,以避免重复写入。",
        tokens=12,
        facts={"写请求不自动重试", "避免重复写入"},
        score=0.92,
    ),
]

query = "3.2 版本中,强一致模式下缓存和重试策略是什么?"
selected = select_evidence(chunks, query, budget=45)

print([chunk.chunk_id for chunk in selected])
print("tokens =", sum(chunk.tokens for chunk in selected))

在给定的权重和预算下,预期会优先选择能覆盖强一致缓存、3.2 版本重试和写请求限制的片段,而不是简单选择文本相似度最高的片段。这个示例有三个明确限制:

  1. 它没有理解“默认规则”和“版本例外”的逻辑关系;
  2. 它没有检测事实冲突;
  3. 它把每个 facts 集合当作人工标注,实际系统需要由解析器、模型或标注数据产生。

因此,它适合说明选择算法,不适合直接作为生产证据判定器。


9. 信息损失的类型与失败表现

9.1 事实删除

原文包含多个事实,压缩后只剩一部分:

原文:请求超时后最多重试 3 次,写请求除外。
摘要:请求超时后最多重试 3 次。

失败表现是模型把“写请求除外”误用于所有请求。诊断方法是比较原文和压缩文本中的断言集合,检查高重要性断言是否有对应证据。

9.2 条件删除

原文:仅当资源未绑定时,管理员才能删除资源。
摘要:管理员可以删除资源。

这是 RAG 中危险度较高的损失,因为条件往往决定权限和操作是否合法。

9.3 否定反转

原文:该字段不支持为空。
摘要:该字段支持为空。

生成式摘要、文本清洗和句子截断都可能造成否定词丢失。安全检查应把“不、禁止、不得、除非、仅当、不能”等词列为高风险标记。

9.4 数值和单位损失

原文:请求体上限为 10 MB,单个文件上限为 2 MB。
摘要:请求大小有限制。

如果用户问“单个文件最大多大”,该摘要完全不够用。数值、范围和单位应该作为不可随意删除的结构化字段处理。

9.5 时间与版本损失

原文:3.1 版本使用旧认证流程,3.2 版本开始改用 OAuth 2.0。
摘要:系统使用 OAuth 2.0。

当用户实际部署的是 3.1 版本时,这个摘要会产生错误配置建议。版本和生效时间必须与事实绑定,而不能作为文档级元数据在压缩时丢弃。

9.6 来源和可追溯性损失

如果压缩后只保留:

缓存必须关闭。

而没有 source_id、章节、页码、版本或原文偏移量,生成答案即使看似正确,也无法完成审计。证据引用不是装饰,它是发现摘要错误、处理文档冲突和支持人工复核的必要状态。

9.7 冲突被“平均化”

两个来源分别写道:

旧文档:超时时重试 3 次。
新文档:超时时重试 1 次。

不安全的摘要可能生成:

超时时通常重试 1 到 3 次。

这句话看似兼容,实际上掩盖了版本冲突,无法指导具体操作。遇到冲突时,系统应优先按照适用版本、发布时间、权威级别和明确的废弃关系处理;不能用模糊措辞代替冲突解析。


10. 如何评测压缩是否安全

只测最终答案准确率不够,因为答案偶然正确并不代表压缩过程可靠。至少需要分别评估召回、压缩和生成。

10.1 检索层指标

  • Recall@k:前 kk 个结果是否包含必要证据;
  • MRR 或 nDCG:相关证据是否排在前面;
  • 权限过滤准确率:是否错误包含无权访问内容;
  • 版本匹配率:是否选用了当前适用版本。

如果必要证据根本没有召回,后面的压缩器无法补救。

10.2 压缩层指标

定义原始必要事实集合为 FF,压缩后仍可验证的事实集合为 FF'。可以计算事实保留率:

Retention=FFF\operatorname{Retention} = \frac{|F' \cap F|}{|F|}

同时还要计算新事实率:

UnsupportedRate=压缩文本中无原文支持的事实压缩文本中的全部事实\operatorname{UnsupportedRate} = \frac{ |\text{压缩文本中无原文支持的事实}| }{ |\text{压缩文本中的全部事实}| }

理想压缩不仅要保留关键事实,还要避免凭空增加事实。

其他有用指标包括:

  • 压缩比;
  • 平均输入 token;
  • 摘要延迟;
  • 摘要失败率;
  • 引用覆盖率;
  • 关键约束丢失率;
  • 冲突检测召回率。

10.3 端到端指标

最终应在固定问题集上比较三种输入:

  1. 原始检索上下文;
  2. 仅去重后的上下文;
  3. 去重、选择和摘要后的上下文。

比较:

  • 答案正确率;
  • 事实一致性;
  • 引用正确率;
  • 拒答正确率;
  • 延迟和成本;
  • 不同问题类型上的退化情况。

问题集必须包含容易被压缩破坏的样本:

  • 否定;
  • 例外;
  • 版本迁移;
  • 数值限制;
  • 多步骤操作;
  • 权限差异;
  • 相互冲突的文档;
  • 需要组合多个片段才能回答的问题。

10.4 诊断矩阵

当压缩后答案错误时,可按以下路径定位:

现象 可能原因
必要文档不在候选集 召回失败、切块不合理、查询改写错误
候选集中有证据但未选中 重排或覆盖算法失败
选中了证据但摘要丢条件 摘要器未保留语义槽位
摘要正确但答案错误 生成器未遵循证据、提示词冲突
答案引用错误来源 provenance 映射丢失或片段合并错误
不同租户出现相同摘要 权限过滤晚于压缩或缓存隔离失败
新旧版本被合并 版本元数据未参与去重

这种分层诊断比只观察最终“答错了”更容易修复系统。


11. 生产系统中的状态、并发与故障路径

上下文压缩最好作为有状态的流水线处理,而不是一个无记录的字符串函数。一个压缩任务至少应记录:

request_id
tenant_id
query
retrieved_chunk_ids
permission_policy_version
dedup_groups
selected_chunk_ids
compressed_text
source_spans
compressor_model
compressor_prompt_version
input_tokens
output_tokens
validation_result

11.1 并发执行

对多个独立候选片段做摘要时,可以并发处理,但应注意:

  • 每个任务必须携带自己的来源 ID;
  • 超时不能把未完成片段误认为“无相关内容”;
  • 结果合并应按确定性规则排序;
  • 取消请求后应停止未开始的模型调用;
  • 同一文档版本的合并任务要避免重复执行。

如果压缩服务部分失败,系统可以采用降级路径:

  1. 使用已完成的抽取式证据;
  2. 缩小证据集合后直接交给生成器;
  3. 若关键证据缺失,则拒答或说明无法确认;
  4. 不能用空摘要替代失败结果并继续生成确定性答案。

11.2 缓存

可以缓存文档级摘要,但缓存键必须包含影响结果的因素:

K=(doc_version,query_class,permission_scope,compressor_version)K = (\text{doc\_version}, \text{query\_class}, \text{permission\_scope}, \text{compressor\_version})

不能只使用文档 ID 作为键。否则会出现:

  • 文档更新后仍返回旧摘要;
  • 不同问题得到同一份不适用摘要;
  • 不同租户共享不应共享的结果;
  • 摘要器升级后读取旧格式。

查询感知摘要的缓存命中率通常低于通用摘要,但它更贴近当前问题。二者应根据延迟、文档稳定性和问题重复度分别设计。

11.3 超时和预算耗尽

压缩流程应有独立预算:

  • 候选片段数预算;
  • 总 token 预算;
  • 摘要模型调用次数预算;
  • 总时间预算;
  • 单片段最大长度。

当预算耗尽时,不应随机截断上下文。优先级通常应为:

  1. 权限和安全约束;
  2. 直接支持核心答案的证据;
  3. 例外和限制条件;
  4. 数值、版本和时间;
  5. 背景说明和重复解释。

这不是普遍适用的固定排序,而是面向事实问答和技术操作的一种风险优先策略。医疗、法律或安全领域可能需要更保守地保留完整原文。


12. 成本取舍:压缩不一定降低总成本

设一次请求的成本近似为:

TotalCost=RetrievalCost+RerankCost+CompressionCost+GenerationCost\text{TotalCost} = \text{RetrievalCost} + \text{RerankCost} + \text{CompressionCost} + \text{GenerationCost}

如果压缩模型调用成本高、延迟大,而原始上下文本身并不长,压缩可能增加总成本。只有当节省的生成 token 成本和延迟收益超过压缩开销时,压缩才有经济意义:

ΔGenerationCost>CompressionCost\Delta \text{GenerationCost} > \text{CompressionCost}

常见的分层策略是:

  • 短上下文:只做精确去重;
  • 中等上下文:做去重和句子级证据选择;
  • 长上下文:再使用查询感知摘要;
  • 高风险问题:优先抽取原文,不使用未经校验的生成式摘要;
  • 高频稳定文档:预计算文档级摘要,并保留原文回退路径。

压缩比例也不应作为单独 KPI。将上下文压缩 90% 可能意味着删除了大量重复导航文字,也可能意味着删除了关键条件;必须和事实保留率、答案正确率及引用覆盖率一起观察。


13. 常见误解与边界

误解一:相似度高的片段就是重复片段

相似度只表示表示空间中的接近,不代表逻辑等价。不同版本、不同主体和不同例外可能拥有很高的相似度。去重必须结合实体、条件、时间和适用范围。

误解二:摘要比原文更容易被模型理解,所以总是更好

摘要更短,但它是二次解释。原文中的结构、措辞和限定条件可能在重述中丢失。对删除、权限、财务、配置和安全问题,原文证据往往比漂亮的摘要更可靠。

误解三:把 top-k 调小就等于上下文压缩

减小 kk 是减少候选片段数量,不是压缩片段内容,也不保证保留互补证据。若多个片段分别包含前置条件、步骤和例外,简单截断会破坏完整答案。

误解四:生成器可以从常识中补回被删信息

如果压缩结果删除了版本号或例外,生成器可能凭训练数据猜测一个看似合理的答案,但这不是从当前知识库推导出的事实。RAG 的证据约束不能依赖生成器“自行补全”。

误解五:只要答案正确,压缩过程就是正确的

答案可能因为模型先验、偶然猜测或训练数据记忆而正确。生产系统还需要保证答案可追溯、权限正确、版本适用,并能在文档更新后稳定变化。


14. 实用的设计边界

对于低风险、事实稳定、重复度高的文档,可以使用:

权限过滤 → 精确去重 → 轻量证据选择 → 原文拼接

对于上下文很长、问题明确、文档结构稳定的场景,可以使用:

权限过滤 → 近重复聚类 → 查询感知证据选择 → 抽取式或受约束摘要 → 引用校验

对于版本敏感或高风险场景,应保守处理:

权限过滤 → 版本筛选 → 冲突检测 → 原文证据选择
→ 只做抽取式压缩 → 逐断言引用

一个可靠的压缩器至少应满足以下可验证条件:

  1. 不跨越权限边界;
  2. 不合并适用范围不同的事实;
  3. 不删除问题所需的否定、条件、数值、单位和版本;
  4. 每条压缩事实都能映射到原文;
  5. 无法确认或存在冲突时保留不确定性;
  6. 压缩失败时能够回退到更长的原文上下文或安全拒答;
  7. 可以通过离线评测量化信息保留和新增事实。

上下文压缩的本质,是在有限预算下构造一个对当前问题足够充分、对原始证据可追溯的表示。去重解决的是冗余,摘要解决的是表达长度,证据选择解决的是答案覆盖,而信息损失描述的是这些操作对事实、条件和可验证性的破坏。只有把四者区分开,RAG 才能在降低 token 成本的同时,避免用更短的上下文换来更难发现的错误。


系列导航与关联阅读

官方资料

本文依据研究论文、标准组织与主流框架官方文档重新梳理;正文、示例与工程清单由 WR BLOG 编写。