July 2, 2026 · 2 min read
Pure vector search misses exact matches; pure lexical search misses paraphrase. Hybrid retrieval covers both — if you combine the scores correctly.
Vector search is good at "these mean the same thing." Lexical search (BM25 and friends) is good at "this contains the exact term." Production search systems usually need both.
Pure vector retrieval struggles with:
Pure lexical (BM25) retrieval struggles with:
The core difficulty isn't running both searches — it's combining two scores that live on different scales. A common approach is Reciprocal Rank Fusion, which combines ranks rather than raw scores, sidestepping the scale mismatch:
def reciprocal_rank_fusion(rankings: list[list[str]], k: int = 60):
scores: dict[str, float] = {}
for ranking in rankings:
for rank, doc_id in enumerate(ranking):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)
return sorted(scores, key=scores.get, reverse=True)
Each retrieval method (lexical, vector, and optionally others) produces its own ranked list; RRF merges them into one without needing calibrated scores.
A fixed 50/50 blend is a starting point, not an answer. Queries containing identifiers or quoted phrases should weight lexical retrieval higher; short conversational queries usually favor vector retrieval. This is itself something worth learning from query logs rather than hand-tuning once.
If hybrid search feeds a RAG pipeline, evaluate retrieval quality (precision/recall against a labeled set) independently of the generated answer's quality. Conflating the two makes it impossible to tell whether a bad answer came from bad retrieval or a bad prompt.