Appearance
3.4.1 为什么生产 RAG 经常要把“检索用块”和“生成用块”分开?
先给结论:因为检索阶段和生成阶段优化的目标并不完全一样。
检索阶段更关心“能不能精准命中”,生成阶段更关心“模型拿到的上下文是否足够完整、自然、可解释”。
这就是为什么越来越多的生产 RAG 系统,会把“检索用块”和“生成用块”拆开设计。
为什么“一套块走到底”经常会开始拉扯
如果你只保留一套 chunk,通常会希望它同时满足两件事:
- 检索时足够细,容易命中答案局部
- 生成时又足够完整,模型可以顺畅理解上下文
问题在于,这两种目标往往会互相拉扯。
比如:
- 块切得小,召回通常更精准,但模型看到的可能是碎片
- 块切得大,模型读起来更顺,但检索又容易变粗
这也是很多系统后面会遇到的典型现象:
- 要么召回很准,但回答像拼碎片
- 要么回答更自然,但召回命中不够尖锐
检索阶段到底在追求什么
检索阶段最核心的目标通常是:
- 尽快缩小候选范围
- 尽量命中真正相关的局部
- 少把无关大段内容带进来
也就是说,检索更喜欢:
- 粒度相对细一点
- 相关性更尖锐一点
- 每个候选更像“答案证据点”
这也是为什么检索阶段通常更容易偏爱较小、更聚焦的块。
生成阶段到底在追求什么
生成阶段最核心的目标通常是:
- 让模型看到足够完整的上下文
- 少让模型自己脑补断掉的前提
- 让回答更自然、更可解释
也就是说,生成更喜欢:
- 上下文更连贯
- 语义更完整
- 来源边界更容易解释
这也是为什么生成阶段常常又更需要稍大一点、或者经过聚合后的内容。
一个更直观的例子
假设原始文档里有这样一段:
text
普通商品签收后 7 天内支持无理由退货,但定制类商品不适用该规则。
如存在质量问题,可在 15 天内申请售后。检索时,更理想的情况可能是先命中:
- “定制类商品不适用该规则”
因为它最贴近问题本身。
但生成时,如果只把这一小句直接丢给模型,模型有时又容易失去前面的“普通商品 7 天退货”这个背景比较关系。
这时更稳的做法往往是:
- 先用小块精准召回
- 再回到相邻或父级更完整块,把上下文组织给模型
这就是“检索用块”和“生成用块”分开的典型思路。
生产系统里常见的几种做法
1. 小块检索,大块回填
先用小块做召回,再根据命中的 chunk 找回它所属的小节、父块或更完整段落送给模型。
2. 子块检索,父文档组织
先在子块层命中,再按文档或章节层聚合,最后按更自然的文档结构组织回答上下文。
3. 检索层细,生成层重组
检索阶段尽量追求精度,生成阶段再把多个相邻块按标题、章节或语义关系重组。
这些做法表面不同,本质上都在解决同一个问题:
召回粒度和生成粒度不一定应该强行相同。
一个最小示意
python
retrieval_chunks = [
"定制类商品不适用该规则。",
"如存在质量问题,可在 15 天内申请售后。"
]
generation_chunk = """
普通商品签收后 7 天内支持无理由退货,但定制类商品不适用该规则。
如存在质量问题,可在 15 天内申请售后。
"""这个例子想表达的是:
- 检索用块更像“命中点”
- 生成用块更像“可读上下文”
什么时候这种分离设计尤其有价值
下面这些场景里,分离设计往往更值得考虑:
- 文档较长
- 规则、前提、例外容易分散在相邻位置
- 检索需要更高精度
- 生成又需要更完整上下文
- 你已经发现“小块检索准,但回答总像碎片”
这些通常都是“继续调一个全局 chunk size 已经不够”的信号。
一个常见误区
很多人会把“分开设计”理解成系统更复杂、维护更麻烦,所以倾向于能不拆就不拆。
这在早期阶段当然可以理解。
但如果你已经开始遇到下面这种拉扯:
- 为了召回更准,不断把 chunk 调小
- 又为了回答更顺,不断想把 chunk 调大
那问题往往已经不是“再调一次 size”能解决,而是两阶段目标本来就该拆开看了。
一句话总结
生产 RAG 经常把“检索用块”和“生成用块”分开,不是为了显得复杂,而是因为检索阶段和生成阶段追求的目标不同。检索更在意精准命中局部,生成更在意上下文完整和自然表达,这两者往往不该强行绑定成同一种块。