在AI的世界里,有句老话叫“Garbage In, Garbage Out”。对于RAG系统而言,检索环节就是那个“In”,如果检索不到精准、全面的信息,那么即便是最强的LLM也只能望“材”兴叹,甚至开始一本正经地“胡说八道”,俗称产生幻觉。 本篇将聚焦于检索前和检索中的优化,从数据的源头和查询的入口解决“找不到”和“找不准”的核心痛点。
一:优化数据源:索引构建的最佳实践
核心痛点:粗暴地按固定长度切分文档,就像把一本好书随机撕成碎片,重要的上下文和语义关联都丢失了。
1.1 智能分块
放弃按字符数切分的“一刀切”模式,转向更智能的方法:
-
语义分块: 利用算法(或LLM)识别文本中语义的自然断点,如段落、标题或一个完整的概念单元,确保每个被索引的文本块都“言之有物”。
-
句子分块: 以完整的句子为单位进行切分,这是最细粒度的语义单元。
1.2 父文档检索器
这是对“句子窗口检索”更通用、更强大的实现,也是我为您补充的一个关键策略。
-
原理:
-
索引时:我们将一份文档切分成许多小的“子文档”(比如单个句子),并对这些“子文档”进行向量化。同时,我们保留一份完整的、较大的“父文档”(比如整个段落或页面)。
-
检索时:我们用用户查询去匹配那些精细的“子文档”。
-
返回时:一旦命中某个“子文档”,我们不返回这个小片段,而是返回它所属的那个完整的“父文档”作为上下文。
-
-
优点:兼具检索的精准度(匹配小块)和上下文的完整性(返回大块),效果拔群。
1.3 从文档生成QA对:创造更多检索入口
这是另一项极大提升召回率的王牌策略。其核心思想是:用户的提问方式千变万化,直接用问题去匹配一段陈述性的文档,在语义上可能存在鸿沟。但用“问题”去匹配“问题”,则要容易和精准得多。
-
原理:
-
对每一个文档块,我们调用LLM,反向生成几个用户可能会提出的、能够被这个文档块回答的问题。
-
在构建索引时,我们只对这些新生成的“代理问题”进行向量化。
-
同时,我们将这些“代理问题”全部链接到它们所源自的那个原始文档块的ID。
-
当用户提问时,系统会在“代理问题”的向量库中进行搜索。一旦匹配成功,系统不会返回这个代理问题,而是通过ID找到并返回那个包含完整答案的原始文档块。
-
-
优点: 为单个知识点创建了多个不同的语义入口,即使用户的提问方式很刁钻,只要能和其中一个代理问题对上,就能找到正确答案,召回率大大提升。
from langchain.storage import InMemoryStore
from langchain_core.documents import Document
from langchain.retrievers.multi_vector import MultiVectorRetriever
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
import uuid
docs


3411

被折叠的 条评论
为什么被折叠?



