RAG在真实文档中失效的原因:分块、混合搜索、重排序及评估机制。
为什么RAG在真实文档上会失效以及解决该问题的方法:两阶段设计、分块策略、混合搜索、重新排序以及单独评估检索结果。
来源: Production-Ready RAG Architecture: Core Patterns Explained
为何演示在真实数据上会出问题
RAG教程通常使用结构整齐的PDF文件或维基百科内容进行测试,但这并非真正的考验。若将同样的处理流程应用于格式不统一、包含支持工单以及内容繁杂的内部维基的真实企业资料,得到的答案往往不完整、凭空编造或明显错误。几乎所有实际应用中的故障都可以用两个原因来解释。
- RAG首先是一个检索系统,其次才是生成系统。当检索到错误的上下文时,无论模型能力多强,几乎都无法给出正确答案。
- 整体质量取决于最薄弱的环节,而非平均水平。再强大的嵌入模型也无法挽救那些将句子一分为二的文本分割器。
两个阶段
每个RAG系统都包含离线索引阶段和在线查询阶段:
Indexing (offline): documents → chunks → embeddings → vector store
Query (online): question → embedding → retrieval → context assembly → LLM
生产环境会在这一核心机制之上添加缓存、重排序、限制规则、监控功能以及备用逻辑,但它依然是所有功能的基础,任何后续工具都无法弥补薄弱的基础。
分块处理效果最佳
大多数系统在分块处理方面存在缺陷,这种缺陷在问题出现三步之后才会在调试错误答案时显现出来。这一点很重要,因为嵌入模型在连贯的文本片段上表现最佳,上下文窗口容量有限,过大的分块会引入噪声,而过小的分块则会遗漏答案所需的周边信息。
固定大小的分块方式是每隔N个标记进行切割,各片段之间有10%到20%的重叠。这种方法简单且可预测,但却无法识别句子和段落的边界,因此可能会在不知情的情况下将一个定义拆分开来。它虽是常见的起点,但很少是最优选择。
递归或分层分块方法会首先尝试使用自然断点,即双换行,再使用单换行,接着是句子,最后是单词;只有当某个部分仍然过大时才会采取其他方式。如今大多数框架都默认采用这种方法,相比纯固定大小的分块方式显然有明显优势。
基于结构的分块则会遵循文档自身的布局:标题、章节、代码块和表格。对于技术文档和 Markdown 文件,这种方式往往能取得最佳效果。请保留父子链接,这样就可以先定位到较小的精确片段,而在需要更多上下文时再扩展到整个父章节。
语义分块则利用嵌入模型或大型语言模型来识别主题真正发生变化的位置并在此处进行分割。虽然每次边界判定都需要调用模型,因此会带来一定的额外成本,但它能生成最为连贯的片段,通常只用于那些检索质量足以支撑相应投入的文档集。
合理的默认设置:
- 优先采用递归或基于结构的分割方式,避免使用固定长度分割。
- 目标是将文本分割为大约300到800个标记的片段,随后根据自身的评估数据进行调整,而非参考博客上的数值。
- 为每个片段存储元数据,如源文档、章节标题以及页码或段落编号,后续的过滤和引用功能都需要这些信息。
- 收集少量实际问题,先查看系统检索出了哪些内容,再使用嵌入模型。不当的分割方式会悄悄破坏结果,因此在调整其他参数之前务必先解决这个问题。
嵌入模型与向量存储
嵌入模型将文本置于高维空间中,在该空间中语义上的接近性会转化为几何上的接近性,通常通过余弦相似度或点积来衡量。模型固然重要,但数据质量和分块方式往往比所使用的模型更重要。嵌入模型主要分为通用型开放模型、针对法律、医疗或代码文本的领域专用嵌入模型,以及专有API。选择时应根据所在领域的词汇特点来决定,而不能仅依据排行榜上的排名。
向量存储的核心功能是实现快速的近似最近邻搜索,因为精确的暴力比对方式仅适用于规模较小的数据集。大多数向量存储会使用基于图的索引(如HNSW)或基于聚类的索引(如IVF),这样就不必将查询项与所有向量逐一比较。其代价是存在一定的、可调节的错过真正最近邻的概率,但换来的是亚线性级的查询速度。虽然通常不会过早调整这一参数,但了解它的存在有助于解释为何某些明显正确的结果未能被检索到。
在选择向量存储时,应考虑它是否支持基于元数据的过滤、是否原生支持向量与关键词(BM25)混合搜索、本地运行与托管服务的便捷程度,以及在不同数据量下的持久化、复制和扩展能力如何。常见的选择包括Qdrant、Weaviate、Pinecone、Chroma、pgvector以及OpenSearch的k-NN功能。
起初,店铺品牌的重要性远低于其他四点:整洁的数据、合理的分类、恰当的元数据,以及结合向量检索与关键词检索的方法。几乎所有成熟的系统都会采用混合搜索方式,因为纯粹的向量检索往往无法找到错误代码、产品SKU和编号等具体文本,而这些内容可以通过关键词检索轻松获取。密集得分与稀疏得分的融合通常由单一权重控制,因此根据不同的应用场景,同一个查询可以更侧重于语义理解或精确匹配特定术语。概念上的混合检索方式大致如此,不过具体API因店铺而异:
# Conceptual shape of a hybrid query — exact API varies by vector store
results = vector_store.hybrid_search(
query=user_question,
alpha=0.5, # weight between dense (1.0) and keyword (0.0) search
filters={"doc_type": "runbook", "product": "checkout-service"},
top_k=25,
)
超越 top-k 相似度
仅获取最相似的 top-k 个片段只是检索的起点而非终点。实际应用中仍存在诸多问题:最接近的片段未必最具用性,答案中的信息分散在多个片段中,模型即便在长上下文存在中间内容时也往往忽视它(即“中间内容丢失效应”),此外嵌入模型可能无法理解特定领域的词汇。
按投入产出比来看,值得采用的技术包括:
- 重新排序。通过廉价的向量搜索获取大量候选项,比如前20到50个,有时甚至100个,然后使用交叉编码器或小型大语言模型为这些候选项重新评分,最后筛选出用于提示词的那些项。这一直是优化处理流程的最佳方法之一。交叉编码器能够同时对查询和文档进行评分,其准确性远高于分别计算两个向量后再比较的方式,但需为每个候选项运行一次,无法处理整个集合。双编码器搜索成本较低且结果较为粗略,只能缩小搜索范围;而交叉编码器虽然成本较高且结果精确,但只能检查剩余的候选项。
- 混合搜索,将密集向量搜索与稀疏关键词搜索相结合,从而避免让特定词元完全依赖运气。
独立评估检索效果
评估集比直觉更可靠。编写20到50道实际问题,注明每道题应检索哪些内容片段,然后直接检查正确的片段是否出现在前5名或前10名中(召回率@k)。只有在手动检查令你满意后,才可加入LLM来判断相关性。如果除了答案质量外从不衡量检索质量,就会在错误的环节上浪费时间。像Ragas这样的工具涵盖了RAG特有的指标,因此无需手动构建整套系统。
从概念上讲,LlamaIndex与LangChain有何区别
LlamaIndex以数据为核心,为索引、检索和查询引擎提供了成熟的抽象设计,当核心问题是一大堆需要高效检索的文档时,使用它显得非常自然。LangChain则更为通用,源自链式结构和智能体概念,拥有更庞大的集成生态系统。它在仅需要检索功能时更加灵活,但相应地也更为复杂。两者都不是什么神奇的工具,因为它们本质上都是基于相同的底层原理来运作的。选择其中一个,深入理解其概念,但不要固执己见,因为无论你使用何种语法,相关的架构知识都是通用的。
总结
- 索引和查询是两个独立的阶段,应分别设计。
- 分块是一种重要的设计选择,而非框架默认的处理方式。
- 向量存储属于基础设施,其价值体现在过滤和混合搜索功能上,而非品牌本身。
相关阅读
- 生产环境中的高级RAG:架构、工具与权衡 —— 梳理了生产环境中的RAG流程:数据摄取、解析、分块、嵌入、向量存储、混合检索、重排序、评估与安全措施,以及每种方法的相应权衡。
- RAG全流程图谱:从基本原理到生产故障 —— 介绍了两种处理流程、三个步骤以及各类典型故障:阐述检索增强生成的工作原理、可能出现的问题,以及相应的解决方案。