When you need guaranteed schema-compliant structured output from Claude, there is a clear reliability hierarchy:
tool_usewith JSON schemas - eliminates JSON syntax errors entirely- Prompt-based JSON - model can produce malformed JSON
Commit this hierarchy to memory. The exam builds on it. With tool use, the tool's JSON schema constrains the shape of what Claude returns, eliminating syntax issues like missing brackets, trailing commas, or unquoted keys. The separate tool_choice parameter is what forces the model to call the tool at all. Prompt-based extraction (asking the model to output JSON in a text response) gives you no structural guarantees and will periodically produce unparseable output in production.
tool_choice: The Three Modes
The tool_choice parameter controls whether and how the model calls tools. Understanding the three modes is critical for the exam:
"auto" (default): The model decides whether to call a tool or return text. It may choose to respond with a text message instead of calling the extraction tool. Use this when the model legitimately needs the option to respond conversationally.
"any": The model MUST call a tool but chooses which one. Use this when you have multiple extraction schemas (e.g., extract_invoice, extract_receipt, extract_contract) and the document type is unknown. The model selects the appropriate tool and returns structured output. Guaranteed structured output, flexible tool selection.
{"type": "tool", "name": "extract_metadata"}: The model MUST call the specific named tool. Use this to force a mandatory first step - for example, ensuring metadata extraction runs before enrichment steps. No flexibility, maximum control.
// Force guaranteed structured output with unknown document type
const response = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 4096,
tool_choice: { type: "any" },
tools: [extractInvoiceTool, extractReceiptTool, extractContractTool],
messages: [{ role: "user", content: documentText }]
});
// Force a specific extraction step
const response = await client.messages.create({
model: "claude-sonnet-5",
max_tokens: 4096,
tool_choice: { type: "tool", name: "extract_metadata" },
tools: [extractMetadataTool],
messages: [{ role: "user", content: documentText }]
});
What tool_use Does NOT Prevent
This is where the exam gets sneaky. tool_use with JSON schemas eliminates syntax errors but does NOT prevent semantic errors:
- Sum discrepancies: Line items that do not sum to the stated total
- Field placement errors: Values placed in the wrong fields (e.g., a date in an amount field when both are strings)
- Fabrication: The model invents values for required fields when the source document lacks the information
The schema guarantees structure. It doesn't guarantee correctness. Semantic validation needs additional logic (covered in Task Statement 4.4).
Schema Design for Production
Effective schema design prevents entire classes of errors at the structural level:
Optional/nullable fields - When source documents may not contain certain information, make those fields optional or nullable. This is the primary defence against fabrication. If a field is required, the model is pressured to produce a value even when the source has none. If the field is nullable, the model can honestly return null.
{
"type": "object",
"properties": {
"invoice_number": { "type": "string" },
"vendor_name": { "type": "string" },
"payment_terms": { "type": ["string", "null"] },
"purchase_order": { "type": ["string", "null"] }
},
"required": ["invoice_number", "vendor_name"]
}
"unclear" enum value - For ambiguous cases where the source is genuinely unclear, add an explicit "unclear" option to enum fields. This prevents the model from forcing a classification when the evidence is ambiguous.
"other" + detail string - For extensible categorisation, include an "other" enum value paired with a freeform detail string field. This captures edge cases that your predefined categories do not cover.
{
"category": {
"type": "string",
"enum": ["invoice", "receipt", "contract", "unclear", "other"]
},
"category_detail": {
"type": ["string", "null"],
"description": "Freeform detail when category is 'other'"
}
}
Format normalisation rules - Include format normalisation instructions in the prompt alongside the schema. The schema enforces structure. The prompt enforces formatting consistency (e.g., "All dates in ISO 8601 format," "All currency amounts as decimal numbers without currency symbols").
Key Concept
tool_use with JSON schemas eliminates syntax errors but not semantic errors. Make fields optional/nullable when source documents may lack information - this prevents the model from fabricating values. Use tool_choice "any" for guaranteed structured output when the document type is unknown.