Prompting and context
Schema design
How to write JSON schemas the model fills in correctly, using flat shapes, enums, explicit nulls and field descriptions as instructions.
A schema is part of the prompt. Field names and description strings are read by the model and steer its output. A well-designed schema makes the right answer easy to produce. A poorly designed one makes wrong answers easy.
Concept
Before you start: a nullable field is one that may hold a value or null. A required field must appear in every object. An enum is a fixed list of allowed values.
The big idea
Design a schema the way you would design an API. Keep it shallow. Make closed sets into enums. Say which fields are required. Use descriptions to answer the questions a careful human would ask.
The analogy is a paper form. A form with clear boxes gets filled in correctly. A form with one big box labeled "notes" gets a paragraph back. Schemas with free-text fields get paragraphs back too.
Walking through it
Here is a simplified invoice schema, with each design choice marked:
{
"type": "object",
"additionalProperties": false,
"required": ["vendor", "invoice_id", "currency", "total_cents", "status"],
"properties": {
"status": {
"type": "string",
"enum": ["ok", "incomplete"],
"description": "incomplete if any required commercial field is missing from the email"
},
"vendor": {"type": "string"},
"invoice_id": {
"type": ["string", "null"],
"description": "Exact invoice id from the text, e.g. INV-4412. Null if not present. Never invent."
},
"currency": {"type": "string", "enum": ["USD", "EUR", "GBP", "UNKNOWN"]},
"total_cents": {"type": ["integer", "null"]}
}
}Each choice does something:
- Enums for
statusandcurrencyclose the set. The model cannot write "dollars." - Nullable
invoice_idgives an honest way to say "not present." - Descriptions act as micro-prompts. "Never invent" sits next to the field it applies to, where the model reads it.
- Integer cents for
total_centskeep money exact. A float such as1204.5invites rounding bugs.
Rules of thumb
- Prefer flat shapes. Deep nesting raises error rates and slows constrained decoding.
- Use one convention for "unknown." Choose
nullor an omitted key. Do not mix missing keys, empty strings and"N/A". - Put the reasoning field before the answer field when the provider keeps key order. A short rationale before the enum reduces flip-flopping. Keep it short, because it costs output tokens.
- Cap list lengths with
maxItems, so the output cannot grow without limit.
Common mistakes
- Large free-text fields inside a structured object. They bring back the prose problems the schema was meant to remove.
- Over-precise number formats.
multipleOf: 0.01fights how decimals are tokenized. Validate money in code, using integer cents. - Assuming all JSON Schema features work everywhere. Arbitrary
oneOftrees and someadditionalPropertiessettings are often unsupported. Check your provider's subset.
Check yourself
- Why is
"enum": ["ok", "incomplete"]better than a free string? The model can only pick one of the two values, so the output can be checked without a parser for prose. - Why store
total_centsas an integer? Integer cents are exact. A float can turn$1,204.50into a rounding error.
Key takeaways
- A schema is a contract and a prompt. Field names and descriptions both steer the output.
- Use enums for closed sets, nullable fields for unknowns and integer cents for money.
- Keep one convention for unknown values.
- Keep the shape shallow, and cap list sizes.
Further learning
Next, see what to do when an object passes the schema but fails a business rule, in validation and repair.
Related
Structured outputs
Getting reliable JSON: schemas, constrained decoding, validation and repair.
Constrained decoding
How a schema can be enforced during generation by masking tokens that would break it, and what that guarantee does not cover.
Prompting
Clear instructions, examples, structure and chain-of-thought: prompting as specification writing.