先看现象。Milvus 2.4 的 hybrid_search 用 WeightedRanker(0.7, 0.3),dense 走 bge-m3 的 1024 维,sparse 走同一个模型输出的词权重,稀疏分支单独调用能召回 40 条,混进 WeightedRanker 后结果全是 dense 的文档,权重改成 0.5 对 0.5 依然看不到稀疏来源。

真正麻烦的地方在于,几个原因的现象几乎一样。稀疏字段的索引没建,hybrid_search 里那一路实际返回空列表。还有一种是查询文本没有经过同一个分词器,稀疏向量的 indices 对不上。

不容易想到的是,WeightedRanker 的权重作用在归一化分数上,两路分数量级差太多时会被压没。

还有一种情况,字段名写错成 sparse_vector 而集合里叫 sparse,参数校验被静默跳过。

单独跑稀疏分支看召回条数

python -c "from pymilvus import Collection;c=Collection('kb');print(len(c.search(data=[{'indices':[3,9],'values':[0.6,0.4]}], anns_field='sparse', param={'metric_type':'IP'}, limit=10)))"

再往下确认稀疏字段有没有建索引

python -c "from pymilvus import Collection;print([(i.field_name, i.params) for i in Collection('kb').indexes])"

接着按字段实际名字重跑两路融合

python -c "from pymilvus import Collection, WeightedRanker;c=Collection('kb');r=c.hybrid_search([c.search(...)] , rerank=WeightedRanker(0.6,0.4), limit=10);print(r[0][0].entity.get('source'))"

剩下的就是把两路各自的前十条打成表对比来源分布

python split_recall.py --dense 10 --sparse 10 --out recall.csv

改完先别撤,确认这几项。融合结果里能稳定看到 sparse 单独召回的文档,两路来源比例接近设定权重,去掉 dense 分支后答案质量才有明显下降。

补一句。权重按分数归一化后才有意义,先确认每一路单独都非空,再谈比例,否则调权重只是在给一条空路子分票。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。

作者 9823189