Runbooks/ RAG Runbook The runbook itself Deep readQuick learn
  1. 00 Start
  2. /
  3. 01 Chunking
  4. 02 Parsing
  5. 03 Identity
  6. 04 Access
  7. /
  8. 05 Models
  9. 06 Vectors
  10. 07 Limits
  11. 08 Model ops
  12. 09 Index I
  13. 10 Index II
  14. 11 Tuning
  15. 12 Capacity
  16. /
  17. 13 Sharding
  18. 14 Filtering
  19. 15 Hybrid
  20. /
  21. 16 Proof
Document 00 · Quick learn Four tracks · Five moves · One cost hierarchy

Sixteen documentsone worked system

How a document becomes a retrievable record, and how millions of those records get searched in fifty milliseconds without leaking anything. Every number is derived where it appears, and where the source notes were wrong, the correction is listed.

“A large library. The index is the card catalogue — the thing that stops you walking every shelf.”
16 documents
four tracks, one order
61 → 382 GB
the quoted number, and the real one
6 levers
the cost hierarchy, cheapest first
14 corrections
re-derived from the source notes
The mapone picture holds the whole subject

Documents become chunks, chunks become vectors, vectors get a structure over them, and then a question walks that structure looking for the few records worth showing a language model.

Documents

The source of truth, unmodified.

Chunks

Text, identity, parent, metadata, ACL.

Vectors

A point on a map of meaning.

The index

The card catalogue that stops you walking every shelf.

Retrieve

Search, filter, rerank.

Generate

The few records worth showing a model.

Three durable stages, each replayable from the one before

Source of truth

The original documents, unmodified.

Rebuild from: nothing — this is the floor
Chunk records

Text, identity, parent, metadata, ACL.

Rebuild from: re-parse and re-chunk the sources · hours
Vectors and index

Embeddings plus the ANN structure.

Rebuild from: re-embed the chunk records · hours to days
The analogy this runbook uses throughout — a large library

Ingestion is acquiring books and deciding what a catalogue entry covers — a whole book, a chapter, a page. Embedding is placing each entry on a map of meaning, so things about the same subject sit near each other regardless of the words they use. The index is the card catalogue: the thing that stops you walking every shelf.

Serving is the reading room, where a member asks a question, a librarian fetches candidates, and a second, more careful librarian decides which are actually worth handing over
The curriculumsixteen documents in four tracks
A · Ingestion and chunking

01 · 02 · 03 · 04
Everything before the vector: chunking, parsing hard content, identity and deletes, access control and freshness.

Where most retrieval quality is actually won or lost
B · Embeddings and index

05 – 12
The vector and the structure over it: models, dimensions, token limits, fine-tuning, five index types, tuning, quantisation and capacity.

The largest track, because it holds the two most expensive decisions
C · Serving at scale

13 · 14 · 15
What changes when it has to serve real traffic: sharding and replication, filtering and multi-tenancy, hybrid retrieval and reranking.

Where the security boundary lives
D · Proving it works

16
Evaluation and observability — and how you run the team that builds it.

The track that separates “it feels better” from “it is better”
If you only have one evening

Read this page, then 01, 05, 09, 12 and 16. That is the spine: what a record is, what space it lives in, how search avoids looking at everything, what it costs, and how you know it works.

The other eleven documents are the depth an interviewer reaches for once you answer those five well
The answer frameworkfive moves, and the fifth separates the levels

Interviewers at this level are not checking your vocabulary. They are checking whether you reason under constraints, commit to a decision, and know what would prove you wrong.

1 · Clarify the workload

“How big, how often does it change, lookups or summaries?”

2 · State it as a number

“Four million chunks, hourly edits, p95 under 800 ms, 200 QPS.”

3 · Give your default

Decisively. “Then I default to 400-token chunks with a parent fallback.”

4 · Name the tradeoff

“The cost is more storage and one extra fetch hop, about ten milliseconds.”

5 · Say how you would measure it

“…and what would change my mind.”

Why step five is the one that separates levels

A strong senior engineer gives you steps one to four. An architect adds the fifth, because an architect has been wrong before and has built the thing that tells them so.

“I would sweep chunk size against recall@10 on a two-hundred-question set, and if recall is flat between 300 and 500 tokens I would take the smaller one for cost.” That names the experiment, the metric and the tie-break rule at once
The two failure shapes to avoid

The encyclopedia. Listing every option without committing to one. It reads as knowledge without judgement.

The assertion. Committing to one without naming its cost or the measurement that would overturn it. It reads as confidence without evidence.

The five moves are the shape that avoids both
The cost hierarchythe single most useful thing to have memorised

Some changes to a RAG system are a configuration edit. Some are a week of compute and a second copy of your data live at the same time.

1
Tune efSearch / nprobeno re-embed · no rebuild · no second copy
minutes
2
Add or swap a rerankerno re-embed · no rebuild · a deploy, plus latency
a deploy
3
Change M / efConstructionno re-embed · rebuild · usually no second copy
hours of CPU
4
Change quantisationno re-embed · rebuild · sometimes a second copy
hours
5
Change chunkingre-embed · rebuild · second copy live
days
6
Change the embedding modelre-embed · rebuild · second copy live
days to weeks
The one-liner worth memorising

“There is a clear cost hierarchy in a RAG system. I work up it — efSearch first, then a reranker, then index parameters, then quantisation, and I only migrate the embedding model when the cheap levers are exhausted. Going the other way round is how teams spend a month re-embedding to fix something a config change would have fixed.”

And it runs backwards too

The two changes at the expensive end are the ones to get right the first time — which is exactly why an interviewer will spend more time on your embedding-model and chunking answers than on anything else.

The reference stackone worked system, all sixteen documents
The corpus

Two million documents averaging twelve pages, chunked at ~400 tokens.

10,000,000 chunks in the worked example
The vectors

1536 dimensions, float32. HNSW at M = 32.

Four indexed filter fields: department, sensitivity, owner, date
The service

Replication factor three — it must survive one node loss.

200 QPS peak, p95 under 800 ms end to end, of which retrieval gets ~50 ms
The churn

Roughly eight per cent of the corpus changes monthly.

So ~25% of records are dead between compactions

The memory stack, in the order you should recite it

1
The base10M × 1536 × 4 bytes — the number everyone quotes
61.4 GB
2
+ the graphM = 32 → 10M × 256 B of edges
+ 2.6
3
+ payload and filters4 indexed fields × 75 B × 10M
+ 3.0
4
+ dead records25% churn on vectors and structure, awaiting compaction
+ 16.0
5
= subtotalbefore anything runs
83.0 GB
6
× 1.15 runtimeallocator, buffers, working set
95.5 GB
7
× 3 replicasto survive one node loss
286 GB
8
+ rebuild headrooma fourth copy live during a rolling rebuild
382 GB
The mistake this exists to prevent

The most common wrong answer to “how much memory will this need?” is vectors × dimensions × 4. On the reference stack that gives 61 GB. The number you actually provision is around 380 GB.

The gap is not one mistake — it is six ordinary additions compounding, and being able to name all six in order is a complete answer to one of the most common capacity questions in this subject
Numbers that should be automaticso you can sanity-check a proposal in real time

Bytes per vector, and what that means at ten million

384
MiniLM classsmall open models · 1,536 bytes per vector
15.4 GB
768
Base-size open models3,072 bytes per vector
30.7 GB
1024
Large open models4,096 bytes per vector
41.0 GB
1536
The common API default6,144 bytes per vector
61.4 GB
3072
Large API tier12,288 bytes per vector
122.9 GB

Flat search: what scanning everything actually costs

100k
100,000 vectors307 MB scanned
~6 ms · 163 QPS
500k
500,000 vectors1.54 GB scanned
~31 ms · 33 QPS
1M
1,000,000 vectors3.07 GB scanned
~61 ms · 16 QPS
10M
10,000,000 vectors30.7 GB scanned
~614 ms · 1.6 QPS
The insight inside that table

Memory bandwidth is shared at the socket, so a second concurrent query does not get its own bus. Sixty-four cores do not give sixty-four times the throughput on flat search — they give roughly the same throughput, with all the cores stalled on memory.

The operational tell is distinctive and worth naming: CPU graphs look healthy while latency collapses, because a stalled core still reports as busy

Latency, and where the milliseconds go

Embed the query

5–20 ms

Blown by: a network hop to a hosted model; batch-of-one inefficiency
ANN search

~50 ms p95

Blown by: efSearch too high, a low-selectivity filter, a cold page cache
Fetch payload

5–15 ms

Blown by: fetching chunk text from a separate store, one row at a time
Rerank 100 → 8

30–120 ms

Blown by: depth — cost is linear in candidates scored
Generation

the rest

Everything above is competing for what the model leaves you
The words people confusevocabulary collisions, not knowledge gaps

QIndex

(a) the ANN structure over vectors; (b) the whole collection, vectors and payload; (c) the verb. Say which sense once, early: “when I say index I mean the HNSW graph specifically, not the collection”.

QEmbedding vs vector

The embedding is the mapping a model performs; the vector is the array of numbers it produced. Minor, but using them precisely signals care.

QChunk vs document vs record

A document is the source file; a chunk is a slice of it; a record is the stored row. Sizing answers go wrong here more than anywhere else — two million documents is seventy-two million chunks.

QRecall vs precision

Recall@k: of what should have been found, how much was in the top k. Retrieval systems are tuned on recall, because a reranker and the model can discard a bad candidate but cannot invent a missing one.

QShard vs replica

Shards divide the data; replicas multiply it. Four shards and three replicas is twelve partitions and three times the footprint. Confusing these is an instant tell.

QANN vs KNN

KNN is the exact answer; ANN is an approximation with a recall number attached. If a vendor quotes latency without recall, the number is meaningless.

QQuantisation vs dimensionality reduction

Quantisation keeps the dimensions and shrinks each number; reduction keeps the numbers and removes dimensions. They compose, and they fail differently.

QTenant vs namespace vs collection

Tenant is the business concept; namespace and collection are the engine’s mechanisms. Ask what the engine calls it before designing the isolation model.

QSemantic search vs RAG

Semantic search returns documents; RAG puts them in a prompt and generates an answer. Retrieval quality caps generation quality — which is why every metric in document 16 is a retrieval metric first.

Self-testfourteen questions — answer out loud before reading on

QA stakeholder says the corpus is two million documents. What do you say next?

Ask how many pages, and how many chunks that becomes. Two million documents at twelve pages and ~3 chunks a page is seventy-two million records — thirty-six times the number you were given. Every sizing number downstream is wrong by that factor if you skip it.

QWhy can you not compare vectors from two different models?

The dimensions are not shared coordinates — each model learns its own space, and dimension seven means something different in each. The numbers will happily produce a cosine similarity; it is arithmetic on unrelated axes. Any migration needs a parallel collection, not a mixed one.

Qp50 is fine, p99 is terrible, after you added a permission filter. Why?

Almost certainly filter selectivity. A filter keeping a large fraction is cheap; one keeping a tiny fraction strands the graph walk, because most neighbours it considers are rejected. Post-filter when selectivity is high, brute-force pre-filter when very low, filtered traversal in the middle.

QYou are told 61 GB for 10M vectors at 1536 dims. What is missing?

Six things: the index structure, a second copy if quantised, payload and filter fields, dead records awaiting compaction, runtime overhead, and replicas — then rebuild headroom on top. The real provisioning number is around 380 GB.

QWhat does efSearch control, and why touch it first?

The size of the candidate list the graph walk keeps as it descends. Raising it raises recall and latency together. First because it is a runtime value: no rebuild, no re-embed, effective on the next query, reversible in seconds.

QTen per cent of the corpus was deleted and memory did not go down.

Deletes in a graph index are tombstones. The vector stays resident and its edges stay in the graph so the structure remains traversable. Space returns at compaction or rebuild. Resident memory rising while the live count is flat is the signal — often the only one you get.

QWhy does quantisation not reduce your disk footprint?

Because the architecture keeps two copies. Compressed vectors live in RAM and do the scanning; full-precision originals stay on disk so the shortlist can be rescored. Quantisation is a RAM optimisation — say that plainly, it is a common trap.

QUsers search for part numbers like A-4471-X and get nothing.

Dense embeddings encode meaning, and an identifier has no meaning to encode — near-identical strings land in unrelated places. The fix is a sparse or keyword retriever alongside, with the two lists fused. Identifier-heavy corpora are where hybrid is not optional.

QWhere must the permission filter be applied?

Inside the search, before results are returned — never as a filter afterwards. Post-filtering can return an empty page when the user does have permitted matches, and any component that sees the pre-filter list has seen data that user is not entitled to. Retrieval is where the security boundary lives.

QHow do you change chunk size on a live system with no downtime?

Build the new collection alongside, dual-write during the backfill, validate against a labelled set and against the old collection on live queries, then move an alias. Keep the old one until you are sure. The cost is a second full copy for the duration.

QHNSW or IVF? What do you ask first?

How much data, how much RAM, and what the write pattern is. HNSW wins on latency-per-recall and takes incremental inserts gracefully; IVF is cheaper in memory and rebuilds cleanly but degrades as data drifts from its centroids. If it does not fit in RAM, both answers are wrong and the conversation moves to DiskANN.

QFastest way to add recall to an already-tuned system?

A reranker over a deeper candidate list. Retrieve 100 instead of 10, then let a cross-encoder that actually reads the query against each candidate reorder them. It costs latency in a budget that usually has room, changes nothing stored, and is reversible in a deploy.

QHow would you prove a chunking change made things better?

A labelled gold set of a couple of hundred real questions with known correct chunks, swept across the parameter, measured on recall@k and NDCG@k, offline before anything reaches production — then an online check on the metric the business cares about. Without the gold set you are guessing with more steps.

QThe team wants to fine-tune an embedding model. What do you ask?

What did the cheaper levers give you? Fine-tuning sits above rerankers, hybrid retrieval and better chunking in cost, and it adds a permanent obligation: every future model upgrade now has a retraining step attached. Ask what the gold set says the current ceiling is, whether a reranker was tried, and who owns retraining in eighteen months.

Corrections carried inevery one re-derived — the archive is not the source of truth

The discussion notes in Chunking/ and Embeddings-and-index/ were written as we worked through the subject, and a handful of numbers and orderings in them are wrong or inconsistent. They are listed here so that if you go back to the archive you know what to ignore.

Chunking 02 · ingest throughput

“Worker count goes from 12 to over 100”

About 36. At 1.4 documents per second per worker and an 8.05-second budget, steady state is 12 workers; a three-times burst allowance gives 36, not 100.
Chunking 04 · time decay

exp(−age / half_life), with 61% and 37% examples

0.5(age / half_life), giving 50% at one half-life and 25% at two. The original mixed an exponential-decay formula with half-life language.
Chunking 05 · evaluation cost

“A few hundred compute-hours”

Roughly eight hundred. Two million items at 1.5 seconds each is 3.0 million seconds = 833 hours.
Embeddings 02 · query cost

200 QPS × 30 tokens ≈ 1.5B tokens/year

About 190 billion. 200 × 30 is 6,000 tokens a second; over a year that is 189 billion — roughly $23k, not $180.
Index types · flat and IVF

“Ten times less work”

Ten times less bucket scanning — the routing scan grows tenfold at the same time, and on small corpora it can dominate.
Index types · scaling levers

Listed replicas first

Load-shedding, then quantisation, then replicas. Three other sections of the same document give that order.
Index types · HNSW

Layer counts in the overview disagreed with the table

The table is right: 1,000,000 / ~31,000 / ~1,000 / ~30 / ~1 across five levels at the stated branching factor.
Index types · ScaNN

The lookup table is “a few kilobytes”

Tens of kilobytes. 96 chunks × 256 entries is 24,576 partial scores — about 24 KB at one byte each.
Memory maths · the assembled stack

Dead vectors “at ~20% churn, ~15.0 GB”

The percentage and the number disagree. This runbook models dead records as churn × (vectors + structure), consistently, everywhere.
Memory maths · worked example

Three indexed filter fields on 72M vectors ≈ 7.2 GB

That is 33 bytes per field per vector, well below the same document’s own stated 200–400 bytes per vector range. The runbook uses 75 B per field per vector.
Sharding · the quantised stack

“One replica, SQ8: 38 GB (114 / 3)”

114 GB is the rebuild peak, not the resident total — dividing it by three mixes two different numbers.
Sharding · cost reduction

Footprint savings reported directly as cluster savings

Shard counts are integers and every shard is replicated, so a 48% cut in bytes can be a 25% cut in nodes — or none at all.
Multi-tenancy · the tiered design

Tiering costs “about a 10 per cent tax”

Under one per cent. The promoted tenants’ vectors and edges were going to exist in the shared graph anyway; the only new cost is per-graph fixed overhead — 21 graphs at ~10 MB is 0.2 GB.
Multi-tenancy · the skew table

Hot-shard sizes computed against an unstated replica size

The runbook computes the hot shard from the formula instead: share × copy + (1 − share) × copy ÷ shards.

Work up the cost hierarchy, not down it. efSearch first, then a reranker, then index parameters, then quantisation — and only migrate the embedding model when the cheap levers are exhausted. Going the other way round is how teams spend a month re-embedding to fix something a config change would have fixed. And the two changes at the expensive end are the ones to get right the first time, which is exactly why an interviewer will spend more time on your chunking and embedding-model answers than on anything else.

Read the full chapter → 01 · Chunking →