See It Flow
System Design · Interview guide · ~26 min read

Enterprise RAG Knowledge Assistant — Interview Perspective

RAG system design interview guide: how to answer "design a RAG assistant over company documents" in 45–60 minutes. Covers clarifying questions, a five-minute estimate, a whiteboard that evolves from a naive baseline through eight stages, deep-dive options, Staff-level follow-ups, strong versus weak answers and a question bank.

Mangesh MandlikLast updated:
Interview 01

Clarify the Problem — and Plan the Hour

"Design an AI assistant over our internal documents" is underspecified in ways that change the architecture. The first five minutes decide whether the rest of the hour is spent on the right problem.

Start by drawing the boundary: this system ingests already-connected sources, retrieves evidence for a question, and generates a cited answer from that evidence. It does not browse the open web, author documents, take autonomous actions, or fine-tune a model on the corpus. Candidates who start describing agent loops or training pipelines are solving a larger, different problem.

MinutesPhaseGoal
0-5ClarifyScope, invariants, and the answers that change the design
5-10EstimateThree conclusions from the numbers: the index is small, the LLM is the bottleneck, context is the cost lever
10-35Whiteboard, Stages 0-8Grow the design from a naive baseline, one limitation at a time; give authorization (Stage 5) the most time
35-50Deep diveOne mechanism in depth — usually the interviewer's choice
50-60Failures and recapDegraded modes, staff-level follow-ups, guarantees and honest limitations
A plan, not a script. Follow the interviewer when they branch, and return to the plan when they hand control back.

The questions that change the design

QuestionWhy it mattersHow the answer changes the design
One company, or a platform serving many customer organisations?Decides whether isolation is a nicety or a hard boundaryMulti-tenant: a per-tenant index partition from day one, plus per-tenant quotas
Which sources, and do they expose permissions and change events?Permissions cannot be reconstructed after ingestionConnectors pull ACLs with content; webhooks plus polling plus a nightly reconciliation sweep
Document-level or field-level permissions?Field-level is a different, much harder problemDocument-level: principal tags per chunk. Field-level: separately permissioned chunks or redaction (a follow-up, not the MVP)
How fast must a revocation take effect?"Immediately" is not achievable; a number isA revocation budget (e.g. 60 s for group removal, 2 min for document ACLs) bounds every cache TTL, the ACL priority lane and the re-check
How fresh must content be?Sets the ingestion architectureAn index-lag SLO (e.g. 5-15 min p95) met by asynchronous ingestion; seconds would need a different design
Do documents have revisions? What happens on deletion?Old and deleted content is the most common wrong answerRevision records, a status filter, tombstones with a purge deadline
Must answers cite sources? Abstain, or answer best-effort?Drives prompt design, validation and evaluationGrounded prompt, citation validation, an abstention threshold
How many documents, users, queries and tenants — how skewed?Separates real bottlenecks from imagined onesCapacity estimate; reveals the LLM tier, not the index, as the bottleneck
Who can edit the sources?Any editor can plant text the model will readRetrieved text treated as data; no tools for the generator
Conversational follow-ups?Follow-ups are not standalone queriesA query-rewrite step on follow-up turns (~120 ms)
Ask only what you need, and say how each answer changes the design — that is the signal, not the length of the list.

“"I'll scope this as a multi-tenant assistant over each customer's wiki, drive and chat. Access control is document-level, enforced on every query against current group membership. Every answer is grounded and cited, and the system abstains when the evidence is insufficient. Freshness and revocation are stated budgets — minutes for content, about a minute for group removal — not 'instant'. Authorization and tenant isolation are zero-tolerance invariants, not trade-offs like latency or cost. Does that match what you want me to focus on?"”

Interview 02

Back-of-the-Envelope in Five Minutes

The numbers matter less than the three decisions they force. State assumptions, round aggressively, and land on the conclusions.

textIllustrative assumptions, matching the Complete Design capacity chapter.
Largest tenant:  150,000 docs x 1,500 tokens
Chunks:          400 tokens, 100 overlap -> 300 stride -> 5 per doc -> 750,000 chunks
Vectors:         1,536 dims x 4 B = 6 KB -> 4.6 GB raw, ~5 GB with HNSW links
Text + metadata: 750,000 x 2.5 KB ~= 1.9 GB
Churn:           1,200 revisions/day -> 6,000 embeddings/day (bulk: initial sync, model migration)

Queries:         1,100 DAU x 4 = 4,400/day ~= 0.05 qps; 8x peak ~= 0.4 qps per tenant
Platform:        500 tenants -> 150-200 qps peak
LLM:             ~2,200 input + 350 output tokens/query; ~5 s streaming
                 -> ~1,000 generations in flight at peak

Latency (p95):   rewrite 120 (follow-ups) | principals 15 || embed 30 | hybrid 80
                 rerank 120 | live re-check 20 | context 5
                 -> ~270 ms first turn, ~390 ms follow-up; +~400 ms to first token -> < 1 s

What to say. "The largest tenant's index is about 5 GB of vectors, which fits in memory on one node — I don't need to shard it for size. Partitions exist for isolation, replicas for throughput. The bottleneck is the LLM tier: about a thousand generations in flight at peak, so provider quota or GPU capacity sets the ceiling, and it needs admission control and per-tenant quotas. Every context token is paid on every query, so context size is the main cost lever — which is why I'll spend a reranker to keep the prompt to four chunks. Ingestion is a trickle except for initial syncs and model migrations, so it runs asynchronously on its own pool." (Complete Design, Ch. 05)

Interview 03

Whiteboard Evolution: Stages 0-8

Grow the design one stage at a time, each motivated by a concrete limitation of the stage before. For each stage: what to draw, what to say, the trade-off to name, and the follow-up an interviewer is likely to ask. The board ends at the production architecture — but you arrive there, rather than drawing it first.

A running example keeps every stage concrete: a payment-reconciliation policy POL-17 with an older revision r2 (manual month-end) and a current revision r3 (automated nightly), a runbook RUN-8, a group grp-payments-eng that may read both, and a user only in grp-sales who may not. The question is "What changed in our payment reconciliation process last month?"

Stage 0 — The naive baseline

INGESTION · asyncINDEXES & STORESQUERYSTAGE 0 · NAIVE BASELINEnightly exportSourceswiki pagesDocs Tablefull-text copyUserChat Appno auth checkRetrieverLIKE %term%LLMwhole documentsOne copied table · keyword LIKE · whole documents · no permission check
Stage 0: a nightly copy of the wiki in one table, keyword `LIKE` search, whole documents sent to the LLM, and no permission check on the answer path.

Draw. Sources copied nightly into a documents table; a chat app runs a keyword LIKE search and sends the matching whole documents to an LLM.

Say. Give it one sentence of credit — it works for a demo — then list where it breaks, because each break becomes a later stage: vocabulary mismatch (the user says "reconciliation", the runbook says "matching job"); whole documents blow the context budget and nothing ranks them; the copy is stale and revision-blind, so r2 and r3 look equally valid; there is no permission check on the answer path; and nothing is cited.

Trade-off to name. Simplicity against every requirement. The point of Stage 0 is not to defend it but to produce the roadmap.

Likely follow-up. "Why not fine-tune a model on the documents, or put everything in a long context window?" — Fine-tuning cannot be updated per edit, cannot forget a deleted document, cannot respect per-user permissions and cannot cite. Long context does not scale to 150,000 documents, multiplies cost on every query, and still lets the model read content the user may not see. Retrieval selects a small, authorized, current, citable subset per question.

Stage 1 — Ingestion pipeline and data model

INGESTION · asyncINDEXES & STORESQUERYSTAGE 1 · INGESTION PIPELINESourceswiki · drive · chatConnectorscontent + ACLsQueuedurableParse + Chunknormalise, hashIndex Writerfenced, idempotentLexical IndexBM25 · chunksMetadata Storedocs · revisionsUserQuery APIauthn, tenantRetrieverBM25 top-kContext + LLMtop chunksNew: connectors, durable queue, chunking, fenced writes, BM25 over chunks
Stage 1: connectors feed a durable queue; documents are parsed, chunked and written by an idempotent, version-fenced index writer into a BM25 index over chunks and a metadata store.

Draw. Connectors (content and ACLs in the same pull) → durable queue → parse and chunk → index writer → lexical (BM25) index over chunks and a metadata store of documents, revisions and ACLs.

Say. "Delivery is at-least-once and out of order, so the writer is idempotent on (doc_id, revision_id) and version-fenced: it moves the current pointer only if the incoming source version is newer and the document is not tombstoned. The sync cursor advances last. Chunk ids are scoped to a revision, so r2 and r3 are different rows and history survives."

Trade-off to name. Webhooks for latency, polling for coverage, nightly reconciliation as the backstop; asynchronous ingestion so a backlog shows up as index lag, never as query latency.

Likely follow-up. "A replay of r2 arrives after r3 is live. What happens?" — The conditional update ... WHERE current_source_version < :v AND tombstoned_at IS NULL matches zero rows; the event is acknowledged without writing and recorded as 409 STALE_REVISION_REJECTED.

Stage 2 — Embeddings and vector retrieval

INGESTION · asyncINDEXES & STORESQUERYSTAGE 2 · + EMBEDDINGS, VECTOR INDEXSourceswiki · drive · chatConnectorscontent + ACLsQueuedurableParse + Chunknormalise, hashEmbedderbatchedIndex Writerfenced, idempotentLexical IndexBM25 · chunksVector IndexHNSW · 1,536-dMetadata Storedocs · revisionsUserQuery APIauthn, tenantRetrieverembed + ANNContext + LLMtop chunksNew: embedder (ingestion and query) and an HNSW vector index
Stage 2: an embedder (batched at ingestion, single call at query time) and an HNSW vector index.

Draw. Embedder (batched at ingestion, one call per query) and an HNSW vector index of 1,536-dimensional vectors beside the BM25 index.

Say. "Embeddings fix vocabulary mismatch. Chunk size now matters: ~400 tokens with ~100 overlap keeps a passage focused without splitting the key sentence. The embedding model version is stored per chunk, because vectors from different models are not comparable."

Trade-off to name. Chunk size: smaller chunks retrieve precisely but lose context; larger ones dilute the vector and cost more prompt tokens.

Likely follow-up. "How big is the vector index, and does it need sharding?" — 750,000 chunks × 6 KB ≈ 4.6 GB, about 5 GB with graph links — it fits in memory; replicate for throughput, partition for isolation.

Stage 3 — Hybrid retrieval

INGESTION · asyncINDEXES & STORESQUERYSTAGE 3 · + HYBRID RETRIEVALSourceswiki · drive · chatConnectorscontent + ACLsQueuedurableParse + Chunknormalise, hashEmbedderbatchedIndex Writerfenced, idempotentLexical IndexBM25 · chunksVector IndexHNSW · 1,536-dMetadata Storedocs · revisionsUserQuery APIauthn, tenantRetrieverBM25 + ANN, fusedContext + LLMtop chunksNew: BM25 and ANN queried in parallel, merged with rank fusion
Stage 3: the retriever queries BM25 and ANN in parallel and merges the two ranked lists with reciprocal rank fusion.

Draw. The retriever fans out to BM25 and ANN in parallel (top 30 each) and fuses them with reciprocal rank fusion into ~40 candidates. There is no separate "hybrid index" — two indexes, one retriever.

Say. "Vectors blur exact identifiers: RUN-8 and RUN-5 embed almost identically. BM25 catches identifiers and rare terms; vectors catch paraphrase. RRF scores by rank, 1/(k + rank) with k ≈ 60, so the two scorers never need normalising."

Trade-off to name. RRF versus weighted score blending (needs normalisation, drifts when a scorer changes); one engine storing text, vector and tags together versus two stores that need a shared commit point and reconciliation to avoid drift.

Likely follow-up. "Why not just add the BM25 score and the cosine similarity?" — They are on different, corpus-dependent scales; a weighted sum needs per-corpus normalisation and re-tuning whenever either scorer changes. Rank fusion is robust by default.

Stage 4 — Reranking and the context budget

INGESTION · asyncINDEXES & STORESQUERYSTAGE 4 · + RERANKING, CONTEXT BUDGETSourceswiki · drive · chatConnectorscontent + ACLsQueuedurableParse + Chunknormalise, hashEmbedderbatchedIndex Writerfenced, idempotentLexical IndexBM25 · chunksVector IndexHNSW · 1,536-dMetadata Storedocs · revisionsUserQuery APIauthn, tenantRetrieverBM25 + ANN, fusedRerankercross-encoderContext + LLM4 chunks · 1.6k tokNew: cross-encoder reranker and a fixed 4-chunk context budget
Stage 4: a cross-encoder reranks the fused candidates and only the top four chunks enter the prompt.

Draw. A cross-encoder reranker over the ~40 fused candidates, and a context builder that keeps the best four chunks (~1,600 tokens), de-duplicates overlap, caps chunks per document and adds citation markers.

Say. "Forty candidates are ~16,000 tokens — ten times the budget — and first-pass scores are too coarse to choose the best four. A cross-encoder reads query and passage together, ~120 ms over 40 pairs."

Trade-off to name. ~120 ms and a model per query against precision; conditional reranking when one candidate clearly dominates; context size against cost and quality.

Likely follow-up. "Why not send twenty chunks and let the model sort it out?" — Each chunk adds ~400 input tokens to every query (~18% more input at this size), marginal passages distract the model, and facts in the middle of long contexts are used less reliably. More context is not monotonically better.

Stage 5 — Authorization as a retrieval constraint

INGESTION · asyncINDEXES & STORESQUERYSTAGE 5 · + AUTHORIZATIONper-tenant partitionACLslive checkSourceswiki · drive · chatConnectorscontent + ACLsQueue+ ACL laneParse + Chunknormalise, hashEmbedderbatchedIndex Writerfenced · ACL tagsIdentity & ACLgroups · doc ACLsLexical IndexBM25 · chunksVector IndexHNSW · 1,536-dMetadata Storedocs · revisionsUserQuery APIauthn · principalsRetrieverfused + ACL filterRerankercross-encoderLive Re-checkfail-closedContext + LLM4 chunks · 1.6k tokNew: tenant partitions, ACL tags pushed into both indexes, live re-check
Stage 5: per-tenant partitions, ACL principal tags pushed into both indexes, principal resolution at the query API, a live re-check before the prompt is built, and an ACL priority lane.

Draw. Per-tenant partitions of both indexes; acl_principals tags on every chunk; an Identity & ACL store; principal resolution in the query API; the filter inside both searches; a live re-check after reranking; a priority lane in the queue for ACL changes.

Say. Walk the rejected options in order — it is the strongest part of the answer. "Tenants get a partition, not a WHERE clause, so a forgotten predicate cannot cross tenants. For document permissions, post-filtering the top 30 starves restricted users: a sales user keeps two or zero chunks even when a readable answer exists at rank 200. Filtering by doc_id IN (list) needs a per-user list of tens of thousands of ids rebuilt on every change. Instead, each chunk carries its document's principals, the query resolves the user's small principal set — user id, expanded groups, a tenant-wide tag — and both indexes evaluate acl_principals ANY_OF principal_set inside the search. Then, before anything enters the prompt, a live re-check against the authoritative ACL store, fail-closed. A denied user gets the same 200 response as 'nothing found': I don't have access to information that would answer that."

Trade-off to name. Filtered HNSW loses recall when a user can read only a small fraction of the tenant — mitigated by filter-aware traversal, an exact scan of a small eligible set, and per-tenant partitions. The TOCTOU window is bounded by the revocation budget, not zero.

Likely follow-up. "A user is removed from a group. When do they lose access?" — Group membership is resolved per query through a cache with a TTL inside the 60-second budget, and the live re-check resolves it afresh, so the next request is denied. A document-level ACL change travels the priority lane and rewrites only that document's chunk tags — no re-embedding — within 2 minutes p95. A request already past its re-check can still complete: that is the bounded TOCTOU window.

Stage 6 — Freshness and lifecycle

INGESTION · asyncINDEXES & STORESQUERYSTAGE 6 · + FRESHNESS, LIFECYCLEper-tenant partitionACLslive checkSourceswiki · drive · chatConnectorscontent + ACLsQueue+ ACL laneParse + Chunknormalise, hashEmbedderbatchedIndex Writerfenced · ACL tagsPurgereconcileIdentity & ACLgroups · doc ACLsLexical IndexBM25 · chunksVector IndexHNSW · 1,536-dMetadata Storestatus · tombstonesUserQuery APIauthn · principalsRetrieverACL + status filterRerankercross-encoderLive Re-checkfail-closedContext + LLM4 chunks · 1.6k tokNew: revision-status filter, tombstones, purge and reconciliation
Stage 6: revision status and tombstones are tags filtered inside retrieval; a reconciliation sweep and a purge job keep the indexes aligned with the sources.

Draw. A status tag in the retrieval filter, tombstones, a nightly reconciliation sweep and a purge job.

Say. "Both policy revisions are readable and relevant, so Stage 5 returns both. The default filter adds status = current. When the question has change intent — 'what changed', 'since last month' — the filter also admits the immediately superseded revision, labelled, so the model can contrast r3 with r2 and cite both. A deletion writes a tombstone that excludes the document on the next query; physical purge follows within 24-72 hours."

Trade-off to name. Keeping history for "what changed" questions against confusing the model with outdated text; purge speed against index-compaction cost.

Likely follow-up. "How do you upgrade the embedding model without downtime?" — Build a shadow namespace; dual-write every mutation — upserts, ACL changes, supersessions, tombstones — from the start; backfill only revisions that are still current and not tombstoned; run a parity check; then flip one alias that switches the index and the query-embedding model together. Keep the old namespace briefly for rollback.

Stage 7 — Grounded generation

INGESTION · asyncINDEXES & STORESQUERYSTAGE 7 · + GROUNDED GENERATIONper-tenant partitionACLslive checkSourceswiki · drive · chatConnectorscontent + ACLsQueue+ ACL laneParse + Chunknormalise, hashEmbedderbatchedIndex Writerfenced · ACL tagsPurgereconcileIdentity & ACLgroups · doc ACLsLexical IndexBM25 · chunksVector IndexHNSW · 1,536-dMetadata Storestatus · tombstonesQuery Traceaudit logUserQuery APIauthn · principalsRetrieverACL + status filterRerankercross-encoderLive Re-checkfail-closedContext + LLMgrounded promptCitation Checkor abstainNew: grounded prompt, citation validation, abstention, query trace
Stage 7: the generator answers from the assembled context; a citation validator checks the output, and every request writes a query trace.

Draw. A framed prompt, a citation validator after the LLM, the abstention path, and the query trace store.

Say. "Instructions, labelled sources and the question go in separate delimited sections; source text is data, never instructions. As the answer streams, a structural check confirms each [n] refers to a chunk in the prompt; afterwards an entailment check tests that each cited chunk supports its sentence, and the final event carries only validated citations. Below a calibrated relevance threshold the pipeline abstains without calling the model. Every request writes a trace of retrieved, re-checked and cited chunk ids."

Trade-off to name. Streaming latency against validating everything before display; abstaining too often (unhelpful) against too rarely (fabrication).

Likely follow-up. "What stops a document from hijacking the model with planted instructions?" — Source-as-data framing, no tools or secrets for the generator, and citation validation reduce the risk; authorization before the prompt limits the blast radius, because the context holds only what this user may read. It is reduced, not eliminated.

Stage 8 — Caching, scaling, failures, evaluation

INGESTION · asyncINDEXES & STORESQUERYFINAL PRODUCTION ARCHITECTUREper-tenant partitionACLslive checkSourceswiki · drive · chatConnectorscontent + ACLsQueue+ ACL laneParse + Chunknormalise, hashEmbedderbatchedIndex Writerfenced · ACL tagsPurgereconcileIdentity & ACLgroups · doc ACLsLexical IndexBM25 · chunksVector IndexHNSW · 1,536-dMetadata Storestatus · tombstonesQuery Traceaudit + evalUserQuery APIauthn · principalsRetrieverACL + status filterRerankercross-encoderLive Re-checkfail-closedContext + LLMgrounded promptCitation Checkor abstainCacheprincipal-scopedPlus: principal-scoped cache, per-tenant quotas, evaluation loop
The final production architecture: the ingestion path (top row), the tenant-partitioned indexes and stores (middle row), and the query path with the principal-scoped cache (bottom row).

Draw. The principal-scoped cache beside the query API; per-tenant quotas and admission control in front of the LLM tier; the degraded mode next to each dependency.

Say. "Only the query embedding is identity-independent. An answer cache key is tenant + normalized query + principal fingerprint + corpus version, with a TTL inside the revocation budget — and per-identity answer caches hit rarely, so caching embeddings and retrieval results is often enough. The LLM tier gets admission control and per-tenant token quotas; partitions isolate data but not compute. Every dependency has a degraded mode that keeps authorization intact. Quality is measured per stage: recall@k for retrieval, groundedness and citation precision for generation, explicit authorization tests."

Trade-off to name. Answer-cache hit rate against leak risk; reserved capacity for large tenants against utilisation.

Likely follow-up. "The identity provider is down. What do you serve?" — Fail closed: answer only from tenant-wide chunks, or refuse. Never an expired principal set, never a skipped re-check. Likewise: LLM down → the authorized, ranked source list; reranker down → fusion order with a tighter context; vector index down → lexical-only, flagged.

StageLimitation that motivates itWhat you add
0—Nightly copy, LIKE, whole documents, LLM
1Stale copy, whole documents, no revisions or ACLsConnectors, queue, chunking, fenced writer, BM25 over chunks
2Vocabulary mismatchEmbedder, HNSW vector index
3Vectors blur identifiersBM25 ∥ ANN with RRF
4Too many coarse candidatesCross-encoder, four-chunk budget
5No tenant boundary, no ACL enforcementPartitions, principal-tag pushdown, live re-check, ACL lane
6Superseded and deleted content retrievedStatus filter, change intent, tombstones, reconciliation
7Unverified answersFramed prompt, citation validation, abstention, trace
8Cost, load, partial failureScoped cache, quotas, degraded modes, evaluation
The same evolution as the Complete Design (Ch. 16) — one line per stage to rehearse.
Interview 04

Choosing a Deep Dive

Around minute 35 the interviewer will pick one mechanism — or ask you to. Offer the one where this system differs most from generic search: authorization. Have two others ready.

Deep diveWhat to explainNumbers and invariants to cite
Architecture Deep Dive → AuthorizationPrincipal resolution, the tag filter inside BM25 and HNSW, filtered-HNSW recall, the live re-check, ACL propagationRevocation ≤ 60 s (group) / ≤ 2 min p95 (document); re-check ~20 ms; fail-closed; uniform denial
Architecture Deep Dive → Hybrid Fusion and RerankingWhy lexical and dense fail differently, RRF, the cross-encoder funnelTop 30 + top 30 → ~40 → 4; k ≈ 60; rerank ~120 ms
Complete Design → IngestionAt-least-once delivery, idempotency, the version fence, tombstones, cursor-lastOne conditional update; 409 STALE_REVISION_REJECTED; purge 24-72 h
Architecture Deep Dive → Embedding MigrationShadow namespace, dual-write, conditional backfill, parity, alias flip750,000 chunks; never mixed vectors; flip switches the query model too
Architecture Deep Dive → Authorization-Safe CachingWhat is identity-independent, key composition, invalidationAnswer TTL ≤ 60 s; fingerprint errors bypass the cache
Architecture Deep Dive → Prompt ConstructionSource-as-data framing, citation checks, abstentionGroundedness ≥ 95% sampled; reduces, does not eliminate
Architecture Deep Dive → ObservabilityPer-stage metrics and the query traceRecall@k, nDCG, groundedness, citation precision, index lag, revocation lag
Each link goes to the page that covers the mechanism in depth.

Whichever you pick, structure it the same way: the invariant it protects, the simplest approach and where it breaks, the mechanism, its cost in latency or complexity, and its residual failure mode. Ending on the residual — "the TOCTOU window is bounded, not zero", "validation reduces unsupported claims, it cannot prove their absence" — is what distinguishes depth from recitation.

Interview 05

Scenario Prompts: Debugging Out Loud

Some interviews replace or follow the design with an incident: "a customer reports X — walk me through it." The pattern is the same each time: scope, read the system's own evidence, localise the stage, fix the instance, then remove the cause.

Prompt A — "The assistant cited a runbook that was deleted two weeks ago"

1. Scope before theorising: which tenant, which document, reproducible now or once, when was it deleted versus when was the question asked.

2. Read the trace: if the deleted document's chunk ids are in the retrieved and cited ids, the model was given it as evidence — this is an index problem, not a hallucination.

3. Check the lifecycle record: a tombstone exists and is correctly dated, so the document should have been excluded on the next query. Are its chunks still in the indexes, and with what status?

4. Find the write: an index write after the tombstone, with no source version, means something bypassed the index writer — the fence was never consulted.

5. Fix the instance: re-apply the tombstone through the writer and disable the tool that wrote directly. Remove the cause: only the writer may write to the indexes; reconciliation checks tombstoned-but-retrievable documents frequently. This is Scenario 03.

Prompt B — "Two employees asked the same question; one saw content they should not have"

Treat it as a security incident first. From the trace: was the response a cache hit? Compare the stored and requester principal fingerprints (Scenario 08). If not, did the pushdown filter admit the chunk — were its tags stale because an ACL change was still in the priority lane? Did the live re-check run and pass, and against what ACL-store state? Was the revocation inside the stated budget (Scenario 07)? Each answer localises the failure to one component; the trace then lists every other affected request.

Prompt C — "Answers got worse after Tuesday's deploy, but nothing is erroring"

Split quality by stage before guessing. Recall@k down means retrieval (a chunking change, an index setting, an embedding model); recall flat but groundedness or citation precision down means generation or context assembly; abstention rate moving means the threshold or the reranker. A platform-wide shape points at infrastructure, a single-tenant shape at content (Scenarios 05, 10, 12).

Interview 06

Staff-Level Follow-Up Questions

These probe whether the invariants are enforced or merely claimed. Expect them in the last fifteen minutes, or as pushback on a core answer.

How do you prove — not just claim — tenant isolation in production?

Strong answer angle

Partitioning is the design claim. Proof is a continuously running canary that attempts, from a test harness, to retrieve one test tenant's content with another tenant's credentials and asserts zero results every cycle; infrastructure audits confirming partitions are separate indexes or namespaces, not a shared store with a column; and penetration tests. A code review that finds the tenant predicate everywhere is a claim; a canary that fails the moment it stops being true is evidence.

Likely follow-ups
  • What would make you suspect the structural claim had quietly regressed? — A new feature, admin tool or loader that reaches the data layer without going through the tenant-selected partition — the same shape as Scenario 03, where a tool bypassed a correct mechanism rather than breaking it.

A user can read only 0.5% of a large tenant. How does the pushdown filter behave?

Strong answer angle

Selective filters hurt filtered HNSW: graph traversal keeps landing on ineligible neighbours, so recall drops and latency rises. Mitigations: filter-aware traversal; an exact scan when the eligible subset is small — a few thousand vectors scan in milliseconds — chosen by estimating filter selectivity; and per-tenant partitions that keep graphs small. Measure recall sliced by filter selectivity, or the problem stays invisible in averages.

Likely follow-ups
  • Why not keep a separate index per group? — Groups overlap and change; a document readable by five groups would be duplicated five times, and every membership or ACL change becomes a re-index. Tags on one copy plus a selective-filter fallback is cheaper and stays correct.

A document is shared with a group of 50,000 people, some through nested groups. Does that blow up the index?

Strong answer angle

No. The chunk stores the group principal, not its members. Nested membership is expanded at query time into the requesting user's principal set — user id, transitive groups, tenant-wide tag — which stays small. That is why membership changes need no index write at all, only an identity-cache refresh within the 60-second budget.

Likely follow-ups
  • What if one user belongs to thousands of groups? — The ANY_OF set grows and filter evaluation slows, but stays correct. The usual mitigation is to keep only the user's groups that actually appear in some ACL of the tenant — typically a small intersection. State it as a scaling edge, not a correctness problem.

How would you detect, after the fact, that a revocation was not honoured in time?

Strong answer angle

Join query traces — principal fingerprint, re-checked chunk ids, timestamps — with the identity provider's and connectors' revocation logs, and look for any access later than the change's commit time plus the stated budget. Run it as a sampled, continuous audit that reports revocation-propagation lag as a metric, not as a forensic exercise after a complaint.

Likely follow-ups
  • Which part of the budget is outside your control? — The source: the 2-minute document-ACL budget starts when the source emits the change. A source outage or a slow permission API delays it, which is why per-connector ACL lag is monitored separately from content lag.

What does defence-in-depth authorization cost, and when would you not pay it?

Strong answer angle

The pushdown filter costs some recall and latency on selective filters; the live re-check costs ~20 ms for a handful of documents. Both are paid on every query because authorization is a zero-tolerance invariant here. The honest answer to "when not" is: only when the product explicitly accepts authorization as a soft guarantee — not a default to assume.

Likely follow-ups
  • Is there a cheaper way to get most of the benefit? — Shorter principal-cache TTLs shrink the window feeding the pushdown, but they tune the staleness budget; they do not remove the need for the authoritative re-check.

If you had to halve the pre-generation latency budget, what would you cut, and what would you refuse to cut?

Strong answer angle

Refuse: the pushdown filter, the live re-check and citation validation — correctness gates, not cost levers. Cut first: conditional reranking when one candidate dominates, a faster rewrite model (it runs only on follow-ups), tighter candidate counts, and keeping principal resolution parallel with embedding. Fewer context chunks mainly shortens time to first token, not retrieval.

Likely follow-ups
  • Why is a smaller context safer to cut than a check? — A smaller context risks a worse answer — measurable in recall and groundedness and reversible. A skipped check risks a leak that has already happened by the time anyone notices.

How do you know the groundedness number is not an artifact of an easy evaluation set?

Strong answer angle

Include adversarial cases deliberately — conflicting current sources, out-of-corpus questions that should abstain, documents with planted instructions — refresh the set with real production near-misses, and cross-check against the feedback rate from real users.

Likely follow-ups
  • What is a concrete sign the set has gone stale? — Offline groundedness flat and high while negative feedback climbs — the set no longer represents what users ask.

The embedding provider changes dimensionality, not just the model. What changes?

Strong answer angle

The migration pattern is the same — shadow namespace, dual-write, conditional backfill, parity, alias flip with the query model. What changes is capacity: going from 1,536 to 3,072 dimensions doubles raw vectors from ~4.6 GB to ~9.2 GB for the largest tenant, plus graph links, and that must be planned before the backfill starts. The correctness argument is unchanged: two vector spaces are incompatible whether or not they have the same size.

Likely follow-ups
  • Does a smaller, cheaper new model make the migration easier? — It lowers ongoing cost, not migration risk — the danger is mixing incompatible spaces, not dimensionality.

How would you support field-level redaction — a salary figure inside an otherwise visible HR document?

Strong answer angle

Document-level tags cannot express it: a chunk passes or fails as a whole. Either split consistently sensitive fields into their own separately permissioned chunks at ingestion, or redact tagged spans after retrieval and before context assembly. Both complicate ingestion and citations, because a citation must not misrepresent what was shown.

Likely follow-ups
  • Which would you choose? — Separately permissioned chunks for a few well-defined field types: citation semantics stay clean. Span redaction is more flexible but risks leaking surrounding context or missing a paraphrase of the same fact.

What is the biggest risk your metrics would not catch?

Strong answer angle

A narrow authorization or caching bug that affects only users with unusual entitlement shapes absent from the canary accounts — for example, a cache-key fallback that triggers only while an identity-cache entry is refreshing (Scenario 08).

Likely follow-ups
  • How would you reduce that blind spot? — Give canaries deliberately varied entitlements — recently changed groups, no groups, many groups, nested groups — and verify every cache hit's stored fingerprint against the requester's.
Interview 07

Weak Answer vs. Strong Answer

The pass/fail line is rarely knowledge of RAG. It is whether authorization gates what the model is shown, or merely filters what it says.

TopicWeakStrong
Where authorization is enforced"Generate the answer, then redact anything restricted.""Filter the evidence before the model sees it — pushdown in retrieval and a live re-check before the prompt. A model that has read restricted text can leak it in a paraphrase no filter catches."
Permission-aware retrieval"Retrieve the top k, then drop what the user can't read," or "pass the list of document ids they can read.""Each chunk carries its document's principals; the query resolves the user's small principal set and the filter runs inside the search. Post-filtering starves restricted users; per-user id lists are huge and churn."
Tenant isolation"Add a tenant_id column and filter on it.""Partition the indexes per tenant; the authenticated tenant selects the partition, so a forgotten predicate cannot cross tenants. Quotas handle noisy neighbours."
Revocation"Re-sync permissions nightly.""Resolve groups per query through a cache inside a 60-second budget, push document ACL changes on a priority lane that rewrites tags only, re-check live, fail closed — and state that the in-flight window is bounded, not zero."
Denial"Return 403: you don't have access to that policy.""Return the same 200 response as 'nothing found'; a different code or wording reveals that restricted content exists."
Caching"Cache answers by query text.""Only the query embedding is identity-independent. Answer keys include tenant, principal fingerprint and corpus version, with a TTL inside the revocation budget."
Scale"Shard the vector database.""The largest index is ~5 GB and fits in memory; the LLM tier is the bottleneck, so admission control, quotas and context size are the levers."
Freshness vs. grounding"If it's cited, it's correct.""Grounded and current are separate properties: a faithful citation of a superseded revision is still wrong today."
Insufficient evidence"Let the model try its best.""Abstain below a calibrated relevance threshold without calling the model; never answer company facts from parametric knowledge."
Contradictory sources"Use the more relevant one.""If current sources conflict and nothing records which replaces which, say so with dates and cite both."
The same knowledge, framed precisely or vaguely — usually the actual pass/fail line.
Interview 08

Rapid-Fire Question Bank

Questions beyond the walkthrough, grouped by theme, each with an answer outline and a follow-up.

Retrieval and generation

Why hybrid retrieval instead of dense vectors alone?

Strong answer angle

Dense vectors are weak on short, near-identical identifiers — RUN-8 and RUN-5 embed almost identically — and on rare exact terms. BM25 handles exactly those; vectors handle paraphrase. Fusing the two with RRF covers both failure modes without score normalisation.

Likely follow-ups
  • Is hybrid always the right default? — No. A narrow, identifier-driven corpus with little prose can do well with BM25 alone; the choice depends on the queries and the corpus.

How do you support multi-turn conversation without weakening authorization or freshness?

Strong answer angle

A rewrite step turns a follow-up ("and last quarter?") into a standalone query (~120 ms, follow-up turns only), and the rewritten query runs the full pipeline — pushdown, re-check, status filter, citation validation. Conversation history is stored with the same tenant and user scoping as any other data.

Likely follow-ups
  • Can context from an earlier turn leak across a permission change mid-conversation? — Only if earlier chunks are reused without re-authorization. Every turn re-retrieves and re-checks; nothing from a previous turn's context is trusted.

How do you stream the answer and still return only validated citations?

Strong answer angle

Stream tokens immediately. While streaming, a structural check drops any marker that does not refer to a chunk in the prompt; after generation, an entailment check tests each cited chunk against its sentence, and the final done event carries only validated citations, with unsupported sentences flagged (Complete Design, Ch. 13).

Likely follow-ups
  • Why not buffer until validation finishes? — It adds seconds to every answer to remove a small residual risk; the design targets ≥ 95% groundedness by sampling, not 100%. A stricter product could buffer — state it as a product decision.

Operations and extensibility

What changes for a regulated tenant — healthcare or financial services?

Strong answer angle

Not the architecture; the tunable budgets move to their strictest settings: shorter principal-cache TTLs, a shorter purge deadline for deletions, data residency that fixes the region of the tenant's partitions, and possibly reserved capacity. The invariants are already zero-tolerance for every tenant.

Likely follow-ups
  • Would you relax a guarantee for a low-stakes tenant? — No. Freshness budgets, purge deadlines and caching are tunable per tenant; authorization and isolation are not.

How do you roll back a reranker that regresses quality?

Strong answer angle

Version it behind a routing flag, validate on recall@k, nDCG and groundedness before rollout, and canary it. Rollback is flipping the flag, not a redeploy. While no reranker is available, the defined degraded mode is fusion order with a tighter context.

Likely follow-ups
  • What if the regression is caught only in production? — Flip back immediately, then add the failing queries to the evaluation set so the gate catches it next time.

How would you estimate and explain the cost of this system to a customer?

Strong answer angle

Cost per query is dominated by LLM tokens: ~2,200 input and ~350 output, ~2,550 total, of which ~1,600 is retrieved context. For a tenant with 4,400 queries a day that is ~11 million tokens a day (Complete Design, Ch. 05). Break out embedding, reranking and generation separately, each tied to stated assumptions, and name the levers: context chunk count, conditional reranking, generator model size.

Likely follow-ups
  • Which lever first for a cost-sensitive customer? — Context size — fewer, better-reranked chunks — because it multiplies every query, provided recall@k and groundedness hold.

If you could add only one more piece of observability, what would it be?

Strong answer angle

A continuously running authorization canary — synthetic users with different entitlements asking the same questions and asserting that restricted content never reaches the wrong one. It would catch a cache-key or filter regression within one cycle (Scenario 08), and authorization is the invariant where an undetected regression is already a completed exposure.

Likely follow-ups
  • Why not a retrieval-quality metric? — Quality regressions are serious but recoverable and surface through feedback; an authorization leak cannot be undone.
Interview 09

Quick-Revision Summary

Five minutes before the interview, rehearse the sequence and the framings — not the diagram.

Key takeaways
  • Open with scope and invariants: authorization and tenant isolation are zero-tolerance; revocation and index lag are stated budgets (60 s group, 2 min document ACL, 5-15 min content); groundedness is measured.
  • Estimate to three conclusions: ~5 GB index fits in memory (no sharding for size), the LLM tier is the bottleneck (~1,000 generations in flight), context size is the cost lever (4 chunks ≈ 1,600 tokens).
  • Build Stages 0-8 in order, each from a named limitation: naive baseline → ingestion with a fenced writer → vectors → hybrid with RRF → rerank and budget → authorization → lifecycle → grounded generation → operations.
  • Authorization is a retrieval constraint: tenant partition, principal tags filtered inside both indexes, a live fail-closed re-check before the prompt, an ACL priority lane that rewrites tags only, and a uniform denial.
  • Lifecycle is on the query path: a status filter, change intent admitting the labelled superseded revision, tombstones excluding immediately, fenced writes rejecting stale replays.
  • Grounding is verified, not hoped for: source-as-data prompt, structural and entailment citation checks, abstention below a threshold, a trace per answer.
  • Operate without leaking: identity-scoped cache keys, quotas for noisy neighbours, and degraded modes that drop relevance (fusion order, lexical-only, source list) but never authorization.
  • Be honest about limits: the TOCTOU window is bounded, not zero; grounded is not the same as current; validation reduces but does not eliminate unsupported claims.