Recasts constrained generation as transitions of a finite-state machine or parser, masks tokens that would leave the allowed language, and precomputes an index from states to allowed tokens so masking is cheap (arXiv preprint).
Structured Decoding: Token Masks From Schema Automata
How a schema automaton or parser state turns next-token logits into constraint-valid generation by masking invalid continuations, while leaving truth and task success outside the formal guarantee.
Intuition
The idea in plain words, before the symbols.
Decoding and sampling turn next-token logits into text. Structured decoding adds one more piece to the loop: the state of a small parser or automaton that tracks where the output is in a schema or grammar (Willard and Louf, 2023).
At each step, the model still proposes ordinary logits over tokens. The schema state asks a stricter question: which next tokens can still lead to a valid completion? Invalid tokens are masked to probability zero. The remaining tokens are renormalized, then greedy decoding, sampling, beam search, or tree search can choose among them.
The guarantee is narrow but useful. If the automaton is correct, every emitted token follows its mask, and decoding stops in an accepting state, then the output belongs to the formal language. That does not mean the source is true, the tool should be called, the answer is safe, or the selected enum is semantically right.
This page studies that finite mechanism, not product JSON modes, tool calling, retries, validators run after generation, or full JSON Schema.
The demo uses toy tokens: real tokenizers can split or merge pieces like "tool" or {, so production masks are built over actual token IDs. The schema here fixes the order of the fields and lists every allowed value; real JSON Schema can admit many equivalent serializations. The naive and cached check counts in the demo show how much mask-checking work is repeated at each decoding step, not a latency model.
Further reading: Scholak et al., "PICARD" (EMNLP 2021), which rejects invalid tokens with an incremental parser; Beurer-Kellner et al., "LMQL" (PLDI 2023); Beurer-Kellner, Fischer and Vechev, "DOMINO" (ICML 2024), on aligning constraints with subword tokens; Dong et al., "XGrammar" (MLSys 2025), on fast grammar masks; and the documentation for the JSON Schema object keyword, Outlines, OpenAI's Structured Outputs and vLLM structured outputs.
Study prompts for this section: Intuition
Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.
Mathematics
Definitions, assumptions and the derivation.
Let be a fixed finite token vocabulary, including an end token .
Represent the schema as a deterministic finite automaton
For a prefix , the schema state is when every transition is defined.
The allowed-token mask should include only tokens that can still reach an accepting state. Given model logits and temperature , constrained decoding uses
The "can reach " condition matters: a token whose transition leads into a dead end, a state from which no accepting state can be reached, must not be allowed even though the transition itself is defined.
Then a decoder selects or samples from and updates
Stopping is accepted only when , usually after an explicit token.
If top- is used, the hard schema mask should be applied first, then top- truncates and renormalizes inside the valid set. Applying top- first can throw away every valid token when the raw model strongly prefers invalid ones.
The guarantee is modest:
If every emitted token is sampled from and the run stops in , then the token sequence is in . Nothing follows about whether the content is true, useful, task-correct, or semantically correct; safety is likewise outside a formal syntax guarantee.
Real tokenizers: characters first, then tokens
The automaton above reads whole tokens, which suits the toy vocabulary. A real schema is written over characters, and one real token can stand for several of them, such as {" or "tool. Then write the automaton over a character alphabet , with transition and its extension to strings, and let be the string that token stands for. A token moves the automaton by its whole string:
defined only when every character is accepted in turn. The mask is then built from , with the same can-reach- condition. A token that stops partway through a keyword is allowed when the keyword can still be completed. This is why practical systems compute masks over the tokenizer's real vocabulary, often by precomputing, for each automaton state, which tokens' strings it can consume (Willard and Louf, 2023).
Worked example: mask, then renormalize
At the state after {"tool":, suppose the model's raw probabilities over five tokens are for "retrieve" and for "lookup", the two allowed tokens, and for "DROP", for "extra" and for true, which the mask removes. The surviving mass is , so
Greedy decoding picks "retrieve", although "DROP" had the highest raw probability; sampling still writes "lookup" one time in three. Renormalizing keeps the ratio between the allowed tokens: the mask removes probability, it does not reorder what is left. Only when , with no allowed token given any probability, is the constrained distribution undefined; any positive , however small, defines it.
The order of the mask and top- matters. Apply top- with to the raw distribution first, and the nucleus is "DROP" alone, since its already reaches ; the mask then leaves nothing to sample. Apply it after the mask, and top- keeps "retrieve", whose reaches .
A valid output is not a correct one. At the state after "source":, let the raw probabilities be for "docs" and for "tickets", with the other on masked tokens. Both sources are allowed, so the constrained probabilities are and and greedy decoding writes "tickets". If the task needs the documentation, the output follows the schema and is still wrong.
Study prompts for this section: Mathematics
Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.
Code
The same calculation as runnable code, in the notation of the derivation.
This code implements the same automaton as the demo: a token-level automaton for a tiny retrieval-call schema with fields in a fixed order. The unconstrained decoder can choose an invalid token immediately. The constrained decoder produces schema-valid output. A semantic-mismatch profile stays schema-valid while choosing the wrong source for the task.
from collections import defaultdict
from math import exp
V = [
"{", "}", ":", ",",
'"tool"', '"source"', '"k"',
'"retrieve"', '"lookup"', '"docs"', '"tickets"',
"1", "2",
'"DROP"', '"extra"', "true", "<eos>",
]
D = {
0: {"{": 1},
1: {'"tool"': 2},
2: {":": 3},
3: {'"retrieve"': 4, '"lookup"': 4},
4: {",": 5},
5: {'"source"': 6},
6: {":": 7},
7: {'"docs"': 8, '"tickets"': 8},
8: {",": 9},
9: {'"k"': 10},
10: {":": 11},
11: {"1": 12, "2": 12},
12: {"}": 13},
13: {"<eos>": 14},
}
ACCEPT = {14}
VALID_PROFILES = {"schema-friendly", "format-confused", "semantic-mismatch"}
def good_states():
rev = defaultdict(set)
for q, arcs in D.items():
for tok, r in arcs.items():
rev[r].add(q)
good = set(ACCEPT)
stack = list(ACCEPT)
while stack:
r = stack.pop()
for q in rev[r]:
if q not in good:
good.add(q)
stack.append(q)
return good
GOOD = good_states()
def allowed(q):
return [tok for tok in V if D.get(q, {}).get(tok) in GOOD]
def step(q, tok):
return D.get(q, {}).get(tok)
def base_logits(q, profile):
if profile not in VALID_PROFILES:
raise ValueError(f"unknown profile: {profile}")
z = {tok: -2.0 for tok in V}
for tok in allowed(q):
z[tok] = 1.0
preferred = {3: '"retrieve"', 7: '"docs"', 11: "2"}
if q in preferred:
z[preferred[q]] = 2.0
if profile == "format-confused":
z['"DROP"'] = 5.0
z['"extra"'] = 4.0
if profile == "semantic-mismatch" and q == 7:
z['"tickets"'] = 5.0
z['"docs"'] = 1.0
return z
def softmax(z, toks):
m = max(z[t] for t in toks)
weights = {t: exp(z[t] - m) for t in toks}
total = sum(weights.values())
return {t: weights.get(t, 0.0) / total for t in V}
def masked_probs(z, q):
return softmax(z, allowed(q))
def rejection_mass(z, q):
raw = softmax(z, V)
return 1.0 - sum(raw[t] for t in allowed(q))
def decode(profile="format-confused", constrained=True, max_steps=32):
q = 0
toks = []
states = [q]
for _ in range(max_steps):
z = base_logits(q, profile)
p = masked_probs(z, q) if constrained else softmax(z, V)
tok = max(V, key=lambda t: (p[t], -V.index(t)))
toks.append(tok)
q_next = step(q, tok)
if q_next is None:
return {"tokens": toks, "states": states, "accepted": False}
q = q_next
states.append(q)
if q in ACCEPT:
return {"tokens": toks, "states": states, "accepted": True}
return {"tokens": toks, "states": states, "accepted": False}
def parse_source(tokens):
i = tokens.index('"source"')
return tokens[i + 2].strip('"')
assert allowed(0) == ["{"]
assert set(allowed(3)) == {'"retrieve"', '"lookup"'}
assert allowed(13) == ["<eos>"]
bad = decode("format-confused", constrained=False)
assert bad["accepted"] is False
assert bad["tokens"][0] == '"DROP"'
good = decode("format-confused", constrained=True)
assert good["accepted"] is True
assert good["tokens"] == [
"{", '"tool"', ":", '"retrieve"', ",",
'"source"', ":", '"docs"', ",",
'"k"', ":", "2", "}", "<eos>",
]
z = base_logits(3, "format-confused")
p = masked_probs(z, 3)
assert p['"DROP"'] == 0.0
assert rejection_mass(z, 3) > 0.90
sem = decode("semantic-mismatch", constrained=True)
assert sem["accepted"] is True
assert parse_source(sem["tokens"]) == "tickets"
Mask, renormalize, then top-p
This standard-library Python code does the worked example above: the masked and renormalized distribution, both orders of the mask and top-, and the second state.
def mask_and_renormalize(raw, mask):
kept = [p * m for p, m in zip(raw, mask)]
surviving = sum(kept)
if surviving == 0: # any positive mass is renormalized
raise ValueError("no allowed token has positive probability")
return [p / surviving for p in kept], surviving
def top_p(dist, threshold):
order = sorted(range(len(dist)), key=lambda i: (-dist[i], i))
keep, mass = set(), 0.0
for i in order:
keep.add(i)
mass += dist[i]
if mass >= threshold: # stop once the kept mass reaches p
break
return [x / mass if i in keep else 0.0 for i, x in enumerate(dist)]
TOKENS = ['"retrieve"', '"lookup"', '"DROP"', '"extra"', "true"]
RAW = [0.20, 0.10, 0.50, 0.15, 0.05]
MASK = [1, 1, 0, 0, 0]
constrained, surviving = mask_and_renormalize(RAW, MASK)
print("surviving mass:", round(surviving, 12), "constrained:", [round(x, 6) for x in constrained])
raw_first = top_p(RAW, 0.4) # keeps "DROP" alone
print("top-p first, then mask:", sum(x * m for x, m in zip(raw_first, MASK)))
print("mask first, then top-p:", top_p(constrained, 0.4))
source, _ = mask_and_renormalize([0.10, 0.60, 0.15, 0.10, 0.05], MASK)
print('"docs", "tickets":', [round(x, 6) for x in source[:2]])
assert mask_and_renormalize([1e-10, 1 - 1e-10], [1, 0])[0] == [1.0, 0.0]
Study prompts for this section: Code
Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.
Demo
Change an input, predict the result, and see which quantities respond.
Explore Structured Decoding: Token Masks From Schema Automata
Observation guide
Optional and ungraded. Opening a guide saves nothing.
Choose what to inspect in Structured Decoding: Token Masks From Schema Automata, then open its guide.
Use the Schema Mask Explorer to test a prediction. At the current automaton state, inspect the raw logits and predict whether the raw highest-logit token survives the schema mask, gets replaced by the highest valid token, or has no valid continuation.
After reveal, compare raw probability mass with the masked-and-renormalized distribution. Then turn the mask off to see a high-logit invalid token break the prefix, or use the semantic-mismatch profile to see the key limitation: schema-valid output can still choose the wrong source for the task.
The practice after the demo works one step of this by hand: the surviving mass, the renormalized distribution, the order of the mask and top-, and a valid but wrong choice.
Study prompts for this section: Demo
Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.
Practice
Renormalize the probability the schema allows, see why top-p must come after the mask, then meet a valid token that is the wrong one.
Concept: Structured Decoding: Token Masks From Schema Automata
What is the smallest example of Structured Decoding you can work through by hand, and what does it show?
DetailsLLM Systems
Reference id
concept:llm-systems/structured-decodingAfter the first pass
Go deeper
Optional material for a second reading. Sources are always shown; the other panels open from their headings or from these links, and closing one keeps what you wrote or revealed.What to look for in each sectionChoose a section and what you expect to see in it, then read short advice.
Section by section
Choose what to look for in each section
How a schema automaton or parser state turns next-token logits into constraint-valid generation by masking invalid continuations, while leaving truth and task success outside the formal guarantee.

Start with the picture, metaphor, or geometric mechanism.
Choose what you expect to see in this section of Structured Decoding: Token Masks From Schema Automata.
Questions to ask of the pictureChoose a part of the picture and what you expect, then read short advice.
Looking at the picture
Questions to ask of the picture
How a schema automaton or parser state turns next-token logits into constraint-valid generation by masking invalid continuations, while leaving truth and task success outside the formal guarantee.
Which part of the picture should you look at first?
Pick the part of the picture you expect to explain Structured Decoding: Token Masks From Schema Automata.
Sources
References for this page
A reference shows where an idea comes from; it does not vouch for every step on this page.Benchmarks constrained-decoding frameworks on about 10,000 real-world JSON schemas, measuring schema compliance separately from coverage of schema features, efficiency and the quality of the generated output (arXiv preprint).
What each claim rests on1 claim, each with its sources, where it appears on this page, and its caveats.
Claims and sources
How a schema automaton or parser state turns next-token logits into constraint-valid generation by masking invalid continuations, while leaving truth and task success outside the formal guarantee.
The checks were made by this site, not by an independent reviewer.
Willard and Louf support FSM/parser state plus token-logit masks that zero invalid vocabulary continuations. Geng et al. support constrained decoding as invalid-token masking from constraints and prefix toke...
This covers the formal constraint, not whether the output is true or useful. Real JSON Schema guarantees also depend on tokenization, supported schema features and implementation. The advice to apply top-p a...
Willard and Louf support the mask mechanism: guided generation uses the state of a finite-state machine or parser to find the valid next tokens and gives invalid tokens zero probability. Geng et al. support treating schema compliance separately from coverage, efficiency and output quality. The page's math, code and demo work at the level of a few toy tokens; validity there is formal, not a guarantee of truth or task success.
Explain it without the pageWrite your own explanation before asking for feedback. Your draft and any hints stay while this page is open.
Practice · Structured Decoding: Token Masks From Schema Automata
Try the idea in your own words
How a schema automaton or parser state turns next-token logits into constraint-valid generation by masking invalid continuations, while leaving truth and task success outside the formal guarantee.
Concept · Selected for practice
Structured Decoding: Token Masks From Schema Automata
What it rests on: Sources: Efficient Guided Generation for Large Language Models; JSONSchemaBench: A Rigorous Benchmark of Structured Outputs for Language Models
Context and links
LLM Systems
Explain the mechanism
For Structured Decoding: Token Masks From Schema Automata: What is the smallest example of Structured Decoding you can work through by hand, and what does it show? Explain your answer, including what changes, why, and which assumption matters.
A rough first thought is enough. Your draft stays when you change tasks.
Your draft stays on this page and is cleared when you leave or reload.
A little help · Explain
Open one hint at a time. These are suggestions, not your answer or a grade.
0 of 3 hints shown for this question.
Clearing your answer keeps this help record. Outside help is your own report; this page cannot check it.
Where am I stuck? (optional)
Take your draft to a feedback conversation
No AI feedback runs here. You can copy a prompt to use elsewhere; nothing is sent automatically. Review the text before sharing, and leave out private information.
Write an attempt before copying a feedback prompt.
A draft, or an AI reply to it, is not a test of what you have learned. To check that, try a different case later without help.
Ask about this pagePick one item on this page and copy a prompt about it into an AI assistant you already use. Notes you write here stay in this browser.
assert allowed(0) == ["{"]Your question
Choose what your question is about
Pick the idea, equation, source, code, claim, misconception or demo state you want to ask about. The prompt you copy includes it, so the answer can stay on that item.Open the draft below to save one note and next action in this browser.
Structured Decoding: Token Masks From Schema Automata
What is the smallest example of Structured Decoding you can work through by hand, and what does it show?
Fixed prompts generated from this item. They are not comments from people or independent reviews.
The 2 cited sources must support this exact item, not just the surrounding topic.
Connect the definition to one equation, piece of code or demo before widening the discussion.
Change one input, then check that the same relationship holds in the mathematics, the code and the demo.
You can state the mechanism in your own words
Local action draftNo local draft saved yetOpen when you are ready to write one next action
This draft stays in this browser and is attached to this item only.
- What the 2 cited sources say about this exact item
- The definition, its prerequisites, and a contrasting concept
- The equation or code that makes the concept concrete
- One demo state that shows what stays the same as the inputs change
- You can state the mechanism in your own words
- You can name the prerequisite that would clear up the confusion
- You can predict how the result changes when one input changes
Reference id
concept/concept-notebook/llm-systems/structured-decoding
concept:llm-systems/structured-decoding