Implementing Structured Outputs for Reliable AI Data Extraction
Structured outputs make the JSON valid. They do not make the extraction correct, and in some setups they make it worse. This post covers the places where a schema guarantee ends: absent fields the model invents, output that is cut off or refused, reasoning that suffers under tight formats, and endpoints that offer no guarantee at all.
What the guarantee covers
With OpenAI Structured Outputs, function calling or Anthropic's tool use, the provider constrains generation so the output parses against your schema. That removes a class of failures (a missing key, a string where you wanted an integer). It says nothing about whether the total is the total or the date is the invoice date. Semantic checks stay your job: line items sum to the total, dates fall in a plausible range, currency matches the vendor. Run them on every result, and route failures to review instead of retrying blindly.
Required fields push the model to invent
OpenAI's examples list every property under required and model optional values as nullable types, such as ["string", "null"]. That design has a side effect for extraction. If a receipt has no tip line and the schema demands a tip, a model without a null option has only one way to comply, which is to produce a number. So every field that can be absent in the source needs an explicit null, and the prompt should say that null is the right answer when the document does not state the value. Then test it: build a set of documents with known-missing fields and measure how often the model fills them in anyway. That fabrication rate is a better quality signal than exact-match accuracy on clean documents.
A valid schema can still be cut off
The provider docs describe two ways a response skips your schema. The model can refuse, and the response then carries a refusal part that may not follow the schema, so check for it before parsing. And generation can stop at the token limit, which returns an incomplete status with the reason max_output_tokens. Treat both as failures with their own handling, never as empty results.
Anthropic's structured outputs page puts both cases in writing. If the response is cut off at the max_tokens limit, the output may be incomplete and not match the schema. If the model refuses (stop reason refusal), the output might not match the schema either, because the refusal message takes precedence over the schema constraints (Anthropic structured outputs). The cut-off case bites hardest on arrays. A fixed cap that suits a ten-item job silently truncates the last array in a job with hundreds of items. For extraction over variable-size input, size max_tokens from the input and fail loudly when the stop reason is a length limit.
The guarantee belongs to the endpoint, not the model
Features also change between model generations. Anthropic's errors page says Claude Opus 5.5, Sonnet 5.5, Fable 5.1 and Mythos 5.1 reject a forced tool_choice of type any or tool with a 400, accept only auto and none, and point to auto with strict tool use, or structured outputs, instead (Claude API errors). Code that forces a tool call to get structured output breaks on upgrade. If you route one workflow across providers, your validator has to run on every path, whether that is a forced tool, a JSON schema response format or strict tool use, because on some paths the schema is a request and not a guarantee.
Strict formats can cost reasoning
The EMNLP 2024 industry paper Let Me Speak Freely? reported that format restrictions lowered reasoning performance, and that stricter constraints did more damage. JSON mode scored worst on the reasoning tasks (GSM8K, Last Letter, Shuffled Objects), and looser prompts did better. Classification went the other way, since restricting the answer space reduced parsing errors. The paper also found key order mattered: with the answer key before the reason key, GPT-3.5-Turbo answered directly and scored substantially worse. Some commentators dispute the comparison because the prompts differed between conditions, and models have changed since 2024, so treat the sizes as a warning and not a law.
For extraction that needs judgment (which of three dates is the effective date, whether a clause is an exclusion), test three variants on your golden set before choosing: the strict schema alone, a schema with a short reasoning string placed before the answer fields, and free-text analysis followed by a second formatting call. Pick on measured field accuracy and cost. For plain field copying and classification, the strict schema alone is usually fine.
Evaluate per field
Build a golden set of clean, noisy and edge-case documents with annotated JSON, including documents with missing fields. Score exact match, precision and recall per field, not per document, since one weak field hides inside a good average. Version the schema, make new fields nullable so old data stays valid, and rerun the golden set whenever the schema, prompt or model changes. The first request with a new schema is also slower, according to OpenAI's docs, so warm it before you measure latency.
Cost and privacy
Mask fields you do not need before the document leaves your boundary: if you extract a total and a date, the customer's account number has no reason to travel. For bulk jobs, a batch endpoint halves cost on some providers but changes the failure model, which the batch processing guide covers, and capability support can differ in batch mode too. Log schema version, model, stop reason and validation result for every call.
References
- OpenAI Structured Outputs guide
- OpenAI Function Calling
- Anthropic Tool Use
- Let Me Speak Freely? A Study on the Impact of Format Restrictions on LLM Performance
- Anthropic: Structured outputs: truncated and refused responses may not match the schema.
- Anthropic: Claude API errors: newer models reject forced tool_choice with a 400.