AI System Design
IC4IC5IC6

The AI System-Design Framework

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.

15 min read · 14 sections
0

1. Quick anchor

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.

2. Why interviewers probe this

  • IC4 — Can you name the full pipeline instead of collapsing "RAG" into "LLM + embeddings"? Do you know that retrieval, ranking, citation, guardrails, and evals are separate concerns? Signal: you don't skip stages, and you can sketch the offline/online split.
  • IC5 — Can you budget? Given a request volume, do you produce a defensible per-request cost and latency number, identify the dominant term, and pick the right architecture for the scale (sub-100K docs vs. 10M+)? Signal: you reach for arithmetic unprompted and reason about which lever moves the bill.
  • IC6 — Can you reason about the system's failure modes and organizational implications? Do you know that agentic workflows turn a cheap per-token price into an expensive per-task cost, that MTEB rank doesn't predict in-domain success, and that observability ≠ monitoring? Signal: you make scale-aware, second-order arguments and defend a position under pushback.

3. Concept build-up

Beginner explainerNew here? The words first

The words first.

  • Pipeline stage — one discrete responsibility (e.g. retrieval) with its own inputs, outputs, and failure mode.
  • Offline vs. online — offline = work done ahead of time (ingest, embed, index); online = work done per user request (retrieve, generate).
  • Retrieval — finding the right documents/chunks to feed the model so it answers from facts, not memory.
  • Model routing — sending each request to the cheapest model that can handle it (simple → small, hard → large).
  • Guardrail — a check on the input or output that blocks unsafe or off-policy content.
  • Eval — an automated measurement of answer quality, run on a fixed test set and on live traffic.
  • Observability — detailed per-request traces that let you explore why something broke (vs. monitoring, which only tracks known metrics).
  • TTFT / TTFA — time-to-first-token / time-to-first-audio; the latency a user actually feels.

Step by step.

  1. Ingest and clean raw data, then chunk and embed it into an index (offline).
  2. A query arrives; optionally rewrite or classify it.
  3. Retrieve candidate chunks (keyword + vector), fuse, and rerank.
  4. Route the request to the right-sized model and call it (with caching where possible).
  5. Run input/output guardrails around the call.
  6. Score a sample of responses with evals, online and offline.
  7. Trace everything; attribute cost and latency per stage.
  8. Add up the dollars and decide what to optimize.

Remember this: drive the eight stages in order, estimate cost/latency out loud, and remember the LLM call is the small part.

3.1 The eight stages, and why the order matters

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.

  1. Data pipeline — ingestion, normalization (dedup, format standardization, metadata extraction), chunking, embedding generation. This is offline and must be physically separated from serving so a re-index doesn't starve live queries of CPU/IO.
  2. Retrieval — query rewrite/expansion, BM25 + vector search, Reciprocal Rank Fusion, cross-encoder reranking. This is where most quality lives.
  3. Model routing — classify request complexity, send to the cheapest sufficient model, define fallbacks.
  4. Inference optimization — caching (prompt + semantic), continuous batching, quantization, speculative decoding.
  5. Guardrails — input guardrails (prompt injection, jailbreak, moderation) and output guardrails (policy, hallucination self-check).
  6. Evals — golden sets + LLM-as-judge, offline regression gates and online sampling.
  7. Observability — per-request traces of the full chain, with cost/latency/quality attribution.
  8. Cost — the running total, and the lever that decides which optimization is worth building.

3.2 The LLM is ~20% of the system

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.

3.3 Offline vs. online: the separation that prevents outages

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.

3.4 Capacity, latency, and cost estimation — the part candidates skip

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:

  • Capacity: requests/day → requests/second (divide by ~86,400, then multiply by a peak factor of 2–5x for diurnal spikes). 5M/day ≈ 58 rps average, ~150–290 rps peak.
  • Latency: add the stage budget. A production hybrid retrieval pipeline (query expansion → BM25 → vector → RRF → rerank) adds 200–400ms; the LLM TTFT is typically the dominant single term. Semantic-cache hits return in <100ms and bypass the model entirely.
  • Cost: the formula is 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.
Per-request RAG cost — on real numbers

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).

  • Raw cost per call = 4,500 × $3.30e-6$0.0149.
  • Daily raw = 5,000,000 × $0.0149$74,250.
  • Now apply a semantic cache with 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.
  • Daily cost with cache = 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.

3.5 Driving the prompt: a conversational protocol

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).

3.6 The forks, and what changes them

The high-bar signal is trade-off clarity: knowing when to cache vs. rerank vs. route vs. rewrite.

  • Cache when queries repeat (>30% semantic overlap) — cheapest possible win, but risks staleness and wrong-answer-on-near-miss if the similarity threshold (0.85–0.95) is loose.
  • Rerank when recall is fine but precision is poor — a cross-encoder on the top 20→top 5 buys 1–9% recall improvement at +50–150ms.
  • Route when request complexity varies — send classification/extraction to a small model (e.g. Haiku-class) for ~12x cost reduction with minimal quality loss; route hard reasoning to the large model.
  • Rewrite when queries are underspecified — query expansion before retrieval, at the cost of an extra small-model call.

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.

4. Minimal implementation

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.

5. Production tradeoffs

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.

6. How it's asked

[IC4] Walk me through the stages of an AI system from raw data to served response, and tell me where most of the effort goes. Offline: ingest → normalize (dedup, metadata) → chunk → embed → index. Online: query rewrite → hybrid retrieve (BM25 + vector) → fuse (RRF) → rerank → route → guardrail-in → LLM → guardrail-out → trace. The bulk of the engineering effort is not the LLM call — it's the retrieval quality, the freshness/sync pipeline, and the eval loop. The model is roughly 20% of the system by surface area; collapsing the whole thing into "LLM + embeddings" is the classic junior mistake.
[IC5] 5M requests/day on RAG — estimate per-request and monthly cost, then name the two biggest levers. At ~4,500 tokens/call and a blended ~$3.30/M, raw cost is ~$0.015/call ≈ $74K/day. The two dominant levers are (1) semantic caching — a 60% hit rate cuts paid calls to 2M/day and the bill to ~$895K/month, and (2) model routing — sending the large fraction of simple queries to a small model is a ~12x reduction on those. I'd reach for caching first because it's the cheapest to build and removes the model call entirely on hits (sub-100ms instead of multi-second).
[IC5] When do you cache vs. rerank vs. route vs. rewrite? Cache when queries repeat (>30% semantic overlap) — cheapest win but tune the 0.85–0.95 threshold to avoid wrong-answer-on-near-miss. Rerank when recall is adequate but precision is poor — cross-encoder on top-20→top-5 for +1–9% recall at +50–150ms. Route when complexity varies across requests. Rewrite when queries are underspecified, accepting one extra small-model call. The skill is recognizing which symptom you have before reaching for a tool.
[IC6] Defend or refute: the LLM is ~20% of the system. What does it imply for staffing? I defend it as a claim about engineering surface area and failure modes, and explicitly refute it as a claim about cost — the model is ~96% of marginal dollars per call but one box among a dozen you actually build and own. The implication: staff the data/retrieval and eval functions as heavily as the modeling function, because that's where quality is won or lost and where you have real leverage (you can't change the weights, but you fully control retrieval and grading). Prioritize the eval loop early — without it you can't tell whether any later change helped, and every regression after that is flying blind.
[IC6] Why does a cheaper per-token price not lower your bill in an agentic product? Because agentic workflows make 50–200 model calls per task, so cost scales with calls-per-task, not price-per-token. The 2023→2026 price drop ($30/M → $0.40/M for GPT-4-class) is real but offset by call volume, plus growing context per call. You have to budget at the task granularity and attack call count — via routing to cheaper models, caching repeated sub-queries, and context compression (verbatim deletion removes 50–70% while preserving exact wording) — not just chase a lower sticker price.

7. Pitfalls & flashcards

  • Collapsing the pipeline. "RAG = LLM + embeddings" ignores retrieval, ranking, confidence, citation, guardrails, evals, and observability — the rubric's #1 pitfall.
  • No arithmetic. Designing without a cost/latency estimate is an automatic IC5 fail. Always divide requests/day, add the stage latency budget, and multiply tokens × price.
  • Trusting MTEB rank. Top-3 MTEB models ranked 5th/7th/2nd on real legal retrieval; the winner was 11th on MTEB. Benchmark is a useful prior, not a decision oracle — test in-domain.
  • Confusing monitoring with observability. Monitoring tracks known metrics (latency, error rate); observability captures per-request traces so you can explore unknown failures. You need both.
  • Pricing the token, not the task. Agentic loops make 50–200 calls/task — cost the task.
  • Sharing offline and online resources. A re-index that starves live queries is a self-inflicted outage; separate the pipelines.
  • Blind speculative decoding. Worthwhile only when decode is memory-bound; a bad 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.

8. Further reading

Next: /rag — apply this framework to the canonical "design a RAG chatbot over 10M documents" problem, where retrieval is the stage that dominates.

Primary sources
← More in AI System Design