Hybrid Search
20 min
Pure vector search is great at semantic matching (a query about "canine companions" can retrieve a document about "dogs" even with zero shared words) but can miss an exact term the user cared about — a product SKU, an exact name. Pure keyword search is the opposite: exact matches guaranteed, but no notion of "close in meaning." Hybrid search blends both signals into one score, capturing the strengths of each.
def keyword_score(query_terms, doc_terms):
query_set = set(query_terms)
doc_set = set(doc_terms)
if not query_set:
return 0.0
return len(query_set & doc_set) / len(query_set)
def hybrid_score(vector_sim, keyword_sim, alpha=0.5):
return round(alpha * vector_sim + (1 - alpha) * keyword_sim, 4)
alpha is a tunable knob a real search system exposes -- turning it toward 1.0 trusts semantic similarity more (better for vague, conversational queries), toward 0.0 trusts exact keyword overlap more (better for queries with specific names/codes the user typed deliberately).
Using the provided `cosine_similarity`, `keyword_score`, and `hybrid_score`, write `rank_hybrid(query_vector, query_terms, candidates, alpha=0.5)`: `candidates` is a list of `(doc_id, vector, terms)`. Compute each candidate's hybrid score and return the doc IDs sorted highest-score first.
Why would a search system combine keyword matching AND vector similarity instead of relying on just one?
You can blend vector similarity and keyword overlap into a single tunable hybrid ranking score, covering each signal's individual blind spots.