RAG 中如何解决跨 Chunk 的信息完整性问题?
做 RAG 时,知识库文档通常不能整篇直接塞给模型,所以我们会先把文档切成很多 Chunk。
例如:
原始文档
↓
Chunk 1
Chunk 2
Chunk 3
Chunk 4
但这里会出现一个很常见的问题:
一段完整的信息刚好被切到两个 Chunk 里。
这就是所谓的:
跨 Chunk 信息完整性问题。
一、跨 Chunk 问题是怎么产生的?
比如原文是:
用户购买产品后,可以在 14 天内申请退款。
退款申请通过后,款项会在 3~5 个工作日内原路退回。
如果刚好被切成:
Chunk 1:
用户购买产品后,可以在 14 天内申请退款。
Chunk 2:
退款申请通过后,
款项会在 3~5 个工作日内原路退回。
用户问:
退款需要多久才能到账?
如果系统只检索到 Chunk 1,那么信息就是不完整的。
所以 Chunk 切得太死,很容易导致:
完整知识
↓
被拆开
↓
只召回其中一部分
↓
LLM 得到的信息不完整
↓
回答不完整甚至产生幻觉
二、最常见的方法:滑动窗口
我自己比较常用的方法就是:
Sliding Window,也就是让相邻 Chunk 之间保留一定重叠内容。
例如原本:
Chunk 1:
A B C D
Chunk 2:
E F G H
改成:
Chunk 1:
A B C D E
Chunk 2:
D E F G H
可以看到:
D E
同时出现在两个 Chunk 中。
这部分就是:
Chunk Overlap。
三、为什么滑动窗口有效?
因为它可以避免重要信息刚好被切断。
例如:
Chunk Size = 500 tokens
Chunk Overlap = 100 tokens
意味着:
Chunk 1:
0 ~ 500
Chunk 2:
400 ~ 900
Chunk 3:
800 ~ 1300
每两个 Chunk 之间都会保留一部分上下文。
这样即使一句话、一个段落或者一个概念刚好出现在边界附近,也有更大的概率完整地存在于某个 Chunk 中。
简单来说:
滑动窗口是用少量重复内容,换取上下文完整性。
四、Overlap 不是越大越好
这里也有一个常见误区:
为了防止信息被切断,把 Overlap 设置得非常大。
例如:
Chunk Size = 500
Overlap = 400
这样会导致大量重复内容。
结果可能是:
索引体积变大
+
检索结果高度重复
+
Context 浪费
+
Embedding 成本增加
所以一般需要平衡。
例如可以从:
Chunk Size:400~800 tokens
Overlap:50~150 tokens
开始测试。
没有一个固定最优值,最终还是需要根据:
文档类型
问题类型
Embedding 模型
检索效果
进行调整。
五、除了滑动窗口,还有哪些方法?
滑动窗口很好用,但不是唯一方法。
1. 按语义切 Chunk
不要单纯按照:
每 500 tokens 切一次
而是尽量按照:
标题
段落
章节
句子
来切。
例如:
一级标题
↓
二级标题
↓
完整段落
这样能够尽量保证:
一个 Chunk 本身就是一个完整语义单元。
这种方法通常叫:
Semantic Chunking。
2. Parent-Child Chunk
还有一种很实用的方法:
检索的时候使用小 Chunk:
Small Chunk
↓
提高检索精度
但是命中以后,不只返回这个小 Chunk,而是把它所属的更大上下文一起返回。
例如:
Parent Document
├── Chunk A
├── Chunk B ← 检索命中
├── Chunk C
└── Chunk D
最终给 LLM 的不是只有:
Chunk B
而是:
Chunk A
+
Chunk B
+
Chunk C
这样既保证:
检索精度
又保证:
上下文完整性。
六、还可以自动补充相邻 Chunk
另一种简单方法是:
检索到某个 Chunk 后,同时把前后 Chunk 一起取出来。
例如检索到了:
Chunk 5
系统自动补充:
Chunk 4
+
Chunk 5
+
Chunk 6
这种方法也非常适合解决跨 Chunk 问题。
流程可以理解成:
Query
↓
检索 Chunk 5
↓
获取 Chunk 4、5、6
↓
重新组合 Context
↓
LLM
这在长文档、技术文档、政策文档中非常实用。
七、实际项目怎么选?
一个比较简单的思路是:
基础方案
↓
Sliding Window
如果文档结构明显
↓
Semantic Chunking
如果既要检索精度又要上下文完整
↓
Parent-Child Retrieval
如果信息经常跨段落
↓
Neighbor Chunk Expansion
实际项目中也经常组合使用:
Semantic Chunking
+
Overlap
+
Neighbor Expansion
这样效果通常会更稳定。
总结
RAG 中的跨 Chunk 问题,本质上是:
完整信息
↓
被 Chunk 切断
↓
只检索到其中一部分
↓
Context 不完整
最常见的解决方法就是:
Sliding Window
+
Chunk Overlap
让相邻 Chunk 保留一定重复内容。
除此之外,还可以结合:
Semantic Chunking
Parent-Child Retrieval
Neighbor Chunk Expansion
一个比较实用的思路是:
检索时尽量小,保证精准;提供给 LLM 时适当扩大上下文,保证完整。
这其实就是处理跨 Chunk 信息完整性的核心。