Back to blog
RAG Systems

Hybrid Search in RAG: When BM25 and Embeddings Work Together

Two retrieval signals complement each other when policy and evidence remain one contract

BM25 for exact matches, embeddings for paraphrases, trusted filters and inspectable fusion

Original HYBRID-8 contract with failure modes and a production acceptance gate
hybrid search RAG, BM25 and embeddings, RAG architecture, lexical search, semantic search, RRF and HYBRID-8
Primary nodeHybrid retrieval contract
Routing modeHYBRID-8
StatusPUBLISHED
Two controlled search lanes converge through permission filters and fusion into source-linked evidence cards
HYBRID_8_V01: lexical and semantic candidates become usable context only after trusted scope and evidence checks.
TERMINAL_PREVIEW.LOG
$ retrieve hybrid --contract HYBRID-8
> scope: tenant / role / lifecycle / locale
> search: bm25 / embeddings / trusted corpus
> fuse: dedupe / ranks / diversity
> verify: source / locator / evidence / no-answer
> route: answer / review / fail-closed
Hybrid retrieval architecture

Hybrid search is not a checkbox that makes a RAG system “smarter.” It is a retrieval design in which lexical search and semantic search produce candidates for the same question, then a controlled pipeline decides which evidence may reach the model. The point is not to make two rankers compete. The point is to cover different kinds of evidence failure while preserving access control, source lineage and a safe no-answer path.

BM25 is useful when the wording itself carries meaning: product codes, legal clauses, error identifiers, release names, acronyms, quoted phrases and uncommon terms. Embeddings are useful when a question is expressed differently from its source: a support request describes an outcome while the manual describes a procedure, or a policy uses formal language while an employee asks in ordinary words. Neither signal proves that a passage is current, permitted or sufficient to support an answer.

For a broader view of the product and operating boundary, start with the RAG systems service. This article stays narrow: how to combine lexical and semantic retrieval without turning the final answer into an opaque blend of scores.

The problem: one query, different evidence shapes

A support user may ask, “Why is invoice 7F rejected after approval?” A lexical index can surface a document that contains 7F, while a vector index can find a troubleshooting note that describes the same state as “approval passed but validation failed.” If the system uses only one method, it may miss an important route. If it blindly concatenates both result lists, it can introduce duplicate, stale or unauthorized passages.

The requirement is therefore not “retrieve more.” It is to retrieve eligible evidence with enough diversity to answer the question, while making its source and limitations inspectable. Before a team selects fusion weights, it should record:

  • the query classes that need exact matching, semantic matching or both;
  • trusted identity attributes that define tenant, role, product, locale and access scope;
  • source lifecycle rules for current, draft, archived and revoked documents;
  • a passage locator and version for every candidate;
  • an explicit outcome when evidence is missing, conflicting or not permitted.

The same rule applies to business requests. A hybrid search feature is only useful when a team can name the workflow owner, the allowed corpus and the cost of an incorrect answer. A technical audit can start from the case-study hub or a project brief; it should not begin with a promise that search quality will be “solved” by a model alone.

A production architecture: two retrieval lanes, one evidence contract

The following sequence is a minimal HYBRID-8 contract. The lanes can use different engines, but the policy boundary must be shared.

  1. Normalize the request. Record query text, trusted caller identity, locale, product scope, request ID and retrieval configuration version. Do not derive permissions from the question itself.
  2. Apply deterministic eligibility filters. Tenant, audience, lifecycle and document-type rules reduce the searchable corpus before either ranking path runs. A filter that is applied after retrieval is not a reliable privacy boundary.
  3. Run the lexical lane. Tokenize and analyze the question using the same language-aware rules used for the source corpus. BM25 or another lexical ranker produces candidates and an explainable exact-match signal.
  4. Run the semantic lane. Encode the normalized query with a versioned embedding model and retrieve semantic neighbors from the already eligible corpus.
  5. Fuse candidates by stable identity. Deduplicate by document version and passage locator, preserve lane-specific ranks and avoid treating an arbitrary raw score as comparable across engines.
  6. Rerank or verify. A second-stage ranker can compare a bounded candidate set, but it must not bypass filters, source status or citation requirements.
  7. Build a bounded context. Keep passages, source titles, locators and versions together. If the context cannot support the intended claim, answer with a limitation, ask for clarification or route to review.

This architecture separates three decisions that often get collapsed: relevance, eligibility and answerability. Relevance is a ranking signal. Eligibility is a policy decision. Answerability is an evidence decision. Keeping them separate makes failures diagnosable.

Fusion: why normalized ranks are safer than raw score arithmetic

BM25 scores and vector similarity scores usually have different scales and distributions. Adding them directly creates a number that looks precise but has no stable interpretation across query types, corpus sizes or model versions. A safer initial approach is rank-based fusion, such as reciprocal rank fusion (RRF), because it combines positions rather than pretending that the scores measure the same thing.

text
eligible = filter(corpus, trustedScope, lifecycle === "current")
lexical = bm25.search(query, eligible, topK = 40)
semantic = vector.search(embed(query), eligible, topK = 40)

candidates = dedupeBy(lexical + semantic, "passageVersionId")
fused = reciprocalRankFusion(candidates, ["lexicalRank", "semanticRank"])
verified = rerank(fused.slice(0, 20), query)

if (!hasCitableEvidence(verified)) return noAnswerOrReview()
return buildContext(verified.slice(0, 6))

RRF is not a universal answer; it is a visible baseline. Weighted fusion can be useful when evaluation shows a repeatable query class where one lane deserves more influence. The weights should be versioned, tested on held-out queries and revisited when the corpus or embedding model changes. A manual boost for a favored document is not a substitute for an evaluation rule.

Key components and their contracts

Lexical index

The lexical lane needs language-aware analysis, fields for exact identifiers and a source update path. For multilingual corpora, the analyzer must be chosen deliberately; a generic tokenization rule can make Armenian, Russian and English identifiers behave inconsistently. Index the fields that users actually query, but do not expose private metadata to the model merely because it is useful for ranking.

Vector index

The semantic lane needs versioned embeddings, source-to-chunk lineage and the same trusted scope filter as the lexical lane. Re-embedding a document after a source update is not enough: the old passage must become ineligible or be removed in a verifiable way. The relevant contract is described further in RAG metadata and filters.

Candidate registry

Every fused candidate should carry a stable passage ID, source ID, source version, locator, lifecycle state, permissions evidence and both lane ranks. This registry lets an operator explain why a passage was selected and gives evaluation a way to distinguish a retrieval failure from a generation failure.

Reranker and answer gate

Reranking improves ordering only after the candidate set is trusted. It is not an authorization layer. The final gate checks that selected passages are current, permitted, non-contradictory and sufficient for the planned response. When those conditions fail, the system should decline to invent a bridge between weak sources.

The storage decision still matters. A hybrid pipeline needs a lexical index, a vector index or vector-capable store, and an update strategy that keeps their document versions aligned. The vector database selection guide covers that boundary: selection is driven by workload, filters, lifecycle, evaluation and operations, not a universal vendor ranking. Hybrid retrieval does not remove that decision; it makes source consistency more visible.

Failure modes worth testing before rollout

Exact identifier is lost. A semantic query finds a general article but misses ERR-7F. Include identifier-heavy queries in the evaluation set and inspect the lexical lane separately.

Semantic paraphrase is lost. A lexical query returns only literal wording while the source uses a different business term. Include paraphrases written by support, sales and subject-matter experts.

Stale duplicate wins fusion. An older passage has strong terms and a high vector score. Enforce lifecycle eligibility before retrieval and deduplicate by source version, not display title.

Filters are applied too late. A retrieved candidate is removed from the final response but was visible to a reranker or model. Apply mandatory filters at query construction, then test denied access with audit traces.

Score drift is mistaken for improvement. A new embedding model changes score distributions and an old numeric threshold silently fails. Version the retrieval configuration, evaluate before promotion and keep rollback data.

Too many near-duplicates consume context. Both lanes retrieve variants of the same paragraph. Diversify by source and section, then retain enough evidence for the answer rather than simply maximizing top-k.

Conflicting sources are compressed into one answer. The system receives two current documents that disagree. Preserve the conflict, identify the owner or route to human review; a fusion score cannot determine policy truth.

A focused production test plan

Start with a labeled set of real but safely sampled questions. Each case should declare the trusted caller scope, expected allowed sources, forbidden sources, required evidence and acceptable no-answer behavior. Measure more than one aggregate score:

  • lexical recall for identifiers, names and quoted language;
  • semantic recall for paraphrases and concept matches;
  • eligibility precision: no denied, archived or wrong-tenant passage reaches context;
  • citation coverage: selected answer claims map to visible locators and versions;
  • diversity and duplication in the final context;
  • latency and failure behavior for either retrieval lane;
  • regression results after changes to analyzers, embeddings, fusion or source ingestion.

Run negative tests deliberately: an expired policy, a document from another tenant, a renamed product, a deleted source, a conflicting update and a question with no supported answer. For each test, record what should be observable in logs without storing unnecessary user content.

Degraded mode and operations

A production system also needs a decision for partial failure. The lexical index may be unavailable while vector retrieval is healthy; an embedding provider may time out while exact search continues to work; an ingestion lag may leave one lane behind the source of record. None of these conditions should silently produce the same confidence as a healthy hybrid path.

Define degraded modes before launch. For a low-risk public help center, a tested lexical-only or semantic-only response may be acceptable if the interface exposes that limitation and citations remain correct. For a role-scoped internal assistant or a high-impact operation, the safer behavior can be to fail closed, provide a source-search link, or create a review task. The decision belongs to the business and risk owner, not to a generic retry loop.

Operational telemetry should distinguish lane availability from evidence quality. Useful signals include request volume by query class, candidate counts before and after filtering, overlap between the two lanes, stale-source rejections, reranker latency, citation coverage, no-answer rate and policy denials. A sudden increase in semantic-only results is not automatically bad, but it is a cue to inspect corpus changes, analyzers and embedding versions. Logs should retain request and configuration identifiers needed for diagnosis while following the product's data-retention rules.

Index updates require reconciliation, not just successful jobs. When a source is created, changed, moved or removed, confirm that both lane records resolve to the expected version and that the prior version cannot return. A small reconciliation sample after each ingestion release is more useful than an unbounded claim that the index is “fresh.” Assign ownership for this check, the rollback procedure and the decision to pause hybrid answers when source state cannot be trusted.

Start with an evaluation matrix, not a tuned score

Before tuning weights, classify a first set of questions by the signal they genuinely need. Exact identifiers, structured names, policy sections and quoted phrases exercise the lexical lane. Paraphrases, symptom descriptions and broad conceptual questions exercise the semantic lane. Mixed questions exercise fusion. Each test case should state the expected passage or source set rather than merely whether a model response sounds plausible.

Compare four views for the same cases: lexical only, semantic only, baseline fusion and the proposed tuned configuration. Review examples where the winner changes. If fusion improves a general aggregate metric but causes a forbidden source to surface or pushes a required exact identifier below the context cutoff, it has not met the production contract. This is also where human reviewers are most valuable: they can identify a misleading answer that still appears fluent and relevant.

Keep the matrix small enough to be rerun when a source pipeline, analyzer, embedding model, fusion rule or reranker changes. The purpose is not to manufacture a leaderboard. It is to make a release decision repeatable and to preserve the evidence needed to reverse that decision.

Production check before enabling hybrid answers

A production-ready hybrid path has a named owner for source ingestion, index updates, access policy, evaluation data and incident response. It does not require a universal quality number, but it does require an agreed acceptance boundary:

text
require(request.trustedScope && request.configVersion)
require(candidate.sourceVersion && candidate.locator)
require(candidate.lifecycle === "current")
require(policy.allows(candidate, request.trustedScope))
require(testSet.exact && testSet.paraphrase && testSet.denied && testSet.noAnswer)

if (!answer.evidenceIsSufficient) route = "no-answer-or-review"
if (lane.failed && !degradedMode.wasEvaluated) route = "fail-closed"

Hybrid search is valuable when it expands coverage without weakening the evidence contract. Begin with a small representative corpus, compare lexical, semantic and fused results, inspect failures, then widen the rollout only when the team can explain what changed. For architecture review or a bounded retrieval audit, use the RAG systems page as the product entry point.

CODE_BLOCK.TXT
require(request.trustedScope && request.configVersion);
require(candidate.sourceVersion && candidate.locator);
require(candidate.lifecycle === "current");
require(policy.allows(candidate, request.trustedScope));
require(testSet.exact && testSet.paraphrase && testSet.denied && testSet.noAnswer);

if (!answer.evidenceIsSufficient) route = "no-answer-or-review";
if (lane.failed && !degradedMode.wasEvaluated) route = "fail-closed";