Vault / wiki/201/structured-output.md
updated 2026-05-28Structured Output
How to make Claude return reliable, machine-parseable output.
Three techniques, ranked
- Tool-use with
tool_choice = {type: "tool", name: "..."}— the most reliable. The tool'sinput_schemais your JSON schema. Claude is forced to emit atool_useblock matching it. - Prefill the assistant turn — start with
{to force JSON, or<answer>for an XML tag. - Prompt-only constraints — say "Return JSON matching this schema" plus a clear example. Cheapest, least reliable.
Forced tool pattern (recommended)
extract_tool = {
"name": "extract_invoice",
"description": "Extract structured fields from an invoice.",
"input_schema": {
"type": "object",
"properties": {
"vendor": {"type": "string"},
"total": {"type": "number"},
"currency": {"type": "string", "enum": ["USD", "EUR", "GBP"]},
"line_items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": {"type": "string"},
"amount": {"type": "number"},
},
"required": ["description", "amount"],
},
},
},
"required": ["vendor", "total", "currency", "line_items"],
},
}
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
tools=[extract_tool],
tool_choice={"type": "tool", "name": "extract_invoice"},
messages=[{"role": "user", "content": invoice_text}],
)
data = resp.content[0].input # already a dict matching schema
Prefill pattern
messages = [
{"role": "user", "content": "..."},
{"role": "assistant", "content": "{"}, # prefill
]
# Claude continues from `{`. Concatenate "{" + response.
Schema design tips
- Use enums when fields have a fixed vocabulary.
- Mark nullable fields explicitly with
"type": ["string", "null"]. - Require what truly must be present — overdoing
requiredcauses hallucination. - Add descriptions to every property — they guide Claude's choices.
- Avoid deep nesting — flatten when you can.
Validation loops
Always validate after generation. Pattern:
1. Generate.
2. Run JSON schema validation (jsonschema, pydantic).
3. If invalid: re-prompt with the validator's error message included.
4. Cap retries (~3) and route to human review if still failing.
Provenance
For extraction, ask Claude to include source pointers alongside the value:
{"vendor": "ACME", "vendor_source": "page 1, line 2"}
This lets you spot-check or audit. CCA-F frequently tests "how do you make extractions auditable?" — provenance is the answer.