Vibe Engines
YouTube
AI System Design

Design a RAG Pipeline

Step 1 / 9

Learn AI system design by building a retrieval-augmented generation (RAG) pipeline step by step.

The numbers to beat~300–800tokens / chunk1 vectorper chunkofflineruns ahead of time

The whole design, in writing

Learn AI system design by building a retrieval-augmented generation (RAG) pipeline step by step. An interactive guide covering document chunking, embeddings, the vector store, top-k retrieval, reranking, grounded prompt assembly, and keeping the index fresh — so an LLM answers from your knowledge, not its training data.

Every step of the build above, written out: the problem each piece solves, the option that was taken and the ones that were not, the numbers, and how it fails in production.

The big idea

Why retrieval at all?

An LLM only knows what it was trained on. Ask it about your internal docs, last week’s release, or a private wiki and it confidently makes something up. How do you make it answer from knowledge it never saw?

Userasks a question
New in this step: User.

Retrieval-Augmented Generation: before generating, fetch the most relevant passages from your knowledge and put them in the prompt. The model reasons over real, current text instead of fuzzy memory — and can cite its sources.

What the new pieces do

Userclient
A person asking a question that should be answered from your documents, not the model’s training data.

Step 1 · The skeleton

Retrieve, then generate

A user asks a question. We can’t just forward it to the LLM — it would answer from training data. What has to happen between the question and the answer?

Userasks a questionRAG Gatewayorchestrator
New in this step: RAG Gateway.

The user asks a question about your private docs. What’s the flow?

  1. It has never seen your documents, so it answers from training data — confidently and often wrong. This is exactly what RAG exists to fix.

  2. A gateway orchestrates retrieve-then-generate: find the chunks that matter, hand them to the model as context, and let it answer from real text.

  3. Fine-tuning bakes knowledge in slowly and expensively, can’t cite sources, and goes stale the moment a doc changes. Retrieval keeps knowledge live and external.

A RAG Gateway orchestrates two phases for every question: retrieve the most relevant passages, then generate an answer grounded in them. Knowledge lives outside the model, so it’s always current and citable.

What the new pieces do

RAG Gatewaybackend
Coordinates the retrieve-then-generate flow: embeds the query, fetches chunks, builds the grounded prompt, calls the model.

Step 2 · Prepare the knowledge

Chunk and embed your documents

Your knowledge is a pile of long documents. You can’t hand a whole 80-page PDF to the model per query. How do you make documents searchable by meaning?

UserRAG GatewayVector StoreDocument StoreIngestionKnowledgeEmbedding Model
New in this step: Vector Store, Document Store, Ingestion, Knowledge, Embedding Model. · swipe to pan the diagram

How do you store documents so you can find the relevant bits by meaning?

  1. Keyword search misses paraphrase — "how do I cancel" won’t match "terminating your subscription." Meaning needs semantic vectors, not exact words.

  2. Whole docs are too big for the context window and too coarse to pinpoint the relevant passage. You need to split first.

  3. An offline pipeline chunks documents, embeds each chunk into a vector capturing its meaning, and upserts vectors + text into the stores. Now "find by meaning" is a nearest-neighbour search.

An offline Ingestion pipeline loads each document, splits it into chunks, runs every chunk through an Embedding Model to get a vector, and upserts the vectors into a Vector Store (plus the raw text into a Document Store). This is the prep work that makes retrieval possible.

  • ~300–800tokens / chunk
  • 1 vectorper chunk
  • offlineruns ahead of time

What the new pieces do

Vector Storeindex
Holds chunk embeddings and answers nearest-neighbour search in milliseconds via an ANN index.
Document Storestore
The original chunk text and metadata, returned alongside vectors so the prompt and citations use real content.
Ingestionbus
The offline pipeline: load documents, split into chunks, embed each, and upsert into the vector + document stores.
Knowledgestore
The source of truth: PDFs, docs, wikis, tickets — whatever the system must answer from.
Embedding Modelbus
Maps text to vectors. The SAME model must embed both stored chunks and incoming queries so they share a space.

Back of the envelope

split on structure
paragraphs/sections, not arbitrary character counts
add overlap
~10–15% so ideas spanning a boundary aren’t cut in half
same embedding model
for chunks AND queries — they must share a space
store metadata
source, title, URL — for filtering and citations

Step 3 · Find the right chunks

Embed the query, search by similarity

A question comes in. The chunks are vectors in a store. How do you find the handful that actually answer this question — out of possibly millions?

RAG GatewayRetrieverVector StoreDocument StoreIngestionEmbedding Model
New in this step: Retriever. · swipe to pan the diagram

You have millions of chunk vectors. How do you find the closest to the query?

  1. Exact nearest-neighbour over millions of high-dim vectors is too slow per query. At scale you trade a little accuracy for huge speed.

  2. Embed the query with the SAME model, then let an ANN index (HNSW/IVF) return the closest chunks in milliseconds — near-exact recall, sub-linear cost.

  3. That’s back to lexical search and its paraphrase blind spot. Semantic retrieval is the whole point. (Hybrid keyword+vector is a refinement, not a replacement.)

The Retriever embeds the query with the same model, then asks the Vector Store for the top-k nearest chunks via an ANN index. The Document Store returns their text. Semantic match means "cancel my plan" finds "terminating your subscription."

What the new pieces do

Retrieverservice
Turns the query into a vector and searches the store for the nearest chunks (semantic, not keyword, match).

Step 4 · How much to fetch

Top-k and the recall/precision dial

Retrieve too few chunks and you miss the answer. Retrieve too many and you bury the model in noise, blow the token budget, and slow it down. Where’s the line?

Retrieverembed + searchVector Storetop-k ANNDocument Storesource chunks
New in this step: Retriever → Vector Store, Retriever → Document Store.

How many chunks should you stuff into the prompt?

  1. More context isn’t better — irrelevant chunks distract the model ("lost in the middle"), raise cost, and slow generation. Precision beats volume.

  2. Too brittle: the answer often spans two or three passages, and the very top hit isn’t always the right one. You need a small set, not a single bet.

  3. Retrieve a modest candidate set (e.g. k≈20), then narrow to the best few. Enough recall to catch the answer, enough precision to keep the prompt clean.

Retrieve a modest top-k candidate set from the Vector + Document stores — wide enough that the answer is almost certainly in there (recall), but not so wide it drowns the prompt. The next step tightens it to the best few (precision).

Back of the envelope

k ≈ 20 candidates × ~500 tok/chunk (mid-range of Step 2’s ~300–800) ≈ 10,000 tok
the retrieval-stage set — only the reranker reads this, not the generator
→ rerank → top 3–5 × ~500 tok ≈ 1,500–2,500 tok
what actually lands in the prompt
on an 8K-context model, worst case that leaves ≈ 5,500+ tok
room for the system prompt, conversation history, and the answer
optional metadata filter
restrict by source/recency before ranking

Step 5 · Sharpen the shortlist

Rerank for precision

The vector search is fast but coarse — it ranks by embedding similarity, which isn’t the same as "actually answers the question." The true best chunk might sit at position 8. How do you fix the order?

Retrieversearch + rerankRerankerprecision
New in this step: Reranker.

ANN similarity ≠ true relevance. How do you get the best chunks to the top?

  1. Bi-encoder similarity is a fast approximation. The genuinely most relevant passage often isn’t the nearest vector — order needs a second, sharper pass.

  2. A cross-encoder reads the query and each chunk together and scores true relevance — far more accurate. Run it on the ~20 candidates (cheap), keep the best 3–5.

  3. More candidates raises recall but not precision — you still feed the model noise. Reranking is what turns a long candidate list into a clean shortlist.

A Reranker (a cross-encoder) re-scores the top-k candidates by reading the query and each chunk together, then keeps only the best 3–5. It’s too slow to run over the whole store — but perfect over a 20-candidate shortlist.

What the new pieces do

Rerankerservice
Re-scores the top candidates with a heavier cross-encoder so the few chunks that reach the prompt are the most relevant.

Step 6 · Ground the answer

Build the prompt and generate

You have the 3–5 best chunks. Now the model has to answer — but you need it to use those chunks and not slip back into making things up. How do you assemble the prompt?

RAG GatewayorchestratorRetrieversearch + rerankLLMgrounded gen
New in this step: LLM.

You have the best chunks. How do you get a grounded, citable answer?

  1. If the chunks aren’t in the prompt, the model can’t use them. Grounding only works when the retrieved text is actually in the context.

  2. The gateway builds a grounded prompt: "answer ONLY from these passages, cite them, say you don’t know if they don’t cover it" — then generates.

  3. Back to noise and cost. You reranked for a reason — feed the clean shortlist, not the raw candidate pile.

The Gateway assembles a grounded prompt — system instructions + the reranked chunks + the question — and tells the LLM to answer only from the passages, cite them, and admit when they don’t cover the question. The chunks carry their source metadata, so citations point at real documents.

What the new pieces do

LLMservice
Generates the answer using only the retrieved chunks as context, with instructions to cite and not invent.

Step 7 · Trust the answer

Citations, "I don’t know", and evals

Even grounded, the model can over-claim or cite the wrong chunk. Users need to trust the answer — and you need to know when retrieval is failing. How do you keep RAG honest?

UserRAG GatewayRetrieverLLM
New in this step: LLM → RAG Gateway, RAG Gateway → User. · swipe to pan the diagram

How do you keep a RAG system trustworthy over time?

  1. Grounding reduces hallucination but doesn’t eliminate it — the model can still misread or over-generalize from a chunk. Trust must be verifiable.

  2. Citations are the trust mechanism — they let a user verify the claim against the source. Hiding them removes the one thing that makes RAG auditable.

  3. Surface the source chunks as citations, let the model decline when context is thin, and run evals (retrieval recall, answer faithfulness) to catch drift.

The answer ships with citations back to the source chunks, the model is allowed to say "I don’t know" when retrieval comes up thin, and an eval harness tracks retrieval quality (did we fetch the right chunks?) and answer faithfulness (did the answer stick to them?). That’s how you catch a silently drifting index before users do.

The payoff

You built a RAG pipeline

From a forgetful model to a grounded, citable system: an ingestion pipeline that chunks and embeds, a vector store for semantic search, two-stage retrieval with reranking, grounded generation, and evals to keep it honest.

UserRAG GatewayRetrieverRerankerLLMVector StoreDocument StoreIngestionKnowledgeEmbedding Model
The finished design, end to end. · swipe to pan the diagram

Now stale the index and watch RAG’s signature failure — confident answers built on outdated chunks — and see why ingestion is a first-class, ongoing system, not a one-time script.

Everything you assembled, in order

  • RAG Gateway — orchestrates retrieve → augment → generate
  • Ingestion — chunk + embed documents, offline and ongoing
  • Embeddings — meaning as coordinates — same model for chunks + queries
  • Vector Store — ANN search returns top-k by similarity in ms
  • Top-k — wide for recall, then narrowed for precision
  • Reranker — cross-encoder sharpens the shortlist to the best few
  • Grounded prompt — answer only from context, cite, decline if thin
  • Evals + citations — make a quiet failure mode visible and auditable

Where an interviewer pokes next

Getting the boxes right is the easy half. These are the questions that separate a candidate who drew the diagram from one who has run the thing. Answer each one out loud before you open it.

  1. Why ~10-15% chunk overlap specifically — what actually breaks at 0% overlap, and what breaks at 50%?

    At 0% overlap, an idea that spans a chunk boundary gets split so neither chunk fully contains it — a sentence explaining "the deadline is X, UNLESS condition Y" could have the condition cut into the next chunk, and retrieval might fetch only the half with the deadline. At 50%+, you are storing and embedding nearly the same text twice, roughly doubling index size and compute for a marginal completeness gain past a certain point. 10-15% is the empirical sweet spot: enough overlap that boundary-spanning ideas usually survive in at least one chunk, without meaningfully bloating the index.

  2. A cross-encoder reranker scores query-document pairs one at a time, not in a batch like the bi-encoder embedder. How do you keep reranking 20 candidates fast enough?

    Cross-encoders ARE more expensive per comparison than the embedding-based ANN search, which is exactly why they only ever run on the narrow shortlist (k≈20) instead of the whole store — the two-stage design isn’t just about precision, it’s what makes the expensive stage affordable at all. Within that budget, reranking latency is managed by running comparisons in parallel (batched inference) and picking a reranker model sized for the latency budget, sometimes a smaller distilled cross-encoder that trades a little precision for meaningfully lower latency.

  3. A user should only see documents they have permission to access. Does that filtering happen before or after the ANN search?

    Before, ideally — as a metadata filter applied AT search time (most vector databases support filtered ANN search), not as a post-hoc filter on the results. Filtering after search is a real bug waiting to happen: if the top-k ANN results are entirely documents the user cannot see, filtering afterward can return an empty or thin result set even though relevant, permitted documents exist further down the unfiltered ranking — the search itself needs to know the access boundary, not just the display layer.

  4. How does the system decide, programmatically, that retrieval "came up thin" enough to say "I don’t know"?

    Typically a similarity-score threshold on the top result (or the reranker’s relevance score) — if even the best-ranked chunk falls below a calibrated confidence cutoff, that’s a signal the knowledge base likely doesn’t cover this question, and the prompt instructs the model to decline rather than stretch a weak match into a confident-sounding answer. The threshold itself needs the same calibration discipline as any classifier cutoff: set too high and it declines answerable questions; too low and it still grounds on near-misses.

  5. How does chunking and embedding change for documents with images and tables, not just prose?

    Tables usually need structure-aware chunking (keep a table intact or chunk it row-group by row-group rather than splitting mid-table, since a table fragment loses its header context) and images need either a multimodal embedding model that can embed image+text jointly, or a captioning step that converts the image to a text description an ordinary text embedder can index — either way, "just chunk every 500 tokens" silently breaks on both, treating a table as prose and destroying its meaning.

Check yourself — the answers, and why

Eight steps in, these are the calls you should be able to make cold. Pick one, then read why.

  1. RAG exists mainly to…

    • Make the model faster
    • Let the model answer from current/private knowledge it wasn’t trained on
    • Shrink the model

    Retrieval puts real, current passages in the prompt so the model answers from external knowledge instead of training-data memory.

  2. The same embedding model must be used for chunks and queries because…

    • It’s faster
    • They must live in the same vector space to be comparable
    • It saves storage

    Nearest-neighbour search only means anything if query and chunk vectors come from the same embedding space.

  3. Two-stage retrieval (ANN then rerank) is used because…

    • One stage is impossible
    • A wide cheap recall pass + a narrow precise ranking pass beats either alone
    • Rerankers are free

    ANN gives cheap recall over millions of vectors; a cross-encoder reranker gives precision over the small candidate set.

  4. RAG’s most dangerous failure mode is…

    • A 500 error
    • A confident answer grounded on a stale or wrong chunk — nothing errors
    • Slow retrieval

    RAG fails quietly: the pipeline runs fine but the index is stale, so the answer is subtly wrong. Citations and evals make it visible.

  5. Someone upgrades the query-time embedding model but doesn’t re-embed the stored chunks. What happens?

    • Retrieval gets slightly less accurate but still works
    • Similarity search returns effectively meaningless chunks, ranked with real-looking scores
    • The system throws a dimension-mismatch error

    Two different embedding spaces aren’t comparable — "nearest neighbor" across them is close to random, but still returns k ranked results with no error. An embedder change requires a full reindex.

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

What it must do

Agree on these before drawing a single box.

  • Retrieve then generate: fetch the most relevant passages first, then have the model answer from them — not from training memory.
  • Ingest & embed: an offline pipeline chunks documents, embeds each chunk, and upserts vectors + text into the stores.
  • Semantic search: embed the query with the same model and pull the top-k nearest chunks via an ANN index.
  • Rerank for precision: a cross-encoder re-scores the candidates and keeps the best few for the prompt.
  • Ground & cite: answer only from the retrieved chunks, cite the sources, and say “I don’t know” when they’re thin.

The qualities that shape everything

Each one names the mechanism that buys it.

Answer from knowledge the model never saw
A RAG Gateway orchestrates retrieve-then-generate, so knowledge lives outside the model and stays current and citable instead of baked into weights.
Find by meaning, not keywords
Chunk each document and embed it into a shared vector space — the same embedding model for chunks and queries so they’re comparable.
Search millions of vectors in milliseconds
An ANN index (HNSW/IVF) trades a sliver of recall for orders-of-magnitude speed over exact nearest-neighbour.
Enough recall without drowning the prompt
Retrieve a modest top-k candidate set (wide enough the answer is almost certainly in it), then narrow it for precision.
The best chunk actually reaches the model
A cross-encoder reranker re-scores the ~20-candidate shortlist by reading query and chunk together, keeping the best 3–5.
Keep answers honest and auditable
Citations back to source chunks, an allowed “I don’t know” when retrieval is thin, and evals on retrieval recall and answer faithfulness.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Retrieve then generate over sending the question straight to the LLM

The model never saw your documents, so it answers from training data — confidently and often wrong, which is exactly what RAG exists to fix.

Chunk + embed for semantic search over keyword-indexing whole documents

Keyword search misses paraphrase (“cancel” vs “terminating your subscription”) and whole docs are too big and coarse; embeddings put similar meanings close together.

Approximate nearest-neighbour (ANN) over exact comparison against every chunk

Exact search over millions of high-dimensional vectors is too slow per query; ANN gives near-exact recall at sub-linear cost.

A small top-k, then rerank over stuffing as many chunks as fit

Irrelevant chunks distract the model (“lost in the middle”), raise cost, and slow generation — precision beats volume.

A cross-encoder reranker over trusting the vector-similarity order

Bi-encoder similarity is a fast approximation, and the genuinely best passage often isn’t the nearest vector; a cross-encoder reads query and chunk together for true relevance.

The answer, out loud

What a strong answer to “Design a RAG Pipeline” sounds like, first question to last trade-off. It is about 6 minutes of talking; the whiteboard and the interviewer fill the rest of the 45. Read it aloud once, then close the page and give it yourself.

  1. 0–3 min

    Pin down what the model can’t know

    Before I draw anything, I want to pin down the problem. A model only knows what it was trained on, and our users are asking about internal docs, last week’s release, a private wiki — things it never saw. Ask anyway and it confidently makes something up. So the requirement is an answer drawn from our own documents, current, with sources a user can check. That changes the real question from “did the model memorize this?” to “did we retrieve the right context?” — and that one we can engineer.

  2. 3–7 min

    Retrieve first, then generate

    The skeleton is a user, a RAG gateway and an LLM. The gateway orchestrates every question: retrieve the most relevant passages, then have the model answer from them. Sending the question straight to the model is the failure we’re here to fix. Fine-tuning it on every document is the tempting alternative, but that’s slow and expensive, can’t cite a source, and goes stale the moment a doc changes. Retrieval keeps knowledge outside the model, where it stays current and citable.

    Built in step 1: Retrieve, then generate
  3. 7–14 min

    Prepare the knowledge offline

    Retrieval needs something to search, so an offline ingestion pipeline loads each document and splits it into chunks of roughly three to eight hundred tokens — on paragraphs and sections, with ten to fifteen percent overlap so an idea crossing a boundary isn’t cut in half. Each chunk goes through an embedding model, which maps text to a point in space where similar meanings sit close together. Vectors go into a vector store; the text and metadata — source, title, URL — into a document store for citations. A keyword index would miss paraphrase: “how do I cancel” never matches “terminating your subscription.” One rule I’d say out loud: the same embedding model embeds chunks and queries, or they aren’t comparable.

    Built in step 2: Chunk and embed your documents
  4. 14–19 min

    Search by meaning, approximately

    At query time the retriever embeds the question with that same model and asks for the nearest chunks. Comparing against millions of vectors exactly is too slow per query, so I’d use an approximate nearest-neighbor index — HNSW or IVF — that answers in milliseconds. The cost is a sliver of recall, meaning the chance the right chunk is anywhere in what we fetch. I’d tune the index, and k — how many chunks we fetch — against latency, and keep keyword search as a later hybrid refinement, not a replacement.

    Built in step 3: Embed the query, search by similarity
  5. 19–24 min

    Decide how much to fetch

    How many chunks? Stuffing in as many as fit sounds safe, but irrelevant chunks distract the model — the “lost in the middle” problem — and add cost and delay. One chunk is too brittle: answers often span two or three passages. So I split the job: fetch a modest candidate set, around twenty, tuned for recall, then narrow it for precision, so only the best few reach the model.

    Built in step 4: Top-k and the recall/precision dial
  6. 24–30 min

    Sharpen the shortlist

    Vector similarity is fast but coarse; the truly best chunk might sit at position eight. So a reranker re-scores those twenty. It’s a cross-encoder: rather than comparing two separately computed vectors, it reads the question and a chunk together and scores how well that chunk answers it. Far more accurate, and far too slow for the whole store — which is why it only sees the shortlist and keeps the best three to five. Just retrieving more wouldn’t help; that raises recall, not precision. The cost is an extra model on the critical path, which I’d keep fast with batching or a smaller distilled reranker.

    Built in step 5: Rerank for precision
  7. 30–36 min

    Ground it, and let it say no

    The gateway then builds the prompt: system instructions, the reranked chunks, the question. Two instructions do the heavy lifting — answer only from these passages, and if they don’t cover it, say so. Pasting all twenty candidates back in would undo the reranking. Chunks carry their source metadata, so the answer ships with citations a user can check. And “I don’t know” is a feature: when even the best relevance score falls below a calibrated threshold, the model declines rather than stretching a weak match.

    Built in step 6: Build the prompt and generate
  8. 36–42 min

    What I’d watch, and how it fails

    RAG fails quietly — nothing errors, the answer just goes subtly wrong — so the dashboard is evals: retrieval recall, did we fetch the right chunks, and faithfulness, did the answer stick to them. Two failures I’d plan for. The index goes stale: retrieval still returns chunks, but from last week’s documents, and the model grounds confidently on them — so ingestion is an ongoing, versioned system, not a one-off script. Or someone upgrades the query embedding model without re-embedding the stored chunks; the two spaces aren’t comparable, so search returns near-random chunks with real-looking scores. An embedder change is a full reindex.

  9. 42–45 min

    Close on the trade-off

    To close, in one breath: ingestion that chunks and embeds, one embedding model on both sides, approximate search for a wide candidate set, a cross-encoder to narrow it, a grounded prompt with citations, and evals watching for drift. Every knob — chunk size, k, how many survive reranking — trades recall against precision and cost. With more time I’d go to permissions, filtering to what a user may see during the search rather than after it, and to tables and images, where plain chunking quietly breaks.

What this teaches

Learn AI system design by building a retrieval-augmented generation (RAG) pipeline step by step. An interactive guide covering document chunking, embeddings, the vector store, top-k retrieval, reranking, grounded prompt assembly, and keeping the index fresh — so an LLM answers from your knowledge, not its training data.

Key takeaways

  • RAG Gateway — orchestrates retrieve → augment → generate
  • Ingestion — chunk + embed documents, offline and ongoing
  • Embeddings — meaning as coordinates — same model for chunks + queries
  • Vector Store — ANN search returns top-k by similarity in ms
  • Top-k — wide for recall, then narrowed for precision
  • Reranker — cross-encoder sharpens the shortlist to the best few
  • Grounded prompt — answer only from context, cite, decline if thin
  • Evals + citations — make a quiet failure mode visible and auditable

Concepts covered

  • Why retrieval at all?
  • Retrieve, then generate
  • Chunk and embed your documents
  • Embed the query, search by similarity
  • Top-k and the recall/precision dial
  • Rerank for precision
  • Build the prompt and generate
  • Citations, "I don’t know", and evals
RUN IT YOURSELF

Retrieval by cosine similarity

RAG finds the most relevant chunks for a query by comparing embedding vectors with cosine similarity, then feeds the top-k to the LLM. Here is that retrieval core in real Python, running live. Read the comments, edit the vectors, and hit Run.

HOW TO READ THE CODE — 4 IDEAS
  1. Text becomes a vector (embedding); similar meaning → similar direction.
  2. Cosine similarity measures the angle between two vectors, ignoring length (steps 1–2).
  3. Score every document against the query, then take the top-k (step 3).
  4. Those k chunks are what actually get stuffed into the LLM prompt.
CPython · WebAssembly
built to be reasoned about, not memorized — make the calls, poison the index, run the quiz.
Finished this one? 0 / 61 AI System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs