•CASE STUDY

AI Prompt Playground (OpenAI / Anthropic Console)

7 min read·1,261 words·Intermediate

Asked at

4 candidate reports between Apr 2026 and May 2026

How to use this case study

SDE-2 / Mid

  • Explain the editor-to-model flow
  • Streaming the response to the browser
  • Saving prompts
  • Versions and runs

SDE-3 / Senior

  • Go deeper on the data model for prompts
  • Versions and runs
  • Handling very large prompts (moving content to object storage)
  • Rate limits and cost control

Staff / Principal

  • Discuss comparing outputs across models
  • Sharing and permissions
  • Reliability of streaming
  • Multi-tenant isolation for enterprise workspaces

Problem RestatementProblem

Design a web-based prompt playground, like the OpenAI Playground or the Anthropic Console. A signed-in developer writes a prompt (system message plus user messages), picks a model and settings (temperature, max tokens), clicks Run, and watches the answer stream in real time. They can save prompts, keep versions, compare runs side by side, and come back later to see history.

Anthropic also asked about very large prompts (hundreds of KB to MB): where should that content live?

RequirementsRequirements

1.1 Functional

  • An editor for prompts with variables (e.g., {{customer_name}}).
  • Run against a chosen model with settings, and stream the output.
  • Save prompts, create versions and view run history.
  • Compare outputs (e.g., two models side by side).
  • Stop a running generation.

1.2 Non-Functional

  • Responsive: time to first token under a second or two, and a smooth UI while streaming.
  • Durable: saved prompts and runs are never lost.
  • Handles large prompts without slowing everything else.
  • Cost and abuse control: rate limits and spend limits per user or organization.

1.3 Scale Estimates

  • 1M monthly developers, 100K daily active, ~20 runs each → 2M runs/day ≈ 25/sec, peak 100/sec.
  • A typical prompt is ~5 KB, but some are 1 MB+. Outputs are ~2 KB.
  • Storage: 2M runs × ~10 KB ≈ 20 GB/day (much more if large prompts are copied into every run, which we'll avoid).

1.4 API Design

POST/v1/prompts{ name, messages, model, params } → { prompt_id, version: 1 }
POST/v1/prompts/{id}/versions(save a new version)
POST/v1/runs{ prompt_version_id | inline messages, model, params, variables } → SSE stream of tokens, then { run_id, usage }
POST/v1/runs/{id}/stop
GET/v1/prompts/{id}/runs?cursor=

High-Level ArchitectureArchitecture

2.1 Overview

  • Frontend (React): the editor, a streaming output panel and a compare view. Autosaves drafts locally.
  • Playground API: auth, rate limits and spend checks, saving prompts and versions, and starting runs.
  • Run Service: builds the final request (fills in variables), calls the model gateway, streams tokens back over SSE, and records usage.
  • Model Gateway: the same inference service the public API uses (routing, batching, quotas).
  • Metadata DB (Postgres): prompts, versions and runs.
  • Object storage: large prompt bodies and outputs, stored by content hash.

2.2 Architecture Diagram

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
    B["Browser - editor + stream view"] -->|"save"| API["Playground API"]
    B -->|"run - SSE"| RS["Run Service"]
    API --> DB[("Postgres - prompts, versions, runs")]
    API --> OS[("Object storage - large bodies by hash")]
    RS --> DB
    RS --> OS
    RS --> MG["Model Gateway"]
    MG --> GPU["Model servers"]
    RS -->|"usage"| BILL["Usage and spend limits"]

Data ModelData model

CREATE TABLE prompts (prompt_id UUID PRIMARY KEY, org_id UUID, owner_id UUID, name TEXT, latest_version INT);
CREATE TABLE prompt_versions (
  version_id UUID PRIMARY KEY, prompt_id UUID, version INT,
  model TEXT, params JSONB,
  body_inline JSONB,         -- small prompts stored directly
  body_ref TEXT,             -- 'sha256:ab12...' when the body is large (object storage)
  body_bytes INT, created_at TIMESTAMP
);
CREATE TABLE runs (
  run_id UUID PRIMARY KEY, version_id UUID, user_id UUID, model TEXT, params JSONB,
  variables JSONB, output_inline TEXT, output_ref TEXT,
  status TEXT, input_tokens INT, output_tokens INT, latency_ms INT, created_at TIMESTAMP
);

Versions are immutable. Editing creates a new version, so every run points to exactly what was sent.

Key FlowsFlows

4.1 Run with streaming

  1. The browser posts the run request and keeps the SSE connection open.
  2. The Run Service checks the rate limit and spend limit, loads the version (from inline or object storage), fills in variables, and calls the model gateway with streaming on.
  3. Each token chunk is forwarded to the browser right away. The service also buffers the output.
  4. On finish (or stop), it saves the run with the output and token usage, and adds the cost to the org's spend.

4.2 Stop

The browser calls stop (or closes the connection). The Run Service cancels the upstream request so GPU time isn't wasted, and saves the partial output with status stopped.

Deep Dive A — When the prompt is a megabyteDeep dive

Developers paste entire documents into the system message. A prompt that was two kilobytes on Monday is a 900 KB contract by Friday, and every saved version stores another copy.

Weak

A text column in Postgres

Store the prompt body in the versions table like any other string.

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  V["Save version - 900 KB body"] --> PG[("versions table")]
  PG --> TOAST["Large values stored out of line"]
  PG --> LIST["List a project's 200 versions"]
  LIST --> HEAVY["Reads drag 180 MB through the DB for a sidebar"]
  V --> DUP["Same document saved 40 times - 36 MB of identical bytes"]

Nothing breaks immediately, which is what makes it dangerous. The table grows fast, backups and replication slow down, and listing versions — which only needs names and timestamps — becomes expensive because the rows are enormous. And the same unchanged document is stored once per version.

Good

Put big bodies in object storage

Above a threshold, write the body to object storage and keep a reference in the row. Rows stay small, listings stay fast, storage gets cheap.

Two things are still wrong. Every save of the same unchanged document creates another object, so the duplication moved rather than disappeared. And the API servers are now proxying megabytes on every save — the request occupies a worker for the whole upload, which is the wrong place to spend request capacity.

Best

A threshold, content addressing, and upload straight past the API

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  B["Body"] --> T{"Under 64 KB?"}
  T -->|"yes"| PG[("Inline in Postgres - fast and simple")]
  T -->|"no"| H["Browser hashes it - SHA-256"]
  H --> EX{"Object already exists?"}
  EX -->|"yes"| REF["Just save the reference - zero bytes uploaded"]
  EX -->|"no"| PUT["Pre-signed URL - browser uploads directly to object storage"]
  PUT --> REF
  REF --> ROW["Version row holds the hash"]

Three things fall out of it:

  • Small prompts stay simple. Most bodies are a few kilobytes, and an extra network hop for them would be a poor trade. The threshold keeps the common path in the database.
  • Hashing deduplicates for free. Key the object by the SHA-256 of its contents and the fortieth save of an unchanged document uploads nothing and stores nothing — it writes a row pointing at bytes that already exist. Version history becomes almost free.
  • The API never carries the payload. The browser uploads to a pre-signed URL and then saves a version referencing the hash, so a 1 MB paste does not tie up a request worker.

Two client-side details worth mentioning: virtualise the editor so a megabyte of text does not render every line, and check the token count against the model's context window before sending, so the user is warned at edit time instead of by an API error after clicking Run.

Deep Dive B — Compare, reliability and costDeep dive

  • Compare mode: fire the same prompt at two models in parallel, with two streams in one SSE connection (tagged by run ID) or two connections.
  • Streaming reliability: if the connection drops, the run keeps going on the server (for a short time) and the output is saved. The UI can reload the finished run by ID.
  • Cost control: per-user and per-org rate limits, a monthly spend cap with alerts at 80% and 100%, and max_tokens limits.
  • History at scale: runs are append-only. Partition the runs table by month and archive old outputs to object storage.

Trade-offs & AlternativesTrade-offs

DecisionChoiceWhyAlternative
StreamingSSEOne-way stream over plain HTTPWebSockets: two-way, more to manage
Large bodiesObject storage by hash, inline when smallSmall DB rows, dedupEverything in the DB: slow, big backups
VersionsImmutable versionsReproducible runsEdit in place: runs lose their exact input
UsageCounted per run, enforced before runPrevents surprise billsBill after the fact only: runaway cost

Common Follow-up QuestionsFollow-ups

  • "Sharing prompts?" Add permissions at the prompt level (private, org, link), and see the prompt-sharing design.
  • "Evaluations?" Let users attach a dataset of inputs, run a version against all of them as a batch job, and score the outputs.
  • "Multi-tenant enterprise?" Scope all data by org_id, add SSO, and optionally turn off saving of run contents for sensitive orgs.

Wrap-UpWrap-up

The browser editor saves immutable prompt versions to Postgres, keeping large bodies in object storage by content hash. The Run Service fills in variables, calls the shared model gateway and streams tokens back over SSE, while saving the output and usage at the end. Rate limits and spend caps control cost, and stopping a run cancels the upstream generation.

More Case Studies

Frequently Asked Questions

What is the AI Prompt Playground (OpenAI / Anthropic Console) system design question?

AI Prompt Playground (OpenAI / Anthropic Console) is a system design interview question asked at FAANG companies. It covers ai / ml, frontend, storage, real-time and tests your ability to design scalable, production-ready systems. InterviewSkool's breakdown walks you through requirements, API design, architecture, and trade-offs.

Which companies ask the AI Prompt Playground (OpenAI / Anthropic Console) question?

Anthropic, OpenAI have reportedly asked variations of this question in system design interviews. The exact wording may differ, but the core design challenges remain the same.

How should I prepare for the AI Prompt Playground (OpenAI / Anthropic Console) interview question?

Start with the problem statement and scale estimates, then design the high-level architecture. Focus on the core components, data model, and API design. InterviewSkool's breakdown covers the full solution with mermaid diagrams and trade-off analysis to help you prep efficiently.

What level is the AI Prompt Playground (OpenAI / Anthropic Console) question?

This question is suitable for SDE-2, SDE-3, and Staff engineer interviews. The level guidance on this page provides specific tips for each level — SDE-2 candidates should focus on core architecture, while Staff engineers should discuss trade-offs, monitoring, and incremental rollouts.

Practice with a Mock Interview

Apply what you learned in a live system design mock interview with InterviewSkool's AI interviewer.

Start System Design Interview →