这道题问的是怎么让向量检索系统既准又相关,其实本质是在解决一个矛盾:用户想要的到底是语义上的相似,还是字面上的精确匹配。这两个东西有时候是冲突的,你要做的是在不同场景下找到平衡点,而不是追求单一指标的最优。
具体来说,我一般从几个层面去考虑。先说最基础的 [[Embedding]] 层。模型选型很重要,通用场景用 BGE 或者 M3E 这类通用模型就够了,但如果是垂直领域,比如电商客服场景里用户问‘退款多久到账’,通用模型可能把‘退款’和‘退货’混在一起,这时候就得用业务数据做对比学习微调,缩小语义鸿沟。不过这里有个前提:你的标注数据质量得过关,不然微调反而会带偏。
再一个就是召回策略。我倾向于走 [[混合检索]],就是向量召回和 [[BM25]] 关键词召回一起上。向量召回擅长理解意图,比如用户说‘想找个便宜点的火锅店’,它能找到语义相似的店;但用户如果明确说‘海底捞’,关键词召回就能保证精确命中。这两条路各有适用场景,落地上我一般会加权融合,权重根据业务数据调。比如在订单检索场景里,用户输入‘2024年3月15号的订单’,关键词匹配订单号或者日期就比语义相似重要得多,我会把关键词召回权重调高。
有了候选集之后,下一步是[[重排序]]。粗排阶段用向量相似度快速筛出 Top-K,精排阶段再用 [[Cross-Encoder]] 这种交互式模型去算 query 和每个文档的相关性分数。这个步骤很关键,因为 [[Bi-Encoder]] 的向量相似度有时候会漏掉一些细微的匹配,比如‘苹果手机’和‘iPhone’在向量空间里可能很近,但‘苹果’和‘水果’也可能很近,Cross-Encoder 能更好地做区分。不过它的计算成本高,所以只能用在精排阶段,对候选集大小有限制,一般几百条以内。
还有一个容易被忽略的点是查询侧优化。用户输入的 query 往往很随意,比如‘火锅’可能想找‘重庆火锅’或者‘老北京涮肉’,这时候做查询改写,扩写同义词、纠错、补全意图,就能显著提升召回率。另外,如果 query 里带有结构化约束,比如‘附近’、‘100块以内’,我会在查询理解阶段把它抽出来做前置过滤,而不是全扔给向量检索。
说到查询改写,这里面其实有个坑:如果改写太激进,比如把‘手机’扩写成‘智能手机、移动电话、手持设备’,反而可能引入噪声,拉低精度。所以我会先做意图分类,对模糊 query 才做改写,对精确 query 尽量保持原样。这个度怎么把握,其实是挺值得细聊的。
最后,整个系统上线后一定要有评估和迭代的闭环。离线指标我会看 Recall@K 和 NDCG,在线通过 AB 实验看点击率和转化率。更重要的是建立 Badcase 分析机制,比如用户搜‘充电宝’返回了‘充电器’,这种案例回流回来,可能发现是 Embedding 模型没区分好,或者重排序模型没学到这个模式,然后针对性优化。
所以整体上,我更倾向于把向量检索系统看作一个组合工程,而不是单个模型的比拼。混合检索搭骨架,重排序做精调,查询改写做入口优化,再加上持续的评估迭代,这样才能在真实业务里跑得稳。