System Design · step by step

Design a Conversational AI

Step 1 / 9
The numbers to beat128kwindow (tokens)~4 chars≈ 1 tokenin + outboth billed

In the interview room

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

Functional requirements

What it must do — agree on these before drawing a single box.

  • Answer a turn: take the user’s message plus the conversation so far, run the model, and stream back a reply.
  • Remember the chat: persist every turn so a conversation survives reloads, new devices, and crashes.
  • Fit the window: assemble system prompt + relevant history + the new message within a fixed token budget.
  • Ground with knowledge: retrieve only the relevant passages from a vector store (RAG) instead of stuffing everything in.
  • Stay safe: screen the incoming prompt and the outgoing answer for unsafe or disallowed content.

Non-functional requirements

The qualities that shape the whole design — each one names the mechanism that buys it.

Cost and latency don’t blow up per turn
The context builder works to a token budget — pin the system prompt, keep recent turns, summarize the rest — since cost and latency both scale with tokens in + out.
A stateless model that still remembers
History lives in a durable Conversation DB, reloaded per turn — the model is fed its memory as text each request rather than holding it.
Isolate the expensive bottleneck
GPU inference gets its own queued, independently-scaled tier so it can be batched, autoscaled, and protected from the cheap stateless work around it.
Serve a giant model economically
Continuous batching packs concurrent chats into shared GPU passes and a KV cache reuses attention state, so each new token is cheap.
Feel instant even on a slow answer
Tokens stream to the browser over a persistent connection as they’re generated — time-to-first-token, not total time, is what the user feels.
Safe on both edges of the model
An independent guardrails layer screens the prompt before generation and the answer before it streams — a clean prompt can still yield unsafe output.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Budget the context windowover send the whole conversation every time

Overflowing the window errors or silently drops the start — usually the system prompt, the most important part — and cost scales with every token. Pin, keep recent, summarize or retrieve the rest.

A durable Conversation DBover keep history only in the browser tab

Close the tab and it’s gone; switch devices and it’s gone. The client is a fast cache, not the source of truth; the stateless model needs history reloaded from a durable store each turn.

Continuous batching + KV cacheover one request per GPU, start to finish

A single chat can’t saturate a GPU and others queue behind it. Batching raises throughput across the fleet, and the KV cache avoids re-running attention over the whole prompt per token.

Stream tokens as generatedover wait for the whole answer

Six seconds of blank screen reads as frozen. Streaming doesn’t speed up generation — it shows progress immediately, and optimizing time-to-first-token beats optimizing total time.

Guardrails on both edgesover filtering only the user’s input

Input filtering catches malicious prompts but not unsafe generations — a clean prompt can still produce output you must block. Safety is an independent layer around the model, not a property of it.

What this teaches

Learn AI system design by building a production conversational AI like ChatGPT step by step. An interactive guide covering the inference gateway, context-window assembly and token budgeting, conversation memory, model serving with batching and KV cache, token streaming, and prompt/output guardrails.

Key takeaways

  • Inference Gateway — one front door — auth, rate limits, orchestration
  • Context Builder — assembles system prompt + history + message
  • Token budget — pin, keep recent, summarize, retrieve (RAG)
  • Memory Service + DB — history lives outside the stateless model
  • Model Servers — batched GPU inference + KV cache
  • Streaming — tokens pushed as generated — fast time-to-first-token
  • Guardrails — screen both the prompt and the answer
  • Event stream — usage + safety events → billing, evals, abuse detection

Concepts covered

  • What is a conversational AI, really?
  • A client, a gateway, a model
  • Assemble the context window
  • It won’t all fit — now what?
  • Where does history live?
  • Inference is the expensive part
  • Batching and the KV cache
  • Stream the tokens back
  • Guard the input and the output

Design a Conversational AI (ChatGPT-style) — read the full walkthrough as text

the same steps, decisions & trade-offs, for reading, reference & search

The big idea

What is a conversational AI, really?

Strip away the chat bubble and a system like ChatGPT does one deceptively simple thing: take your message plus everything said so far, run it through a language model, and stream back an answer — for millions of people at once.

That sentence hides the hard parts. What exactly do we feed the model (and how much fits)? How does it remember earlier turns? How do we serve a giant model fast and affordably? And how do we keep the output safe?

How to read this: Each step opens with a real design decision — you make the call before I show you what ships. Then watch the diagram on the right grow. Hover any box, replay the flow, and at the end overload the model servers to see what breaks. Hit Begin.

Step 1 · The skeleton

A client, a gateway, a model

Your browser can’t hold a 100-billion-parameter model, and it can’t be trusted with the keys, the rate limits, or other users’ data. So where does the request go?

Design decision: You type a message. Where does the thinking happen?

The call: A gateway in the middle authenticates, then calls the model servers. — A neutral backend holds the keys, enforces limits, assembles the prompt and routes to the GPU fleet. This is the spine of every LLM product.

We put an Inference Gateway in the middle. The browser sends a turn; the gateway authenticates, rate-limits, and orchestrates everything behind it. This is the client → gateway → model backbone under every chat product.

Client–gateway–model: The browser is the client (it asks). The gateway is the orchestrator (auth, limits, routing). The model servers are the brain. Keep these roles separate and everything else gets easier.

Step 2 · What the model sees

Assemble the context window

A language model has no memory between calls — it only sees the text you hand it this request. So what exactly do we put in front of it for each turn?

Design decision: The model is stateless. What do you send it for each new message?

The call: A built prompt: system instructions + relevant history + the new message. — A Context Builder composes the model input each turn — the system prompt, the recent/relevant history, and the new message — shaped to fit the window.

A Context Builder composes the model input for every turn: the system prompt (who the assistant is), the conversation history, and the new message. The model is stateless, so the full context is rebuilt and resent on each request.

The context window: Everything the model "knows" in a turn is the text in its context window — a fixed maximum number of tokens. There is no hidden memory; if it isn’t in the window, the model can’t see it.

Step 3 · Budget the tokens

It won’t all fit — now what?

The window is finite (say 128k tokens) and every token costs money and latency. A long chat — or a big document — blows past it. How do you decide what makes the cut?

Design decision: The conversation is longer than the context window. What gets sent?

The call: Budget the window: keep the system prompt + recent turns, summarize or retrieve the rest. — Allocate the token budget deliberately — pin the system prompt, keep recent turns, compress older ones, and pull in only the relevant facts (RAG) instead of everything.

The builder works to a token budget. It pins the system prompt, keeps the most recent turns, and summarizes or drops older ones. For knowledge beyond the chat, it retrieves only the relevant passages from a vector store (RAG) instead of stuffing everything in.

Tokens are the currency: Cost and latency both scale with tokens in + tokens out. Treating the context window as a budget to allocate — not a bucket to fill — is the core discipline of LLM system design.

Step 4 · Make it remember

Where does history live?

The model is stateless and the browser can’t be trusted to hold the true history. Yet tomorrow the user reopens the chat and expects it all to be there. Where does the conversation actually live?

Design decision: The model forgets between calls. Where is the real conversation stored?

The call: A Memory Service backed by a durable Conversation DB. — Persist every turn server-side. The builder loads history from it each request and saves the new turn — survives reloads, devices and crashes.

A Memory Service reads and writes the Conversation DB — the durable source of truth for every turn. Each request, the builder loads recent history from it; after each answer, the new turn is appended. The browser is just a fast cache of what the database already knows.

Memory lives outside the model: Because inference never updates the weights, all "memory" is just text we store and re-feed. Persist it durably, reload it per turn — that’s what makes a stateless model feel like it remembers.

Step 5 · The real cost

Inference is the expensive part

Assembling a prompt is cheap. Running a hundred-billion-parameter model to generate each token is not — it needs GPUs, and a single request can hold one for seconds. How should the system treat this?

Design decision: What dominates the cost and latency of a chat turn?

The call: GPU inference — generating the answer token by token. — Each output token is a full forward pass through the model on scarce GPUs. This dominates both cost and latency, so it gets its own carefully-managed tier.

Generation runs on a dedicated Model Servers tier — a fleet of GPUs holding the model weights. Because each output token is a full pass through the model, this tier is the bottleneck we design everything else around: it gets its own queue, its own scaling, and its own failure handling.

Isolate the bottleneck: The most expensive, scarcest resource (GPU inference) becomes its own tier so it can be queued, batched, scaled and protected independently from the cheap stateless work around it.

Step 6 · Serve it efficiently

Batching and the KV cache

GPUs are most efficient when fully fed, but chat requests arrive one at a time and each generates tokens one at a time. Run them naively and the GPUs sit half-idle while users wait. How do you serve a giant model fast?

Design decision: How do you keep scarce GPUs busy across many concurrent chats?

The call: Continuously batch many requests together and reuse the KV cache. — Pack concurrent requests into shared batches and cache the attention state (KV cache) so each new token is cheap. This is how real serving stacks hit high throughput.

The model servers use continuous batching — packing many in-flight chats into shared GPU passes — and a KV cache that stores attention state so each new token reuses prior work instead of recomputing the whole prompt. Every turn also emits usage events (tokens, latency) to the stream for billing and evals.

Throughput vs latency: Batching raises throughput (chats/sec across the fleet) at a small cost to any single request’s latency. The KV cache makes per-token generation cheap. Together they make serving a frontier model economical.

Step 7 · Don’t make them wait

Stream the tokens back

A long answer can take many seconds to finish. If the user stares at a blank screen until it’s done, the product feels broken — even when it’s working perfectly. How do you make it feel instant?

Design decision: A full answer takes 6 seconds to generate. What does the user see?

The call: Tokens streamed to the screen as they’re generated. — Stream over a persistent connection (SSE/WebSocket) so words appear as they’re produced. Time-to-first-token is what users actually feel.

The model generates tokens incrementally, and the gateway streams them straight to the browser over a persistent connection (server-sent events or WebSocket). The user sees the answer appear word by word. The metric that matters is time-to-first-token, not total time.

Perceived latency: Streaming doesn’t make generation faster — it makes it feel instant by showing progress immediately. Optimizing the first token often beats optimizing the total.

Step 8 · Keep it safe

Guard the input and the output

Users will send prompts that try to jailbreak, extract secrets, or produce harmful content — and even a well-behaved model can generate something unsafe. Where do you put the checks?

Design decision: Where do safety checks belong in the request path?

The call: Screen both the incoming prompt and the outgoing answer. — A Guardrails layer checks the prompt before generation and the answer before it reaches the user — defense on both edges, independent of the model.

A Guardrails layer screens both edges: the incoming prompt (block jailbreaks, abuse, PII leaks) before it reaches the model, and the outgoing answer before it streams to the user. Safety flags are emitted to the event stream so abuse can be detected and rate-limited over time.

Defense at both edges: Treat safety as an independent layer around the model, not a property of the model. Check input and output — a clean prompt can still produce unsafe output, and vice versa.

The payoff

You built a conversational AI

From one box to a production LLM system: a gateway that orchestrates, a context builder that budgets tokens, durable memory, a batched GPU serving tier, streaming, and guardrails on both edges.

Now overload the model servers and watch how the serving tier — isolated behind its own queue — sheds load and degrades to a smaller model instead of taking the whole product down.

  • Inference Gateway — one front door — auth, rate limits, orchestration
  • Context Builder — assembles system prompt + history + message
  • Token budget — pin, keep recent, summarize, retrieve (RAG)
  • Memory Service + DB — history lives outside the stateless model
  • Model Servers — batched GPU inference + KV cache
  • Streaming — tokens pushed as generated — fast time-to-first-token
  • Guardrails — screen both the prompt and the answer
  • Event stream — usage + safety events → billing, evals, abuse detection
built to be reasoned about, not memorized — make the calls, trip the guardrail, run the quiz.
Finished this one? 0 / 61 AI System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs