逐步优化RAG答案,一次只做一项调整
一种用于改进薄弱RAG回答的以度量为先的工作流程:依次调整分块、top_k、重排序、混合搜索和查询重写策略,并持续监控检索指标。
构建第一个RAG流程相当简单:加载文档、对文档进行嵌入、存储向量、获取若干片段并将其传递给大语言模型。虽然流程可以运行,但即便文档中明确包含正确信息,得到的答案往往仍是错误的。在大多数这类情况下,问题并不出在模型本身,而在于检索步骤未能为模型提供正确的上下文。本指南将介绍通常最为重要的检索相关要素,更重要的是,会提供一种系统化方法来验证哪些要素真正能提升系统的性能。
先将错误答案视为检索问题
在更改提示词或模型之前,先检查出错问题对应的检索结果。如果相关段落缺失于上下文中,再多的提示词调整也无法修正错误答案。
按照语义边界进行分块
分块大小对检索质量的影响之大出乎意料。过大的分块会混入多个主题,导致其嵌入向量变得模糊,并引入无关文本;而过小的分块则会将句子与其具有意义的上下文割裂开来。不必盲目每500个字符就进行分割,而应将相关的段落或整节内容保持在一起,以标题和段落分隔符作为自然的分割点。
调整top_k值而非凭猜测设定
许多管道会选取最相似的五个片段,仅仅因为五是一个常见的默认值。然而,正确的段落可能位于第六或第七位。top_k值调高可以提高召回率,但每多一个片段也会为提示词增添噪声和标记。应将top_k视为需要根据自身需求进行测试的参数,而非直接照搬的固定值。相关性阈值是另一种选择,相关内容可在“超越top-k:阈值、混合搜索与重排序”一文中了解。
添加重排序阶段
向量搜索速度很快,但精度较低:它擅长找出看似合理的候选项,而在判断哪个才是最佳选项方面表现较差。重排模型会进行第二次筛选。首先,向量搜索会返回更大量的候选项,可能多达十个片段;随后重排模型——通常是能够同时读取查询语句和各个片段的交叉编码器——会对这些候选项进行评分,最终保留最佳的三到四个结果。虽然这种方式能获得更清晰的上下文,但会带来额外的延迟以及每条查询更高的成本。
结合语义搜索与关键词搜索
嵌入模型能够很好地捕捉文本含义,但有时具体的词汇比含义更为重要。像ERROR_CODE_4291这样的查询几乎没有任何语义内容,因此相似度搜索很可能会漏掉其中提到该代码的文档。而BM25之类的关键词排序算法则能很好地处理这类情况。因此,许多系统会同时运行向量搜索和关键词搜索,并将结果合并起来,这种方法被称为混合搜索。
缩小查询中的词汇差距
用户提问的方式很少与文档的表述方式一致。有人可能会询问为何付款失败,而相关页面讨论的是卡片授权失败的问题。查询重写、MultiQuery(生成多种表述并分别进行检索)以及HyDE(生成假设性答案并利用其嵌入信息进行搜索)都能缩小这一差距。不过这些方法会增加LLM调用次数和系统复杂性,因此应在尝试上述更简单的方案之后再使用。
以基准值衡量每一项变更
最重要的习惯就是避免主观假设。“我们加入了重新排序功能,所以检索效果更好”这一说法在得到实际测量之前仅属于假设。可以先准备一小组包含已知相关内容的真实问题,然后跟踪以下指标:
- Recall@K:正确的信息是否出现在检索到的前K个结果中。
首先记录一个基准值。在示例测试中,结果可能如下所示:
Baseline Recall@5: 68%
然后一次只做一项修改,重新运行相同的问题并记录每次的结果。一系列改进过程可能如下所示:
Better chunking: 74%
Hybrid search: 82%
Reranking: 89%
这些数据仅为示例,并非基准标准;实际数据的表现会有所不同。关键在于,通过每次修改后的记录可以判断哪一步带来了复杂性,哪些没有。如需更深入地了解故障定位方法,请参阅按故障阶段评估RAG。
总结
从最简单的流程开始,即查询、检索、上下文构建到LLM应用,然后逐个环节进行优化:做出改动后进行测量,评估其延迟和成本,再重复此过程。一个你能够理解且能衡量性能的简单RAG系统,通常比那些充斥着无法用数据证明其价值的复杂系统更有价值。