这道题本质问的是,怎么把非结构化的文本变成计算机能高效检索的语义空间。说白了,向量召回就是用向量距离代替关键词匹配,来找语义相似的内容。
先说[[嵌入]]。我用 BERT 或者 Sentence-BERT 这类模型,把一段文本变成一个稠密向量,维度一般在 256 到 1536 之间。这里有个前提:模型必须在你业务数据上微调过,否则通用模型对专业术语的语义区分很差。举个例子,客服场景里“退款”和“退货”在通用模型里可能很近,但在业务里一个涉及资金一个涉及物流,必须分开。如果不微调,召回就会混。
相似度计算,最常用的是余弦相似度。但实际工程里,我一般先做向量归一化,这样余弦就等价于内积,计算更快。归一化之后,找最近邻就是找内积最大的,直接调 [[Faiss]] 的 inner product 搜索就行。欧氏距离也有用,但前提是你的向量空间分布均匀,比如用 [[CLIP]] 做的[[多模态]]嵌入,否则内积更稳定。
索引这块是核心。暴力搜索不可能,必须用 [[ANN]] 近似最近邻。主流方案两个:[[HNSW]] 和 [[IVF]]-[[Product Quantization]]。HNSW 召回率高,但构建慢、内存大,适合几百万级别的库。[[IVF-PQ]] 把向量量化压缩,内存能省 4 到 32 倍,但召回会掉。真正落地往往是两者结合:比如用 IVF 做粗分,每个桶里再跑 HNSW,或者用 HNSW 做索引,但向量做了 PQ 量化来省内存。
大规模系统里,光一个索引不够。我会做数据分片,按 doc ID 或者向量聚类分到多台机器,查询时广播到所有分片再归并。量化压缩必须上,否则 1536 维向量存一亿条就要 600G 内存,压到 128 维量化之后只要 50G。热点查询结果要缓存,这能扛住 80% 的重复流量。还有就是[[混合检索]],向量召回 + [[BM25]] 关键词召回,用 RRF 融合排序。因为向量对罕见词、精确数字、订单号这类不敏感,关键词正好补上。比如搜“订单 12345 退款”,关键词能把那条记录精确捞出来,向量只能捞语义相似的退款场景。
这里有个常见的失败场景:很多人以为向量召回可以完全替代关键词,结果上线后发现长尾 query 召回率暴跌。所以我的做法是,先分析 query 类型,如果是实体类、精确匹配类,走关键词;如果是意图类、同义改写类,走向量。两条路并行,靠融合排序兜底。
总结一下,我会把向量召回看成召回层的语义通道,它和关键词通道互补,不能互相替代。我更倾向用 HNSW 做主力索引,配合 IVF-PQ 压缩内存,再根据业务 query 分布动态调整混合比例。