Advanced RAG Techniques
Beyond retrieve-then-read
Naive RAG is a straight line: embed the user's question, grab the top-k nearest chunks by vector similarity, paste them into the prompt, and generate. It works surprisingly often — and fails in surprisingly predictable ways. The question may be phrased nothing like the answer. The chunks may be the wrong size. Vector similarity may rank a passage highly just because it shares vocabulary, not because it answers anything. And the model has no way to notice that the retrieved context is junk before it confidently writes from it.
Advanced RAG is the collection of techniques that intervene at each of these failure points. They cluster into stages of the pipeline:
Pre-retrieval
Transform the query and chunk the corpus better.
Retrieval
Hybrid search and rank fusion for broad, robust recall.
Post-retrieval
Rerank and compress the candidates for precision.
Self-correction
Grade the context and re-retrieve when it is poor.
This lesson assumes you already know the basic RAG pipeline (embed → index → retrieve → generate). We focus only on what to add on top of it, and why each addition earns its complexity.
1. Query transformation: fix the question before you search
The user's raw question is often a bad search key. It may be terse, ambiguous, multi-part, or phrased in language that looks nothing like the documents that answer it. Query transformation rewrites the question into one or more better retrieval keys before embedding.
Query rewriting
An LLM cleans up the query — resolving pronouns from chat history, expanding acronyms, fixing typos — so the embedded vector lands in a better neighbourhood. Cheap and almost always helpful in conversational RAG.
Multi-query expansion
Generate several paraphrases of the question, retrieve for each, and union the results. This trades a little latency for much better recall: if one phrasing misses the relevant chunk, another phrasing often catches it.
HyDE — Hypothetical Document Embeddings
The key insight: a question and its answer live in different regions of embedding space. So HyDE asks the LLM to hallucinate a plausible answer to the query, then embeds that fake answer — not the question — and searches with it. The fake answer may be factually wrong, but it “looks like” the real documents you want, so it retrieves them better than the bare question would.
query = "What causes the northern lights?"
hypo = llm("Write a passage answering: " + query)
# -> "The aurora is caused by charged solar
# particles exciting oxygen and nitrogen..."
results = vector_search(embed(hypo)) # embed the ANSWER, not the queryStep-back prompting
For narrow or detail-heavy questions, first ask a more general “step-back” question (“What are the general principles of X?”), retrieve broad background for it, and use that alongside the specific retrieval. Grounding the answer in the underlying concept reduces brittle, over-specific reasoning.
Mental model: rewriting and multi-query work on the question side; HyDE bridges the question/answer gap by searching with a synthetic answer; step-back searches at a higher level of abstraction. They are not mutually exclusive — production systems often stack them.
2. Better chunking: retrieval quality starts at index time
You cannot retrieve well from badly cut chunks. Fixed-size chunking (e.g. every 512 tokens) routinely slices a sentence — or a key fact — in half, leaving each piece too incomplete to embed meaningfully. Smarter chunking respects the structure of the text.
| Strategy | How it works | Why it helps |
|---|---|---|
| Recursive | Split on paragraphs, then sentences, then words, until each piece fits the token budget. | Keeps natural boundaries; a sensible default. |
| Semantic | Embed sentences, start a new chunk where adjacent-sentence similarity drops below a threshold (a topic shift). | Chunks correspond to coherent ideas, not arbitrary lengths. |
| Sentence-window | Index single sentences for sharp matching, but at generation time attach the surrounding sentences as context. | Precise retrieval target, with enough context to answer. |
| Parent-document | Index small child chunks; when one matches, return its larger parent passage to the LLM. | Decouples the matching unit from the context unit. |
The recurring idea behind sentence-window and parent-document retrieval: the best unit to search (small and specific, so the vector is crisp) is usually not the best unit to read (large enough to contain the full answer). Decouple them.
3. Re-ranking: the two-stage retrieve → rerank pattern
This is the single highest-leverage upgrade to naive RAG, and the centrepiece of this lesson. The idea: use a cheap, recall-oriented retriever to pull a broad candidate set, then a more expensive, precision-oriented reranker to reorder just those candidates. The split exists because of a fundamental tradeoff between two model architectures.
Bi-encoder (Stage 1)
Encodes the query and each document separately into a single vector each, then compares with cosine similarity:
score = cos( E(query), E(doc) )
Because document vectors do not depend on the query, you can precompute and index them and search millions with approximate nearest neighbours. Fast, scalable — but the query never “sees” the document, so it misses subtle relevance.
Cross-encoder (Stage 2)
Feeds the query and document together, concatenated, through one transformer, with full attention between every query and document token:
score = Transformer( [query] [SEP] [doc] )
Far more accurate — it can tell a true answer from a vocabulary look-alike. But the score depends on the query, so nothing can be precomputed: you run one forward pass per (query, doc) pair. Too slow for the whole corpus, perfect for a short candidate list.
The pattern in one line: bi-encoder retrieves N ≈ 50–100 candidates for cheap recall → cross-encoder reranks them for precision → keep the top k ≈ 3–5 for the prompt. You get cross-encoder accuracy at bi-encoder scale.
Interactive: retrieve → rerank
A fixed corpus of 12 short passages with toy precomputed vectors. Stage 1 scores the whole corpus by cosine similarity (the bi-encoder). Stage 2 reranks only the top-N candidates with a query-aware cross-encoder score. Pick a query, adjust N, and watch the ranking change — and the precision improve.
Stage 1 — Bi-encoder retrieval
Cheap cosine similarity between precomputed vectors. Fast, scans the whole corpus, but query-blind: it only compares vectors.
- #1cos 0.94
Vector similarity alone can rank a passage highly just because it shares surface vocabulary with the query, even when the passage never actually answers the question being asked.
⚠ embedding false positive — shares vocabulary, does not answer
- #2cos 0.69
Cross-encoders read the query and a document together in one pass, producing a single relevance score. They are far more accurate than bi-encoders but cannot be precomputed.
- #3cos 0.44
A bi-encoder embeds the query and every document independently, so document vectors can be indexed ahead of time and searched with fast approximate nearest-neighbour lookups.
- #4cos 0.21
Reciprocal Rank Fusion merges several ranked lists by summing 1/(k + rank) for each document across the lists, rewarding items that appear near the top of many rankings.
- #5cos 0.15
HyDE generates a hypothetical answer to the query with an LLM, then embeds that fake answer instead of the question, because an answer looks more like the documents we want to find.
- #6cos 0.00
Recursive character chunking splits on paragraphs first, then sentences, then words, keeping each chunk under a token budget while respecting natural boundaries in the text.
Stage 2 — Cross-encoder rerank
Re-reads the query jointly with each candidate. Slower per item, but only runs on the 6 candidates — and it reorders them by true relevance.
- #1▲1 (was #2)ce 1.00
Cross-encoders read the query and a document together in one pass, producing a single relevance score. They are far more accurate than bi-encoders but cannot be precomputed.
- #2▲1 (was #3)ce 0.32
A bi-encoder embeds the query and every document independently, so document vectors can be indexed ahead of time and searched with fast approximate nearest-neighbour lookups.
- #3▼2 (was #1)ce 0.15
Vector similarity alone can rank a passage highly just because it shares surface vocabulary with the query, even when the passage never actually answers the question being asked.
- #4 (was #4)ce 0.15
Reciprocal Rank Fusion merges several ranked lists by summing 1/(k + rank) for each document across the lists, rewarding items that appear near the top of many rankings.
- #5 (was #5)ce 0.00
HyDE generates a hypothetical answer to the query with an LLM, then embeds that fake answer instead of the question, because an answer looks more like the documents we want to find.
- #6 (was #6)ce 0.00
Recursive character chunking splits on paragraphs first, then sentences, then words, keeping each chunk under a token budget while respecting natural boundaries in the text.
4. Hybrid & fusion retrieval
Dense vector search captures meaning but can miss exact terms — product codes, rare names, acronyms. Sparse keyword search (BM25) nails exact terms but misses paraphrases. Hybrid retrieval runs both and combines them, so you get semantic recall and lexical precision together.
Reciprocal Rank Fusion (RRF)
The robust way to merge two ranked lists without having to calibrate their incomparable score scales (cosine vs BM25). RRF ignores the raw scores and uses only the rank of each document in each list:
RRF(d) = Σᵢ 1 / (k + rankᵢ(d))
Sum over every list i the document appears in, where rankᵢ(d) is its position in list i (1 = best) and k is a small constant (commonly 60) that dampens the influence of top ranks. A document near the top of several lists scores highest. Simple, scale-free, and remarkably strong.
5. Contextual compression & self-corrective RAG
Contextual compression
Even good chunks contain filler. Contextual compression runs each retrieved chunk through a filter — an LLM or a smaller extractor model — that keeps only the sentences relevant to this query and drops the rest. The result: less noise in the prompt, lower token cost, and the model's attention spent on evidence rather than padding.
Self-reflective & corrective RAG
Naive RAG generates from whatever it retrieved, even if the context is irrelevant. Self-correcting variants add a grading loop: judge the retrieved context, and act on the verdict.
Self-RAG
Trains the model to emit special reflection tokens that decide whether to retrieve at all, grade whether each passage is relevant, and critique whether the final answer is actually supported by the cited passages.
Corrective RAG (CRAG)
Runs a lightweight retrieval grader. If confidence is high, it refines and uses the docs; if low, it falls back to a web search and query rewrite rather than answering from weak context.
Both turn RAG from a one-shot pipeline into a small control loop: retrieve → grade → (re-retrieve / rewrite / fall back) → generate. This is the bridge from “advanced RAG” to full agentic RAG.
6. Evaluating a RAG system
You cannot improve what you cannot measure, and RAG has two halves to measure separately: did retrieval find the right evidence, and did generation use it faithfully? Frameworks like RAGAS compute these, often using an LLM as a judge so you do not need human labels for every example.
Faithfulness
Generation
Are the answer's claims actually supported by the retrieved context? Measures grounding — the inverse of hallucination.
Context precision
Retrieval
Of the chunks you retrieved, how many are actually relevant? High precision means little noise — and rewards ranking the relevant chunks near the top.
Context recall
Retrieval
Of the evidence actually needed to answer, how much did you retrieve? Low recall means the answer was impossible to ground — no reranker can fix a missing document.
How to read them together: low recall is a Stage-1 retrieval problem — fix chunking, hybrid search, or query transformation. Low precision is what reranking and compression fix. Low faithfulness with good context is a generation/prompting problem. The metrics tell you which stage to invest in.
Putting it together
A mature RAG pipeline threads these techniques together — though you should add each one only when evaluation shows it earns its latency and cost:
Key Takeaways
- Naive retrieve-then-read fails at predictable points; advanced RAG adds targeted fixes at the pre-retrieval, retrieval, post-retrieval, and self-correction stages.
- Query transformation repairs the search key before embedding: rewriting and multi-query expansion improve recall, HyDE embeds a hypothetical answer to bridge the question/answer gap, and step-back prompting retrieves at a higher level of abstraction.
- Chunking determines the ceiling on retrieval quality. Recursive and semantic chunking respect text structure; sentence-window and parent-document retrieval decouple the small unit you match from the larger unit you read.
- The retrieve → rerank pattern is the highest-leverage upgrade: a cheap bi-encoder (separate, precomputable vectors) recalls a broad candidate set, then an accurate cross-encoder (query and doc encoded jointly, not precomputable) reorders just those candidates for precision.
- Hybrid retrieval combines dense and sparse search; Reciprocal Rank Fusion merges ranked lists using only ranks — RRF(d) = Σ 1/(k + rankᵢ(d)) — so incomparable score scales never need calibrating.
- Contextual compression trims retrieved chunks to the relevant sentences; Self-RAG and CRAG add a grade-and-correct loop that re-retrieves or falls back when the context is poor.
- Measure retrieval and generation separately: context recall/precision diagnose retrieval, and faithfulness diagnoses grounding. RAGAS automates these with an LLM judge, telling you which stage to improve.