Context Engineering & Prompting
IC4IC5

Structured Outputs and Reasoning

Two knobs on the decoder — a grammar that constrains the *shape* of every token, and a thinking budget that buys *quality* with latency — and the engineering judgment to know which one the problem actually needs.

15 min read · 12 sections
0

1. Quick anchor

There are two independent knobs sitting on the autoregressive decoder, and most engineers conflate them. The first is shape: at every generation step, a constrained decoder masks out tokens that would violate a grammar (JSON schema, regex, context-free grammar), so the output is syntactically guaranteed to parse. The second is depth: a reasoning model spends extra tokens thinking before it answers, trading latency and cost for accuracy on problems that genuinely need search. Shape is a decode-time filter — it changes which tokens are legal, not how smart the model is. Depth is a compute allocation — it changes how much the model deliberates, not whether the output parses. The senior skill is knowing that constraining shape can hurt quality if the schema fights the model's natural reasoning order, and that buying depth is pure waste on problems the model already one-shots.

2. Why interviewers probe this

  • IC4 — mechanism literacy. Can you explain how constrained decoding works (logit masking, FSM/pushdown automaton) rather than just "I used the response_format param"? Can you name the cost of a thinking budget and read a quality-vs-tokens curve? Do you know that "100% valid JSON" and "100% correct JSON" are different claims?
  • IC5 — judgment under tradeoffs. Given a latency SLA and an accuracy target, can you decide between a reasoning model, a constrained decoder, both, or neither — and defend the call with numbers? Do you understand that structure and reasoning interact (forcing JSON-first can suppress chain-of-thought)? Can you design an eval that catches schema-valid-but-wrong outputs?
  • IC6 — systems thinking. How do these interact with prompt caching, agent loops, and cost at fleet scale? When does a process reward model or best-of-N beat a single long thinking budget? How do you keep a reasoning model from "thinking" itself into a worse answer?

3. Concept build-up

Beginner explainerNew here? The words first

The words first.

  • Logit — the raw score the model assigns each possible next token before it's turned into a probability.
  • Constrained decoding — forcing the output to follow a fixed shape by banning illegal next tokens at every step.
  • JSON schema — a machine-readable contract describing the exact fields, types, and structure the output must have.
  • Grammar / FSM — a rulebook (finite-state machine or pushdown automaton) that says which characters can legally come next.
  • Chain-of-thought (CoT) — the model writing out intermediate reasoning steps before its final answer.
  • Reasoning model — a model (o1, o3, DeepSeek-R1, Claude with extended thinking) trained to produce long internal reasoning before answering.
  • Test-time compute — spending more tokens at inference time to get a better answer, instead of training a bigger model.
  • Thinking budget — a cap on how many tokens the model may spend reasoning before it must answer.

Step by step.

  1. The model produces a logit for every token in its vocabulary at each step.
  2. A constrained decoder checks the grammar and sets illegal tokens' logits to negative infinity.
  3. Softmax turns the surviving logits into probabilities; the banned ones are now exactly zero.
  4. The model samples a legal token; the grammar state advances; repeat. The output must parse.
  5. Separately, a reasoning model can be told to spend up to N thinking tokens first.
  6. It deliberates, then emits the final answer; you pay for every thinking token whether you see it or not.
  7. More thinking helps hard problems and wastes money on easy ones — that's the budget decision.

Remember this: constrained decoding controls the shape of the answer; a thinking budget controls the depth — they are different knobs and you tune them independently.

3.1 Constrained decoding: masking the impossible

An autoregressive LLM generates one token at a time. At step t it produces a logit vector z over the whole vocabulary (≈100K+ tokens), and softmax(z) gives the probability of each next token. Constrained decoding intervenes between the logits and the softmax: a grammar engine tracks the current parse state and computes, for that state, the set of tokens that would keep the output valid. Every token not in that set has its logit set to -, so after softmax its probability is exactly 0. The model can only sample something legal.

The grammar engine is the interesting part. For a JSON schema, you compile the schema into a state machine. Simple shapes (a flat object with known keys) are a finite-state machine (FSM) — Outlines builds a regex-derived FSM. Nested/recursive structures (arbitrarily deep arrays, balanced braces) need a pushdown automaton because they require a stack to track nesting depth — this is what XGrammar does for full context-free grammars, and it's why XGrammar can handle grammars that a pure regex FSM cannot. At each step the engine maps "current state" → "allowed token set," which is precomputed and cached so the per-token overhead is small (a mask lookup, not a re-parse).

The guarantee is syntactic validity, not semantic correctness. Constrained decoding promises the output parses against the schema. It says nothing about whether the values are right. You can get a perfectly valid {"sentiment": "positive"} on a negative review — 100% schema-valid, 100% wrong. Internalize that gap; it drives the whole quality discussion in §5.

There's a subtler cost: naive masking distorts the probability distribution. When you zero out tokens and renormalize, you've changed the conditional distribution the model was trained to produce — you're sampling from a truncated, reweighted distribution that may not reflect the model's true belief. Most of the time this is harmless, but in fact-dense or ambiguous fields it can push the model toward a token it didn't actually "want." This is what methods like (G)I-DLE address: they minimize KL divergence between the constrained and unconstrained distributions, excluding banned tokens while perturbing the surviving probabilities as little as possible.

Logit masking — on real numbers

Symbols: z is the logit (raw score) per candidate token; p is the probability after softmax. The grammar's job is to decide which tokens are legal right now.

Suppose we're decoding a JSON value for a field whose schema says it must be the string "yes" or "no". The model is mid-string and just emitted "y. Legal next tokens per the grammar: only es". Illegal: everything else.

Raw logits the model produced for four candidates:

  • es"z = 2.0
  • ep"z = 2.4 (model actually prefers this — "yep")
  • (space) → z = 1.1
  • 123z = 0.5

Unconstrained softmax would pick ep" (highest logit). But the grammar only allows es", so we set the other three logits to -:

  • es"z = 2.0p ≈ 1.00
  • everything else → z = -p = 0.00

Output: es", completing "yes". What it did to the data: it overrode the model's top choice (ep") to keep the output schema-valid — buying a guaranteed parse at the cost of the model's preferred (here, junk) token. That override is the whole tradeoff in miniature.

3.2 Tool-use as schema — the other way to get shape

Constrained decoding isn't the only path to structured output. Tool/function calling gives the model a tool whose input is a JSON schema, and the model fills it in. On frontier APIs (Anthropic, OpenAI) this is often the preferred way to get structured data, because the model was post-trained heavily on tool-shaped JSON — gpt-4o-2024-08-06 was trained for 100% reliability on complex JSON schemas, and Claude's tool-use path is similarly hardened. The practical difference: pure constrained decoding (vLLM + Outlines/XGrammar) is enforced by the runtime and works on any open model; tool-use schema conformance is trained-in and leans on the model's own competence, with the API layer adding validation. Direct prompting ("respond in JSON") sits at the bottom — roughly 70% reliable in the wild, fine for prototypes, unacceptable for a pipeline that parses the output downstream.

3.3 Chain-of-thought and the rise of reasoning models

CoT started as a prompting trick: ask the model to "think step by step" and accuracy on multi-step problems jumps, because the intermediate tokens give the model scratch space to externalize computation it can't do in a single forward pass. The 2024–2025 shift was to bake reasoning into the weights. o1 (OpenAI, Sept 2024) is RL-trained to produce long internal chains-of-thought before answering. DeepSeek-R1 (Jan 2025) matched o1-class performance at a fraction of the cost using GRPO (group relative policy optimization) and — crucially — released the recipe openly, showing that long-CoT reasoning emerges from RL on verifiable rewards without needing a separate value network. o3 (early 2025) added adaptive compute: the model decides how much inference budget to spend based on perceived difficulty.

The mental model: test-time compute is a third scaling axis alongside training data and model size. Instead of a bigger model, you let a fixed model spend "orders of magnitude more tokens" deliberating. The scaling curve is real but bends — quality rises with the thinking budget and then hits diminishing returns above roughly 10–32K thinking tokens for most tasks. Past that, you're paying linearly more for marginal (or negative) quality.

How do you make those extra tokens count? Three verification strategies: outcome reward models judge only the final answer; process reward models (PRMs) score each reasoning step, catching a wrong turn early; best-of-N samples N independent reasoning paths and picks the best (by a verifier or by majority vote). Best-of-N is "parallel" test-time compute; a long single chain is "sequential." They trade differently — N short chains parallelize across hardware; one long chain has lower total token count but can't be sped up by adding GPUs.

3.4 Thinking as an explicit, billable budget

Frontier APIs expose the budget directly. Anthropic's extended thinking emits thinking content blocks (with encrypted signatures for multi-turn continuity) and you set budget_tokens — e.g. 10K tokens of reasoning headroom, though the model may use less. You are billed for every thinking token regardless of whether you display it (summarized shows reasoning with minimal latency; omitted gives faster time-to-first-text but still bills the hidden thinking). On Opus 4.5+ thinking tokens don't consume the context window; on earlier models they did.

The 2026 evolution is adaptive thinking (Claude 4.6+): instead of a fixed budget you set an effort level — low, medium, high (default), xhigh, max — and the model decides how much to actually think, with effort as a soft preference. For Opus 4.7+ this type: "adaptive" form replaces the legacy fixed-budget type: "enabled". And interleaved thinking (Opus 4.8+) lets the model reason between tool calls — think, call pricing service, think about the result, query a database, think, then answer — with a total thinking budget that can exceed max_tokens because it's pooled across blocks. That's the bridge from "reasoning model" to "reasoning agent."

4. Minimal implementation

Two reference implementations: enforced constrained decoding on an open model, and an adaptive thinking budget on a frontier API.

# A. Enforced JSON via constrained decoding (vLLM + a JSON schema).
# The runtime MASKS illegal tokens — validity is guaranteed by construction.
from vllm import LLM, SamplingParams
from vllm.sampling_params import GuidedDecodingParams
 
schema = {
    "type": "object",
    "properties": {
        "sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
        "confidence": {"type": "number", "minimum": 0, "maximum": 1},
        "evidence": {"type": "string"},
    },
    "required": ["sentiment", "confidence", "evidence"],
    "additionalProperties": False,
}
 
llm = LLM(model="Qwen/Qwen3-8B")
# XGrammar backend -> pushdown automaton, handles nested schemas.
guided = GuidedDecodingParams(json=schema, backend="xgrammar")
params = SamplingParams(temperature=0.0, max_tokens=256, guided_decoding=guided)
 
review = "Shipped late and the API kept 500ing, but support was responsive."
out = llm.generate(f"Classify this review as JSON:\n{review}", params)
print(out[0].outputs[0].text)   # ALWAYS parses against `schema`

The grammar engine compiles schema into an automaton once, then per token it intersects "vocabulary" with "tokens legal in the current state" and applies the mask. temperature=0.0 plus the mask makes this fully deterministic. Note "evidence" is before the model is forced to commit — but it comes after sentiment in key order, which matters in §5.

# B. Adaptive thinking budget on a frontier model (Anthropic).
# Always read the live API skill/docs before shipping — params drift.
import anthropic
 
client = anthropic.Anthropic()
resp = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=2048,
    thinking={"type": "adaptive", "effort": "medium"},  # low|medium|high|xhigh|max
    messages=[{
        "role": "user",
        "content": "A train leaves A at 60mph, another leaves B (180mi away) "
                   "at 40mph toward it. When do they meet? Show reasoning, "
                   "then give the answer.",
    }],
)
for block in resp.content:
    if block.type == "thinking":
        print("[thinking tokens billed but here summarized]")
    elif block.type == "text":
        print(block.text)
print("usage:", resp.usage)  # input/output tokens — thinking is billed

effort: "medium" is a soft preference; the model spends more on a genuinely hard step and less on an easy one. You pay for usage thinking tokens either way, so the eval question is whether medium beats low on your task by enough to justify the cost — measure, don't assume. Before relying on exact parameter names, model IDs, or pricing, consult the live claude-api reference — these change.

5. Production tradeoffs

Technique Cost Latency Quality effect Primary failure mode
Direct "respond in JSON" prompt Baseline Baseline ~70% parse rate Silent malformed JSON breaks the parser downstream
Constrained decoding (Outlines/XGrammar) Negligible compute overhead +small per-token mask cost 100% valid, can lower accuracy Schema-valid-but-wrong; distribution distortion
Tool-use schema (frontier API) Standard token price Baseline High validity + trained correctness Model declines / hallucinates a field value
Extended thinking (fixed budget) + every thinking token + thinking time (can be seconds) +accuracy on hard reasoning Overthinking easy tasks; wasted budget
Adaptive thinking (effort levels) Variable, model-decided Variable Best cost/quality if calibrated Effort misjudged on out-of-distribution inputs
Best-of-N reasoning N× generation cost Parallelizable +accuracy, needs a verifier Verifier becomes the bottleneck/oracle

The structure-vs-quality trap is the one that gets shipped broken. Constraining shape can degrade accuracy through two mechanisms. First, distribution distortion (§3.1): masking + renormalize samples from a perturbed distribution. Second, and more important in practice, ordering: if your schema forces the model to emit the answer field first and the reasoning field last, you've effectively banned chain-of-thought — the model must commit to sentiment before it writes evidence, so the evidence is post-hoc rationalization of an answer it already locked in. The fix that keeps the guarantee: put a reasoning or evidence field first in key order, then the answer field. The grammar still enforces shape, but now the model reasons into the JSON before committing. This single reordering often recovers most of the accuracy gap versus free-form CoT.

Reasoning is not free quality. The diminishing-returns elbow (~10–32K thinking tokens) means a fixed budget_tokens=32000 on a classification task is mostly waste — you pay 32K tokens of latency for a task that needed 200. At fleet scale this dominates your bill. The discipline: default to no thinking (or low/adaptive), and promote to high effort only for task slices where an eval shows the lift. Reserve big budgets for verifiable, search-heavy problems (math, code, multi-hop planning) where the scaling curve actually pays.

What changes at scale. (1) Caching interacts with thinking — preserved thinking blocks let you hit the prompt cache on the next turn, but clearing thinking invalidates it; in long agent loops this is a real cost lever. (2) Constrained decoding + batching — grammar masks must be computed per-sequence, which complicates high-throughput batching; XGrammar's precomputed transition caches matter here. (3) Overthinking is a tail-latency problem — a model that occasionally burns its full budget blows your p99, so cap budgets even on reasoning slices. (4) Schema-valid-but-wrong is invisible to your monitoring unless your eval checks values, not just parses — a green "100% valid JSON" dashboard can sit on top of a 60%-correct pipeline.

6. How it's asked

[IC4] How does constrained decoding guarantee schema-valid JSON, and what does it cost you? At every decode step the runtime computes which next tokens keep the output valid given the current parse state (tracked by an FSM for flat schemas, a pushdown automaton for nested ones), and sets every other token's logit to -, so after softmax their probability is exactly zero — the model can only sample legal tokens, so the result always parses. The costs: a small per-token masking overhead, and — more importantly — it guarantees only syntactic validity, not correctness. It can even lower accuracy by distorting the sampling distribution and, if the schema forces the answer before any reasoning field, by suppressing chain-of-thought.
[IC5] A reasoning model gets a task right but burns 18K thinking tokens at 4x the latency of a non-reasoning model that gets it right 80% of the time. How do you decide which to ship? I'd make it a cost-of-error decision, not a vibe. Quantify the value of the extra 20% correctness against the marginal latency and token cost — for a high-stakes, low-volume task (legal extraction, irreversible agent action) the 20% is worth 4x latency; for a high-volume, low-stakes task (tagging) it's not. Then I'd check whether I even need the full 18K: run an effort/budget sweep, because the diminishing-returns elbow is often well below 18K, and a medium/adaptive setting may capture most of the lift at half the tokens. Finally I'd consider a router — cheap model first, escalate to the reasoner only on low-confidence cases — which usually beats committing the whole fleet to either model.
[IC5] Constrained decoding forces valid JSON but your extraction accuracy drops versus free-form prompting. What's the mechanism, and how do you fix it without giving up the schema guarantee? Two mechanisms. (1) Distribution distortion: masking and renormalizing samples from a truncated distribution that no longer matches the model's true conditional, which can push it off the right value. (2) Ordering suppression: if the schema emits the answer field before any reasoning field, the model commits before it can reason, so you've effectively banned CoT. The fix that keeps the guarantee is to reorder the schema so a reasoning/evidence field comes first and the answer field comes after — the grammar still enforces shape, but the model now reasons into the JSON before committing. If distortion still bites in fact-dense fields, a KL-preserving masking method like (G)I-DLE perturbs surviving probabilities less.
[IC6] When does best-of-N or a process reward model beat simply raising the single thinking budget? A long single chain is sequential compute — it can find deep solutions but compounds errors (one wrong step poisons everything after) and can't be sped up by adding hardware. Best-of-N is parallel — N independent chains, pick the best by a verifier; it's robust to any single chain going wrong and parallelizes across GPUs, so it wins when you have a cheap, reliable verifier (e.g., unit tests for code, a checker for math) and latency headroom to fan out. A process reward model wins when errors are localizable — it scores each step and prunes bad branches early, so you spend budget only on promising paths rather than letting a long chain wander. Raising a single budget is best when the task is genuinely sequential, verification is expensive, and the scaling curve hasn't flattened yet.

7. Pitfalls & flashcards

  • "Valid" ≠ "correct." A green dashboard of 100% schema-valid JSON tells you nothing about value accuracy. Your eval must assert on field values, not just that it parses.
  • Answer-first schemas kill reasoning. Ordering the schema so the verdict precedes the rationale silently disables CoT under constrained decoding. Put reasoning fields first.
  • Fixed thinking budgets waste money. A 32K budget on an easy task pays for latency you don't need. Default to low/adaptive; promote per-slice with eval evidence.
  • Overthinking is a tail-latency bug. Uncapped reasoning blows p99. Cap budgets even on reasoning slices.
  • Direct-prompt JSON in a pipeline is tech debt. ~70% reliability means ~3 in 10 calls break a downstream parser. Use tool-use schema or constrained decoding for anything machine-consumed.
  • Thinking is billed even when hidden. omitted/summarized display modes still charge every thinking token — display setting changes latency, not cost.
  • Clearing thinking blocks invalidates the prompt cache. In agent loops, preserve thinking for cache hits or pay to recompute the prefix.

Flashcard. Constrained decoding controls the shape of the output (logit masking → guaranteed parse, not guaranteed truth); a thinking budget controls the depth (test-time compute → better accuracy with diminishing returns past ~10–32K tokens). Tune them independently, and never let an answer-first schema silence the model's reasoning.

8. Further reading

Next: /context-engineering/memory-and-compaction — what to do when the answer plus its reasoning no longer fit in the window.

Primary sources
← More in Context Engineering & Prompting