An eight-stage pipeline you drive in any AI design interview — where the LLM call is only ~20% of the system and tokens, freshness, and hallucination are the real constraints.
Every AI system-design question is the same eight-stage pipeline wearing a different costume: data pipeline → retrieval → model routing → inference optimization → guardrails → evals → observability → cost. The single most important reframe is that the LLM call — the part candidates obsess over — is roughly 20% of the system; the other 80% is the data plumbing, retrieval quality, routing logic, safety perimeter, and measurement loop that decide whether the thing works in production. Interviewers are not testing whether you know what an embedding is; they are testing whether you treat the model as a component inside an engineered system with explicit cost, latency, and quality budgets. The strongest candidates drive the conversation through these stages, make capacity/latency/cost estimates out loud, and name the tradeoff at each fork (cache vs. rerank vs. route vs. rewrite). This lesson is the spine you hang every other system-design lesson on.
The words first.
Step by step.
Remember this: drive the eight stages in order, estimate cost/latency out loud, and remember the LLM call is the small part.
The framework is a directed pipeline because each stage constrains the next. Garbage chunking caps retrieval recall no matter how good your reranker is; weak retrieval caps answer quality no matter how large your model is; and no amount of model quality saves you if you can't measure regressions. Walking the stages in order is also a conversational tactic — it stops you from rabbit-holing on the LLM while the interviewer is waiting to hear about freshness or guardrails.
Here is the load-bearing claim. If you list the components that determine whether an AI product succeeds — data quality, chunking, embedding choice, hybrid retrieval, reranking, freshness/sync, routing, caching, guardrails, golden sets, judges, tracing, cost controls — the raw model invocation is one box among a dozen. The common pitfall the rubric explicitly calls out is "treating RAG as LLM + embeddings instead of the full pipeline." The model is also the part you have the least leverage over: you can't change its weights (usually), but you fully control the retrieval that feeds it and the evals that grade it. So the engineering effort — and the interview signal — lives in the other 80%. When an interviewer hears you say "the model is the easy part, the retrieval and the eval loop are the hard part," you've signaled seniority in one sentence.
The single most common architecture mistake is letting indexing compete with serving. The offline pipeline (ingest → chunk → embed → index) is throughput-bound and bursty; the online pipeline (retrieve → rank → generate) is latency-bound and must hold a p90 TTFT under ~2 seconds for enterprise RAG. Run them on shared resources and a 10M-document re-index spikes your serving latency. Separate them, and you also get clean ownership boundaries: the data team owns freshness SLAs, the serving team owns latency SLAs.
This is the skill that separates IC5 from IC4. You should be able to produce a back-of-envelope estimate in under two minutes. The method:
requests × (input_tokens + output_tokens) × price_per_token, minus whatever your cache hit rate removes. The dangerous subtlety: agentic workflows make 50–200 model calls per task, so a "cheap" $0.40/M-token model becomes expensive per task. Always cost the task, not the token.Symbols in plain words: R = requests per day, T_in/T_out = input/output tokens per call, P = price per million tokens, h = semantic-cache hit rate (fraction served without a model call).
Worked example. Suppose R = 5,000,000 requests/day. Each RAG call stuffs retrieved context, so T_in = 4,000 tokens and T_out = 500 tokens — 4,500 total. Take a GPT-4-class price of P = $2.50 per million input, $10 per million output (use blended ~$3.30/M here for simplicity, so $3.30e-6 per token).
4,500 × $3.30e-6 ≈ $0.0149.5,000,000 × $0.0149 ≈ $74,250.h = 0.6 (60% hit rate, realistic for high-repetition support traffic). Only 40% of requests hit the model: 5,000,000 × 0.4 = 2,000,000 paid calls.2,000,000 × $0.0149 ≈ $29,700. Monthly ≈ $891,000.What it did to the data: the cache turned a ~$2.2M/month bill into ~$891K/month — a 60% cut — by never sending repeat queries to the model. That single number is why caching, not a fancier model, is usually the first cost lever.
When the interviewer says "design X," do this in order: (1) clarify scale, latency SLA, freshness requirement, and quality bar — these pick your architecture; (2) sketch the eight stages as boxes; (3) estimate capacity/cost out loud; (4) deep-dive the 1–2 stages that dominate this problem (retrieval for RAG, sandboxing for agents, latency for voice); (5) name tradeoffs at each fork; (6) close with failure modes and observability. The clarifying questions are not throat-clearing — "sub-100K docs vs. 10M+" and "24-hour vs. sub-minute freshness" lead to different systems (pgvector vs. Turbopuffer; batch re-index vs. CDC).
The high-bar signal is trade-off clarity: knowing when to cache vs. rerank vs. route vs. rewrite.
The same scale-awareness applies to every stage: under 10M chunks, pgvector's operational simplicity beats specialized stores; past 10M, you move to serverless ANN (Turbopuffer, HNSW/IVFPQ) for cost-effectiveness.
Here is a runnable skeleton that makes the eight stages concrete and the cost arithmetic explicit. It's a planning/estimation harness — the kind of thing you'd actually whiteboard, parameterized so you can change one assumption and see the bill move.
from dataclasses import dataclass
@dataclass
class Stage:
name: str
p50_ms: float # added latency at the median
per_call_usd: float # marginal $ this stage adds per request
# The eight-stage pipeline as a list of boxes with budgets attached.
PIPELINE = [
Stage("query_rewrite", p50_ms=40, per_call_usd=0.00005), # small model
Stage("bm25", p50_ms=20, per_call_usd=0.0),
Stage("vector_search", p50_ms=35, per_call_usd=0.00002), # ANN + embed
Stage("rrf_fusion", p50_ms=5, per_call_usd=0.0),
Stage("rerank", p50_ms=120, per_call_usd=0.0002), # cross-encoder
Stage("guardrail_in", p50_ms=15, per_call_usd=0.00003),
Stage("llm_generate", p50_ms=900, per_call_usd=0.0149), # the ~20% box
Stage("guardrail_out", p50_ms=15, per_call_usd=0.00003),
]
def estimate(requests_per_day: int, cache_hit_rate: float):
paid = requests_per_day * (1 - cache_hit_rate)
# Latency: cache hits return <100ms; misses pay the full chain.
miss_latency = sum(s.p50_ms for s in PIPELINE)
# Cost: only the LLM + paid stages run on a miss.
cost_per_miss = sum(s.per_call_usd for s in PIPELINE)
daily_cost = paid * cost_per_miss
llm_share = sum(s.per_call_usd for s in PIPELINE if s.name == "llm_generate")
return {
"p50_miss_latency_ms": miss_latency,
"cache_hit_latency_ms": 90,
"paid_calls_per_day": int(paid),
"daily_cost_usd": round(daily_cost, 2),
"monthly_cost_usd": round(daily_cost * 30, 2),
"llm_pct_of_cost": round(100 * llm_share / cost_per_miss, 1),
}
print(estimate(requests_per_day=5_000_000, cache_hit_rate=0.6))
# -> p50 miss latency ~1150ms, ~2M paid calls/day,
# ~$29.8K/day, ~$895K/month, LLM ~96% of *marginal $ per call*Two things this surfaces that interviewers reward. First, the LLM box is ~20% of the stages but ~96% of the marginal dollars per call — "the LLM is 20% of the system" is a claim about engineering surface area and failure modes, not about cost share. State that distinction explicitly; conflating them is a trap. Second, changing one assumption (cache_hit_rate) is the highest-leverage edit in the file — which is exactly why caching is the first cost lever in real systems. Swap requests_per_day and cache_hit_rate to pressure-test any scenario the interviewer throws at you.
| Stage | Cost lever | Latency cost | Quality effect | Failure mode | What changes at scale |
|---|---|---|---|---|---|
| Data pipeline | Batch embed offline | None (offline) | Sets the ceiling on retrieval | Orphaned vectors after deletes | Move to CDC for sub-minute freshness; track versions |
| Retrieval | Hybrid > vector-only | +200–400ms | +1–9% recall | BM25 missing on semantic-only | pgvector → Turbopuffer/HNSW past 10M chunks |
| Routing | Small model for easy tasks | <1–100ms overhead | ~12x cheaper, minimal loss | Misroute hard query to weak model | Add ML classifier + fallback chain |
| Inference opt | Prompt + semantic cache | Saves 65x on hits | None (or staleness risk) | Wrong answer on loose threshold | Continuous batching, PagedAttention, spec-decode |
| Guardrails | Cheap classifier first | +15–30ms each | Blocks unsafe output | False positives (over-refusal) | Fine-tuned BERT for injection; NeMo self-check |
| Evals | LLM-judge vs. humans | Offline | Catches regressions | Judge miscalibration | Golden sets per failure mode; online sampling 10–20% |
| Observability | Sample traces | Negligible | Enables debugging | Confusing monitoring with traces | Trace full chain, not just LLM call |
The cross-cutting story: stacking model-, system-, and application-level optimizations reaches ~80% cost reduction vs. naive serving. Model-level (quantization FP16→INT8/INT4: 2–4x memory, 95–99% accuracy retained), system-level (continuous batching: 3–10x throughput; PagedAttention: up to 24x; speculative decoding: 2–3x, but only worthwhile when decode is memory-bound, and a badly chosen k can raise cost 175%), and application-level (prompt caching: 80–90% latency cut on cached prefixes; semantic caching: 68.8% API-call reduction; routing: 2–5x aggregate savings). At scale the dominant failure mode shifts from "wrong answer" to "expensive task" — because agentic loops make 50–200 calls each, the per-token price war ($30/M in 2023 → $0.40/M now) is offset by call volume per task. Cost the task.
k can raise cost 175%.Flashcard. Drive the eight stages — data → retrieval → routing → inference opt → guardrails → evals → observability → cost — estimate the bill out loud, and remember the LLM is ~20% of the system but ~96% of the marginal dollars.
Next: /rag — apply this framework to the canonical "design a RAG chatbot over 10M documents" problem, where retrieval is the stage that dominates.