跳到主要内容

RAG 流水线中被你忽略的查询重写层

· 阅读需 12 分钟
Tian Pan
Software Engineer

当 RAG 系统回答错误时,大多数团队的第一反应是归咎于编码器(encoder)。更换更大的嵌入模型(embedding model)。尝试针对特定领域微调过的模型。增加维度。三个迭代周期(sprint)后,召回率曲线只提升了几个百分点,而用户的投诉看起来还是老样子。

诊断错了。大多数检索失败并非嵌入失败。它们是查询形状(query-shape)失败——在编码器运行之前就存在的词汇不匹配,无论如何调整向量都无法修复。

用户输入“如何取消”。相关的文档标题却是“订阅生命周期管理”,并使用了“终止”、“计费周期结束”和“服务停用”等词汇。世界上没有任何编码器能靠词汇运气将这两个字符串拉入同一个邻域。余弦相似度(cosine similarity)的差距是真实存在的,它存在于输入中,而非模型中。位于检索之前的查询重写层是大多数流水线跳过的步骤,随后他们却要花一个季度的时间试图在下游进行补偿。

你一直误诊的词汇缺口

标准的 RAG 检索将用户的提问视为一个现成的查询。它对其进行编码,在索引中进行点积运算,并返回 top-k 结果。这在有人同时编写了问题和文档的演示数据集上有效。但在生产环境中会失效,因为真实用户是以向人类提问的方式书写的:简短、描述不足、充满代词和隐含语境,且通常被表述为一个问题而非一个主题。

另一方面,文档是由那些自认为在解释某事的人编写的。它们冗长、结构化、充满章节标题和抽象名词,且几乎从不以“如何”开头。嵌入空间的几何形状反映了这种不对称性。简短的用户查询与其它简短的用户查询聚在一起。冗长的说明性段落与其它冗长的说明性段落聚在一起。真实查询与实际回答它的文档之间的平均距离,比两个无关文档之间的平均距离还要大。

这就是为什么微调嵌入模型通常令人失望的原因。如果你有标记过的配对数据并且有耐心,你可以从领域调优的编码器中挤出 5–10% 的召回率,但你优化的是流水线中错误的一端。更廉价的做法是重塑查询,使其与文档处于相同的邻域。这就是查询重写的作用。

HyDE:嵌入你希望拥有的答案

假设性文档嵌入(Hypothetical Document Embeddings,简称 HyDE)彻底改变了检索问题。与其对问题进行嵌入,不如要求 LLM 为该问题写一个虚假的答案,然后对 那个答案 进行嵌入并据此进行检索。

直觉是几何学上的。真实的答案文档和虚构的答案文档是以相同的语调编写的:相同的长度、相同的词汇、相同的名词短语密度、相同的说明性语气。它们位于嵌入空间的同一区域。简短的用户提问则不然。因此,虚构的答案充当了翻译器,在触达索引之前就将查询带入了文档所在的邻域。

HyDE 不需要假设内容在事实上的正确性。它只需要在风格和词汇上具有代表性。LLM 可以自信地编造一个错误的答案,而检索依然会得到改进,因为你匹配的是形状,而非事实。根据语料库的不同,报告的增益从几个百分点到几十分点的精确率和召回率不等,而在用户词汇与文档词汇差异最大的领域提升最为明显——这正是微调编码器成本最高昂的情况。

成本是在检索之前增加了一次额外的 LLM 调用。在小型模型上,这需要 200–500 毫秒。这是一项真实的权衡,在对延迟敏感的路径上,你可能希望在 HyDE 之前进行置信度检查(先运行普通检索;如果 top-1 相似度得分低于阈值,则回退到 HyDE 并重新检索)。对于批量摄取或分析查询,你几乎总是可以毫不犹豫地支付这项延迟成本。

分解:一个问题隐藏了三个子问题

多跳(Multi-hop)问题以另一种方式破坏了检索。“向 CTO 汇报的工程经理中,谁是在 B 轮融资前加入的?”无法通过单一文档回答。答案需要三次检索——谁是 CTO,谁向他们汇报,以及每个人何时加入——原始问题的嵌入并不指向其中任何一个。它指向一个包含三者碎片的模糊中间地带,对于其中任何一个点来说都不是正确的文档。

分解(Decomposition)将多跳查询重写为一系列单跳子查询,对每个子查询进行检索,然后进行合并。一次性完成(“将此拆分为独立的子问题”)已经优于单查询检索。迭代完成——检索,查看发现的内容,根据部分答案生成下一个子查询——可以匹配更繁重的多跳基准测试的召回率,同时每步提取的块更少。

需要注意的失败模式是过度分解。被要求分解的 LLM 会乐于将一个单跳问题拆分为三个冗余的子查询,从而在没有任何收益的情况下使你的检索成本翻倍。廉价的缓解措施是使用路由器:在调用分解器之前,使用一个小型分类器来决定问题是单跳还是多跳。这种成本意识版本将简单问题通过普通检索路由,仅对复杂问题进行升级,从而保持 p50 延迟持平,并确保 p99 延迟处于合理范围内。

多查询扇出与排名融合

即使是单跳问题,用户的表述也只是各种合理解法分布中的一个样本。“我该如何取消”有许多同义表达:“结束我的订阅”、“停止计费”、“移除我的计划”。每个同义表达嵌入到向量空间的位置都略有不同,每个表达检索到的前 k 个结果(top-k)也略有差异。不同表述之间的交集比任何单一检索结果都更可靠。

多查询扇出(Multi-query fan-out)会生成用户查询的 N 个同义改写,对每个改写进行检索,并融合结果。标准的融合算法是倒数排名融合(Reciprocal Rank Fusion, RRF),它根据文档在各个列表中出现的排名,按照 1/(k + rank) 的总和为每个文档评分(k 通常设置为 60)。在多个列表中排名靠前的文档会胜出;仅在某一个列表中排名靠前但在其他列表中未出现的文档则会被视为主题偏移而被过滤。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates