Handbooks / RAG & AI Agents / Chapter 1
The RAG Pipeline
7 pages · ~5 min✓ Reviewed
Start here. Next up: Chunking and Retrieval.
Why RAG and the Mental Model
1. What RAG Is
- RAG = retrieve text at query time, add it to the prompt
- Model stays frozen; knowledge lives outside it
- Gap 1: stale knowledge (training cutoff)
- Gap 2: private data it never saw
- Gap 3: hallucination with no source to check
2. Two Pipelines
Offline: indexing
- 1Ingest
- 2Chunk
- 3Embed
- 4Index
Online: query
- 1Embed query
- 2Retrieve
- 3Rerank
- 4Assemble prompt
- 5Generate + cite
3. Pick the Tool
| RAG | Fine-tuning | Long context | |
|---|---|---|---|
| Best for | Fresh or private facts, citations | Style, format, behaviour | Small, static corpus |
| Cost per query | Low | Low | High |
| Weak spot | Retrieval misses | Facts go stale | Recall drops in the middle |
Remember
Fine-tune the behaviour, retrieve the facts.
4. Interview Point
- Why not fine-tune facts? They go stale, give no citations, and cost a retrain to update
- Two pipelines? Offline indexing; online query
- Skip RAG when? The corpus is small and static, so it fits in context
Ingestion and Chunking
1. Ingestion
- LoadPDF, HTML, DB rows
- Parseclean text
- Stripheaders, footers, nav
- Tagmetadata
- Metadata on every document: source, title, page, date, ACL
2. Chunking
- Too big: diluted embedding
- Too small: lost context
- Start at 200-500 tokens with 10-20% overlap
| Strategy | Strength | Catch |
|---|---|---|
| Fixed-size | fast | cuts mid-sentence |
| Recursive | good default | paragraph, then sentence |
| Structure-aware | best for docs | needs headings or code blocks |
| Semantic | follows meaning | costly |
3. Split on Headings
- Prepend the section title so an orphan paragraph keeps context
python
for sec in split_on_headings(doc): for part in cap_tokens(sec.text, 400): chunks.append({ "text": sec.title + "\n" + part, **sec.meta})
pseudocode: 400-token cap per chunk
Embeddings and Indexing
1. Embeddings
- Embedding model: text → fixed-length vector, similar meaning lands close
- Compare by cosine or dot product; equal when normalised
- Same model for chunks and queries
- Model change → re-embed corpus
- Check max input tokens: no silent truncation
2. Search and ANN
- ANN trades a little recall for speed
| Flat (exact) | HNSW (ANN) | IVF (ANN) | |
|---|---|---|---|
| Method | Compare all | Graph hops | Nearest clusters |
| Recall | Perfect | Slightly lower | Slightly lower |
| Speed | O(N) | Fast | Fast |
3. Vector Stores
| Store | Kind | Use when |
|---|---|---|
pgvector | Postgres extension | You already run Postgres |
FAISS | In-memory library | Local, in-process |
| Pinecone, Qdrant, Weaviate, OpenSearch | Managed or dedicated | Scale and filtering |
Store beside vector
Keep chunk text and metadata next to each vector.
4. Interview Point
- Same model? Different models, different spaces: distances mean nothing
- HNSW? Graph hops skip most vectors: faster, may miss a true neighbour
- Flat enough? Small corpus: an O(N) scan is fast
Retrieval and Reranking
1. Search and Fusion
- Embed the query, fetch top-k chunks (k often 5-20)
- Similarity threshold drops weak matches
- Dense = meaning; BM25 = IDs, names, error codes
- Merge both lists with RRF: score = Σ 1/(60 + rank)
2. Rerank
- Top 50bi-encoder, fast
- Rescorecross-encoder
- Top 5precise
| Bi-encoder | Cross-encoder | |
|---|---|---|
| Reads | query, chunk apart | query + chunk together |
| Latency | low | higher |
| Precision | lower | higher |
3. Filters and Rewriting
- Metadata filters at query time: tenant, date, doc type, permissions
- Query rewriting and HyDE help vague or multi-turn questions
ACL in retrieval
Filter before the prompt so users never see forbidden chunks.
4. Interview Point
- Why BM25 plus vectors? Vectors miss exact IDs; BM25 misses meaning.
- Cross-encoder edge? Reads query and chunk jointly, so scores are sharper.
- Measure quality? recall@k (right chunk in top-k), MRR (mean of 1/rank of the first right chunk).
Prompt Assembly and Cited Generation
1. Prompt Layout
- System rulesanswer from context only
- Chunks [1] [2]each with source ID
- Questionuser query
- Rule: answer only from the context
- Answer missing → say "I don't know"
- Top-k must fit the window, room left for the answer
- Dedupe near-identical chunks; order by relevance
- Best chunks first and last; middle recalled worst
2. Citations
- 1Tag chunks[1], [2] with source + page
- 2Cite inlinemodel writes IDs
- 3Check IDsmust exist in retrieved set
- 4Map to linksunverified = failure
3. Mistakes and Evaluation
- Chunks too large or too small
- No reranking
- Stuffing too much context
- No refusal rule
- Index not versioned
- Never evaluating
Evaluate separately
Retrieval: recall@k. Generation: faithfulness and answer relevance.
RAG Cheat Sheet
1. Pipeline
| Path | Stages |
|---|---|
| Offline | ingest + metadata → chunk → embed → index |
| Online | embed query → hybrid retrieve → rerank → assemble prompt → generate + cite |
2. Defaults and Debug
- Chunk 200-500 tokens, 10-20% overlap
- Retrieve top 20-50, rerank to 5
- Same embedding model for index and query; cosine similarity
- HNSW index for scale
- In index?chunk exists
- In top-k?retrieved
- In prompt?reaches LLM
- Follows context?grounded
Debug rule
Fix the earliest failing stage first.
3. Quality Checklist
- Hybrid search + reranker
- Metadata and ACL filters
- Refusal instruction, verified citations
- Index versioning
- Eval set: recall@k, faithfulness
4. Interview Point
- 60 seconds: index the docs offline; online embed, retrieve, rerank, prompt, cite
- Top 3 levers: hybrid search, reranking, chunking
- Fresh index: re-ingest and re-embed changed docs by doc ID, delete removed ones
- Model change: bump the index version, re-embed everything
Check yourself
You switch to a new embedding model and re-embed only the new chunks. Queries use the new model too. Old documents stop appearing. Why?
- Old vectors live in a different vector space, so distances to new query vectors mean nothing.
- Re-embed the whole corpus whenever the model changes.
RRF with 60: doc A is rank 1 dense and rank 3 BM25. Doc B is rank 2 in both. Which ranks higher?
- A: 1/61 + 1/63 ≈ 0.03227. B: 2/62 ≈ 0.03226.
- A wins, barely.
The answer cites [4], but only 3 chunks were retrieved. What is wrong, and what is the fix?
- The citation is invented, so the answer is unverified.
- Check that every cited ID is in the retrieved set. Treat a miss as a failure.