How it works
The explanation, with examples you can run.
Layer 1: describe the shape
JSON Schema is the common language. Name every field, give it a type, list allowed values with enum, mark what is required, and forbid extra properties. Field descriptions are part of the prompt: the model reads them, so say what each field means and what to put there when the information is absent (often null rather than a made-up value).
{ "type": "object", "properties": { "category": { "type": "string", "enum": ["billing", "bug", "feature", "other"] }, "priority": { "type": "integer", "minimum": 1, "maximum": 4 }, "order_id": { "type": ["string", "null"], "description": "As written in the ticket, or null if none is given" } }, "required": ["category", "priority", "order_id"], "additionalProperties": false}Layer 2: let the provider enforce it
Several APIs now accept a schema and use constrained decoding: at each step, tokens that would break the schema are not allowed, so the output always parses and fits. Older "JSON mode" options only promise valid JSON, not your fields. Tool or function calling is another route: the arguments of a tool call follow the tool's schema (see the next topic).
Check the provider's list of supported schema keywords; some features (certain formats, recursive schemas, very large enums) may be unsupported or limited. Also handle the cases that are not schema output at all: a refusal, or an answer cut off by the token limit.
Layer 3: validate and retry
Schema-valid is not the same as correct. Validate with a real validator (Zod, Pydantic, Ajv or similar), then apply business rules: totals add up, dates are plausible, ids exist in your database. When something fails and you have retries left, send the error back so the model can fix that specific problem.
The example uses a fake model that gets it wrong twice before producing a valid answer, so you can watch the loop.
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
A few practical rules:
- Keep the retry budget small (one or two). If an input fails repeatedly, the prompt or schema needs fixing, and the failure belongs in your evaluation set.
- Do not regex JSON out of prose as a long-term solution. It hides problems that schema enforcement or a better prompt would remove.
- Version your schemas with your prompts. Adding a required field is a breaking change for every stored example and test.
- Keep schemas small and flat where you can. Deeply nested, many-field schemas are harder for models to fill correctly; split one big extraction into a few focused calls if accuracy suffers.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.