Background: RAG Quality Degradation Is Often Not the Model’s Fault
When RAG systems go live, teams often attribute answer quality issues to “the model not being strong enough.” However, in production debugging, the real root cause is usually earlier in the pipeline: the evidence that should have been retrieved was never recalled, or the retrieval results were unstable, leading the model to work with a biased context.
Relying solely on vector search, the system excels at understanding semantically similar questions but may miss exact matches for error codes, product models, contract IDs, API names, dates, version numbers, and other precise terms. Relying solely on keyword search, the system is more sensitive to exact terms but may miss paraphrases, reworded natural language questions, and cross-lingual expressions. Azure AI Search documentation explicitly defines hybrid search as running both full-text and vector queries simultaneously, merging results with RRF; Pinecone documentation similarly notes that semantic search may miss exact keywords, while lexical search may miss synonyms and paraphrased expressions.
Therefore, a production-grade RAG system should not just ask “should we use a vector database?” but rather: what retrieval strategy should different queries follow, how to fuse results, when to fall back, and how to prove the retrieved evidence is sufficient to support the answer.
Core Principles: Hybrid Retrieval Is Not Simply Running Two Queries
A stable hybrid retrieval pipeline typically consists of three layers.
Layer 1: Sparse Retrieval
Typical implementations include BM25, BM25F, or sparse vectors. It is highly sensitive to literal matches, field weights, IDs, entity names, and terminology, making it suitable for finding “explicitly mentioned terms.”
Layer 2: Dense Retrieval
This is embedding-based vector retrieval. It maps queries and documents into a vector space, suitable for handling synonyms, paraphrases, conceptually similar terms, and natural language questions.
Layer 3: Fusion Layer
The fusion layer is not simply adding two scores together, because the score ranges of BM25, dense vectors, and sparse vectors are not naturally aligned. Azure AI Search’s hybrid search uses Reciprocal Rank Fusion (RRF); Elasticsearch documentation also explains that RRF can combine multiple result sets with different relevance metrics into a single result set without requiring those metrics to be correlated.
Why RRF Is a Good Default Fusion Strategy
The intuition behind RRF is simple: a document that ranks high in multiple retrieval lists should receive a higher fused rank. It does not directly compare BM25 scores with vector similarity scores; instead, it considers each result’s position in its respective list.
The simplified formula is:
score(document) = Σ 1 / (rank_constant + rank_in_each_result_list)
Where rank_constant controls the influence of lower-ranked results on the final ranking. Elasticsearch’s RRF documentation provides a similar formula and exposes parameters like rank_constant and rank_window_size; Azure’s RRF scoring documentation also explains that RRF re-scores and merges ranking results from multiple parallel queries.
This makes RRF a solid default strategy for the first version of hybrid retrieval: it is more stable than directly weighting raw scores and easier to explain than manually writing a set of ranking rules.
Query Routing Matters More Than Full Hybrid
Many teams interpret “hybrid retrieval” as running both BM25 and vector search for every request and then merging results. This is a viable starting point, but it is not the optimal production form.
A more robust approach is to add a lightweight query classifier that categorizes queries into several types:
| Query Type | Characteristics | Recommended Strategy |
|---|---|---|
| Exact Query | Contains error codes, order IDs, class names, function names, product models, version numbers | Prefer BM25 / sparse retrieval |
| Semantic Query | Complete natural language question, no clear entities | Prefer vector retrieval |
| Hybrid Query | Contains both entities and natural language intent | BM25 + vector + RRF |
| Low-Confidence Query | Low retrieval scores, few results, large divergence between sparse and dense | Trigger fallback or secondary retrieval |
The goal is not to reduce query cost, but to avoid forcing all requests into the same weight template.
Engineering Implementation: From Full Hybrid to Explainable Routing
In the online pipeline, a retrieval request can be decomposed into a RetrievalPlan. It describes which retrievers to use, their respective topK, fusion method, fallback conditions, and whether to use a reranker.
type RetrievalMode = "keyword" | "vector" | "hybrid";
type RetrievalPlan = {
mode: RetrievalMode;
bm25TopK: number;
vectorTopK: number;
fusion: "rrf" | "weighted" | "none";
rankWindowSize: number;
useReranker: boolean;
fallback: {
minCandidates: number;
maxVectorDistance?: number;
retryWithHybrid: boolean;
};
};
function buildRetrievalPlan(query: string): RetrievalPlan {
const hasCodeLikeToken = /[A-Z]{2,}-\d+|v\d+\.\d+|[a-zA-Z_]+\(\)/.test(query);
const isShortExactQuery = query.trim().split(/\s+/).length <= 3;
const looksNaturalLanguage = /什么|如何|为什么|怎么|how|why|what/i.test(query);
if (hasCodeLikeToken || isShortExactQuery) {
return {
mode: "keyword", bm25TopK: 50, vectorTopK: 0,
fusion: "none", rankWindowSize: 50, useReranker: false,
fallback: { minCandidates: 5, retryWithHybrid: true }
};
}
if (looksNaturalLanguage) {
return {
mode: "hybrid", bm25TopK: 50, vectorTopK: 50,
fusion: "rrf", rankWindowSize: 50, useReranker: true,
fallback: { minCandidates: 8, maxVectorDistance: 0.45, retryWithHybrid: true }
};
}
return {
mode: "vector", bm25TopK: 0, vectorTopK: 40,
fusion: "none", rankWindowSize: 40, useReranker: true,
fallback: { minCandidates: 5, retryWithHybrid: true }
};
}
This code is illustrative. In production, the query classifier can start with rules and gradually introduce a lightweight classification model. The key is to make the strategy configurable and log every retrieval plan to facilitate debugging “why this query failed to recall.”
Tune RRF Parameters with the Recall Window
The effectiveness of RRF heavily depends on the recall window. If BM25 only takes the top 5 and vector search takes the top 100, then fusing with RRF, the sparse side may lose enough candidates. Conversely, if both lists are too large, latency, memory, and subsequent reranking costs increase.
A good starting configuration is:
retrieval:
default_plan:
bm25_top_k: 50
vector_top_k: 50
fusion: rrf
rank_constant: 60
rank_window_size: 50
final_top_k: 8
exact_query_plan:
bm25_top_k: 80
vector_top_k: 20
fusion: rrf
final_top_k: 8
semantic_query_plan:
bm25_top_k: 30
vector_top_k: 80
fusion: rrf
final_top_k: 8
Azure documentation mentions that hybrid queries execute full-text and vector queries in parallel, merging unified results with RRF; Elasticsearch documentation also notes that a larger rank_window_size generally benefits relevance but incurs performance costs. Therefore, tuning should not only look at the final answer but also at the recall window, pre-fusion candidates, post-fusion candidates, and changes before and after reranking.
Weights Are Not Global Constants
Weaviate’s hybrid search provides an alpha parameter to adjust the relative weight of keyword and vector components; Pinecone documentation warns that when storing dense and sparse vectors in a single index, BM25 or sparse scores and dense vector ranges are not naturally normalized, and without explicit weights, the sparse part may dominate the score.
In production, it is better to set weights by query type rather than using a global fixed value:
| Scenario | Recommended Weight Bias |
|---|---|
| Error codes, SKUs, contract IDs | Higher BM25 weight |
| ”What does this sentence mean?” “How to configure permissions?” | Higher vector weight |
| Legal, medical, insurance clauses | Ensure exact hits first, then allow semantic expansion |
| Multilingual knowledge bases | Prioritize verifying cross-lingual vector recall, then supplement with keyword fallback |
Weight tuning is not about finding a perfect parameter; it is about aligning query intent with retrieval strategy.
Fallback Strategy: Don’t Let the Model Guess When Recall Is Insufficient
One risk of hybrid retrieval in production is “having results, but not enough to support the answer.” Therefore, fallback conditions must be defined at the retrieval layer.
Common Fallback Signals
- Candidate count below threshold
- Keyword retrieval hits very few results, but the query contains obvious entities
- Vector retrieval distance is too high or similarity too low
- BM25 and vector results have almost no overlap, and top results after RRF are scattered
- Reranker scores are low, or the gap between top1 and top2 is too small
- Final evidence snippets lack title, timestamp, source, or permission fields
Fallback Processing Order
- Relax filter conditions
- Increase topK
- Switch weights
- Enable another field index
- Enter cross-index retrieval
- Return “insufficient evidence found” or ask the user to provide more keywords
Core principle: Do not let the LLM fabricate an answer when evidence is insufficient.
Applicable Scenarios
Technical Documentation and Knowledge Bases
Developers might search for ERR_AUTH_401 or ask “Why am I still getting a permission denied error after login?” The former requires exact matching, the latter semantic understanding.
Enterprise Policies, Contracts, Insurance Clauses, Regulatory Documents
Users may input clause numbers, product names, dates, or ask a natural language question. Pure vector search easily misses clause numbers, while pure keyword search may miss paraphrased expressions.
Customer Support and Ticketing
Historical issues often mix product models, error symptoms, OCR text from screenshots, and user colloquial descriptions, requiring both exact terms and semantic recall.
E-commerce, Product, and Inventory Q&A
Models, brands, specifications, aliases, and use cases appear simultaneously; a single retrieval strategy struggles to cover them all stably.
Common Misconceptions
Misconception 1: Hybrid Retrieval Automatically Improves Quality
Hybrid retrieval only increases the candidate pool; it does not automatically guarantee more accurate answers. If chunking quality is poor, metadata is missing, permission filters are incorrect, topK is too small, or fusion parameters are unreasonable, hybrid retrieval can also introduce noise.
Misconception 2: Directly Adding BM25 and Vector Scores
Different retrievers have different score ranges; directly adding them can easily let one side dominate the results. Pinecone documentation also specifically notes that sparse and dense score distributions differ, requiring explicit weights. The value of RRF is to reduce the risk of score incomparability.
Misconception 3: Only Tuning topK, Ignoring Recall Distribution
Increasing topK may improve recall, but it may also just add reranking noise. It is essential to simultaneously observe BM25 hits, vector hits, fusion overlap, snippets that ultimately enter the context, and the evidence cited in the final answer.
Misconception 4: Ignoring Filter Placement
Whether permission, time, product line, or tenant filters are applied before or after retrieval affects the recall scope and fusion results. Azure documentation on hybrid queries also reminds that filters can be applied at different stages of the processing pipeline, and whether to use post-filtering needs testing. In production, permission filtering must be correct; relevance tuning cannot bypass permission boundaries.
Go-Live Checklist
Before going live, prepare at least four types of query sets:
| Query Type | Test Goal | Verification Focus |
|---|---|---|
| Exact Query | Error codes, IDs, API names, class names, config items, contract clause numbers | Whether BM25/sparse side hits stably |
| Semantic Query | User intent is clear but does not use document terms | Whether vector recall finds conceptually similar snippets |
| Hybrid Query | Contains both entities and natural language descriptions | Whether RRF fusion is more stable than single-path retrieval |
| Failure Query | No answer in knowledge base, permission invisible, time range mismatch | Whether the system can refuse, fall back, or prompt for more info |
Metrics to track:
- Recall@K, MRR, NDCG
- BM25 hit count, vector hit count, fusion overlap ratio
- Top document changes before and after RRF
- Top document changes before and after reranking
- Source of snippets entering the LLM context
- Fallback trigger rate, refusal rate, manual review pass rate
- p95 retrieval latency and end-to-end answer latency
References
- Azure AI Search: Hybrid search using vectors and full text
- Azure AI Search: Relevance scoring in hybrid search using RRF
- Elasticsearch: Reciprocal rank fusion
- Weaviate Documentation: Hybrid search
- Pinecone Docs: Hybrid search
- Efficient and Effective Retrieval of Dense-Sparse Hybrid Vectors using Graph-based Approximate Nearest Neighbor Search