From retrieval to RAG
We can retrieve the right passages, by meaning, by the letter, by their fusion. But retrieving is not answering. How do we wire our engine to a language model so it composes a grounded answer, without making things up?
In the previous chapter, we married the two worlds of search: meaning, captured by vectors, and the letter, pinned down by BM25, fused by rank to stay robust. So we can now retrieve the right passages from a store, whatever the shape of the query. That is an achievement. But it is also a frustration that has been building for eight chapters: when you ask your store a real question, you do not want a pile of documents to read yourself. You want an answer, written, and grounded on those documents. Retrieving is not answering. This final chapter takes that step.
The brilliant candidate who is not allowed to open their notes
Picture an exceptional candidate at a major oral exam. They have vast general knowledge, they speak with ease, they string arguments together. But they sit the exam empty-handed: not a single note allowed. Ask them a general-knowledge question and they shine. Ask them the exact amount of your invoice number 8831, and they are lost: that information was never part of what they learned. A language model on its own is that candidate.
It suffers from three very specific gaps:
- Its knowledge is frozen. It was trained on data cut off at a certain date. Everything after that escapes it. It does not know what it could not have read.
- It ignores your private documents. Your personal store, your invoices, your notes, your contracts: none of that was in its training. Your particular is invisible to it.
- It fills the gaps. This is the most dangerous one. Rather than admit ignorance, a model spontaneously produces a plausible answer, stated with the same confidence as a correct one. This is hallucination Hallucination Output by a language model of a false statement, asserted with confidence. A structural flaw of training by likelihood maximisation, which pushes the model to always produce a plausible answer even when it should say it does not know. : a structural flaw of training, which pushes the model to always say something, even when it should say it does not know.
A foundation model Foundation model A very large neural network trained on a massive amount of general-purpose data, which can then be adapted to many specific tasks. The term was coined by Bommasani et al. in 2021. Typical 2026 examples include GPT-4, Claude and Gemini. Source: Bommasani et al., 2021 is therefore wonderful on the general and blind on your particular. The problem is not that it is dim, it is brilliant. The problem is that its notes have been confiscated. The whole idea of this chapter is the opposite gesture: give the notes back, at the right moment.
The RAG idea: slip it the right notes before it answers
What if, just before letting the model answer, we went and fetched from your store the passages that speak to the question, and laid them under its eyes? It would no longer answer from memory, but by reading. This is exactly the architecture called retrieval-augmented generation RAG Retrieval-Augmented Generation: an architecture that, before letting a language model answer, first retrieves the relevant passages from a document store, then feeds them to the model as context so it can ground its answer on them. Popularised by Lewis et al. in 2020. Source: Lewis et al., 2020 , or RAG. It breaks into three stages, and the first is already perfectly familiar to you.
Here is how the chain reads. First, for a question , we build the context by keeping the best passages returned by the previous chapter’s hybrid search. Then we assemble a prompt that contains the question and those passages. Finally, we hand that prompt to the model, which produces the answer . The remarkable point is the first term: the retrieval in RAG is nothing other than the search we built throughout the course. RAG does not replace what we learned, it crowns it. Embeddings, nearest neighbors, HNSW, durable index, hybrid fusion: all of it was the foundation that makes this final stage possible.
Ground the answer, or stay silent
Handing the passages to the model is not enough: we must also require that it use them, and only them. This is the notion of grounding Grounding Constraining a language model's answer to rely only on provided sources (the retrieved passages) rather than on its internal memory. A well-grounded model cites its sources and declines to answer when the context lacks the information, instead of making something up. : the answer must rest on the provided context, not on the model’s internal memory, and it must cite its sources so they can be checked. A reader must be able to trace every claim back to the passage that supports it.
Then comes the question that makes all the difference: what happens when the context does not contain the answer? The model’s temptation, as we saw, is to fill the void. The right behavior is the opposite: say so. Answering “I cannot find this information in your documents” is a good answer, not a failure. It is the direct remedy to hallucination: we no longer ask the model to know, we ask it to read, to cite, and to admit when the source is missing. The honest refusal is a feature, not a defect.
This shift has a heavy consequence, which we must face squarely: the quality of the answer is now capped by the quality of the retrieval. If the right passage is not retrieved, or if it does not make it into the context, the most grounded model in the world can only refuse. That is exactly what the component below puts into your hands.
Wire the pipeline yourself
The component below runs the full pipeline on a small personal store. Pick a query, then switch between the two worlds: the model alone, which answers from memory, and the model plus RAG, which first reads the retrieved passages. In RAG mode, a lever sets the context budget , that is the number of passages we allow into the model’s view. Watch what happens when you squeeze it.
Query
Model
Context budget k
Retrieved passages
- #1in context
Invoice FR-2024-8831, consulting services, amount 4,200 euros.
- #2over budget
Strategy board meeting of March 12, minutes.
- #3over budget
Invoice FR-2024-2207, annual hosting.
- #4over budget
Invoice FR-2023-1180, software license.
- #5over budget
Expense report, trip to Lyon.
- #6over budget
Staff internal regulations.
Answer
Invoice 8831 corresponds to consulting services for an amount of 4,200 euros. [1]
Cited sources: d1
What you observe
The right passage has entered the context. The model grounds its answer on it and cites its source. The answer is correct because retrieval did its job.
Three questions to ask yourself while playing:
- On the query “amount of the consulting invoice”, stay in LLM alone mode. What does the model answer, and does that figure come from your store? Then switch to LLM plus RAG with a budget : what happens, and why is the right passage not there yet?
- Still on this query, raise to . The answer changes status. Which passage just entered the context, at what rank was it, and why can the model finally answer?
- Switch to the query “due date of invoice 2207”. Compare the model alone and RAG. Which one invents, which one refuses? In what way is the refusal here the best possible answer?
Three horizons to go further
The pipeline we just wired is RAG in its simplest form. Three refinements separate it from real systems. We formalize them here, because these are the ones you will meet as soon as you leave the toy corpus.
Split before you index: chunking
So far, one passage equaled one document. But a real document runs forty pages. Indexing it as a single block would be absurd: its answer would drown the useful paragraph in an ocean of text, and it would never fit in the model’s context. So we cut it into short pieces before indexing. This is chunking Chunking Splitting a long document into shorter pieces (chunks, or passages) before indexing them. One indexes and retrieves passages, not whole documents, to focus the answer and fit within the model's context window. Chunk size and overlap are sensitive settings. .
The size remains to be chosen. Too large, and each passage mixes several ideas and wastes the budget; too small, and we cut an idea in two, so each piece becomes incomprehensible on its own. So we make the passages overlap: each piece repeats the end of the previous one, so that a sentence cut by a boundary is found whole in at least one passage. If a document has length (in tokens), and we choose passages of size with overlap , the number of passages produced is about:
This formula reads: each new passage advances by tokens (the size, minus what we repeat from the previous one), the step of a sliding window. We count how many steps it takes to cover the whole length, plus the initial passage. The larger the overlap , the smaller the step, so the more passages we produce: the safety against cuts is paid in index volume.
The context window: a budget you cannot exceed
We handled the lever without really naming it. What bounds it is a hard constraint of the model: its context window Context window The maximum amount of text, measured in tokens, that a language model can read at once as input. It is bounded: one cannot pour an entire document store into the prompt, only the top k passages kept by retrieval. This limited budget is what makes the retrieval ranking decisive. , the maximum amount of text, measured in tokens, that it can read at once. We therefore cannot pour the whole store into the prompt, only the best passages. It is precisely this ceiling that makes the retrieval ranking decisive: if the right passage comes out at rank and the budget only holds , it is lost, exactly as in the component at .
From this comes a very useful refinement in practice: reranking Reranking A second sorting stage applied to the already-retrieved passages. A fast first retrieval (BM25, vectors) brings back a batch of candidates; a more expensive model, often a cross-encoder that reads the query and each passage together, finely reorders them so the best ones take the top spots, the ones that will fit in the context window. . We first let the fast retrieval (BM25, vectors) return a large batch of candidates, say fifty. Then a more expensive but finer model, often a cross-encoder that reads the query and each passage together, reorders them so the very best take the top spots, the ones that will fit in the window. Two stages: a first, fast one that rakes wide, a second, precise one that sorts tight.
The model that searches on its own: agentic retrieval
In everything above, the pipeline is a one-way trip: one query, one search, one answer. But some questions cannot be resolved in a single search. “Compare the amount of my consulting invoice with my annual training budget” requires fetching two distinct things, then confronting them. A one-way trip fails.
The idea that lifts this limit is to give the model search not as a fixed step, but as a tool it wields itself, as many times as it deems useful. This is the ReAct loop (for Reasoning and Acting, Yao et al. 2022), which alternates three gestures until the model judges it can conclude:
The model reasons about what it is missing, decides what to search for, observes the passages returned, then starts again based on what it just learned. Retrieval becomes dynamic, driven by the model itself. This is the bridge to what we now call agents: a model that no longer merely answers, but goes and fetches, in a loop, what it needs in order to answer.
Exercises
In one sentence
RAG turns a search into an answer: it retrieves the best passages (via the course’s hybrid search), slips them into the model’s bounded context, and asks it to compose an answer grounded on them while citing its sources, or to refuse honestly when the source is missing, so that the quality of the answer stays capped by that of the retrieval.
1. Why does a language model alone hallucinate a private invoice amount?
2. In the RAG pipeline, what does the context given to the model refer to?
3. What does the fact that RAG refuses at k=1 but answers at k=2 on the same query reveal?
The closing word
Here we are at the end of the thread. Think back over the path: we started from an almost philosophical idea, meaning as a position in space, and built, step after step, everything needed to make it a real engine. Represent a text as a vector. Measure proximity, and run into the curse of dimensionality. Bypass exhaustive comparison with HNSW, then place that choice within the landscape of approximate indexes. Give ourselves a differential oracle to verify without fooling ourselves. Make the index durable, able to survive a restart. Marry it with the lexical side so as to miss neither a synonym nor an exact number. And, today, wire all of it to a language model to move from the retrieved document to the written answer.
Each chapter answered a limit left by the previous one, and this last one closes the loop: RAG only exists because everything else exists first. That is the beauty of this chain, and it is what I wanted to share by writing it. The sovereign engine I am building will keep evolving, it will become the memory of a personal assistant that reads my own documents to answer me. But the essential is now in your hands: you know what hides behind these buzzwords, not the marketing version, the version that compiles. Thank you for walking this path.
Sources
- Lewis, P. et al. (2020). “Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks.” NeurIPS 2020. arXiv:2005.11401
- Karpukhin, V. et al. (2020). “Dense Passage Retrieval for Open-Domain Question Answering.” EMNLP 2020. arXiv:2004.04906
- Yao, S. et al. (2022). “ReAct: Synergizing Reasoning and Acting in Language Models.” ICLR 2023. arXiv:2210.03629
Going further
- Ji, Z. et al. (2023). “Survey of Hallucination in Natural Language Generation.” ACM Computing Surveys 55(12), 1-38. DOI 10.1145/3571730
- Gao, Y. et al. (2023). “Retrieval-Augmented Generation for Large Language Models: A Survey.” arXiv:2312.10997