Structured Outputs

How structured outputs turn text into reliable data

LLMs speak prose, but software needs data. Structured outputs make the model return machine-readable JSON that conforms to a schema you define, so its answers drop straight into your code, APIs, and databases without brittle string parsing or fingers-crossed regex.

JSON Schema Constrained decoding Pydantic & zod Validate & reask
01 - What

What are structured outputs?

Structured outputs are model responses that follow a machine-readable shape you specify in advance - typically JSON matching a schema - instead of free-form prose. You hand the model a contract ("return an object with these fields and these types"), and the response comes back as data your program can parse, validate, and act on directly.

๐Ÿ“ Define the shape

You describe the target as a JSON Schema or a typed model (Pydantic, zod). This is the contract the output must satisfy.

๐Ÿ”’ Constrain generation

The model is steered to emit only tokens that keep the output valid against the schema, so it cannot drift into prose.

โœ… Parse & validate

The result is parsed into a typed object and validated. Anything that slips through is caught before it reaches your code.

The spectrum of approaches

ApproachWhat it doesGuarantee
Prompt-and-hopeAsk nicely for JSON in the prompt; parse whatever comes backNone - best effort only
JSON modeProvider flag forcing syntactically valid JSON, but any shapeValid JSON, not your schema
Schema-constrainedDecoding restricted to a supplied JSON Schema at token levelValid JSON and your schema
Grammar-basedA formal grammar (GBNF, regex) masks illegal tokens each stepAny formal shape you can express
Typed modelsPydantic / zod define, coerce, and validate the result in codeRuntime type safety + retries
Key mental model: a prompt asking for JSON is a request; schema-constrained decoding is a guarantee. The stronger the enforcement, the less defensive parsing your downstream code needs to carry.
02 - Why

Why structured outputs exist

Prose is great for humans and terrible for machines. The moment an LLM output feeds another system - a database, an API call, a UI component - you need a predictable shape, not a paragraph you have to reverse-engineer.

๐Ÿ”Œ Machine-readable by default

Downstream systems consume fields, not sentences. A guaranteed schema means you can index, store, and route the output without fragile text extraction.

๐Ÿงฑ Fewer parsing failures

No more regex against "roughly JSON-ish" text. Constrained decoding removes the whole class of "the model added a friendly sentence before the braces" bugs.

๐Ÿงฉ Reliable tool & API calls

Function and tool calling is structured output under the hood - arguments must match the tool's parameter schema exactly or the call fails.

๐Ÿค Contracts between components

A schema is a typed interface. Producers and consumers agree on it, so teams can evolve each side independently and catch drift early.

In Plain Terms

Structured outputs explained with analogies

Same idea, four ways to picture it, so it clicks whoever you are.

๐ŸŽ“ For a student

It's a multiple-choice answer sheet instead of an essay. You cannot scribble in the margins; you fill the exact boxes, so the grading machine can read your answers instantly.

๐Ÿ‘ฉโ€๐Ÿ’ป For a developer

It's a typed return value instead of a raw string. The function signature promises a shape, and the type checker refuses anything that does not match before it reaches your logic.

๐Ÿข For a professional

Think of a standardized intake form. Every applicant supplies name, date, and amount in the same slots, so processing is uniform and nothing gets misfiled.

๐Ÿ“ Everyday version

Like filling in a strict paper form with labeled boxes: one letter per square, date in DD/MM/YYYY. The boxes force the shape, so whoever reads it later never has to guess.

03 - How

How it works under the hood

Structured output has two moving parts: constraint during generation, which shapes what the model can emit, and validation after generation, which verifies and coerces the result into a typed object your code can trust.

๐Ÿ”’ Constrained decoding

At each step the decoder computes which tokens would keep the output valid against the schema, masks the rest to zero probability, and samples only from what remains. Invalid JSON becomes impossible to produce.

โœ… Validate then coerce

The raw text is parsed and checked against a typed model. Missing fields, wrong types, or extra keys are rejected, and a failed check can trigger an automatic reask instead of a crash.

The pipeline at a glance

Pipeline - schema drives constrained decoding, then validation into a typed object
flowchart LR
    subgraph Define["๐Ÿ“ Define"]
        SC["๐Ÿงพ JSON Schema / typed model"]
    end
    subgraph Generate["๐Ÿ”’ Constrained generation"]
        PR["๐Ÿงฉ Prompt + schema"] --> CD["โ›“๏ธ Constrained decode"]
        CD --> RAW["๐Ÿ“„ Raw JSON tokens"]
    end
    subgraph Consume["โœ… Validate & use"]
        VAL["๐Ÿ” Validate"] --> OBJ["๐Ÿ“ฆ Typed object"]
        OBJ --> SVC["๐Ÿš€ Downstream service"]
    end
    SC --> PR
    RAW --> VAL
        

A realistic structured call in Python

from pydantic import BaseModel, Field
from openai import OpenAI

class Invoice(BaseModel):
    vendor: str
    invoice_number: str
    total_amount: float = Field(ge=0)
    currency: str = Field(pattern=r"^[A-Z]{3}$")
    due_date: str  # ISO 8601

client = OpenAI()

# The SDK sends Invoice's JSON Schema and constrains decoding to it.
resp = client.chat.completions.parse(
    model="gpt-model",
    messages=[
        {"role": "system", "content": "Extract invoice fields from the text."},
        {"role": "user", "content": document_text},
    ],
    response_format=Invoice,   # schema-constrained output
)

invoice: Invoice = resp.choices[0].message.parsed
save_to_ledger(invoice)        # already a validated, typed object

The response_format is the whole trick: the schema derived from Invoice both steers generation and validates the result, so parsed is a real typed object, not a string you still have to trust.

04 - Sequence Diagrams

Detailed sequence diagrams

Three views: token-by-token constrained decoding, the validate-then-reask loop, and the end-to-end path from schema to a typed object handed downstream.

Diagram 1 - Constrained decoding: enforcing the schema token by token
sequenceDiagram
    autonumber
    participant App as ๐Ÿ–ฅ๏ธ App
    participant Dec as โ›“๏ธ Decoder
    participant SM as ๐Ÿ“ Schema mask
    participant LM as ๐Ÿง  Language model

    App->>Dec: generate(prompt, schema)
    Dec->>SM: compile schema to token constraints
    SM-->>Dec: valid next-token set
    loop for each token
        Dec->>LM: next-token logits
        LM-->>Dec: raw probabilities
        Dec->>SM: which tokens keep JSON valid
        SM-->>Dec: allowed token mask
        Dec->>Dec: mask illegal tokens then sample
    end
    Dec-->>App: guaranteed schema-valid JSON
        
Diagram 2 - Validate then reask when the output fails the schema
sequenceDiagram
    autonumber
    participant App as ๐Ÿ–ฅ๏ธ App
    participant LLM as ๐Ÿง  LLM
    participant Val as ๐Ÿ” Validator

    App->>LLM: prompt + schema
    LLM-->>App: candidate JSON
    App->>Val: validate(candidate, schema)
    alt valid
        Val-->>App: typed object
        App-->>App: proceed
    else invalid
        Val-->>App: errors (wrong type, missing field)
        loop until valid or max retries
            App->>LLM: reask with errors + prior output
            LLM-->>App: corrected JSON
            App->>Val: validate again
        end
        Note over App,Val: give up gracefully after max retries
    end
        
Diagram 3 - Schema to prompt to generate to a typed object for a downstream service
sequenceDiagram
    autonumber
    participant Dev as ๐Ÿ‘ฉโ€๐Ÿ’ป Schema author
    participant App as ๐Ÿ–ฅ๏ธ App
    participant LLM as ๐Ÿง  LLM
    participant Svc as ๐Ÿš€ Downstream service

    Dev->>App: define typed model (Pydantic / zod)
    App->>App: derive JSON Schema from model
    App->>LLM: prompt + schema (constrained)
    LLM-->>App: schema-valid JSON
    App->>App: parse into typed object
    App->>Svc: call API with typed payload
    Svc-->>App: 200 OK, record created
    App-->>Dev: structured result logged
        
05 - Step by Step

The 0 โ†’ 100 flow

From an unstructured request to a validated, typed object flowing into your systems - the whole journey in order.

00
Model

Define the target shape

Write the desired output as a typed model or JSON Schema: fields, types, enums, required keys, and constraints like ranges and patterns.

10
Derive

Generate the schema

Turn the typed model (Pydantic, zod) into a JSON Schema the provider understands. This is the contract both sides agree on.

20
Prompt

Assemble the request

Combine your instructions with the schema. Describe each field's meaning so the model fills the right value, not just the right type.

30
Constrain

Attach the output format

Pass the schema as the response format so the provider switches on schema-constrained decoding rather than free-form text.

40
Compile

Build the token mask

The decoder compiles the schema into a state machine that knows, at every position, which tokens keep the JSON valid.

50
Decode

Generate under constraint

At each step the model's logits are masked to the allowed set, so it can only emit tokens that continue a valid document.

60
Stream

Emit partial structure

Optionally stream tokens as they arrive, parsing the growing JSON so the UI can render fields the moment they complete.

70
Parse

Turn text into an object

Once complete, parse the JSON and load it into the typed model, coercing values where the schema allows it.

80
Validate

Check business rules

Run semantic validation the schema can't express - cross-field logic, referential checks, sane ranges - beyond mere type correctness.

90
Reask

Retry on failure

If validation fails, send the errors and prior output back for correction, looping up to a retry cap before falling back gracefully.

100
Deliver

Hand off the typed object

Pass the validated object to the API, database, or UI. Logs of failures and reasks feed evals to tighten the schema over time.

Common Pitfalls

Pitfalls & anti-patterns

Valid JSON is not the same as correct data. These are the failure modes that bite in production.

โœ‚๏ธ Truncation

Hitting the token limit mid-object yields JSON that starts valid and ends abruptly. Set a generous max, detect unterminated output, and reask rather than ship half a record.

๐Ÿ”ข Wrong types

Without constraint the model returns "42" for a number or "true" for a boolean. Typed validation with coercion catches and fixes these before they poison downstream math.

๐Ÿ‘ป Hallucinated fields

The model invents keys you never defined or fills required fields with confident guesses. Forbid extra properties and treat "unknown" as a first-class allowed value.

๐Ÿ“ฆ Over-nested schemas

Deeply nested, sprawling schemas raise error rates and latency. Flatten where you can and split huge extractions into smaller, focused calls.

๐ŸŽญ Valid but wrong

A schema guarantees shape, not truth. A perfectly typed invoice can still carry the wrong total. Pair structural checks with semantic evals and spot audits.

๐Ÿ” Infinite reask loops

Retrying forever on an impossible constraint burns tokens and stalls requests. Always cap retries and define a clean fallback for genuine failures.

How to Measure

How to measure structured outputs

Track structural validity and semantic correctness separately, so you know whether to fix the schema or the prompt.

MetricWhat it tells youGood sign
Schema validity rateFraction of outputs that parse and match the schema first tryHigh: constraints are working
Field-level accuracyAre extracted values actually correct against ground truth?High: right data, not just right shape
Reask rateHow often a retry is needed to reach a valid outputLow: fewer wasted round-trips
Truncation rateShare of responses cut off before completionLow: token budget is sufficient
Hallucinated-field rateHow often invented or wrongly-filled fields appearLow: model stays inside the contract
Latency & cost / callExtra time and tokens from constraints and reasksWithin your SLA and budget
Rule of thumb: if validity is high but field accuracy is low, fix the prompt and field descriptions; if validity itself is low, tighten the schema or turn on stronger constrained decoding first. Measure both before blaming the model.
06 - Case Studies

Real-world case studies

Three representative patterns showing structured outputs in production-style use.

๐Ÿ“„

1 ยท Extracting fields from documents

Pattern: unstructured text to typed records

A finance team needs vendor, amount, currency, and due date pulled from thousands of PDF invoices into their ledger.

  • A Pydantic model defines every field with types, patterns, and ranges as the extraction contract.
  • Schema-constrained decoding guarantees each response parses cleanly into that model.
  • Cross-field validation (totals equal line-item sums) triggers a reask when the numbers don't add up.
โœ… Outcome: Invoices flow straight into the ledger as validated objects, with only genuine ambiguities routed to a human instead of every document.
๐Ÿ”ง

2 ยท Generating tool and API call arguments

Pattern: structured output as function calling

An assistant must translate natural-language requests into precise calls against internal APIs - search, booking, refunds.

  • Each tool's parameter schema constrains the arguments the model is allowed to emit.
  • Enums and required fields ensure the model picks real operations with complete inputs.
  • Invalid argument sets are rejected and reasked before any API is ever touched.
โœ… Outcome: The assistant calls real endpoints with well-formed arguments, so malformed requests never reach production systems and error handling stays simple.
๐Ÿ—‚๏ธ

3 ยท Form and data-entry automation

Pattern: conversation to completed form

An onboarding flow collects applicant details from free-form chat and must populate a strict backend form.

  • A zod schema mirrors the form's exact fields, formats, and required boxes.
  • Partial structured output streams into the UI, filling boxes live as the model resolves them.
  • Missing or malformed entries prompt targeted follow-up questions rather than a silent failure.
โœ… Outcome: Conversations become clean, validated form submissions, cutting manual re-keying while keeping the backend's strict contract intact.
07 - Future

Where structured outputs are heading

Structured outputs are moving from a bolt-on parsing trick into a first-class, guaranteed capability of the model runtime.

โ›“๏ธ Native constrained decoding

Grammar and schema enforcement built into the serving stack, so valid output is the default rather than an SDK wrapper around retries.

๐ŸŒŠ Streaming typed objects

Standardized partial parsing that yields typed, incrementally-complete objects, so UIs render fields the instant they resolve.

๐Ÿงฉ Richer schema support

Fuller JSON Schema coverage - recursion, unions, conditionals, formats - closing the gap between what you can express and what decoding can enforce.

๐Ÿ”— Structured plus tools plus RAG

Typed outputs as the connective tissue of agents: retrieval fills fields, tools consume them, and each hop stays machine-checkable.

๐Ÿงช Semantic validation layers

Model-graded and rule-based checks that verify meaning, not just shape, catching the "valid but wrong" outputs schemas miss.

โšก Lower-overhead enforcement

Faster constraint compilation and token masking, shrinking the latency cost so structured output is free to turn on everywhere.

Bottom line: structured outputs are how you make a probabilistic model behave like a dependable software component - typed, validated, and safe to wire into real systems. As enforcement moves into the runtime, "just give me valid data" stops being a hope and becomes a guarantee.