Start with two independent retrieval jobs

Dense and sparse retrieval answer different versions of the same question. Dense search can surface content with related meaning even when its wording differs from the query. BM25 can strongly reward exact terms, identifiers, product names, error codes, and uncommon phrases.

Rather than assuming one path should always rank first, retrieve a bounded candidate pool from each path. The application can then merge and rank the union. This keeps a lexical match from being discarded simply because its embedding is less similar, and keeps a semantically relevant result available when the query wording is incomplete or indirect.

  • Run dense search against the vector representation of the query.
  • Run BM25 against the original query text.
  • Request a defined candidate count from each retrieval path.
  • Keep document identifiers and source-specific ranks with every candidate.

Merge by document identity before scoring

The two candidate lists will often overlap. Deduplicate them by a stable document or chunk identifier before applying a combined ranking rule. A shared result is useful evidence: it was selected by both semantic and lexical retrieval, even if neither raw score is directly comparable to the other.

Avoid adding raw dense similarity to a raw BM25 score without calibration. The values come from different scoring systems and may have different ranges, distributions, and meanings. Rank-based methods are often safer when an application does not have a validated score-normalization procedure.

  • Build a union of dense and sparse candidates keyed by identifier.
  • Record whether each candidate appeared in dense search, sparse search, or both.
  • Use ranks, normalized scores, or a learned application-side ranker instead of unexamined raw-score addition.
  • Preserve metadata needed for later filtering, display, and auditing.

Use reciprocal rank fusion as a transparent baseline

Reciprocal rank fusion, commonly abbreviated RRF, is a straightforward baseline for merging ranked lists. For each candidate, add a contribution based on its position in each list. A candidate returned by both paths receives two contributions; a candidate that ranks highly in one path can still remain competitive.

The standard form is RRF(d) = sum over lists of 1 divided by k plus rank of d. The constant k reduces the difference between nearby ranks and should be chosen and evaluated for the application’s corpus and query mix. RRF does not remove the need for relevance testing, but it provides an explainable starting point before introducing more complex ranking logic.

  • Choose a candidate-pool size that allows useful results from both retrieval paths to reach the merge stage.
  • Calculate an RRF score from each candidate’s available ranks.
  • Apply required filters and business rules consistently around the retrieval workflow.
  • Evaluate separately on exact-term queries, natural-language questions, and mixed queries such as product names plus symptoms.