ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

混合检索实践:关键词与向量检索的边界与融合方案

混合检索实践:关键词与向量检索的边界与融合方案 下面这篇是我自己复盘内部搜索项目时整理的完整思考。项目里上过纯向量检索也踩过不少坑最后回到“关键词向量”混合老路上来这中间的过程和结论值得拿出来聊聊。如果你正打算给系统加语义搜索或者已经加了但效果不理想这篇文章应该能帮你省掉一些试错成本。先交代一下背景我们做的是一个企业级内容检索平台文档量在几百万级别覆盖技术手册、业务FAQ、合同模板、内部Wiki。最初用的就是Elasticsearch做全文检索后来团队想借着大模型的热度把语义搜索接进来上线一段时间后发现体验显著提升但同样暴露出一堆纯关键词检索时代没有的问题。比如搜“怎么请年假”居然能召回“离职流程”搜一段代码报错日志却找不准对应的异常项。当时我才意识到一个被很多人忽略的常识语义搜索不是万能的向量检索和关键词检索各有清晰的边界。边界不是靠口号划出来的是靠真实查询样本一锤一锤砸出来的。1. 先理清两种检索方案的本质差异很多搞工程的同学容易陷入一个误区把向量检索和关键词检索当成“新老技术替代”的关系。实际上它俩解决的是检索链条上不同层面的问题不存在谁取代谁只存在谁适合哪种查询模式。1.1 关键词检索基于倒排索引的“字面匹配”关键词检索的核心是倒排索引加BM25这类词频统计模型。文档被切词器拆成一个个token建立从token到文档列表的映射。查询进来后同样拆词然后计算每个词在文档里的出现频率、逆文档频率、字段长度归一化等指标最后算出一个相关性得分。这套机制的特点非常明显它只看“表面文字是否命中”。你搜“苹果手机”它就只找同时包含“苹果”“手机”这两个词的文档包含“iPhone”的文档它是认不出来的除非配置了同义词扩展。但换个角度看正因为这种严格性它天然适合处理那些必须精确匹配的场景比如订单号、错误码、API名称、用户名这些字符串一旦语义化反而坏事。BM25在Elasticsearch、Lucene、OpenSearch里都是默认排序算法它本质上是在做一个概率模型假设一个词在文档中出现次数越多越重要但同时在所有文档中出现越频繁就越不重要。这种朴素的统计思想在绝大多数长尾查询里表现稳定没有花哨的模型推理也不吃GPU集群成本通常远低于向量方案。1.2 向量检索基于语义空间的“意图匹配”向量检索的原理是把文本通过嵌入模型映射成一个高维稠密向量也就是把一段话的语义压缩成几百维的浮点数数组。查询和文档的距离通过余弦相似度、内积或者欧氏距离来衡量。它的强项是打破字面差异的壁垒。比如用户输入“工资发放延迟怎么办”文档里写的是“薪资到账时间超过约定周期”二者字面没有重叠但语义高度相关。这套逻辑的核心假设是相似的语义在向量空间里应该有相近的位置。这就把检索问题转化成了“在向量空间里找最近邻”的问题。实际工程里文本向量化之后通常存进支持ANN近似最近邻检索的引擎比如Faiss、Milvus、Elasticsearch的kNN接口、Weaviate、Qdrant等。查询时不再依赖分词准确度而是依赖模型对语义的理解能力。1.3 为什么“语义搜索”被高估了这里有一个很容易忽略的大前提嵌入模型是在通用语料上预训练的。它学到的语义是“大众理解”不一定是你的业务语义。如果你面对的是法律合同、医疗知识库、芯片设计规范这类强领域文本通用模型的语义空间往往会出现明显偏差。更麻烦的是语义检索的结果高度不可解释。它能找到“看起来相关但根因完全跑偏”的文档而且你很难从系统层面立刻定位是哪一层出了问题是模型不好是分块太大还是阈值设低了。我自己对标过一批查询结论是语义搜索解决的是“词不达意”类问题关键词搜索解决的是“精准命中”类问题。二者面对的不是同一批用户需求自然也不该用同一个方案硬顶。2. 向量检索的边界五个我踩过的坑这部分是全篇最想分享的内容。向量检索的边界从哪里画不是靠理论推演下面这五类场景是我的实际数据说话。2.1 词汇表冷启动和未见词如果你要在生产环境跑语义搜索第一个绕不开的问题就是模型没见过这个词怎么办。嵌入模型的词典是固定的。专业名词、新造词、品牌名黑客、内部项目代号这些词在预训练阶段几乎没有出现过模型没有稳定的token表示最后出来的向量往往被映射到某个“通用词汇”附近语义完全失真。举个例子我们内部有个产品代号叫“Kylix”通用模型基本没听说过这个词。用户搜“Kylix部署失败”纯向量检索返回一堆基于模糊上下文匹配的垃圾链接而关键词检索因为倒排索引里老老实实存了这个token一查一个准。所以我的第一个边界结论词汇在领域内足够特殊和稀缺时关键词检索完胜向量检索。这不是模型参数量能解决的本质上是知识覆盖面的问题。2.2 语义歧义和同义词区分语义搜索擅长找相关但也容易被“相关”带跑。最经典的例子是“苹果”。搜“苹果怎么调音”,向量检索可能把所有关于手机和电脑的内容都当作强相关因为模型里“苹果”和“手机”的关系紧密。但你如果是在一个数字音频工作站知识库里搜用户要的是水果公司吗显然不是。向量模型对同一段文本在不同语境下的区分能力完全取决于训练语料里有多少这种消歧样本。通用模型在这块的表现远远达不到把“苹果”在不同行业下分开的程度。这带来一个直接的工程后果你需要在查询输入前加一层意图识别和改写甚至配置行业实体库。不然语义搜索会像一个大方的推荐员把所有沾边的候选都捧到你面前但精度却被冲淡。2.3 短文本和碎片化文本向量检索在长文本检索上确实很强句子越长、上下文越完整语义向量越稳定。但在真实业务里用户搜索往往是一两个词比如搜“退款”文档分块后也可能缺上下文。查询向量和文档向量都“短小单薄”余弦相似度算出来就很不稳定抖动非常大。同样的问题也出现在搜索栏建议、自动补全和拼写纠错这些偏字面操作的场景里。向量模型在高频短查询上不仅没有优势有时候还不如一个简单的ngram模糊匹配。另外短查询的向量召回噪音很大。你用“退款”做向量检索可能把“退款政策”“退款流程”“退款失败原因”“退款金额错误”全拉进来因为它们和“退款”的相似度差距不显著最终排序要靠精排模型或者规则去兜底。2.4 结果不可解释和无法调试关键词检索的优势之一是可以给用户一个清晰的展示逻辑“因为文档中包含关键词X、Y、Z”。但向量检索给不出这种可回溯的答案。项目上线后被业务方质疑“为什么这个文档排在第一位”我只能从相似度分数去解释可这分数本身是个黑盒。更麻烦的是向量索引的更新是不可控的。你重训了模型参数或者换了一个embedding模型所有历史向量全部需要重算重写。如果业务方问“为什么昨天能搜到、今天搜不到了”你不知道是该怪检索环节还是索引同步环节排查链路长了很多。因此在面向严格合规要求的场景比如金融、医疗纯向量检索往往过不了审计关。做项目时如果还有客户支持“人工审核检索结果来源”的需求就必须保留原文字段检索作为兜底和证据链。2.5 资源消耗、成本与延迟很多人只盯着检索效果忽略了向量检索的资源账。文本向量化需要GPU或者高配CPU索引后的向量数据占用存储是原始文本的几十倍。查询时ANN检索本身还算便宜但如果文档量大、向量维度高延迟一不小心就超预期。我们当时的线上压测数据很直接关键词检索P95延迟在40ms左右向量检索一批误召回导致精排到数据集上P95直接到180ms以上。在多轮迭代的场景里这种延迟能让用户的耐心清零。所以向量检索不应该成为默认路径。我的实践结论是除非你的场景里有30%以上的查询都存在“同义改写”或“口语化表达”否则仅靠关键词检索加少量规则性价比远高于全量向量化。3. 关键词检索的优势和优势边界聊完向量检索的坑再回头看关键词检索。很多人以为关键词检索已经是上一代的老古董但在边界问题的讨论里它反而是那根最稳定的定海神针。3.1 关键词检索的不可替代之处最突出的优势是可解释性。BM25打分机制完全可复现用户可以知道为什么某条记录排前面业务方可以精确调整某个字段的权重。排查线上问题时你能直接打开倒排索引看每个词命中了哪些文档。其次是对精确无歧义查询的极高命中率。搜索“error 503”“product_id2243”“张三的劳动合同”这些查询天然是字面匹配的语义化反而是负优化。还有一点常被忽略的是分词灵活性。Elasticsearch的ngram分词器可以处理中文的班尼破译错别字问题配合同义词表和拼音插件覆盖面比直觉上大得多。很多所谓“需要语义搜索”的场景其实靠一层同义词扩展就解决了90%的问题。3.2 关键词检索的边界在哪里说回关键词有几类查询真不是它擅长的。用户口语化表达是最大的盲区。搜索“请假要怎么走流程”和文档里“申请休假所需审批步骤”没有任何字面重叠BM25直接给零分。这种场景下再怎么调参也没用。用户拼写错误也够呛。英文还好允许编辑距离的模糊匹配能兜住中文错别字和拼音输入混用分词之后基本全错。比如用户搜“离职壁开锅”你没法期待倒排索引直接命中“离职避坑”。最后一类就是长尾语义查询。如果用户对领域术语本来就不熟悉会用自己的理解描述需求关键词检索很难抓到那条正确的记录。这时候必须靠“语义改写”或者向量召回否则用户第一轮搜不到就会流失。3.3 不同场景下的检索方案选型基于边界分析我在项目里整理了一个粗略的选型表工程上可以直接拿来对照。业务场景推荐方案理由订单号、编号、错误码查询关键词检索优先精确匹配零误召回维基百科/帮助中心FAQ混合检索向量兜底强同义词口语化但仍保留关键词兜底代码片段/日志检索关键词检索优先子串匹配语义模型对代码理解能力有限商品搜索弱品牌词混合检索精排泛场景下两者互补明显长尾学术/技术文档向量检索为主关键词辅助专有名词多靠关键词太脆这个表不是标准答案但它提醒你一件事检索方案选型之前先看看你的查询样本里“词不达意”的比例有多高。如果连10%都不到直接用纯关键词方案就够了。4. 实操让混合检索成为可落地的默认方案到这里真正的核心问题来了既然都存在边界怎么做才能让两个方案形成互补我强烈建议所有新项目直接把“混合检索”作为默认架构。下面把我实际做的技术方案原原本本地摊开。4.1 整体架构与核心流程混合检索不是简单地把两个结果拼在一起而是一个“双路召回加单路精排”的流水线。整体分三步。第一路是BM25关键词召回从倒排索引拿一批候选第二路是向量召回从ANN索引拿一批候选。两路候选先通过某种融合算法合并成一个list再送进排序模型或规则精排最后输出给用户。我习惯把召回数量设宽松一点比如要求每路候选都取top 200。因为召回阶段宁多勿少就算有一些明显噪声到了精排阶段还有机会被压下去。如果召回阶段就卡死到top 20两路交叉覆盖的优势就很难发挥出来。4.2 双路召回的细节配置关键词召回环节我基本沿用Elasticsearch的BM25配置。这里有几个值得注意的参数调整。k1控制词频过饱和的阈值上调会让高频词影响变大下调则更克制b控制文档长度归一化力度b越大越长文档越吃亏。向量召回环节关键是选好embedding模型。我先后换过三版模型最后选定了针对中文优化的检索专用模型效果远好于通用文本向量模型。这步不要图省事省直接把文档切一个句子一个向量去存最后召回效果会很难看。我建议文档分块以段落为单位控制在200-600个token之间重叠控制在20%-30%。这样既保留上下文语义也避免了整篇文档一个向量导致的位置敏感度丧失。4.3 融合算法RRF和加权融合融合这一步最稳定且省心的做法是RRF全称Reciprocal Rank Fusion也就是倒数排名融合。核心公式就一行score(d) sum(1 / (k rank_i(d)))其中rank_i(d)是文档d在第i路检索结果里的排名位置k一般取60。这个公式看下来很朴素不看分数绝对值只看排名位置。因为BM25分数和余弦相似度根本不是一个量纲直接加权等于拿猫和狗比体重完全没有可比性。我试过用z-score归一化之后再加权融合效果波动比较大。原因是余弦相似度在不同查询下的分布差异太大有时候全班分数都在0.8附近有时候0.3就是头部了自适应归一化非常难做。反而RRF因为只依赖排位天然免疫分数分布漂移最终线上效果最稳。简单算一笔账关键词召回的一篇文档排第5位分数记1/(605)另一篇文档只在向量召回路排第3记1/(603)。如果一篇文档两路都在前面累积优势就非常大。这也符合直觉两路都认为相关的内容大概率是真相关。4.4 精排和线上评测融合之后的候选集可能还存在两路带来的“语义幻觉”所以必须上一道轻量精排。我们团队用的是一个cross-encoder模型相比双塔向量检索它能将查询和文档标题、内容拼接成一个序列强交互地计算相关性。这个模型不吃太多资源精排只用计算top 100以内的结果延迟可控。精排模型本质上弥补了向量检索的解释性缺失。你可以直接查看每个候选的交互分数判断某个结果究竟是因为关键词撞了还是语义贴合。业务方来质疑检索质量时这一步拿出的证据比向量相似度有说服力得多。评测环节我推荐去看两类指标叠加一是在离线测试集上算Recall20和MRR二是在小流量上线后用点击率、无结果率、长会话率来监控真实效果。我反复提醒团队离线AUC涨了不一定代表用户体验变好因为离线样本常常只覆盖了现有查询分布搜索体验里“惊喜感”和“无噪点”很难用指标完全量化。5. 常见问题快查与避坑实录最后这部分是线上的坑和排查笔记。每个问题我都见过别人反复踩这里一次性写透。5.1 混合得分没有明显改善相当常见的现象加了两路召回结果线上效果还更差了。这时候先检查两路召回结果的重合率。如果BM25和向量召回有70%以上重叠说明数据本身没有明显语义多样性向量路只是给关键词路“陪跑”融合后分数又引入了额外方差。解决办法是分别抽查两路召回的独有结果。如果向量路除了“语义相关”还带进来大量不同主题的噪声就把向量召回阈值拉高或者减少向量路的召回数量改成top 100。同时可以给关键词路加一点点权重因为它毕竟是精确命中可信度更高。5.2 向量侧带进来的“语义垃圾”向量召回经常基于“意图相似”找出一大堆跑题文档最典型的就是搜“如何降低库存损耗”却把“仓库选址策略”拉进来。原因是模型觉得两个文档的语义空间接近但业务上这是两个完全不同的问题。我的处理思路是做一个“业务域白名单”。如果查询里包含强领域关键词比如“库存”“损耗比例”那就在向量召回之后强制加一个字段级的filter比如产品线ID或者文档类型。把向量召回从纯ANN检索改成“ANN检索结构化过滤”能有效压制语义放飞的噪声。这个方案比单纯调阈值更精准。5.3 同义词问题解决不彻底混合检索不是万能药。用户搜“驾照”和文档写“驾驶证”向量模型可能知道它们相关但如果你把向量路关了或者文档未被正确分块关键词路就完蛋。我的建议是在关键词路上保留一套业务同义词表不要全部指望模型。同义词表写起来很快但维护成本高。我每个季度会拉一次搜不到结果或者低点击的查询列表人工筛选一批高频同义表达进词库。这个过程不性感但效果立竿见影很多“语义搜索应该解决的问题”其实在同义词表这一步就消化掉了。5.4 索引更新与一致性混合检索上线后最头疼的其实是工程运维侧的问题。关键词索引更新是实时的新文档写入后秒级可见向量索引的构建和更新如果走离线批量流程就会有时间差导致某个文档关键词能搜到但向量搜不到甚至反过来。我在项目里的做法是把向量索引分两段存量文档每天全量重建一次向量索引新增文档写入后实时做一个轻量向量化并追加到临时索引里查询时同时查主索引和增量索引再合并结果。这个设计牺牲了一点复杂度但保障了双路召回的数据一致性。6. 个人经验与最后建议走到这一步我想给同样做检索项目的同行一句实在话不要急着上向量检索。先回去把你线上真实的查询日志拉出来整理出前100个搜不到、或者用户反复换词搜的样例自己判断里面有多少是“字面匹配解决不了”的。如果占比不到三成关键词检索加同义词表加拼写纠错是最稳的成本方案。如果占比确实高再把语义搜索作为补充融进来但务必像我上面说的一样保留关键词路做RRF融合加精排别把全站检索压在纯向量模型上。我见过不少项目把embeddings当银弹结果上架后用户投诉又改回去来回折腾信心直接磨没了。还有一点小技巧想分享检索系统的迭代一定以“失败查询分析”为准而不是以“模型论文指标”为准。每次模型升级都跑一遍过去三个月里用户的失败查询集看新模型能覆盖多少之前的bad case同时也看它有没有把原本正确的结果挤掉。语义搜索尤其容易在“修复一个集合”的同时“破坏另一个集合”这种回归风险必须用真实查询长期盯住。我目前手上这套混合检索架构跑了将近一年效果虽然谈不上惊艳但胜在稳定可回溯。每次业务方问“为什么搜不到”我都能快速定位是分词问题、权重问题还是向量模型没有掌握某个领域术语这比任何嘴上说的“我们使用了AI搜索”都更有说服力。检索这件事从来不是比谁更炫而是比谁在真实数据上更靠谱。
返回列表