Building RAG Pipelines
Chunking, hybrid retrieval, reranking and the metrics that tell you which half of your pipeline is broken.
What you'll be able to do
- Measure retrieval and generation separately, with the right metric for each
- Design a chunking and indexing strategy from the shape of your corpus
- Decide when hybrid search and reranking are worth their latency
Assumes: Intermediate lesson 3 β RAG: Teaching AI Your Data Β· Lesson 3 β Production Prompting & Context Engineering
The discipline in one sentence
Most RAG systems fail for a reason nobody measured, because the team optimised the half that was already working.
So the organising principle of this lesson is: retrieval and generation are two systems with two failure modes and two sets of metrics. Instrument them separately or you will tune blind.
Measure first
Before any tuning, build an evaluation set: 50β100 questions with the passage that should answer each one, drawn from real usage where possible.
That labelled passage is what makes everything else measurable.
Retrieval metrics
| Metric | Question it answers | Use when |
|---|---|---|
| Recall@k | Was the right passage anywhere in the top k? | Primary metric β the model can only use what arrives |
| Precision@k | What fraction of returned passages were relevant? | Diagnosing dilution as k grows |
| MRR | How high did the first relevant passage rank? | One right answer exists |
| nDCG@k | Are the most relevant passages ranked highest? | Several relevant passages, graded relevance |
Recall@k is the one to fix first. If the right passage is not in the top k, nothing downstream can recover. Everything else is secondary.
Generation metrics
Assuming retrieval succeeded:
- Faithfulness β is every claim supported by the retrieved context? Catches hallucination over correct evidence.
- Answer relevance β does it address the question actually asked?
- Correctness β does it match ground truth? Needs labelled answers.
- Refusal accuracy β does it decline when the corpus genuinely lacks the answer? Routinely omitted, and it is where trust is won or lost.
The diagnostic that resolves most confusion:
Right passage retrieved?
βββ No β retrieval problem: chunking, embeddings, query, k
βββ Yes β generation problem: prompt, model, context ordering
Ingestion
Quality is decided here, before any clever retrieval.
Parsing. Preserve structure β headings, tables, lists carry meaning that flat text extraction destroys. A table flattened into a wall of numbers is unretrievable and, worse, misleading when it does get retrieved. For PDFs specifically, budget real time: multi-column layouts, headers and footnotes are genuinely hard, and a bad parse is an invisible ceiling on the entire pipeline.
Chunking. One vector represents one chunk, so boundaries determine what is findable.
| Strategy | Mechanism | Suits |
|---|---|---|
| Fixed-size | Every N tokens with overlap | Uniform, unstructured prose |
| Recursive | Split on headings, then paragraphs, then sentences | Most real documents β start here |
| Semantic | Break where embedding similarity drops | Mixed-topic documents without structure |
| Parent-child | Embed small chunks, return their larger parent | Precision plus context β strong default |
Sensible starting point: recursive splitting at 300β500 tokens with 10β15% overlap. Then measure recall@k and adjust β this is a parameter to tune, not a setting to get right first time.
The parent-child pattern deserves particular attention. Small chunks embed precisely; large chunks give the model enough context to answer. Doing both β retrieve on the small chunk, pass the parent to the model β resolves a tension that otherwise forces a compromise.
Metadata. Attach source, section, date, permissions, version to every chunk. This enables filtered retrieval, recency weighting, citation, and β critically β access control. A RAG system that retrieves across permission boundaries is a data breach, and metadata filtering is how you prevent it. Filter before the vector search, not after.
Embeddings
Selection criteria in order of practical importance: retrieval quality on your data, dimensionality (memory and speed), sequence limit, and whether it can be self-hosted for privacy.
Two rules that cause real incidents:
- The same model must embed queries and documents. Different models produce incompatible spaces. Mixing them fails silently and badly.
- Changing the embedding model means a full re-index. Plan for it: version your index, and be able to run two in parallel during migration.
Do not choose on a public leaderboard alone. Benchmarks measure general retrieval; your corpus may be legal contracts, or code, or clinical notes. Take twenty labelled query-passage pairs from your own data and compare candidates on those. It takes an hour and regularly overturns the leaderboard ordering.
Retrieval
Vector search alone
Query embedding, nearest neighbours, done. Good on paraphrase and conceptual similarity. Weak on exact tokens β error codes, part numbers, surnames, API names. Embeddings blur precisely the things you needed matched exactly.
Hybrid: vector plus lexical
Run BM25 alongside vector search and fuse the rankings. Reciprocal Rank Fusion is the standard method because it needs no score normalisation between two incomparable scales:
where is the documentβs rank in result set , and damps the influence of top positions.
Hybrid is close to a free win. It reliably improves recall on real corpora because lexical and semantic matching fail on different queries.
Reranking
The strongest single improvement available, and the reason is architectural.
Vector search uses a bi-encoder: query and document are embedded independently, so the score is a distance between two fixed vectors. Fast enough for millions of documents, but the two never interact.
A cross-encoder processes query and document together, attending across both. Far more accurate, and far too slow to run over a corpus.
So: use the bi-encoder to get 50β100 candidates, then the cross-encoder to rerank down to 5β10.
query β hybrid retrieval (top 50) β cross-encoder rerank β top 8 β generate
Typical cost is 100β300ms. On most pipelines it is the highest-value latency you will spend.
On k
Raising k does not monotonically improve answers. Recall rises, precision falls, and extra marginal passages give the model more chances to ground its answer in the wrong place. This is why reranking a wide candidate set beats simply widening k β you get the recall of a wide net with the precision of a narrow one.
Generation
Context assembly matters as much as the prompt. Order by relevance with the strongest passages at the beginning and end, since recall dips in the middle of a long context. Label every chunk with an identifier so citation is possible. And keep a [1]-style marker in the text so a claim can be traced to a source.
Answer using ONLY the numbered sources below.
Cite the source number after each claim, like [2].
If the sources do not contain the answer, reply exactly:
"I don't have information about that in the available documents."
[1] (handbook.pdf, "Parental Leave", 2026-01-14)
{chunk}
[2] (policy.pdf, "Entitlements", 2025-11-02)
{chunk}
Question: {query}
Three deliberate choices there. Only is stated positively rather than as a prohibition. Citation is required inline, which makes verification cheap and measurably reduces unsupported claims. And the refusal is given exact wording, so it is detectable in logs and testable in evals β a refusal you cannot detect is a refusal you cannot measure.
Failure modes and their fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Answers when it should refuse | No similarity floor | Threshold on score; explicit refusal instruction |
| Right chunk retrieved, wrong answer | Generation prompt | Strengthen grounding, require citations |
| Exact identifiers never found | Pure vector search | Add BM25, go hybrid |
| Relevant passage ranked 30th | No reranking | Add a cross-encoder |
| Contradictory answers | Multiple document versions | Metadata filter, recency weighting |
| Answer split across documents | Question needs synthesis | Different architecture β see below |
| Quality fell after re-index | Embedding model changed | Version indexes; re-baseline evals |
Where standard RAG stops
Top-k retrieval answers local questions β the answer sits in one or a few passages. It structurally cannot answer global ones: βhow has this policy evolved?β, βwhat themes recur across these 400 reports?β
No amount of reranking fixes this, because the answer is not in any k passages. It requires a different shape: hierarchical summarisation over the corpus, or a graph index built by extracting entities and relationships and querying over community summaries. That is real additional machinery, and worth building only once you have confirmed your users actually ask global questions.
Knowing the boundary is the senior skill. Recognising βthis is a global question and my architecture is localβ saves months.
Build order
1. Evaluation set: 50-100 questions with expected passages
2. Baseline: recursive chunking + vector search. Record recall@k
3. Add hybrid retrieval. Re-measure
4. Add reranking. Re-measure
5. Tune chunk size against recall@k
6. Only now tune the generation prompt
7. Add refusal handling and measure refusal accuracy
Steps 2 through 5 are all retrieval. The generation prompt comes sixth deliberately β it is what teams tune first, and it is what matters least while recall is poor.
Try this: Log the retrieved chunk IDs for a hundred real queries and hand-check twenty against what should have been retrieved. That recall@k number is the ceiling on your whole system, and most teams find it far lower than they assumed.
Go deeper
Quick Quiz
Test what you just learned. Pick the best answer for each question.
Q1 Answers are poor. Which measurement tells you where to look first?
Q2 What does a cross-encoder reranker do that vector search cannot?
Q3 Why combine BM25 with vector search?
Q4 Which question is standard top-k RAG structurally unable to answer well?
Q5 You raise k from 5 to 20 and answer quality falls. Why is that plausible?