Star on GitHub
DocsConcepts

Hybrid search

Dense vectors are great at meaning. Keyword search is great at exact tokens. The best retrievers use both.

Why hybrid

Dense embeddings struggle with rare tokens — product IDs, error codes, proper nouns. Sparse retrievers like BM25 nail those but miss paraphrases. Combining the two reliably outperforms either alone.

Reciprocal Rank Fusion (RRF)

ts
function rrf(rankings: number[][], k = 60): Map<number, number> {
  const scores = new Map<number, number>();
  for (const ranking of rankings) {
    ranking.forEach((id, i) => {
      scores.set(id, (scores.get(id) ?? 0) + 1 / (k + i + 1));
    });
  }
  return scores;
}

RRF requires no score calibration between retrievers — it only uses ranks. It's the default fusion in Weaviate, Qdrant and OpenSearch.

Re-ranking

After fusion, pass the top-50 results through a cross-encoder (Cohere Rerank, BGE-reranker) to re-score with full attention between query and document. Latency cost is real but quality lift is the single biggest win in modern RAG.