这道题其实是在问,怎么让机器真正理解一段文本的意思,然后从海量数据里快速找到最相关的内容。我会从五个环节来讲,但重点放在那些容易踩坑的地方。
先说文本预处理。很多人上来就切固定长度,但语义检索最怕把一句话腰斩。我的做法是按语义段落切分,比如一个POI描述,我会把名称、地址、营业时间这些结构化字段单独处理,正文部分用重叠窗口保证上下文连贯。这里有个前提,[[分块]]粒度要跟你的检索场景匹配,如果是搜客服退款政策,块可以大一点,包含完整条款;如果是搜具体商品参数,块就得小,甚至按属性拆。另外元数据一定要保留,比如时间戳、地理位置,后面做混合过滤全靠它。
然后是[[Embedding]]模型选型。通用场景我会首选BGE或者M3,但如果业务有领域特性,比如地图POI或者医疗文档,最好在通用模型基础上做领域微调。维度上768或1024就够了,更高收益递减,存储和计算成本反而直线上升。真正落地时,我通常会同时跑一个轻量模型做初筛,再用大模型做精排,效果和成本之间取个平衡。
向量索引这块,我默认选[[HNSW]],因为它查询快、精度高,对大多数场景够用。但如果数据量到十亿级,内存扛不住,我会用[[IVF-PQ]]做量化压缩。这里有个常见失败场景:很多人直接拿HNSW做高频增量更新,结果索引碎片化严重,召回率暴跌。我的做法是增量插入走独立小索引,查询时合并,定期全量重建。
存储方案上,向量和标量一定要分开存。向量丢到[[Faiss]]或Milvus里,标量字段比如订单号、用户ID放在PostgreSQL或ES里,通过ID关联。分片策略按业务线或者地理区域切,查询时路由到对应分片,避免全表扫。冷热分离也很有必要,高频向量放内存或SSD,冷数据压缩归档,能省不少成本。
最后是查询优化。第一步是查询重写,比如用户搜“退货运费谁出”,我会用Query2Doc扩展成“退款运费承担”,或者同义词改写,解决语义鸿沟。第二步是两阶段检索,向量召回Top-K之后,用[[Cross-Encoder]]做精排,这一步能显著提升精度,但延迟也高,所以K不能设太大。第三步是缓存,热门Query的向量结果和Embedding结果都缓存起来,能挡住大部分重复请求。
其实还有一个经常被忽略的点,评估。线上效果好不好,光看Recall@K不够,还要关注用户实际点击行为和业务指标。比如在客服场景,用户搜了之后有没有解决问题,比召回率更重要。这个评估体系怎么搭,我觉得比检索本身更考验工程能力。
所以整体来说,我会把[[向量数据库]]看成一个系统工程,每个环节都有trade-off,没有银弹。更倾向根据业务场景做组合方案,而不是套一个固定模板。