Specifying LLM Output Shape with Schemas
Getting an LLM to return valid, predictable JSON is one of the most common pain points in AI application development. You describe the structure you want in plain English, and the model returns something close but not quite right. A field is named differently, a nested object is flattened, or an array comes back as a single item.
The fix is straightforward: show the model the exact shape you expect, using a schema instead of a prose description.
The problem with prose descriptions
Consider this system prompt:
Return a JSON object with the user's name, email, a list of
their recent orders (each with an ID, total, and status),
and their shipping address with street, city, and zip code.
This leaves too much to interpretation. The model might call the field orders or recent_orders or order_list. It might nest the address or flatten it. Each response is structurally different, which breaks your parser.
A schema removes ambiguity
Instead of describing the shape in words, include a compact schema directly in your prompt:
Respond with JSON matching this exact structure:
{
name: string,
email: string,
orders: [
{
id: string,
total: number,
status: string
}
],
address: {
street: string,
city: string,
zip: string
}
}
The model now has an unambiguous reference. Field names are fixed. Nesting is explicit. Types are clear. There is no room for the model to improvise on structure, which means your parsing code can rely on a consistent shape.
Generating schemas from example data
If you already have a sample of the JSON you want the model to produce, you do not need to write the schema by hand. Paste the sample into a schema generator, and you get the structural description in seconds.
This is especially useful when the target shape is complex. A sample response from an existing API with 15 fields, nested objects, and optional properties would take minutes to describe accurately as a schema. Generating it takes one paste.
Handling optional fields
Real-world output often includes fields that should only appear under certain conditions. A compact schema marks these with a ? suffix:
{
name: string,
email: string,
phone?: string,
orders: [
{
id: string,
total: number,
status: string,
tracking_number?: string
}
]
}
Include a one-line instruction like "omit optional fields when the data is not available" and the model handles it correctly. This is far more reliable than writing "the phone field is optional and should only be included if the user has one on file."
Token cost is minimal
A schema that describes a complex output shape might be 80-120 tokens. The equivalent prose description is often 200-400 tokens and still less precise. You save tokens and get better results. The schema pays for itself twice.
Pairing with validation
A schema in the prompt tells the model what to produce. But models still hallucinate structure occasionally, especially with longer outputs. Parse the response and validate it against the same schema you put in the prompt. When the shapes match, you have a closed loop: the prompt specifies the contract, and your code enforces it.
When to use this approach
This pattern works best when you control the prompt and need consistent JSON output across many calls. Chatbots with structured responses, data extraction pipelines, code generation tools that output config files. Anywhere the downstream code expects a fixed shape.
For one-off queries where you just need a quick answer, prose is fine. For production systems processing thousands of responses, a schema in the prompt is the difference between reliable parsing and constant edge-case debugging.
Generate a schema from your target output shape with the converter.