Skip to content
beginner Phase 9 · RAG Architecture End-to-End

RAG Pipeline Overview

Map the stages from document ingestion through retrieval to grounded generation.

40m
0 problems
Topic Progress 0%

The Answer That Was Not in the Prompt

An HR support bot answered "The relocation cap for Berlin is $3,000" in a confident, well-formed sentence. The handbook sitting in its index said €4,000 plus a 90-day residency requirement. Nobody had put the handbook in front of the model for that question, so the model produced a number from what it had seen during training — plausible, specific, and wrong. An employee filed a reimbursement against it.

Retrieval-augmented generation (RAG) exists to make that class of answer structurally impossible rather than unlikely. It imposes two obligations on the system:

  1. Put the right text in front of the model for each question — retrieved from your documents, not from the model's memory.
  2. Make the answer come from that text — every factual claim traceable to a passage you actually sent.

Those two obligations map onto a fixed sequence of stages. Each stage takes a defined input, produces a defined output, and fails in its own characteristic way:

 OFFLINE — runs on a schedule              ONLINE — runs once per question
 --------------------------------          --------------------------------
                        +--------+
 documents ----------->| parse  |--+
                        +--------+  |
                                    v
                              +---------+
                              | clean   |
                              +---------+
                                    |
                                    v
                              +---------+        +--------+
                              | chunk   |------->| embed  |----> vector index
                              +---------+        +--------+
                                                          ^
  "what is the Berlin cap?"                               |
        |                                                 |
        +----> embed query --> retrieve top-k ------------+
                                          |
                                          v
                                assemble numbered context
                                          |
                                          v
                              generate answer, each claim [n]

Read the diagram left to right: the offline half converts files into vectors and can take minutes or hours; the online half must answer in the time a user will wait. Nothing on the right half writes to the index, and nothing on the left half ever sees a user question.

The retrieval stage may itself be two searches merged — keyword and vector — which is where fusion methods such as reciprocal rank fusion come in. The rest of this topic treats retrieval as one stage producing a ranked list of passages.

When a bot gives a bad answer, one of the two obligations broke. Learning to name which one is the whole job of this phase.

Two Clocks, One Pipeline

The most common architectural mistake is treating RAG as one synchronous job. The two halves run on different clocks, tolerate different failures, and expose different metrics.

Stage Clock Input → output Metric What a bad number looks like
parse + clean offline file → structured text with headings extraction coverage tables flattened, sections missing
chunk + embed + upsert offline text → indexed passages chunk coverage whole folders never indexed
retrieve online, ~ms query → top-k passages retrieval recall@k gold passage absent from the list
assemble online, ~ms passages → numbered prompt block context relevance filler passages crowd out the answer
generate online, ~s prompt → answer with markers answer faithfulness claims no passage supports

Two consequences fall out of the table.

Failures repair on the clock that owns them. A missing document is an ingestion defect: re-run the job, fix the parser, backfill. Reprompting the model will not index the file. Conversely, a correct passage retrieved and then ignored by the model is a generation defect: no amount of re-chunking helps.

Offline work can be retried; online work cannot. The ingestion job may batch, resume, and re-run overnight. The query path has one budget — the user's patience — so every stage there is sized for latency: small k, a single embed call, one generation.

This split is also why index freshness matters. A handbook revised yesterday is not visible until the offline job runs again; until then the online path serves the old chunks with perfect faithfulness to stale text. Freshness is an ingestion metric, and it is the one most often missing from dashboards. The mechanics of that offline job — parsing, deduplication, provenance — are the subject of the ingestion pipeline topic.

Interviewers ask a sharper version of this: if quality drops 20% after a model upgrade, how do you know which stage regressed? The answer is the metric column: pull recall@k and faithfulness separately, because they implicate different halves of the diagram.

Swap One Stage, Not the Whole Pipeline

A pipeline earns its architecture when one line of the diagram can change without touching the others. The cheapest way to get that is to give every stage a narrow signature and pass plain data between them.

from dataclasses import dataclass

@dataclass
class Passage:
    pid: str          # stable id from ingestion, survives every re-rank
    text: str
    score: float = 0.0

def chunk(doc: str) -> list[str]:
    return [p.strip() for p in doc.split("\n\n") if p.strip()]

def embed(texts: list[str]) -> list[list[float]]:
    # stand-in for a real embedding call: counts of three known words
    vocab = {"berlin": 0, "cap": 1, "relocation": 2}
    out = []
    for t in texts:
        vec = [0.0] * len(vocab)
        for w in t.lower().split():
            if w in vocab:
                vec[vocab[w]] += 1.0
        out.append(vec)
    return out

def retrieve(index, qvec, k=2) -> list[Passage]:
    def sim(a, b):
        dot = sum(x * y for x, y in zip(a, b))
        norm = (sum(x * x for x in a) ** .5) * (sum(y * y for y in b) ** .5)
        return dot / norm if norm else 0.0
    scored = [Passage(pid, text, sim(vec, qvec)) for pid, text, vec in index]
    return sorted(scored, key=lambda p: p.score, reverse=True)[:k]

DOCS = [
    "relocation policy: the relocation cap for Berlin is 4000 euro",
    "expense rules: travel is reimbursed at 90 percent for economy fares",
    "handbook: parental leave is 14 weeks in Germany",
]

index = [(f"doc{i}", d, embed([d])[0]) for i, d in enumerate(DOCS)]
hits = retrieve(index, embed(["relocation cap Berlin"])[0])
print([(p.pid, round(p.score, 2)) for p in hits])

Output:

[('doc0', 0.94), ('doc2', 0.0)]

Three lines carry the design:

  • Passage.pid is minted during ingestion and never recomputed, so a citation like [doc0] stays valid across embedding upgrades and re-rankers.
  • embed takes a list and returns a list — batching is part of the contract, not an optimization bolted on later.
  • retrieve only reads score for ordering; the moment a reranker changes the order, nothing else in the pipeline notices.

To upgrade, you replace the function, not the system: point embed at a hosted model with the same list[str] -> list[list[float]] shape, re-run ingestion, and the query path is untouched. That is what the blueprint means by designing each stage as a replaceable module — the model can improve on its own cadence while serving code stays frozen.

Where the pipeline goes wrong once it is assembled — and how to tell retrieval problems from generation problems — is the subject of diagnosing RAG failure modes.

Quiz

1. Which stage of a RAG pipeline runs on a schedule rather than inside a user request?

Question 1 options

2. A handbook was revised yesterday, but the bot still quotes the old figure with a perfect citation. What is wrong?

Question 2 options

3. Retrieval recall@5 is healthy, but answer faithfulness has dropped after a prompt change. Where do you investigate?

Question 3 options

4. Why is each RAG stage kept behind a narrow interface?

Question 4 options

Flashcards

Question

What are the two clocks of a RAG pipeline, and what runs on each?

Answer

Offline — parse, clean, chunk, embed, upsert on a schedule, with metrics like chunk coverage and freshness. Online — embed the query, retrieve, assemble, generate per request, with metrics like recall@k and faithfulness. Failures repair on the clock that owns them.

Question

What does chunk coverage measure?

Answer

The share of source documents that produced at least one indexed passage. Low coverage means the crawler or parser is silently dropping files — no prompt change can recover text that was never indexed.

Question

What does answer faithfulness measure, and which stage does a drop implicate?

Answer

Whether every factual claim in the generated answer is supported by the passages in the prompt. A drop with healthy recall points at the generation stage — instructions, model, or decoding — not at retrieval.

Question

Why keep stable passage ids minted at ingestion time?

Answer

Citations, logs, and eval datasets all reference them. If the id were recomputed on every re-index, every stored citation and every eval expectation would break when the embedding model changes.

Question

Where do hybrid-search fusion methods sit in the pipeline?

Answer

Inside the retrieval stage, where keyword and vector result lists are merged into one ranked list before passages reach context assembly — for example with reciprocal rank fusion.

Revision Notes

Key Takeaways

  • 1. RAG enforces two obligations — retrieve the right text, generate only from it — and each failure maps to exactly one of them
  • 2. The offline and online halves run on different clocks with different metrics; staleness is an ingestion defect, not a prompting defect
  • 3. Read recall@k and answer faithfulness as separate signals: they point at opposite ends of the pipeline
  • 4. Stable passage ids minted at ingestion keep citations and eval expectations valid across model upgrades
  • 5. Narrow stage signatures are what let you swap the embedder or add a reranker without a rewrite

Interview Tips

  • • When asked to design a RAG system, draw the two halves and label the metric under each stage before naming any vendor or database
  • • Answer "how would you tell which stage regressed?" with the metric split: recall down means retrieval, faithfulness down with recall steady means generation
  • • Be ready with the staleness case — document updated, index not re-run — because it tests whether you understand that ingestion is asynchronous
  • • Say explicitly what you pass between stages and why the passage id must survive every upgrade; interviewers listen for the citation stability argument

Cheat Sheet

RAG Pipeline Overview — Cheat Sheet

The two obligations: put the right text in front of the model; make every claim come from that text.

OFFLINE (schedule)                       ONLINE (per question)
parse -> clean -> chunk -> embed -> index
                                          embed query -> retrieve top-k
                                        -> assemble numbered context
                                        -> generate answer with [n] markers

Stage table

Stage Clock Metric Typical defect
parse/clean offline extraction coverage tables flattened, headings lost
chunk/embed/upsert offline chunk coverage, freshness folders never indexed, stale docs
retrieve online recall@k gold passage outside top-k
assemble online context relevance filler crowds out evidence
generate online answer faithfulness claims no passage supports

Triage rule: pull recall@k and faithfulness separately. Recall down → offline half (parser, chunker, embedder, query rewrite). Faithfulness down with recall steady → online half (prompt, model, decoding). Both steady but users still complain → the questions or the documents are wrong, not the pipeline.

Stage contract (Python): chunk(str) -> list[str], embed(list[str]) -> list[list[float]], retrieve(index, vec, k) -> list[Passage]. Passage.pid is minted at ingestion and never recomputed.

Upgrade rule: a new embedding model or reranker is a re-run of the offline job plus one module deploy. Serving code stays frozen.

Continue Learning