Advanced RAG — Reranking, Hybrid Search and Evaluation
Reranking retrieved chunks, hybrid dense-sparse search, RAG evaluation metrics, and the patterns that separate production RAG from toy RAG.
Module 52 built a working RAG pipeline. This module explains why it fails in production and how to fix every failure mode systematically.
Naive RAG — embed query, retrieve top-k chunks by cosine similarity, inject into LLM prompt — works well in demos and poorly in production. The problems are consistent: semantic search alone misses exact keyword matches that users expect. The top retrieved chunks are often related to the query but do not actually answer it. Evaluation is absent — you do not know if the system is getting better or worse as you iterate.
A Stripe knowledge base assistant built with naive RAG will struggle with queries like "what is error code 400?" — semantic search finds chunks about general payment errors (semantically similar) but misses the chunk that contains exactly "400" (keyword match). It will struggle with queries that require synthesising across multiple chunks. It will hallucinate when the retrieved chunks are tangentially related but do not contain the answer. And the team will have no objective way to know which of these failures is happening most.
A good research librarian does two things a bad one does not. First: when you ask "find me information about ACH payment limits," they search both the subject index (semantic) and the keyword catalogue (exact match) — not just one. Second: after gathering candidates, they skim each one to pick the three most directly relevant — they rerank. A naive RAG pipeline skips both steps. Hybrid search is the librarian searching two catalogues. Reranking is the librarian reading before recommending.
Adding a reranker alone typically improves end-to-end RAG quality by 10–25% with minimal engineering effort. It is the single highest-leverage improvement you can make to a naive RAG system.
Hybrid search — dense semantic + sparse keyword, combined with RRF
Dense retrieval (embedding similarity) excels at semantic matching — it finds chunks about "payment declined" when the query is "transaction rejected." But it fails at exact keyword matching — "error code BAD_REQUEST_ERROR" might retrieve irrelevant chunks because the semantic embedding averages across all words. Sparse retrieval (BM25) excels at exact term matching but misses synonyms and paraphrases. Hybrid search combines both signals.
Cross-encoder reranking — score every chunk against the query precisely
Bi-encoder retrieval (embedding similarity) is fast because query and document are encoded independently — you embed the query once and compare to pre-computed document embeddings. But this independence is also a weakness: the model cannot consider the specific interaction between a query word and a document word. A cross-encoder takes both query and document as a single input and computes a relevance score from their full interaction — much more accurate, but too slow to use on every document in the corpus.
The solution is a two-stage pipeline: use bi-encoder retrieval to quickly narrow down to top-100 candidates, then use a cross-encoder to precisely rerank those 100 candidates to find the true top-3. The cross-encoder only runs on 100 documents per query, not millions, so the extra latency is acceptable.
HyDE, parent-child chunking, and query decomposition
Three more techniques that consistently improve RAG quality beyond hybrid search and reranking. HyDE (Hypothetical Document Embeddings) generates a hypothetical answer to the query and embeds that instead of the query — producing richer query embeddings that match document style. Parent-child chunking indexes small chunks for precision but retrieves their larger parent for context. Query decomposition breaks complex questions into sub-questions that are each easier to answer individually.
RAG evaluation — faithfulness, answer relevance, and context recall
Without evaluation, RAG iteration is guesswork. You make a change — better chunking, different embedding model, added reranking — and you have no objective measure of whether it helped. Three metrics cover the full RAG pipeline end to end. Faithfulness measures whether the answer is grounded in the context. Answer relevance measures whether the answer addresses the question. Context recall measures whether the retrieved chunks contain the answer.
Production RAG pipeline — all components integrated
Every common advanced RAG mistake — explained and fixed
Production RAG architecture beyond a single retrieval call
A knowledge-base assistant that only ever runs one retrieval call per question hits a hard ceiling the moment a real user asks something that needs synthesis — "which enterprise customers who signed up this quarter also had a refund dispute over a thousand dollars?" No single chunk in the knowledge base answers that; it requires finding the signup-timing information, finding the dispute records, and combining them. Production systems handle this with iterative, multi-hop retrieval: the system retrieves an initial set of chunks, has the LLM decide whether it has enough information or needs to issue a follow-up retrieval for a sub-question, and repeats until it can answer or gives up and says so. This is the same retrieve-then-read loop that shows up again, in a more general form, once you get to agents that call tools autonomously.
The infrastructure choices matter as much as the algorithm. Teams pick a vector store — Pinecone or Weaviate as managed services, pgvector when the data already lives in Postgres and a separate system is not worth the operational overhead — paired with OpenSearch or Elasticsearch for the BM25 half of hybrid search. Embedding model version pinning is a real operational headache: switching embedding models means every vector in the index is now in a different geometric space, so upgrading requires re-embedding the entire corpus and running both indexes in parallel during the cutover, not a quick config change. Once a pipeline has hybrid search, reranking, and multi-hop retrieval in it, teams add tracing tools — LangSmith, Arize Phoenix, or an internal equivalent — because debugging "why did the answer miss this fact" by eye across four separate retrieval and reranking stages is no longer feasible without seeing exactly what each stage returned.
Slack message from support ops: "The assistant told a customer their refund would arrive in two to three days, but the actual policy for their payment method is five to seven. Someone needs to look at this today." Pulling the trace shows the retrieval step correctly found both the ACH refund timeline and the credit card refund timeline in its top chunks — context recall was fine. The reranker, trained on general web search data, scored the ACH chunk higher because it happened to share more surface wording with the question, even though the customer's payment method was a credit card. The fix is not "add more chunks" — it is tightening the prompt to require the model to identify the payment method before answering, and evaluating whether the reranker needs domain fine-tuning on exactly this kind of near-duplicate, policy-variant content.
Five things people get wrong about advanced RAG
Past a certain point, adding more retrieved chunks makes answers worse, not better. Every additional chunk is more text competing for the LLM's attention, and irrelevant or tangentially related chunks measurably increase the chance the model blends in something that does not belong in the answer — a direct hit to faithfulness. Retrieved context also costs money and latency per token whether or not it helped. The right target is the smallest set of chunks that actually contains the answer, which is exactly what reranking is for — narrowing a broad, high-recall candidate set down to a small, high-precision one, rather than simply raising the top-k number and hoping the LLM sorts it out.
Naive RAG is good enough for demos, where queries are hand-picked to work well, and for small, low-stakes internal tools. It reliably breaks on the failure modes this whole module exists to fix: missing exact keyword matches like error codes, retrieving chunks that are semantically similar but do not actually answer the question, and giving the team no objective way to know whether a change made things better or worse. The gap between a naive RAG demo and a production RAG system is not a matter of scale — it shows up on day one with any real, adversarial user base, which is exactly why hybrid search, reranking, and evaluation are treated as standard components, not optional extras, on any RAG system serving real users.
Chunk size and boundaries directly determine what can possibly be retrieved: a chunk boundary that splits a table in half, or separates an error code from its explanation, makes the correct answer unretrievable no matter how good the embedding model or reranker is downstream. Fixed-size chunking with no regard for document structure is the default because it is easy to implement, not because it performs best. Parent-child chunking, semantic or structure-aware splitting, and per-document-type chunking rules all exist because the "right" chunk size and boundary genuinely depends on the content — an FAQ entry, a legal contract clause, and an API reference table need different chunking logic to stay retrievable and coherent.
RAG reduces hallucination by giving the model relevant context to ground its answer in, but it does not force the model to use that context. As this module's error section covers, an LLM with strong prior knowledge on a topic will readily blend its own training-time beliefs with the retrieved context, or ignore the context outright, unless the prompt explicitly and strongly instructs it to answer only from what was retrieved. This is precisely why faithfulness is measured as its own separate metric from context recall — a RAG system can have perfect retrieval and still produce an unfaithful, hallucinated answer if the generation step is not held to the context.
They solve different stages of the same pipeline. Hybrid search is about recall at the first, cheap retrieval stage — making sure the correct chunk is somewhere in a reasonably sized candidate set by combining semantic and exact-keyword signals, since dense retrieval alone can miss exact terms and BM25 alone can miss paraphrases. Reranking is about precision at the second stage — given that broader candidate set, a cross-encoder scores each one far more accurately than either retrieval method could, to find the true top few. Skipping hybrid search means the correct chunk may never reach the reranker to be found in the first place; skipping reranking means a correct chunk that made it into the candidate set may still not surface at the very top.
Advanced RAG — 5 questions interviewers actually ask
A bi-encoder embeds the query and each document independently, which is what makes it fast enough to search millions of documents, but that independence means it can never model the specific interaction between a query term and a document term — it can only compare two fixed vectors. A cross-encoder reranker takes the query and a candidate document together as one input and scores their actual joint relevance, which is far more accurate but too slow to run over an entire corpus. Simply retrieving more documents with the bi-encoder does not fix this — it just hands the LLM a larger set of imprecisely ranked candidates. Reranking is what turns a fast, approximate top-100 into an accurate top-3.
A single retrieval call assumes the answer lives in one place, which breaks down on genuinely multi-hop questions. I would use query decomposition to split the question into sub-questions that are individually answerable, retrieve and answer each one separately, then have the LLM combine the sub-answers into a final response — or, for more open-ended multi-hop cases, run an iterative retrieve-then-read loop where the model reads an initial retrieval, decides what information is still missing, issues a follow-up retrieval for that gap, and repeats until it has enough to answer or explicitly reports that it cannot. Either approach needs its own evaluation, because standard single-hop context recall does not capture whether all the needed sub-facts were actually retrieved.
I would separate the pipeline into two things that can independently fail. Retrieval quality is measured with context recall — do the retrieved chunks actually contain the information needed to answer the question — which isolates whether the search and reranking stages are doing their job. Generation quality is measured with faithfulness — is every claim in the answer actually supported by the retrieved context — and answer relevance — does the answer address what was actually asked. Separating these matters for debugging: low context recall means the fix is in retrieval, chunking, or search strategy, while high context recall with low faithfulness means the fix is in the prompt or the generation step, not in retrieval at all.
I would start from the structure of the documents rather than picking an arbitrary token count — an FAQ entry, a paragraph of a policy document, and a row of an API reference table all have a natural unit that should not be split mid-thought. From there I would test a small range of chunk sizes against a hand-labelled evaluation set, measuring context recall at each size, since chunks that are too small lose surrounding context and chunks that are too large dilute relevance and hurt reranker precision. For content with an important internal hierarchy, like a document with sections and subsections, I would use parent-child chunking so retrieval can stay precise on small chunks while the LLM still receives the larger parent for full context.
I would explain that this trades one failure mode for a worse one: retrieving 20 chunks does raise the odds the right information is somewhere in the context, but it also means many more irrelevant or tangential chunks compete for the model's attention, which measurably increases hallucination risk and directly increases cost and latency on every single call. The fix for "we might be missing relevant chunks" is to widen the first-stage retrieval — increase the coarse candidate set to something like the top 20 or 50 — and then let a cross-encoder reranker narrow that back down to a precise top 3 for the actual prompt. That gets the safety margin the stakeholder wants without paying the attention-dilution and cost penalty of stuffing 20 chunks directly into the LLM.
You can build production RAG. The final module of Section 10 covers the complete AI agent architecture.
Advanced RAG gives your agent access to a knowledge base. Module 68 — the final module of the Generative AI section — covers the complete production agent: planning across multiple steps, calling real APIs, maintaining memory across turns, handling failures gracefully, and the architectural patterns used at companies like Stripe, Amazon, and DoorDash to build internal AI tools that handle thousands of requests per day.
LLMs that plan, use tools, and execute multi-step tasks autonomously. ReAct, tool calling, memory, and production agent architecture patterns.
🎯 Key Takeaways
- ✓Naive RAG (embed → cosine similarity → top-k) fails on exact keyword queries, returns tangentially relevant chunks, and has no quality measurement. The three systematic fixes are hybrid search (dense + sparse + RRF), cross-encoder reranking, and evaluation metrics that measure each failure mode independently.
- ✓Hybrid search combines dense retrieval (semantic similarity via embeddings) and sparse retrieval (BM25 keyword matching) using Reciprocal Rank Fusion. RRF score = Σ 1/(k + rank_i) with k=60. No tuning required. Best improvement comes on queries with specific technical terms (error codes, product names, API parameters) that semantic search misses.
- ✓Cross-encoder reranking is the single highest-leverage improvement to any RAG system. Two-stage pipeline: bi-encoder retrieves top-100 candidates fast (~10ms), cross-encoder scores each (query, chunk) pair precisely (~200ms for 100 docs). The cross-encoder sees both query and chunk simultaneously — much more accurate than independent embeddings.
- ✓Three advanced retrieval patterns: HyDE (embed a hypothetical answer instead of the query — matches document style better for short queries), parent-child chunking (index small precise chunks, return their full parent for LLM context), query decomposition (split complex multi-part questions into sub-questions, retrieve and answer each separately).
- ✓Three RAG evaluation metrics: faithfulness (are all answer claims supported by retrieved context — measures hallucination), answer relevance (does the answer address the question — measures off-topic responses), context recall (do retrieved chunks contain the reference answer — measures retrieval quality). Low context recall means fix retrieval. Low faithfulness means fix the LLM prompt.
- ✓When context recall is high but faithfulness is low, the retrieval is working but the LLM is ignoring the context. Fix the grounding instruction: "Answer ONLY using the numbered context. Say I do not have that information if the answer is not there." Verify by injecting a deliberate false fact into context and checking that the LLM reports it. Always supplement automated metrics with monthly human evaluation on a 50-100 query sample.
Discussion
0Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.