Skip to content

8.1.2 RAG 的上下文构造到底在做什么? ​

先给结论:RAG 的上下文构造,本质上是在把“检索阶段找到的候选材料”转换成“生成阶段适合模型使用的证据输入”。它不是简单拼接文本,而是在做筛选、排序、补全、压缩和结构化组织。

如果把 RAG 拆成更清楚的链路,可以大致理解成三层:

  1. 找什么
  2. 选什么
  3. 怎么给

其中:

  • 检索更偏向回答“找什么”
  • 重排和结果精选更偏向回答“选什么”
  • 上下文构造更偏向回答“怎么给”

很多系统其实前两步都不差,但最后回答仍然不稳,问题常常就出在第三步。

上下文构造不是“最后拼一下 Prompt” ​

这是最需要先纠正的认识。

很多人会把上下文构造理解成:

  • 把检索到的几段文本拼进 Prompt 模板

这只是最表层的形式。真正要解决的是:

  • 哪些材料该进入 Prompt
  • 这些材料以什么顺序进入
  • 这些材料需要保持原样,还是先做合并、压缩、摘要或结构化
  • 哪些背景该保留,哪些噪声该删掉

也就是说,上下文构造是一个“证据编排”过程,而不是简单的字符串拼接。

它在系统里到底承担什么职责 ​

更具体一点看,上下文构造通常承担下面几件事。

1. 把候选变成最终证据集合 ​

检索得到的是候选池,但模型不应该直接面对整个候选池。

上下文构造需要先决定:

  • 哪几条最值得进入最终上下文

2. 恢复局部完整性 ​

候选往往是切块结果,单块不一定完整。

上下文构造需要根据情况决定:

  • 是否扩展相邻块
  • 是否合并同一章节下的多个块
  • 是否按文档结构重新组织

3. 控制噪声和重复 ​

候选之间常常会有:

  • 重复表达
  • 模板块
  • 背景块
  • 边缘相关块

上下文构造需要把这些内容压下去,否则模型就会被“看起来很多、实际增量很少”的材料包围。

4. 控制长度和预算 ​

上下文不是越长越好。系统需要决定:

  • 最终给模型多少 token
  • 哪些材料优先保留
  • 哪些材料在预算不够时先删

5. 给模型更清晰的阅读结构 ​

有些系统最终不是把块原样堆在一起,而是会把材料组织成:

  • 按主题分组
  • 按文档来源分组
  • 按证据强弱排序
  • 按问题子任务拆分

这类组织方式,本质上都是上下文构造的一部分。

一个更直观的最小链路 ​

python
candidates = retrieve(query, top_k=20)
ranked = rerank(query, candidates)
selected = select_relevant_chunks(ranked)
context = build_context(selected, max_tokens=2500)
answer = llm(generate_prompt(query, context))

这段代码想说明的是:

  • retrieve() 不是最终输入
  • build_context() 才是把候选转成模型可用材料的关键一步

为什么这一步常被低估 ​

因为它看起来不像“核心算法”,更像一些中间处理规则。

但真实系统里,回答质量经常就卡在这些规则上。比如:

  • 要不要补相邻块
  • 要不要压缩背景段
  • 要不要保留同文档多个片段
  • 要不要把不同来源的证据分组展示给模型

这些决定不会改变“库里有什么”,却会直接改变:

  • 模型最终看到什么

而模型看到什么,往往就决定了它怎么答。

一个常见误区 ​

很多人会把上下文构造理解成:

  • 检索做完以后再随手处理一下格式

这会低估它在链路中的位置。

更准确的理解是:

  • 它是检索和生成之间的关键中间层

没有这层,系统就会把“粗候选”直接交给生成;有了这层,系统才能把候选整理成更像“证据输入”的东西。

一句话总结 ​

RAG 的上下文构造,做的不是简单拼接文本,而是把检索到的候选材料转成模型真正适合使用的最终证据输入。它承担的是筛选、补全、压缩、排序和结构化组织的职责,是连接检索和生成的关键中间层。