Book a 30-min callBook a call

Hybrid search: combining BM25 and vector search, and how rank fusion works

How BM25 and vector search fail in different ways, how reciprocal rank fusion and weighted score fusion merge them, and how to tune and test the result.

A keyword list and a field of dots merging in a fusion box, then a single ranked list.
Written by
BYD Tech
Published
Topic
Deep dives
Reading time
12 min read

Questions people ask

Is BM25 still worth running if I have good embeddings?

Usually, yes. Embeddings are weakest on exact identifiers, rare terms and numbers, which is where BM25 is strongest. Measure it: run both retrievers on labeled queries and count how many relevant chunks only one of them finds.

What value of k should I use in reciprocal rank fusion?

Start with 60, the value from the original paper, which the authors found near-optimal but not critical. Smaller values give more weight to the very top ranks of each list. Tune it only if your labeled queries show a clear difference.

Should I normalize BM25 and vector scores before combining them?

If you combine scores, yes, per query and per retriever, because the two scales are unrelated. Min-max is common but stretches small differences in short lists. Reciprocal rank fusion avoids the question by using ranks only.

Do I need a reranker if I already use hybrid search?

Not always. Fusion improves which candidates you find; a cross-encoder reranker improves their order. Add one when the right chunk is often in the candidates but not in the few you send to the model, and when the added latency is acceptable.

Is PostgreSQL full-text search the same as BM25?

No. Its built-in ranking functions, ts_rank and ts_rank_cd, use term frequency, proximity and weights within a document, and the documentation says they use no global information, so there is no IDF. You can still fuse its results with vector results by rank.

Building something like this?