What Is Structured Output in AI Agents?
Quick Definition#
Structured output is the practice of configuring an AI model to return data in a defined, machine-readable format — most commonly a JSON object conforming to a specific schema — rather than free-form natural language text. When agents use structured output, their responses can be consumed directly by downstream systems, stored in databases, passed to other tools, or validated programmatically without any fragile string parsing.
For foundational context, see Function Calling and Tool Use in AI Agents before exploring structured output in depth. Browse the full AI Agents Glossary to explore all core agent architecture terms.
Why Structured Output Matters for Agents#
Language models are trained to produce fluent natural language. Left unconstrained, an agent asked to extract customer data might return "The customer's name is Sarah and she lives in Austin, Texas" in one call and {"name": "Sarah", "city": "Austin"} in the next. Both are technically correct responses to the same prompt, but only the second can be reliably parsed by a downstream system.
This variability is manageable in a chatbot where a human reads the output. It becomes a critical reliability problem in an agentic pipeline where the output of one model call feeds the input of a tool, another agent, or a database write operation. Structured output eliminates that ambiguity by making the response format a contract.
See AI Agent Workflow Examples for real-world pipelines where structured output is the difference between a brittle prototype and a deployable system.
Free-Form Text vs. Structured Output#
| Concern | Free-Form Text | Structured Output | |---|---|---| | Parsing reliability | Requires fragile regex or LLM re-parsing | Direct JSON deserialization | | Schema enforcement | None — fields may be missing or renamed | Guaranteed field presence and types | | Downstream integration | Requires adapter layer | Direct consumption by APIs and databases | | Debugging failures | Ambiguous — was the answer wrong or formatted wrong? | Schema mismatch is immediately identifiable | | Token cost | Can include unnecessary prose | Returns exactly the required fields |
JSON Mode vs. Structured Output#
JSON Mode#
JSON mode instructs the model to return syntactically valid JSON but does not constrain the schema. The model decides what fields to include and what types to use. This eliminates JSON parse errors but does not prevent missing fields, renamed keys, or unexpected nesting.
Use JSON mode when:
- You need valid JSON but the exact schema is flexible
- You are prototyping and do not yet have a stable schema
- The model output will be inspected by a human before use
Structured Output (Schema-Constrained)#
OpenAI's Structured Outputs API and similar features from other providers go further: you provide a JSON Schema (or a Pydantic model that is automatically converted to one), and the model is constrained to produce output that matches it exactly. Required fields are always present. Types are always correct. Optional fields conform to their declared type when present.
Use full structured output when:
- The model response feeds a database write, API call, or another agent
- Missing or mistyped fields would cause downstream failures
- You need audit trails that can be validated programmatically
Pydantic for Output Validation#
Pydantic is the most common validation layer in Python-based agent frameworks. You define expected output as a class:
from pydantic import BaseModel
from typing import Optional
class CustomerRecord(BaseModel):
name: str
email: str
company: Optional[str] = None
sentiment: str # "positive" | "neutral" | "negative"
priority: int # 1-5
Libraries like Instructor (built on top of the OpenAI SDK) or LangChain's .with_structured_output() accept this class, automatically generate the JSON Schema, pass it to the model, and then validate and deserialize the response:
import instructor
from openai import OpenAI
client = instructor.from_openai(OpenAI())
record = client.chat.completions.create(
model="gpt-4o",
response_model=CustomerRecord,
messages=[
{"role": "user", "content": "Extract customer info from: 'Sarah from Acme Corp, sarah@acme.com, very happy with the product, urgent follow-up needed.'"}
]
)
print(record.name) # "Sarah"
print(record.sentiment) # "positive"
print(record.priority) # 5
If the model returns a response that does not match the schema, Instructor can automatically retry with the validation error fed back to the model, dramatically increasing reliability on the first pass.
OpenAI Structured Outputs API#
OpenAI introduced native structured output support using the response_format parameter with json_schema:
from openai import OpenAI
import json
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o-2024-08-06",
messages=[
{"role": "user", "content": "Analyze this support ticket and return structured data."}
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "ticket_analysis",
"strict": True,
"schema": {
"type": "object",
"properties": {
"category": {"type": "string"},
"urgency": {"type": "integer"},
"summary": {"type": "string"},
"requires_escalation": {"type": "boolean"}
},
"required": ["category", "urgency", "summary", "requires_escalation"],
"additionalProperties": False
}
}
}
)
result = json.loads(response.choices[0].message.content)
With strict: true, the model is constrained to match the schema exactly. This is the most reliable mechanism available at the API level.
Anthropic Tool Use for Structured Output#
Anthropic's Claude models do not have a native structured output mode equivalent to OpenAI's, but tool use achieves the same result. You define a tool with the exact schema you want the model to fill, then prompt it to call that tool with the extracted or generated data:
import anthropic
client = anthropic.Anthropic()
tools = [{
"name": "record_ticket",
"description": "Record the structured analysis of a support ticket",
"input_schema": {
"type": "object",
"properties": {
"category": {"type": "string"},
"urgency": {"type": "integer", "minimum": 1, "maximum": 5},
"summary": {"type": "string"},
"requires_escalation": {"type": "boolean"}
},
"required": ["category", "urgency", "summary", "requires_escalation"]
}
}]
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
tools=tools,
tool_choice={"type": "tool", "name": "record_ticket"},
messages=[{"role": "user", "content": "Analyze: 'Login broken for all enterprise users since 2pm, high impact'"}]
)
tool_input = response.content[0].input
# tool_input is already a validated dict matching the schema
By setting tool_choice to force a specific tool call, you guarantee the model returns the structured format. This is the standard pattern for structured output with Claude.

Practical Examples#
Example 1: Extraction Agent#
An agent that processes inbound sales emails extracts structured lead data:
{
"company": "Acme Corp",
"contact_name": "Sarah Lin",
"contact_email": "slin@acme.com",
"use_case": "Automate invoice reconciliation",
"company_size": "200-500",
"urgency": "high",
"next_action": "schedule_demo"
}
This feeds directly into a CRM without any intermediate parsing step.
Example 2: Classification Agent#
A content moderation agent classifies each piece of content:
{
"content_id": "msg_12345",
"category": "hate_speech",
"confidence": 0.94,
"action": "remove",
"review_required": false
}
Example 3: Multi-Step Pipeline#
In a research agent, each step produces structured output that becomes the next step's input:
- Search step returns
{"query": "...", "results": [...]} - Extraction step returns
{"key_facts": [...], "sources": [...]} - Summary step returns
{"summary": "...", "citations": [...], "confidence": 0.87}
Common Failure Modes and Fixes#
Schema too complex: Deeply nested schemas with many optional fields produce more failures. Flatten your schema where possible and split complex outputs across multiple calls.
Ambiguous field names: A field named status could mean many things. Use descriptive names like approval_status with an enum of allowed values to reduce model confusion.
Missing enum constraints: If a field should only accept specific string values, declare them explicitly. Unconstrained string fields produce inconsistent values across calls.
Ignoring validation errors: Always validate model output against your schema before using it. Silent schema mismatches are harder to debug than explicit validation failures.
Related Concepts and Further Reading#
- Function Calling in AI
- Tool Use in AI Agents
- Agent Loop
- Agent Evaluation
- AI Agent Workflow Examples
- Build an AI Agent with LangChain
- Best AI Agent Platforms
Frequently Asked Questions#
What is structured output in AI agents?#
Structured output is when an AI model returns data in a defined schema — such as a JSON object with specific fields — instead of free-form natural language. It makes model responses directly usable by downstream systems without fragile text parsing. It is essential for any agent that needs to extract data, invoke tools reliably, or pass results between pipeline components.
What is the difference between JSON mode and structured output?#
JSON mode guarantees syntactically valid JSON but does not enforce a specific schema. Full structured output constrains the model to match an exact schema — required fields are always present and types are always correct. Structured output is strictly more reliable for production agents.
How does Pydantic work with LLM structured output?#
You define a Pydantic BaseModel class with the expected fields and types. Frameworks like Instructor or LangChain convert that class to a JSON Schema, pass it to the model, then validate and deserialize the response automatically. Validation failures produce clear errors rather than silent downstream bugs.