Skip to content

Return structured output

Use structured output when your application needs an object with specific fields. Define a schema, pass it to the agent, and read the validated object from the result.

flowchart LR
A[Schema Definition] --> B[Agent Invocation]
B --> C[LLM] --> D[Validated Object]
D --> E["result.structured_output"]

To request structured output, pass a schema with structured_output_modelstructuredOutputSchema. Read the validated object from result.structured_outputresult.structuredOutput.

Define the output fields with a Pydantic model:

from pydantic import BaseModel, Field
from strands import Agent
class PersonInfo(BaseModel):
name: str = Field(description="Name of the person")
age: int = Field(description="Age of the person")
occupation: str = Field(description="Occupation of the person")
agent = Agent()
result = agent(
"John Smith is a 30 year-old software engineer",
structured_output_model=PersonInfo
)
person_info: PersonInfo = result.structured_output
print(person_info.model_dump_json(indent=2))

Example output:

{
"name": "John Smith",
"age": 30,
"occupation": "software engineer"
}

For async code, pass structured_output_model to await agent.invoke_async(...).

Migrating from deprecated methods: Pass structured_output_model to the agent invocation instead of calling Agent.structured_output() or Agent.structured_output_async(). Read the model from result.structured_output.

Structured output converts your schema into a tool specification that guides the model toward a correctly formatted response. Every model provider Strands supports works with structured output.

Strands accepts the structured_output_modelstructuredOutputSchema parameter in agent invocations, which manages the conversion, validation, and response processing automatically. The validated result is available in the AgentResult.structured_outputAgentResult.structuredOutput field.

When structured output validation fails, Strands throws a custom exception that can be caught and handled appropriately:

from pydantic import ValidationError
from strands.types.exceptions import StructuredOutputException
try:
result = agent(prompt, structured_output_model=MyModel)
except StructuredOutputException as e:
print(f"Structured output failed: {e}")
  • Keep schemas focused: Define specific schemas for clear purposes
  • Use descriptive field names: Include helpful descriptions with field metadata
  • Handle errors gracefully: Implement proper error handling strategies with fallbacks

Automatically retry validation when initial extraction fails due to schema validation:

from strands.agent import Agent
from pydantic import BaseModel, field_validator
class Name(BaseModel):
first_name: str
@field_validator("first_name")
@classmethod
def validate_first_name(cls, value: str) -> str:
if not value.endswith('abc'):
raise ValueError("You must append 'abc' to the end of my name")
return value
agent = Agent()
result = agent("What is Aaron's name?", structured_output_model=Name)

Stream agent execution while using structured output. The structured output is available in the final result:

from strands import Agent
from pydantic import BaseModel, Field
class WeatherForecast(BaseModel):
"""Weather forecast data."""
location: str
temperature: int
condition: str
humidity: int
wind_speed: int
forecast_date: str
streaming_agent = Agent()
async for event in streaming_agent.stream_async(
"Generate a weather forecast for Seattle: 68°F, partly cloudy, 55% humidity, 8 mph winds, for tomorrow",
structured_output_model=WeatherForecast
):
if "data" in event:
print(event["data"], end="", flush=True)
elif "result" in event:
print(f'The forecast for today is: {event["result"].structured_output}')

Combine structured output with tool usage to format tool execution results:

from strands import Agent
from strands.vended_tools import notebook
from pydantic import BaseModel, Field
class NotesResult(BaseModel):
notebook_name: str = Field(description="the notebook that was updated")
item_count: int = Field(description="the number of items added")
tool_agent = Agent(
tools=[notebook]
)
res = tool_agent(
'Create a notebook named "ideas" and add three project ideas.',
structured_output_model=NotesResult,
)

Reuse a single agent instance with different structured output schemas for varied extraction tasks:

from strands import Agent
from pydantic import BaseModel, Field
from typing import Optional
class Person(BaseModel):
"""A person's basic information"""
name: str = Field(description="Full name")
age: int = Field(description="Age in years", ge=0, le=150)
email: str = Field(description="Email address")
phone: Optional[str] = Field(description="Phone number", default=None)
class Task(BaseModel):
"""A task or todo item"""
title: str = Field(description="Task title")
description: str = Field(description="Detailed description")
priority: str = Field(description="Priority level: low, medium, high")
completed: bool = Field(description="Whether task is completed", default=False)
agent = Agent()
person_res = agent("Extract person: John Doe, 35, john@test.com", structured_output_model=Person)
task_res = agent("Create task: Review code, high priority, completed", structured_output_model=Task)

Extract structured information from prior conversation context without repeating questions:

from strands import Agent
from pydantic import BaseModel
from typing import Optional
agent = Agent()
# Build up conversation context
agent("What do you know about Paris, France?")
agent("Tell me about the weather there in spring.")
class CityInfo(BaseModel):
city: str
country: str
population: Optional[int] = None
climate: str
# Extract structured information from the conversation
result = agent(
"Extract structured information about Paris from our conversation",
structured_output_model=CityInfo
)
print(f"City: {result.structured_output.city}") # "Paris"
print(f"Country: {result.structured_output.country}") # "France"

You can also set a default structured output schema that applies to all agent invocations:

class PersonInfo(BaseModel):
name: str
age: int
occupation: str
# Set default structured output model for all invocations
agent = Agent(structured_output_model=PersonInfo)
result = agent("John Smith is a 30 year-old software engineer")
print(f"Name: {result.structured_output.name}") # "John Smith"
print(f"Age: {result.structured_output.age}") # 30
print(f"Job: {result.structured_output.occupation}") # "software engineer"

Even when you set a default schema at the agent initialization level, you can override it for specific invocations:

class PersonInfo(BaseModel):
name: str
age: int
occupation: str
class CompanyInfo(BaseModel):
name: str
industry: str
employees: int
# Agent with default PersonInfo model
agent = Agent(structured_output_model=PersonInfo)
# Override with CompanyInfo for this specific call
result = agent(
"TechCorp is a software company with 500 employees",
structured_output_model=CompanyInfo
)
print(f"Company: {result.structured_output.name}") # "TechCorp"
print(f"Industry: {result.structured_output.industry}") # "software"
print(f"Size: {result.structured_output.employees}") # 500