首页 / 文章

在746页的手册上对8种RAG最佳实践进行了测试。

在三份产品手册的测试中,哪些常见的RAG优化方法表现良好:软连字符处理、分块大小调整、路由策略、升级阈值以及重新排序。

发布者:REACTAPP.TOP阅读约 9 分钟2005 词

来源: I Measured Every RAG “Best Practice” on 746 Pages of Product Manuals. Only Four Survived.

这个基于三份真实PDF文件构建的辅助机器人——一份408页的电动汽车用户手册、一份329页的电视节目指南,以及一份包含无线鼠标的9页宣传册——成为了八种常用RAG优化方法的测试平台。规则十分严格:只有当在该语料库上的测试结果,通过对比标注好的标准答案后证明有效时,相关方法才会被采用;文献资料和习惯因素均不予考虑。

测试结果显示,准确率从72%上升到了89%,而每次查询所需的提示词数量则从14个减少到了57个。这一提升源自四项改进,而非八项。其中两项被否决的方案反而带来了负面影响,还有一项已投入使用的优化方法最终并未真正生效。整个测试过程仅消耗了约0.015美元的API调用费用。

以下是各项改进的测试结果。列表中最有价值的方案其实根本不属于RAG技术范畴。

一个比任何超参数都严重的漏洞

在调整检索参数之前,先确认究竟有哪些内容被纳入了索引。大家都确认PDF文件已成功解析。更关键的问题是这些文本的状态如何。

一个循环程序可以回答这个问题:

SOFT = "­"    # U+00AD, the soft hyphen
for manual in manuals:
    print(manual.name, extract(manual).count(SOFT))

汽车手册中出现了2,272个这样的词。

软连字符是排版师给渲染器的指令:如果一行写满,则在此处断字。PDF阅读器会遵从这一指令并将其隐藏,因此人们看不到它,但文本提取工具则会原样保留它。页面上显示的是完整的单词,而索引中则会出现诸如“ve”、“软连字符”、“换行符”、“hicle”之类的字符串。

在那本手册中,有999个不同的单词被拆成了两半,其中最常被拆分的便是所有汽车手册中最重要的名词“Vehicle”。该词作为完整单词出现了1,486次,而按理应出现1,580次。另有94次出现的情况无法通过搜索找到。

这会同时损害所有的检索路径。关键词搜索会匹配精确的字符串,而“hicle”并非“vehicle”,因此不会获得任何分数。向量搜索的表现更差于预期,因为嵌入模型会将文本拆分成子词片段,像“hicle”这样的词会碎裂成毫无意义的零散部分。即便偶尔有匹配结果,也会将混乱的上下文插入提示词中。

修复方法只有一行代码,但其中存在陷阱。两端对齐的文本在真正的连字符处也会断裂。拼写错误的“cus-tomer”应该重新组合为“customer”,但“toll-free”必须保持连字符形式,要区分二者需要词典。共有95个这样的词,全部保持断裂状态,仅留下数字标注。95个词的明确缺失与那种悄无声息地破坏语料库的盲目猜测是完全不同的现象。无论选择多大的分块大小,都无法恢复索引中从未作为独立词存储的单词。

最佳分块大小其实是个陷阱

更大的文本块能包含更多上下文,因而能与更多问题匹配,但同时也会将额外的无关文本永久地包含在每个查询中。设置重叠是为了避免位于边界处的答案被一分为二。

我们用标准答案对九种配置进行了测试。准确率无需任何成本即可衡量,而成本则是使用这些配置所需支付的代价。

重叠机制其实并非理所当然,尽管初看之下似乎如此。在700个字符的长度下,取消重叠可使准确率提升11点,这一结果看似很糟糕,但当测试1800个字符的长度时,重叠量为0、100和200时的准确率均为89%。一种比较表明重叠会降低效率,另一种则认为它毫无影响。

导致这两种结果的原因是一个几乎未被提及的统计数据。在所有配置下,即便是最小的那种,每个答案都完全包含在单个数据块中,且从未有答案在边界处被截断。重叠机制是为了防止出现该语料库所缺乏的问题,因此其测量值会在零和负数之间波动。在支付这笔额外费用之前,应先查明答案真正跨越边界的频率。

接着就是陷阱所在。最佳得分89%对应的是1400个字符以及200%的重叠率。选择这种配置意味着每次查询需要处理1,283个标记,而非750个,在整个产品使用期内每次查询的处理量将增加71%。在包含18道题的测试集中,89%的准确率意味着16题正确,而83%则意味着15题正确。最佳配置仅比最便宜的配置多答对1道题。

因此不能默认认定获胜者。应选择价格最低且其得分与获胜者无法区分的配置,在此标准下即得分在8分以内的配置。根据该规则,9种配置中有6种得分相同,最终得分最低的那一种获胜。这样一来,准确率从72%上升到了83%,同时所需的计算资源也更少,这是该项目首次因放弃优化衡量指标而获得的“免费午餐”。

代价为22分的过滤器

该语料库涵盖三种产品。关于鼠标的查询没有理由去搜索408页的汽车手册,因为另外两份文档只会增加出错的概率。原本的计划是先识别出产品类型,再将搜索范围限制在该手册内。

一个分类器完成了这项任务。它在不到4毫秒的时间内就能以78%的准确率做出正确判断,且无需任何成本,这一比例远高于搜索三倍量文本时的准确率。然而其准确率却下降了22分。

这一机制的显现耗时之长令人尴尬。错误的过滤规则不会只是降低正确答案的排名,而是直接将其删除。如果没有过滤机制,对三本手册的全面搜索仍可能找到正确段落,只不过位置可能在第4页而非第1页,此时可通过重新排序机制将其救出。而有了过滤机制后,错误引导的问题会去搜索错误的文档,由于正确内容并不存在于候选集中,它就不会被排在较低位置。

因此,一个78%的准确率分类器并不会以牺牲少量质量为代价。无论后续处理流程有多完善,它都会为所有结果设定78%的严格上限。

解决办法并非使用更精确的分类器,而是允许选择放弃使用分类器:只有在有把握时才应用过滤器,而当排名靠前的两个产品得分相近时则对所有产品进行搜索。在“如何通过蓝牙配对它”这样的问题中,“它”这个词具有很大的权重,而分类器也会像其他人一样无法确定答案。

或者完全跳过推理过程

最便宜的路由器只需通过下拉菜单选择即可。对于这类工具,只需轻点一下即可确定问题涉及的是哪款产品,而这通常是用户从产品帮助页面打开聊天窗口时就会进行的操作。这样一来,分类器只能猜测的歧义就能立即得到解决,而且过滤器每次都能给出正确结果。

这些数字是完全准确的。该分类器将18道题中的4道错误引导。不使用过滤条件时找到了15个答案,使用过滤条件时找到了11个,而每一次错误引导都会让原本正确的答案变成错误答案。去掉这些错误引导后,准确率依然保持不变,同时过滤条件所承诺的精确度也得以实现。

从未被执行的优化方案

为了表面好看,这部分内容很值得被删除。

原计划是在检索结果较为可靠时使用成本较低的200亿参数模型,而在结果不可靠时则升级到1200亿参数模型。错误答案往往集中在检索结果不稳定的情况下,而且重排器本身就已经能生成置信度分数,因此无需额外成本。

阈值被设定为0.0,这一做法看似合乎原则:交叉编码器会输出一个对数几率值,sigmoid(0)等于0.5,而当该值低于0时,则意味着最高分块更可能是无关的而非相关的。但实际分布情况却有所不同——观测到的最高分最低为3.24,中位数为8.29,最高为10.00。

该阈值低于有记录以来的最低分,因此从未触发过,一次都没有。这种递增策略实际上等同于始终使用廉价模型,再加上额外的开销,即在结果表中添加一行看似有意义但实际上毫无信息的内容。零阈值并非中立的默认值,它其实是对一个未经任何人验证的分布的假设;所有绝对对数几率阈值都有这一缺陷,因为一旦重排器或语料库发生变化,分布就会随之改变。

解决方案是自行计算该数值。只需运行“真实”“始终低成本”和“始终高成本”这两种端点,记录每个问题的得分、正确性及成本。介于两者之间的所有阈值值都是对内存中已有数据的重新处理,无需额外的 API 调用,采用全面扫描而非抽样方式。

测试结果出人意料:每个阈值仅改变成本,而准确率却丝毫不变。诊断结果解释了原因。在十二道题目中,只有一道能够被正确检索出来,这意味着廉价模型失败了,而昂贵模型则成功了。那道题的得分是10.00,处于最高水平,而廉价模型已经答对的题目中位数仅为8.29。唯一值得升级处理的失败案例是检索系统最有把握答对的那一道,因此任何基于在不确定时升级规则的机制都无法在任意阈值下捕捉到它。路由信号未能预测它本应预测的内容。

还出现了一个副作用。实时运行结果显示准确率为92%,而对已付费数据行的回放测试则预测为83%。这种差异并非由阈值问题导致,而是由于大语言模型在二次评估时对相同的答案给出了不同分数,从而避免了宣布那9分的提升其实只是无意义的噪声。

带来双重收益的变革

重新排序是最有效的解决方案,其背后的原理值得了解并加以应用。

向量搜索会分别将问题及每个文本片段嵌入模型,模型永远不会将它们视为一对。而交叉编码器则将这对内容视为单一输入直接进行评分,虽然精度更高,但处理整个语料库时速度实在太慢。因此,该流程会以较低成本获取25个候选结果,再对它们进行重新排序,最终保留排名前4的选项。

召回率从83%上升至89%。更值得关注的是答案所处的位置。在重新排序之前,正确的片段通常处于第二或第三位,因此会发送五个片段以确保安全。排序之后,它通常处于第一位,四个片段就足够了。若满足基础准确率要求,只需两个片段,即可减少57%的标记数量。

由于排序效果的提升,达到相同准确率所需的片段更少,而费用则是按提示词标记来计算的。这一改变在提升准确率的同时也带来了成本折扣。其成本为73毫秒的本地CPU运算时间,而调用大型语言模型的时间为1,252毫秒,这一差距几乎无人察觉。

延续下来的做法

  • 在处理检索器之前先检查输入内容。一个简单的循环就能发现诸如连字符问题之类的问题,或者确认不存在限制,从而让后续操作更加稳妥。
  • 配置默认值往往是别人的测量结果,或者根本没有任何依据。100%的重叠度从未带来任何好处,0.0的阈值也从未触发过警报,而在实际使用中这两种情况都不会被显现出来。唯一的表现可能就是账单金额莫名增加,却没有任何解释。
  • 不仅要考虑优化方案出错的频率,还要分析其在出错时的表现。那种有22%的遗漏率但仅会重新排序结果的过滤器尚可接受;而那种有22%的遗漏率且会导致无法获取正确答案的过滤器则不可用。
  • 被否决的方案也是一种结果。有四个方案未被部署,那就意味着多了四样无需维护、监控、版本控制或夜间警报的东西。

有一个需要注意的地方。答案键中包含18组经过验证的题目与引用对,因此每道题的分数为5.6分。导致22分得分崩溃的情况涉及四道题,这一数值足以作为处理依据;而11分的重叠结果仅涉及两道题,因此最多只能说明存在方向性差异。相较于依赖他人的数据,以团队自身数据为基础生成的答案键更为可靠。在笔记本电脑CPU上完成整个计算过程大约需要5美分,由于存在严格的预算上限,相关数值会以例外形式呈现,而不会出现在发票上。

相关阅读

  • 实用笔记:为何您的 RAG 流水线需要重新排序器(以及实现方法 — 详细讲解实用笔记:为何您的 RAG 流水线需要重新排序器(以及实现方法,包括相关契约、检查项以及供采用该模式的团队直接使用的代码片段。