RAG 索引进阶笔记:不是只存进去,而是要存得聪明
这篇主要讲什么
这一份 PDF 对应 Part 12 ~ Part 14,主题是索引升级。
如果说基础 RAG 解决的是“先把资料放进库里”,那这一篇关注的是:
怎么存,后面才更容易找对。
这其实是个很现实的问题。很多系统不是搜不到,而是存得太粗糙,导致检索阶段天然吃亏。
1. Multi-representation Indexing:一份内容,存多个表示
原文先讲的是多表示索引。
简单理解就是:
同一份原始文档,不只存原文,还可以存它的摘要、标题、关键片段,甚至其他形式的描述。
这样做的好处很明显:
用户提问时,有时更容易和“摘要版”对上,而不是和“原始长文”直接对上。
文中的做法是:
- 原文保留在存储层
- 用摘要去建立向量索引
- 检索时先靠摘要命中
- 命中后再回到原文取完整内容
我觉得这个思路很实用,因为它兼顾了两件事:
- 检索时更轻、更聚焦
- 回答时还能拿到完整原文
这比“全部直接按大块原文建索引”要聪明很多。
2. RAPTOR:先做层级摘要,再逐层检索
这一部分原文更多是在给论文和代码入口,但核心思想其实很好懂。
RAPTOR 可以理解成:
先把文档分块,再对这些块做摘要,然后把摘要继续往上归纳,形成一个树状结构。
这样检索时,不只是从底层小块里硬搜,还可以先从更高层的摘要节点入手。
它像什么?
很像你读一本书时,不会每次都从某一页某一段开始翻,而是会先看目录、看章节摘要,再决定往哪里钻。
我对 RAPTOR 的理解是:
它特别适合长文档、结构复杂的资料库。因为这类资料如果只靠平铺的小块检索,很容易只看到局部,看不到全局。
3. ColBERT:不是整段比,而是细到词级别去匹配
基础向量检索,通常是一整段文本压成一个向量,再去比较相似度。
ColBERT 的思路更细:
不是整段只给一个表示,而是让文本里的每个 token 都有自己的向量表示,然后再做更细粒度的匹配。
通俗点说,它不是问:
“这一整段和问题像不像?”
而是更像在问:
“问题里的每个关键点,能不能在文档里找到很像的局部对应?”
我觉得这类方法的优势是:
对一些细节命中要求高的场景,会比普通向量检索更强。
但代价也很现实:
- 更复杂
- 更重
- 部署和维护成本更高
所以它不一定适合所有项目,尤其不适合刚起步就上来堆复杂方案。
这份内容的核心思想
我觉得这一篇的核心思想是:
索引不是仓库,它更像检索系统对资料的“预加工”。
你怎么存,几乎决定了你后面能不能找得准、找得快、找得全。
我自己的理解
这一篇让我更明确了一件事:
RAG 的上限,很大程度上不是生成模型决定的,而是索引策略决定的。
因为模型回答之前,已经被“喂了什么内容”限制住了。
如果检索阶段只能拿到粗糙、片面、缺上下文的内容,那后面回答再流畅,也只是把不完整的信息说顺而已。
我自己的偏好是:
- 小型项目,先把基础切分和摘要索引做好
- 中型项目,再考虑多表示索引
- 文档真的很长、层次很复杂时,再看 RAPTOR
- 对精确匹配要求很高且资源够用时,再考虑 ColBERT
也就是说,先把简单但有效的招数吃透,再谈更重的方案。
这一篇最值得带走的经验
- 原文不一定是最适合检索的表示
- 摘要、标题、结构化信息都可以成为索引材料
- 长文档不能只平铺切块,层级信息很重要
- 更细粒度的匹配更强,但也更贵
一句总结
这一篇讲透了一个常被忽视的问题:知识库不是把资料“塞进去”就结束了,真正重要的是把资料变成“容易被找对”的形式。索引做得聪明,后面的检索和回答才有底气。
核心代码实现
方法 1:Multi-representation Indexing
1 | # 原文 |
1 | def retrieve_with_multi_representation(question): |
方法 2:RAPTOR
1 | def build_raptor_tree(chunks): |
1 | def raptor_retrieve(question, tree): |
方法 3:ColBERT 思路
1 | def colbert_score(query_tokens, doc_tokens): |