Runbooks/RAG RunbookTrack D · Proving it worksLLM Inference Runbook →0%
  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
RAG Runbook · Document 16 of 16 · Track D — Proving it works

Track D · Document 16 · Proving it works

Evaluation, Observability and Running the Team

Every failure mode in the previous fifteen documents is silent. This one is about making them loud — and about who owns each of them on a Tuesday.

Reads in about 45 minutes · 9 figures, 4 of them live calculators · 10 interview questions · prints to clean A4

What is in this document

  1. You cannot evaluate by looking at answers
  2. The gold set
  3. The metrics, and their blind spots
  4. The diagnostic: which layer is broken
  5. Labels that survive a re-chunk
  6. The sweep, and the plateau
  7. The harness as a system
  8. What runs on every commit
  9. Online evaluation
  10. The observability stack
  11. What to alarm on
  12. The incident playbook
  13. Unit economics
  14. Running the team
  15. The decision framework
  16. How candidates lose points
  17. Interview questions
  18. FAQ
  19. Cheat sheet, and how to use this runbook

1 · You cannot evaluate a pipeline by looking at answers

A bad answer could be bad ingestion, bad retrieval, bad ranking, bad context assembly or bad generation. Five layers, one symptom — and until you can isolate them you cannot tune any of them.

A BAD ANSWER TELLS YOU ALMOST NOTHING. FIVE LAYERS CAN PRODUCE IT. 1 · ingest chunking, parsing 2 · retrieve the index and its filters 3 · rank fusion and reranking 4 · assemble prompt and context 5 · generate the model You cannot tune any of them until you can isolate them. That isolation is the whole discipline. RETRIEVAL QUALITY ANSWER QUALITY Did the right chunk come back? Was the response correct and grounded? A labelled set. No model call. Cheap, fast, deterministic. An LLM judge or human raters. Slow and expensive. Run it on every commit. Run it nightly, or per release.

Everything in the previous fifteen documents is a choice; this one is how you know whether the choice was right. If you can justify a chunk size by reasoning, you are a thoughtful engineer. If you can say how you would measure which is right — and what result would change your mind — you are an architect. That distinction is not a slogan; it is the actual difference interviewers are scoring at this level.

Where the leverage is, and it is not where people look

Chunking and retrieval are tuned on the first question — did the right chunk come back. If the right chunk never surfaces, no amount of prompt work saves you, and no reranker can fix an item that was never retrieved.

That question is also the cheap one: a labelled set, no model call, deterministic, fast enough to run on every commit. Answer quality needs a judge and costs real money, which is why it runs nightly and not per commit. Getting those two on the right cadence is most of the practical value in this document.

2 · The gold set

Questions paired with the chunks or spans that actually contain the answer. A hundred to three hundred to start — and the number matters far less than the stratification.

“BUILD AN EVAL SET” IS EASY TO SAY AND EASY TO NEVER DO. HERE IS THE WEEK. Day 1 · sample 200 documents, stratified by type, source and length — then generate three candidate questions per document with an LLM, each with the span that answers it → about 600 raw candidates Days 2–3 · an SME review session. Cut anything that is not a question a real person would ask; fix the spans → about 200 verified. This is the step that cannot be skipped and the one everyone tries to skip Day 4 · add adversarial cases by hand: near-duplicate documents, superseded policy, questions spanning two documents → +30. These are the ones that separate a real system from a demo Day 4 · add the no-answer slice: plausible questions the corpus genuinely does not cover → +40. A system that confidently answers what it should not is worse than one that says it does not know Day 5 · wire the harness, get a baseline number, and commit the set to git. Version it alongside the code so a change to the set is reviewable, and give it an owner and a refresh cadence. Target having the majority be real production queries within a quarter.
  1. Day 1: sample 200 documents stratified by type, source and length; generate three candidate questions each with an LLM, plus the answering span — about 600 raw candidates.
  2. Days 2–3: an SME review session. Cut anything that is not a question a real person would ask; fix the spans — about 200 verified.
  3. Day 4: add adversarial cases by hand — near-duplicates, superseded policy, questions spanning two documents. +30.
  4. Day 4: add the no-answer slice — plausible questions the corpus genuinely does not cover. +40.
  5. Day 5: wire the harness, take a baseline, commit the set to git with an owner and a refresh cadence.

The trap in an LLM-generated set, said out loud: the questions come out suspiciously well aligned to your current chunk boundaries — because they were generated from those chunks. So the set flatters the chunking you already have, and a sweep against it will cheerfully tell you your current configuration is optimal. That caveat is subtle enough that stating it reads as experience. The cure is the same as the durability plan: replace generated questions with real ones from production logs as they arrive.

Where the questions come from, best first

SourceWhyThe catch
Real user queries from logs Always the best source. Real users are terse, misspell things, and ask two questions at once — synthetic questions do none of that You need the product to exist first
SME-written Ask the people who own the documents what actually gets asked Their time, and a tendency to write the question they wish people asked
LLM-generated, human-verified The fast way to bootstrap: feed a chunk to a model, ask for a question that chunk answers, have a human confirm it is sensible It flatters your current chunking — see below

Stratify it, and do not skip the last slice

Include lookup questions, multi-hop questions, table questions, and adversarial near-misses where two documents look similar but only one is right. And include questions with no answer in the corpus at all.

A system that confidently answers questions it should not is worse than one that says it does not know, and the no-answer slice is the only way to measure that. It is also the slice the business usually cares about most and the one almost nobody builds.

3 · The metrics, and what each one is blind to

Everything here is computable by hand on a single ranked list, and doing it by hand once is the fastest way to stop confusing them.

TEN RETRIEVED CHUNKS FOR ONE QUESTION, RANKED. THE METRICS, BY HAND. #1#2#3#4#5#6#7#8#9#10 k = 5 hit@k yes did any relevant chunk appear in the top k? the coarse one recall@k 0.50 1 of the 2 relevant chunks that exist — the primary retrieval metric precision@k 0.20 1 of the 5 retrieved — the context-waste metric, and it caps low by construction reciprocal rank 0.50 1 ÷ 2 — averaged over questions this is MRR, and it only sees the first hit Move k and watch which numbers move. Recall rises with k and precision falls — they are not two views of the same thing.

Two traps in that panel. Recall@k is meaningless without stating k, and the k before reranking and the k after it are different numbers — quoting one without saying which is the most common way to mislead yourself. And precision@k caps low by construction: with one correct chunk per question, precision@10 can never exceed 0.1, so chasing it is chasing an artefact of the label set rather than a quality problem.

MetricDefinitionWhat to watch out for
Recall@k Fraction of questions where at least one relevant chunk appears in the top k Meaningless without stating k — and k before reranking and k after it are different numbers
Precision@kOf the k retrieved, the fraction that are relevant With one correct chunk per question, precision@10 caps at 0.1. Do not chase it blindly
MRRMean of 1 ÷ rank of the first relevant result Only cares about the first hit — useless when several chunks are jointly needed
NDCG@kRanking quality with graded relevance and a position discount Needs graded labels, which are expensive and inconsistent between annotators
Context precision Fraction of retrieved tokens that were actually relevant The cost metric — it drives what you pay per query for noise
Faithfulness Fraction of claims in the answer supported by the retrieved context Judged by a model, so it has its own error rate. Validate the judge against humans on a sample
Answer correctness Does the answer match the reference answer Conflates retrieval and generation. Never tune chunking on this
Abstention rate On the no-answer slice, how often it correctly declines The most neglected metric, and often the one the business cares about most

The division of labour to state out loud

Chunking is optimised for recall; reranking owns precision. A reranker can fix ordering, but nothing can fix an item that was never retrieved.

So measure recall at your retrieval k — say 50, before reranking — and again at your final k — say 5, after. The gap between those two numbers is the whole diagnostic, and it is the subject of the next section.

4 · The diagnostic: which layer is actually broken

Four numbers, read in order, name the layer. This is the single most useful thing in this document to be able to do out loud.

FOUR NUMBERS, READ IN ORDER, NAME THE BROKEN LAYER chunking / embedding low recall@50 ranking recall@50 high, recall@5 low generation recall@5 high, faithfulness low the corpus faithful, but not correct Ranking is the problem. Retrieval found it and the shortlist buried it. Invest in reranking. The answer is already in the candidate list, so this is the one case where a reranker pays for itself immediately. The order matters, because the earlier layers make the later numbers meaningless There is no point reading faithfulness while recall@50 is low: the model is being asked to ground an answer in context that never contained it, and it will either abstain or invent. Fix the layers left to right, and re-measure after each one. Being able to run this diagnostic out loud is worth more than any single number in it.

The pairing that does the work is the first one. High recall@50 with low recall@5 means retrieval is fine and ranking is the problem — invest in reranking. Low recall@50 means chunking or embedding is the problem, and reranking cannot help you at all, because nothing downstream can fix an item that was never retrieved. Those two look identical from the outside: a user gets a bad answer either way.

PatternWhere the problem isWhat to do
Low recall@50Chunking or embedding Reranking cannot help. Sweep chunking, check the embedding model, check whether a filter is cutting the pool
High recall@50, low recall@5Ranking Invest in reranking — the answer is already in the candidate list
High recall@5, low faithfulnessGeneration Prompt and context assembly: ordering, truncation, how much noise is in the window
High faithfulness, low correctnessThe corpus You are grounded in the wrong documents. Superseded policy, duplicates, missing source — a content problem

5 · The chunking wrinkle: labels that survive a re-chunk

This one only occurs to people who have actually tried to run a chunk-size sweep, which is exactly why raising it unprompted lands.

CHANGE THE CHUNK SIZE AND EVERY CHUNK ID CHANGES WITH IT the document, and the span that actually answers the question chars 4200–4600 config A — 300-token chunks c-100c-101c-102c-103c-104 config B — 500-token chunks c-700c-701c-702 “the answer is in c-102” — meaningless in config B “chars 4200–4600 of doc 7” — scores both configs
  1. A document, and the span of characters that actually answers the question.
  2. Two chunking configurations cut it differently. Every chunk id in config A is absent from config B.
  3. A label pointing at a chunk id is worthless across configurations; a label pointing at a span scores both.

Two fixes, and the crude one is often enough. Span-level labels — “the answer lives in document 7, characters 4200 to 4600” — let any configuration be scored by whether a retrieved chunk overlaps that span. Answer containment — does the retrieved text contain the known answer string? — is cruder, configuration-independent and trivial to implement. Raising this problem unprompted is a strong signal, because it only occurs to people who have actually tried to run a chunk-size sweep.

6 · The sweep, and why the plateau is the finding

Fix everything except one variable. Rebuild the index for each configuration. Run the gold set. Plot it. That is the whole method, and it works identically for parent size, overlap, contextual augmentation, and structural against fixed boundaries.

FIX EVERYTHING EXCEPT ONE VARIABLE. REBUILD. RUN THE GOLD SET. PLOT IT. recall@10 0.70 chunk size in tokens 150200300500800 the plateau — and the plateau is the finding at your choice 0.86 recall@10 4,000 tokens sent at k=10 On the plateau. Choose on the secondary axis, and say which axis you chose. 300 and 500 give the same recall, so the tie breaks on cost: 300 sends fewer tokens per query, 500 stores fewer vectors and rebuilds faster.

You usually get a plateau, not a peak — and that is itself the useful information. A flat primary metric means the decision has been handed to the secondary axis, and stating why you broke the tie is what turns a measurement into a decision. The same method works for parent size, overlap, contextual augmentation on or off, and structural against fixed boundaries. One variable at a time, and rebuild the index for each.

The decision rule when the primary metric is flat

Choose on the secondary axis, and say which axis you chose. If 300 and 500 give identical recall, pick 300 for cheaper generation or 500 for fewer vectors and cheaper indexing — depending on whether query cost or index cost dominates for you.

Stating why you broke the tie is what turns a measurement into a decision, and it is the difference between “we tested it” and “we decided it”.

7 · The harness as a system, not a script

Anyone can compute recall@10. What separates a system from a script is that runs are comparable.

the gold set is versioned in git, alongside the code — so a change to the set is reviewable and every run records which version it used
the corpus snapshot is pinned a run is against a stated state of the data, not against “production, some time on Tuesday”
every run records the full config chunk size, model, index parameters, k, filters, fusion rule — all of it, in the result
and the reason all three matter if the corpus grew 5% between two runs, a two-point recall difference means nothing — and teams lose weeks to exactly this

The honest limitation, worth volunteering before you are asked

Gold sets go stale. The corpus changes, user behaviour changes, and a set built in January flatters a system tuned in January. So you refresh it from production logs on a cadence, and you treat evaluation-set maintenance as ongoing work with a named owner — not a one-off project that finished in the second sprint.

8 · What runs on every commit

This is the operational answer that shows you have run a system rather than built one, and every item on it comes from a failure documented earlier in this runbook.

GateWhat it catchesWhere it came from
A fifty-question gold set, recall@10
Fail the build if it drops more than a couple of points
Every silent quality regression in this runbook This document
Canary documents
Synthetic users asserting they cannot retrieve restricted content
Permission regressions — the failure mode with a legal consequence 04 · Access control
Known-value assertions
Specific numbers from specific tables must be retrievable
Parser regressions on PDFs, which are invisible in aggregate metrics 02 · Parsing hard content
Chunk-count and id-overlap drift alarms A chunker change that silently re-cuts the whole corpus and orphans every label 03 · Identity and updates
A short-result-set counter
Queries returning fewer than k
The filtered-search shortfall, which produces no error at all 14 · Filtered search

Why a gate rather than a dashboard

Every failure mode in this runbook is silent. A dashboard requires somebody to look at it on the day it moved; a gate fails the build. The gate is the one mechanism that turns a silent regression into a loud one, and it is cheap: fifty questions, no model call, a few seconds of CI.

The threshold matters less than its existence. Two points of recall on a fifty-question set is noise; the point is that a ten-point drop stops the deploy.

9 · Online evaluation: what production tells you that the gold set cannot

Offline metrics tell you about your gold set. They do not tell you about your users, and several of the most useful production signals cost nothing to collect.

SignalWhat it means
Thumbs up and down, joined to the chunk ids retrieved The direct signal — and the join is exactly why stable chunk ids matter
Query reformulation rate A user rephrasing is a strong implicit failure signal, and it is free to collect
Escalation to human support The business-visible version of the same thing
Citation click-through Do users open the source? A low rate can mean they do not trust the answer
No-result and low-score rates Queries where nothing scored well are your coverage gaps. Cluster them and you have a prioritised list of what content is missing

That last row is the one to raise

Most teams treat a low-scoring query as a retrieval failure to tune away. Clustered, they are a content roadmap — the questions people are asking that the corpus genuinely cannot answer. That output goes to whoever owns the documentation, not to the retrieval team, and it is usually the highest-value artefact the retrieval system produces for the rest of the organisation.

And when you A/B test, remember chunking changes are index-wide

You cannot assign a chunking configuration per request, because the index is the configuration. So it is a blue/green deployment with a traffic split, not a per-request experiment — which means the comparison is slower to run, more expensive while both exist, and worth planning before you promise a date.

10 · The observability stack: four planes

Everything the previous fifteen documents said needed watching, organised into the four planes it actually falls into. Teams typically instrument one of them well and none of the others.

QUALITY · THE PLANE THAT FAILS SILENTLY Nothing on this plane raises an error. Every metric here degrades without a single log line, which is why it needs a fixed set and a schedule. recall@k on a fixed gold set per commit in CI · the only true signal — and report it per slice, not as one average recall per tenant, as a distribution p50 and p10 across tenants — the mean is the largest tenant’s number abstention rate on the no-answer slice the most neglected metric, and often the one the business cares about most queries returning fewer than k results the filtered-search shortfall from document 14, made visible fraction of fused results from one side only catches one half of hybrid silently returning junk, or nothing If you instrument one thing on this plane, make it recall on a fixed set in CI. Everything else is a refinement of it. LATENCY · THE PLANE EVERYONE ALREADY HAS, MEASURED WRONG Almost every team has a latency dashboard and almost none of it is broken down by the stage that actually varies. p50 / p95 / p99, end to end the mean hides every failure in this document per stage: retrieve, fuse, rerank, generate without this you cannot tell a slow index from a slow model per shard, not just per cluster fan-out latency is the slowest shard’s — the cluster p99 cannot show you which rescore read latency and cache hit rate the I/O dependency quantisation introduced, and the cold-cache cliff filtered against unfiltered, separately a tight filter makes a query slower, so mixing them hides both The single most valuable addition to a typical dashboard: break p99 down by stage, and by shard. CAPACITY · THE PLANE THAT DECIDES WHETHER YOU CAN STILL MAINTAIN THE SYSTEM Every number here comes from documents 12 and 13, and every one of them is a slow-moving trend rather than a spike. resident memory per node alarm at 70% — not because 71 is dangerous, but because rebuild needs the headroom live count against storage count memory rising while live is flat means dead accumulation. Neither series alone says anything dead ratio alarm above 20% — it has no user-visible symptom until the node runs out of memory build duration against the maintenance window the day a rebuild stops fitting is the day you find out you cannot rebuild page faults and swap tiering gone wrong. Any sustained rate is a problem in a memory-mapped setup These belong on a monthly review, not a pager. They are trends, and a page for a trend teaches people to ignore pages. FRESHNESS · THE PLANE NOBODY BUILDS UNTIL AN INCIDENT The user-visible question is “is the answer current?”, and none of the other three planes can answer it. ingestion lag, source change to searchable measured end to end, not per stage — the queue is usually where it hides oldest unprocessed item in the queue a better alarm than queue depth, which looks fine while one item starves deletion lag — revoked to unsearchable this one is a security metric, not a quality one drift between the source of record and the index a periodic id-set comparison. It always finds something idf drift on a fixed identifier query set the keyword half ageing, from document 15 Deletion lag is the one to instrument first, because it is the only metric here with a compliance consequence.

Four planes, and teams typically instrument one of them. Latency is easy, so everyone has it. Quality is the one that decides whether the product works, and it fails without a single error being raised. Capacity decides whether you can still maintain the system in six months. Freshness is the one users actually complain about, phrased as “the answer is out of date”. If a dashboard has only latency on it, the system is unobserved in the three ways that matter.

11 · What to alarm on, and what not to

An alarm that fires on a trend teaches people to ignore alarms. The distinction that keeps a pager useful is between things that are broken now and things that will be broken in a month.

Page someoneReview it monthlyNever alarm on it
p99 latency above budget, sustainedResident memory trend per node Individual slow queries
Error rate or timeout rateDead ratio approaching the compaction threshold Any single recall measurement
Ingestion lag past the freshness SLO Build duration against the maintenance window Cost per query on a single day
Deletion lag past its SLO — this one is compliance Index size and growth rateThumbs-down on one answer
The recall gate failing in CI Per-tenant recall distributionQueue depth — use oldest item instead

The three SLOs worth actually defining

Latency: p95 end to end, stated with the query mix it applies to. A single number across filtered and unfiltered queries is not an SLO, it is an average of two different systems.

Freshness: time from a source change to being searchable, and separately from a permission revocation to being unsearchable. The second is a security control wearing an SLO’s clothes.

Quality: recall@k on the fixed gold set, per slice, with a floor. This is the unusual one — most teams have never written a quality SLO down, and it is the only one that protects the thing the product is for.

The alarm nobody has, and everybody needs

Resident memory rising while the live vector count is flat. Neither series alone means anything — memory rises for many reasons and a flat live count is normal — but together they are unambiguous dead accumulation. It is one derived metric, it has no user-visible symptom until a node runs out of memory, and it takes ten minutes to add.

12 · The incident playbook

“Search is bad” is the report you will get. It is not a diagnosis, and the order in which you narrow it decides whether this takes twenty minutes or two days.

“SEARCH IS BAD” — THE FIVE QUESTIONS, IN ORDER 1 · is it everyone, or someone? Split by tenant, by query type and by language before doing anything else. One tenant means skew, a filter or a promotion boundary. One query type means the sparse arm, or fetch depth. Everyone means a deploy or the index. 2 · did anything change? Parameters, data or traffic — in that order, and the answer is usually not parameters. If no configuration changed, stop looking at configuration. A bulk load, a new customer or a new document type is the usual cause. 3 · run the four-metric diagnostic. recall@50, recall@5, faithfulness, correctness — in that order. This names the layer in one pass, and it stops the team from tuning the model when the chunker is at fault. 4 · check the silent failures before the loud ones. Queries returning fewer than k. Dead ratio. Ingestion lag. Cache hit rate. Every one of them degrades without an error, so none of them will be in the alert that brought you here. 5 · before closing it: what would have caught this automatically, and does that thing exist now? Almost every incident in this runbook has a one-line metric that would have caught it a week earlier. The incident is worth nothing if that metric does not exist by the time it closes.
  1. Is it everyone, or someone? Split by tenant, query type and language first.
  2. Did anything change? Parameters, data or traffic — and the answer is usually not parameters.
  3. Run the four-metric diagnostic: recall@50, recall@5, faithfulness, correctness, in that order.
  4. Check the silent failures before the loud ones: short result sets, dead ratio, ingestion lag, cache hit rate.
  5. Before closing: what would have caught this automatically, and does that thing exist now?

Step one is the one people skip, and it is the one that saves the day. “Search is bad” from one customer and “search is bad” from everyone have almost no diagnostic overlap — the first is skew, a filter or a tenant boundary, and the second is a deploy or the index itself. Splitting the population before forming a hypothesis costs five minutes and routinely saves a day of looking in the wrong layer.

The postmortem question that compounds

“What would have caught this automatically, and does that thing exist now?”

Almost every failure in this runbook has a one-line metric that would have caught it a week earlier: a short-result-set counter, a dead-ratio alarm, a per-tenant recall distribution, an ingestion-lag SLO. An incident that closes without adding its detector is an incident you have agreed to have again. That is the single most valuable habit an engineering manager can enforce here, and it costs nothing but the discipline to ask.

13 · Unit economics, and what the manager actually owns

The question an engineering manager gets is not “what is our recall”. It is “what does this cost per query, and what would make it cheaper” — and the honest answer surprises people.

COST PER QUERY · GENERATION INPUT AT $3 PER MILLION TOKENS context tokens sent 4,000 10 chunks × 400 tokens — this is the number you control generation input $0.01200 retrieval infrastructure $0.00030 total per query $0.01230 $123,000 per month at 10,000,000 queries Generation is 40× the retrieval cost. The index is not where the money is — the context you send is. Which is why the sweep in section 6 is a cost decision as much as a quality one On the recall plateau, moving from 500-token to 200-token chunks changes nothing about quality and cuts the largest line on this page by more than half.

The number an engineering manager is asked for is cost per query, and the honest answer surprises people. Retrieval infrastructure — the nodes, the memory, the replicas, everything documents 12 and 13 were about — is usually a rounding error against the tokens you hand the model. That does not make capacity work pointless; it makes it a reliability investment rather than a cost one, and framing it that way is how it gets funded. The cost lever is k and the chunk size, and both of them are decided by a measurement you already have.

How to frame capacity work so it gets funded

If retrieval infrastructure is a rounding error against generation, then documents 12 and 13 were not cost work — they were reliability work. Quantisation, sharding, replication and headroom buy you a system that survives a node loss and can still be rebuilt in a year, not a smaller invoice.

Say it that way and the conversation improves in both directions: the capacity work gets funded as reliability, and the cost conversation goes where the money actually is, which is k and the chunk size. Both of those are decided by a measurement you already have.

14 · Running the team

The engineering-manager half of this topic is not a different set of facts. It is the same facts with owners, cadences and a definition of done attached — and that attachment is what interviewers at this level are listening for.

What needs an owner, and what happens when it does not have one

ThingCadenceWhat happens with no owner
The gold setRefreshed from production logs, quarterly It goes stale, flatters the configuration it was built against, and quietly stops being evidence
The capacity note — copy size, shards, replication factor, utilisation cap, and why each number is what it is Reviewed at every rebuild Every number gets re-litigated by somebody who was not in the room, once a quarter
Codebook and calibration retraining, if you use product quantisation On the rebuild schedule Recall degrades over months with no deployment to blame it on
The embedding model version pinReviewed, not drifted Half the corpus ends up in one vector space and half in another
The rebuild checklist — nlist, dead ratio, headroom, filter fields, dimensionEvery rebuild Three of those five drift continuously without anyone deciding anything
Per-tenant recall distributionWeekly The largest tenant’s number becomes “our recall” and everybody else degrades unnoticed

The three cadences that hold the system together

Per commit: the recall gate, the canaries, the known-value assertions. Automatic, fast, blocking.

Weekly: the per-slice and per-tenant evaluation, the short-result-set count, the one-sided-fusion rate. Fifteen minutes of somebody reading numbers, and it catches drift before a customer does.

Per rebuild, and at least quarterly: the capacity checklist and the gold-set refresh. This is the one that gets dropped, and it is the one that decides whether the system is still maintainable in a year.

How to split the work, and the boundary that matters

ConcernWho owns itWhy the split is here
Data isolation — tenant Z never sees tenant A’s rows The platform team, and they must be able to prove it It is a property of the store and the read-time join, and it is a security incident when it fails
Performance isolation — A’s traffic does not slow Z The API team No store gives a per-tenant CPU share. It is rate limits and scheduling, which live above the store
Retrieval qualityWhoever owns the gold set Quality without a measurement is an opinion, and the measurement is the deliverable
Corpus coverage — the questions nothing can answer Whoever owns the documentation Clustered low-score queries are a content roadmap, not a retrieval bug

What “done” means for a retrieval change

Not “it is deployed”. A retrieval change is done when: the gold set says it helped, per slice and not on average; the change is reversible or the rollback is written down; the detector that would have caught its failure mode exists; and the capacity note reflects whatever it did to the footprint.

Four items, none of them expensive, and a team that applies them stops having the same incident twice. That is the actual output of this document.

15 · The decision framework

Five steps, and almost everyone skips the last one. It is the step that turns an opinion into an engineering decision.

1 · clarify the workload “What is the corpus, how often does it change, and what does the query mix look like?” One or two questions, not five
2 · state the constraint as a number corpus size, QPS, p95 target, freshness, cost ceiling. If they have not given you numbers, propose them and invite correction
3 · give your default, decisively one recommendation with a reason beats a survey of five options
4 · name the tradeoff you are accepting every choice costs something, and saying what it costs proves you understand it rather than having read about it
5 · say how you would measure it, and what would change your mind the step almost everyone skips, and the one that makes it engineering

All five steps, worked, in one paragraph

“What is the query mix — mostly lookups or summarisation? Assuming lookups dominate and we are at four million chunks with a p95 budget of 800 milliseconds: I would go with structural chunking, 200-token children carrying a pointer to their parent section, parent capped at 1,200 tokens. The tradeoff is an extra fetch hop, about ten milliseconds, and roughly double the text storage — both cheap against the recall gain. I would validate by sweeping child size against recall@10 on a two-hundred-question labelled set with span-level labels so the configurations stay comparable. If recall came back flat across 150 to 400 I would take the smallest, for generation cost. And if the query mix turned out to be mostly summarisation I would abandon this entirely and route to document-level summaries instead.”

16 · How strong candidates still lose points

These are not knowledge gaps. Every one of them is something a capable engineer does under interview pressure, and each has a one-line fix.

PatternWhy it costs youDo this instead
Listing every option without choosing Reads as an inability to decide under uncertainty, which is the job Pick one, justify it, and name what would change your mind
Jumping straight to the answer Signals you think one design fits every workload Two clarifying questions first, then answer
Never mentioning costAt manager level, cost is half the decision Attach a rough number to at least one choice
Only discussing the happy path The interview is largely about failure modes Name the failure mode of your own preferred choice
Naming tools instead of propertiesTools change; reasoning does not “I want filter-aware traversal and cheap upserts” beats a product name
Claiming prompt injection is solved Anyone who has worked on it knows it is not Contain the blast radius; assume the injection succeeds
Treating evaluation as an afterthought It is the difference between engineering and opinion Bring up measurement unprompted, and early
Over-indexing on the model Most RAG failures are ingestion failures Say so, and say where you would actually spend the effort

The eight sentences worth having ready

17 · Interview questions

ArchitectHow would you evaluate a RAG system?

By separating two questions that get conflated. Retrieval quality — did the right chunk come back — needs a labelled set and no model call, so it is cheap, deterministic and runs on every commit. Answer quality — was the response correct and grounded — needs a judge, so it is slow and expensive and runs nightly.

Chunking and retrieval are tuned on the first, because if the right chunk never surfaces no amount of prompt work saves you. The primary metric is recall@k, measured twice: at the retrieval k before reranking and at the final k after it.

And I would build the gold set before the system, not after: a couple of hundred questions with span-level labels, stratified to include multi-hop, tables, adversarial near-misses, and a no-answer slice — because a system that confidently answers what it should not is worse than one that declines, and the no-answer slice is the only way to measure that.

ArchitectRecall is 0.71 and users are unhappy. Where do you look?

At four numbers in order, because they name the layer. Low recall@50 means chunking or embedding, and reranking cannot help. High recall@50 with low recall@5 means ranking, and a reranker pays for itself immediately. High recall@5 with low faithfulness means generation — prompt and context assembly. High faithfulness with low correctness means the corpus: you are faithfully grounded in the wrong documents.

The order matters, because the earlier layers make the later numbers meaningless. There is no point reading faithfulness while recall@50 is low; the model is being asked to ground an answer in context that never contained it.

And I would also split the population before forming any hypothesis — by tenant, query type and language. “Users are unhappy” from one customer and from everyone have almost no diagnostic overlap.

ArchitectHow do you compare two chunking configurations fairly?

The problem to raise first is that chunk ids change when chunk size changes, so labels pointing at old ids are meaningless and the two configurations are not comparable at all.

Two fixes. Span-level labels — “the answer is in document 7, characters 4200 to 4600” — and any configuration is scored by whether a retrieved chunk overlaps that span. Or answer containment, which is cruder but configuration-independent and trivial: does the retrieved text contain the known answer string.

Then the sweep: fix everything else, rebuild the index per configuration, run the gold set, plot it. You usually get a plateau rather than a peak, and the plateau is itself the finding — it hands the decision to the secondary axis, and saying which axis you chose is the actual answer.

ArchitectWhat would you put in CI, and why a gate rather than a dashboard?

Five things: a fifty-question recall gate; canary documents asserting that synthetic users cannot retrieve restricted content; known-value assertions that specific numbers from specific tables are retrievable, which catches parser regressions; chunk-count and id-overlap drift alarms; and a counter for queries returning fewer than k results.

A gate rather than a dashboard because every failure mode in this area is silent. A dashboard needs somebody to look at it on the day it moved. A gate fails the build. And it is cheap — fifty questions, no model call, a few seconds.

The threshold matters less than its existence. Two points on a fifty-question set is noise; the point is that a ten-point drop stops the deploy rather than reaching a customer.

ArchitectWhat would you instrument in production that most teams do not?

Three things. Queries that returned fewer than k results — the filtered-search shortfall produces no error at all, and this one metric would catch most of the incidents in this area. Resident memory rising while the live vector count is flat, which is dead accumulation and has no user-visible symptom until a node runs out of memory. And per-tenant recall as a distribution rather than a mean, because the mean is the largest tenant’s number and everyone else can degrade underneath it.

I would add deletion lag as a separate SLO from ingestion lag, because it is a security control rather than a quality one, and it is the only freshness metric with a compliance consequence.

And I would clarify what belongs on a pager against a monthly review. Capacity numbers are trends, and a page for a trend teaches people to ignore pages.

ArchitectYour evaluation says the change helped and production says it did not. What happened?

Most likely the runs were not comparable. If the corpus grew five percent between them, a two-point recall difference means nothing — which is why the gold set has to be versioned, the corpus snapshot pinned and the full configuration recorded with every run. Teams lose weeks to exactly this.

The second candidate is that the gold set is stale or synthetic. LLM-generated questions come out suspiciously well aligned to the chunk boundaries they were generated from, so a sweep against them will cheerfully confirm that your current configuration is optimal.

The third is that the offline set does not look like production traffic at all — real users are terse, misspell things and ask two questions at once. That is the argument for refreshing the set from logs on a cadence and targeting a majority of real queries within a quarter.

Eng managerWhat does this system cost per query, and how would you make it cheaper?

The honest answer usually surprises people: the retrieval infrastructure is a rounding error against the tokens we hand the model. Ten chunks of four hundred tokens is four thousand context tokens, which at typical input pricing is about a cent a query; the index, amortised over ten million queries a month, is a few hundredths of that.

So the cost levers are k and the chunk size, and both are decided by a measurement we already have. On the recall plateau, moving from five hundred to two hundred token chunks changes nothing about quality and more than halves the largest line on the invoice.

Which reframes the capacity work rather than devaluing it. Quantisation, sharding and headroom are reliability investments, not cost ones — they buy a system that survives a node loss and can still be rebuilt next year. Framing them that way is how they get funded, and it stops the cost conversation looking in the wrong place.

Eng managerYour team says they need a week to improve retrieval. How do you scope it?

By asking what measurement will tell us it worked, before agreeing to the week. If the answer is “it will feel better”, the first two days are building the gold set and that is a better use of the week than anything else in it.

If a gold set exists, I would ask which layer the diagnostic points at. A week spent on reranking when recall@50 is low is a week spent on something that provably cannot help, and that conversation takes five minutes and saves the week.

And I would define done up front: the gold set says it helped, per slice; the change is reversible; the detector for its failure mode exists; and the capacity note reflects what it did to the footprint. Those four turn a week of work into something the next person can build on.

Eng managerHow do you keep a retrieval system healthy over two years?

Three cadences and a named owner for each. Per commit, the automated gates — recall, canaries, known values, drift alarms. Weekly, fifteen minutes of somebody reading the per-slice and per-tenant numbers. Per rebuild and at least quarterly, the capacity checklist and the gold-set refresh.

The last one is the one that gets dropped, and it is the one that decides whether the system is still maintainable. Three of the five items on the rebuild checklist drift continuously without anyone making a decision — corpus growth staling nlist, dead ratio crossing its threshold, filter fields multiplying — so they need a scheduled check rather than an alarm.

And one habit: no incident closes without adding the detector that would have caught it. Almost every failure in this area has a one-line metric that would have found it a week earlier. An incident that closes without one is an incident we have agreed to have again.

Eng managerHow would you onboard an engineer onto this system?

By handing them the capacity note and the gold set, in that order, and asking them to reproduce the current baseline number before changing anything. If they cannot reproduce it, we have found a reproducibility problem that was going to cost us later anyway.

Then a real, small change with the full loop attached — a measurement before, a change, a measurement after, and a note saying what tradeoff was accepted. The loop is the thing being taught, not the change.

What I would deliberately not do is start them on the model or the prompt. Most RAG failures are ingestion failures, and an engineer who learns this system from the retrieval end outward will diagnose faster for the rest of their time on it.

18 · FAQ

How many questions does a gold set need?

A hundred to three hundred to start, and the stratification matters far more than the count. A hundred questions covering lookups, multi-hop, tables, adversarial near-misses and a no-answer slice is worth more than a thousand generated from the same template. Note also what the size implies about noise: on fifty questions, one question is two points of recall, so set the CI threshold accordingly.

Can I use an LLM to build the whole thing?

To bootstrap, yes, and it is the fastest way to have something. But say the caveat out loud: the questions come out aligned to your current chunk boundaries because they were generated from those chunks, so the set flatters the configuration you already have and a sweep against it will confirm it is optimal. Human verification is the step that fixes that, and replacing generated questions with real ones from logs is the cure.

Should the LLM judge be trusted?

Only after you have validated it against humans on a sample, and only for the metrics it is actually good at. A judge has its own error rate, and that error rate is not uniform — it is usually worst on exactly the borderline cases you care about. Use it for faithfulness and abstention at volume, sample-check it against human raters, and never tune chunking on anything a judge produced.

Why not just measure end-to-end answer correctness?

Because it conflates every layer and tells you nothing about which one to fix. It is also the slowest and most expensive metric you have, so it is the worst possible thing to run on every commit. Measure it — it is the number the business cares about — but measure it nightly, and diagnose with the four-metric pairing rather than with it.

What is a good recall number?

There isn’t one, and being suspicious of the question is the right instinct. Recall depends on your corpus, your k, your query mix and how your labels were made — a number from someone else’s system is not comparable to yours. What is meaningful is your own number over time, per slice, against a pinned corpus. The useful target is “better than last month on the slice that was failing”.

How often should the gold set be refreshed?

Quarterly as a floor, and after any material change to the corpus or the user base. The failure mode is subtle: a set built in January flatters a system tuned in January, so the number keeps looking healthy while the thing it measures drifts away from what users actually ask. Give it an owner — a cadence with no name attached does not happen.

We have no labelled data and no time. What is the minimum?

Twenty questions you write yourself in an hour, with the answer span noted, committed to git, and run in CI. That is enough to catch a chunker change that halves recall, which is the failure this protects against. It is not enough to choose a chunk size. Start there and grow it — the gap between twenty and zero is far larger than the gap between twenty and two hundred.

What is the difference between an SLO and an alarm here?

An SLO is a promise about a distribution over a window; an alarm is a statement that something is broken now. Latency, freshness and deletion lag deserve both. Quality deserves an SLO and a CI gate rather than a pager, because a single recall measurement is noisy and a pager for a noisy metric trains people to ignore pagers. Capacity numbers deserve a monthly review and nothing else — they are trends.

Who should own retrieval quality?

Whoever owns the gold set, and it must be one named person rather than a team. The reason is that quality here has no natural owner: it is not the model team’s, not the infrastructure team’s, and not the product team’s, so it becomes nobody’s. Coverage — the questions nothing in the corpus can answer — belongs to whoever owns the documentation, and handing them the clustered low-score queries is one of the more valuable things a retrieval team produces.

What is the one thing to add if we can only add one?

A fifty-question recall gate in CI. Every failure mode in this runbook is silent, and that gate is the single mechanism that makes any of them loud. It costs an afternoon to build, a few seconds per commit to run, and it is the difference between finding a regression in a pull request and finding it in a customer escalation three weeks later.

19 · Cheat sheet, and how to use this runbook

The metrics, one line each

recall@k any relevant chunk in the top k · the primary retrieval metric · always state k
precision@k fraction of the k that were relevant · the cost metric · caps low by construction
MRR mean of 1 ÷ rank of the first hit · blind to everything after the first
NDCG@k graded relevance with a position discount · needs graded labels
faithfulness claims supported by the retrieved context · judged by a model, so validate the judge
abstention rate on the no-answer slice, how often it correctly declines · the most neglected one

The diagnostic, in four lines

low recall@50 chunking or embedding — reranking cannot help
recall@50 high, recall@5 low ranking — invest in reranking
recall@5 high, faithfulness low generation — prompt and context assembly
faithfulness high, correctness low the corpus — grounded in the wrong documents

The five CI gates

The four observability planes

PlaneThe one metric to haveWhy it is neglected
Qualityrecall@k on a fixed set, per slice, in CI It fails without an error
Latencyp99 broken down by stage and by shard Everyone has the aggregate and stops there
Capacityresident memory rising while live count is flat It is a trend, so nothing pages
Freshnessdeletion lag — revoked to unsearchable Nobody builds it until the incident

The ninety-second version

“You cannot evaluate a RAG pipeline by looking at answers, because six layers can produce the same bad one. So you separate retrieval quality — did the right chunk come back, which needs a labelled set and no model call and runs on every commit — from answer quality, which needs a judge and runs nightly.

The gold set is a couple of hundred questions with span-level labels, because chunk ids change when chunk size changes and any label pointing at an id stops being comparable. Stratify it, and include a no-answer slice, because a system that confidently answers what it should not is worse than one that declines.

Then four numbers name the broken layer: low recall@50 is chunking or embedding and no reranker can help; recall@50 high with recall@5 low is ranking; recall@5 high with faithfulness low is generation; faithful but not correct is the corpus. Running that diagnostic out loud is worth more than any single number in it.

Operationally the whole thing rests on one property: every failure mode here is silent. So the gate goes in CI rather than on a dashboard, the four observability planes get one metric each — recall in CI, p99 by stage and shard, resident memory against live count, deletion lag — and no incident closes without adding the detector that would have caught it.”

How to use this runbook from here

If you haveDo this
A week before an interview Read 00, then the cheat sheet and the ninety-second version of each document. Then do the Q&A cards with answers hidden
A day The ninety-second version of all sixteen, plus this document in full — because measurement is the thing candidates most often cannot do and interviewers most reliably ask
A system to build Track A in order, then 12 for sizing, then this document. Build the gold set before the second design decision
A system that is already broken Section 4 of this document, then the symptom tables in 11, 14 and 13

Where this connects

Thread from this documentResolved in
Why chunk ids must come from identity, not position 03 · Identity, updates and deletes
Canary documents and the permission failure mode they catch 04 · Access control and freshness
The flat baseline every recall number is measured against 09 · Flat, IVF and HNSW
The parameter sweep, and reading the shape of the curve 11 · Parameters and tuning
The capacity metrics behind the third observability plane 12 · Quantisation and capacity
Per-shard latency, and why the cluster p99 hides it 13 · Sharding and replication
Per-tenant recall, and why the mean is the whale’s number 14 · Filtered search and multi-tenancy
Per-slice evaluation, and idf drift on the keyword half 15 · Hybrid retrieval and reranking

Questions to ask them