混合检索:语义搜索与词汇搜索
为什么要结合词汇搜索与语义搜索?两者各有优势与限制。词汇搜索,例如 BM25,精确匹配关键词,善于识别少见术语、名称和准确短语;查询与文档使用不同词语时,则可能检索失败。纯语义搜索通过稠密 embedding 捕捉上下文含义和同义表达,取得概念相关文本,但可能忽略精确关键词或专业术语。我们已在上一个 Sprint 使用过稠密 embedding。
可以这样理解:
- 词汇搜索:适合精确词语匹配,例如代码中的
PyTorchLightning,但较难处理同义词和改述。它可能遗漏上下文,或返回包含关键词、实际语境却无关的文档。 - 语义搜索:捕捉含义和改述,例如
CEO与executive,有助于召回与理解。但它可能忽略某个关键少见词是否真正出现,返回概念相关却缺少重要关键词的文档。例如,询问“微调机器学习模型”时,向量搜索若错误理解语境,也可能返回“烘焙”相关内容。
混合检索并行执行语义与词汇搜索,再合并结果,以提高文本搜索相关性。用户因此更容易取得既包含关键词、又符合查询意图的内容。
结果如何合并?可以应用融合算法。简单方法是归一化两个系统的分数,再加权求和,例如 70% 向量分数加 30% BM25 分数。原始 BM25 与余弦相似度的量纲不同,必须先处理。某次查询中,BM25 分数可能位于 0~12,余弦相似度可能位于 0~1,需要转换到可比较的范围或分布。常见方法包括 min-max scaling 和 Z-score:
- Min-max scaling:将数值缩放到固定范围,通常为 [0, 1]。每个值减去最小值,再除以最大值与最小值之差,使不同尺度的分数能够按比例比较。
- Z-score:每个值减去均值,再除以标准差,得到均值为 0、标准差为 1 的分布,极端值位于距均值较远的位置。均值和标准差本身也受异常值影响,因此这种标准化仍需关注异常值对结果的影响。
归一化之后,可以采用线性组合,并调节语义和词汇分数各自的权重。
简单加权和往往不如基于排名的融合方法稳定。Reciprocal Rank Fusion(RRF,倒数排名融合)已成为常用方法。它使用排名位置计算组合分数:将文档在各结果列表中的 1 / (k + rank) 相加,k 通常取 60,以平缓最高排名的影响。排名第一的贡献为 1 / (60 + 1) ≈ 0.0164,第二名为 1 / (60 + 2),以此类推。排名越高,贡献越大。相加后,多个方法都认为重要的文档更容易居前;只在一种方法中排名很高的文档,也有机会进入合并结果。RRF 无需监督训练,研究中曾优于更复杂的学习型融合方法。
这种方式简单且稳健:每个排名较高的结果都获得一票,排名较低时权重递减。许多向量搜索引擎已内置 RRF 支持。
混合检索的取舍
主要成本来自同时执行稠密与稀疏两类查询,可能增加延迟与基础设施复杂性。Pinecone 混合索引、Qdrant 多向量搜索等方案通过同一引擎处理两类搜索,能够减轻这一问题。收益通常包括更高召回率,找到单一方法遗漏的信息,以及更高精确率,排除另一方法可能引入的无关结果。长尾查询和专业领域搜索尤其适合混合检索,例如企业资料中的专业术语,或同时包含一般概念与专有名称的查询。动态调整语义与词汇权重,可以让某些查询更重视准确关键词匹配,形成关注关键词的语义搜索。
检索结果的重排序策略
即使采用混合检索,最初取得的前 100 个文本块仍可能包含无关项,或排序不理想。重排序利用能力更强的模型,对初步候选结果重新排序。关键是只对少量候选使用成本较高的深度模型,避免对整个语料库执行。这也是推荐系统在 LLM 出现前就采用的常见结构:先搜索,再重排已检索结果,必要时再对前 N 项进行下一轮排序。
本节介绍 bi-encoder、cross-encoder 和基于 LLM 的评分三类方法。典型 RAG 流水线先用向量或 BM25 等快速方法取得前 N 项,再用更准确的模型得到最终前 K 项。例如,简单搜索先取得 1000 项,强模型重排其中前 50 项,再对最终 10 项采用成本更高的方法。
Bi-encoder 重排序:增强语义相似度。Bi-encoder 与检索中的双编码器属于同类模型,但重排序阶段可以使用更先进或经过领域微调的版本。假设最初用 BM25 取得 100 篇文档,再用 Sentence Transformer 分别编码查询和这些文档,计算每一对的余弦相似度,即可按语义相关性排序。重排序模型可以比初检模型更大或更专门化,以取得更好的相关性分数。
Bi-encoder 速度快、易于扩展,并可并行处理。文档向量可以预先计算并保存,方法也较简单。但查询与文档分别编码,最终通过点积等方式比较,无法像更深的联合模型一样捕捉跨文本交互。它通常比 cross-encoder 便宜,能够在较少增加延迟的情况下改善基线。例如,先用 BM25 或轻量 embedding 保证召回,再用强 embedding 模型排序,常能改善 MRR 等指标。它仍可能遗漏微妙的相关性信号,因为它主要比较整体相似度,难以联合分析文档为何与查询相关。Bi-encoder 适合广泛召回和初步排序;要求最终顺序更准确时,可以采用 cross-encoder。
Cross-encoder 重排序:基于 Transformer 的评分。Cross-encoder 将查询与单篇文档一起输入,直接输出相关性分数。典型模型为 BERT 或其变体,输入形如 [CLS] Query [SEP] Document [SEP],输出一个分数,例如 0~1,或相关/不相关分类。模型能够联合考虑查询和文档中的词语,捕捉细微语义、上下文和精确短语匹配。Cross-encoder 在 MS MARCO 等基准上取得过领先排序效果。
实际应用中,可以为 50 篇候选文档分别计算查询—文档分数,再按 50 个分数排序。训练良好的 cross-encoder 可以纠正 bi-encoder 的错误,例如降低关键词齐全、语境却无关的文档排名,或根据查询区分水果 Apple 与公司 Apple。
代价是计算成本:每个查询—文档对都需要通过完整 Transformer,未经优化时可能增加数百毫秒延迟。高查询量、每次又有大量候选时,会消耗较多 GPU。可以采用蒸馏或轻量模型,例如 cross-encoder/ms-marco-MiniLM-L-6-v2;将文档限制为例如 512 个 token;批量计算;或缓存常见查询结果。Cross-encoder 不产生可复用的独立文档 embedding,因此更适合重排其他方法已筛选的候选子集。
其优势是准确性高,能够识别微妙相关性,适合法律检索等高精度任务,以及希望减少无关上下文和模型幻觉的问答系统。Cross-encoder 重排序被广泛用于改进 RAG,因为它能够减少进入答案生成器的无关信息。
基于 LLM 的重排序:LLM 作为评判者。可以向 GPT-4.1 等模型提供查询和一篇或多篇候选文档,通过提示词评估文档对问题的帮助程度,或选出最相关结果。例如:“你是一位乐于助人的助手。给定用户问题和文档,请输出 1~5 的相关性分数,表示文档对回答该问题有多大帮助。”
优势在于灵活:LLM 能理解复杂相关性标准,并遵循具体要求。例如,用自然语言要求优先考虑可信来源、较新信息或领域规则,让语义理解与业务要求共同影响排序。复杂相关性难以单靠相似度表达时,这很有价值。例如:“请重新排序这些结果,优先考虑最新发布、来自权威域名的内容。”后续课程还会讨论相关方法。
但 LLM 请求通常更慢、成本更高。每篇或每小组文档都成为一次提示词输入,即使 token 价格下降,长期累计成本仍需考虑,实时搜索也可能无法接受其延迟。因此更适合请求量低、单次价值高的场景。另一个问题是评分一致性:提示词措辞或上下文的微小变化,可能改变同一批文档的顺序,需要认真设计和评估。也可以结合其他方法,仅对低成本方法筛出的最后 5 项使用 LLM,或仅对重要查询启用。
推荐采用多阶段排序:快速检索器,例如 bi-encoder 或混合搜索,取得较广的相关候选;cross-encoder 对候选子集精确排序;有充分价值和预算时,再让 LLM 检查最后少数结果。
按业务需要调整分数与排序
实际应用中的相关性还受到业务优先级和元数据条件影响。例如,企业搜索可能优先官方文档,新闻搜索重视新内容,电商搜索可能提高有库存、高评价或特定价格商品的排名。元数据可以保存在向量数据库 payload 或外部系统中,查询时据此调整分数或顺序。
常见规则与信号包括:
- 来源与权威性:提高可信知识库的排名,降低来源不明的博客排名,让高质量信息优先显示。多个部门贡献内容时,也可以优先用户所在部门的内部资料。
- 时效性:新闻、技术等快速变化领域中,可以优先较新文档。例如,使用时间衰减函数,让额外分数随文档年龄减少,或把相关性乘以新鲜度系数。
- 质量与受欢迎程度:利用平均评分、赞同数等用户反馈。某结果经常被相似查询的用户点击时,也可以提高排名。
- 个性化与上下文:已知用户是工程师时,“故障排查指南”可以优先技术资料,降低入门 FAQ 的顺序。“本地办公室”等查询可以优先用户当前所在地或居住地的结果。
- 字段与类型:食谱搜索可以让标题匹配比原料字段匹配更重要;企业搜索中,标签或摘要匹配可能比正文偶然出现更相关。若 PDF 手册通常比自动生成的 API 文档更有用,也可优先手册。
- 业务规则与领域逻辑:例如,查询包含 X 且文档属于 Y 类时,将其优先展示。合规要求可能规定特定查询必须先显示某些文档;电商促销也可能需要提高某品牌商品的排名。
- 降低不合适属性的权重:可以降低过旧资料,或相对于经过验证的内容,降低未核实社区资料的分数。
一些平台提供 function score 或 scoring profile。Azure Cognitive Search 可以在索引中定义字段权重,例如字段 X 权重为 5,并根据日期曲线提高新内容分数;查询时引擎应用这些因子。Elasticsearch 的 function_score 可以根据数字字段使用衰减、线性等函数。例如,文档类型为 official 时加 3 分,document.category = "HR" 时乘以 1.2。需要实验校准:权重太高,可能仅因某属性就把无关文档排到前面;太低则可能没有明显效果。
明确的排除规则也可以通过过滤实现。例如,用户无权查看的内容,或业务要求不展示的缺货商品,可以完全排除。Qdrant 等向量数据库支持在向量查询中加入过滤条件,例如 product.in_stock == true 或 document.language == "EN"。过滤决定是否纳入,分数调整则表达相对优先程度。实际应用经常结合二者:先排除不允许展示的内容,再调整其余结果。
引擎不原生支持某种元数据加权时,可以执行多次搜索并合并。例如,一次普通搜索,一次仅针对高优先级子集搜索,再合并结果,让部分前排位置由优先内容占据。始终测量调整对相关性指标和用户满意度的影响。过度加权容易让字段符合条件、却实际不相关的内容排在前面。可以对不同权重进行 A/B 测试,或利用小型标注查询集评估。排序调整应支持业务目标,同时保持搜索提供相关答案的基本目的。