Asking for JSON in the prompt and hoping is not a strategy. You get JSON most of the time, then a markdown code fence, then a helpful preamble, then a trailing comma, and your parser throws on input that was substantively correct.
Structured outputs make the shape a constraint rather than a request. You supply a schema, and the response conforms to it.
The shape
import { generateObject } from 'ai'
import { z } from 'zod'
const { object } = await generateObject({
model: openai('gpt-5-mini'),
schema: z.object({
sentiment: z.enum(['positive', 'neutral', 'negative']),
confidence: z.number().min(0).max(1),
reasons: z.array(z.string()).max(3),
}),
prompt: review,
})
object.sentiment // typed, parsed, and within the enumThe enum is doing real work: sentiment cannot come back as "quite positive" or "POSITIVE". You have made an entire category of downstream defensive code unnecessary.
Where it changes the most
- Anything you branch on. A classification you route with must be one of a fixed set, or the routing has an undefined case.
- Evaluation. A rubric score has to be aggregatable, which means parseable, which means schema-constrained.
- Chained steps. In prompt chaining, one step's output is the next step's input. Loose shapes compound.
What it does not fix
A schema constrains form, not truth. The model will happily return a perfectly valid object with a confidently wrong sentiment, and confidence: 0.95 is a generated number, not a calibrated probability. Schema validation passing tells you the response is readable, not that it is right. That is still the job of evaluation.
{ reasoning, answer } gives you something close to chain of thought for free; { answer, reasoning } produces a post-hoc justification of an answer already committed to.Related terms
Guardrail
A guardrail is a deterministic check that runs around a model call, on the way in or the way out, and refuses to pass something through. It is ordinary code enforcing what a prompt can only request.
Read definition →PatternValidatedRetry and repair
Retry and repair feeds a failed validation back to the model as the next input, so it fixes its own output instead of you regenerating blind. It converts a hard failure into one more turn, with the error as the instruction.
Read definition →PatternProvenTool use
Tool use lets a model do more than produce text: you expose named actions with typed inputs, and the model calls them to read data, run code, or reach the outside world. It is the bridge from talking to doing.
Read definition →PatternValidatedSelf-consistency
Self-consistency samples the same prompt several times and takes the majority answer. It trades a few extra calls for a big drop in variance, turning a model that sometimes slips into one that reliably lands on its best answer.
Read definition →