You need machine-readable JSON, every single time — but a language model only predicts likely next-tokens, and 'likely' isn't 'valid'. Constrained decoding closes the gap: at every step it forbids any token that would break the structure, so the output is valid by construction.
In Sampling strategies you saw the model score every token and pick one it likes. Nothing in that loop knows what valid JSON is — so a fluent, confident model still hands you text a parser rejects.
JSON.parse throw on the entire response.The fix doesn't change the model — it edits the model's choices. At every decode step a validator lists which tokens keep the JSON well-formed, and masking sets the probability of every other token to zero.
p = 0. Every invalid token's logit is set to −∞, so after softmax its probability is exactly zero — it can never be sampled.Masking needs a source of truth: which tokens are valid right now? That source is a grammar — a compact set of rules describing every legal string. The engine tracks where you are in the grammar and reads off the allowed set.
{, then the key "age", then a number, then } — nothing else."age": the grammar permits only number tokens — a digit or a minus sign. A quote is off the table.Masking controls the form of the output. Next, reasoning tokens: hidden scratch-work the model generates before its answer — where the goal is the opposite, to let it wander freely so the final answer comes out better.