Core components
Structured output
Structured output allows agents to return data in a specific, predictable format. Instead of parsing natural language responses, you get structured data in the form of JSON objects, Pydantic models, or dataclasses that your application can directly use.
LangChain's create_agent handles structured output automatically. The user sets their desired structured output schema, and when the model generates the structured data, it's captured, validated, and returned in the 'structured_response' key of the agent's state.
def create_agent(
...
response_format: Union[
ToolStrategy[StructuredResponseT],
ProviderStrategy[StructuredResponseT],
type[StructuredResponseT],
]Response Format
Controls how the agent returns structured data:
ToolStrategy[StructuredResponseT]: Uses tool calling for structured outputProviderStrategy[StructuredResponseT]: Uses provider-native structured outputtype[StructuredResponseT]: Schema type - automatically selects best strategy based on model capabilitiesNone: No structured output
When a schema type is provided directly, LangChain automatically chooses:
ProviderStrategyfor models supporting native structured output (e.g. OpenAI, Grok)ToolStrategyfor all other models
The structured response is returned in the structured_response key of the agent's final state.
Provider strategy
Some model providers support structured output natively through their APIs (currently only OpenAI and Grok). This is the most reliable method when available.
To use this strategy, configure a ProviderStrategy:
class ProviderStrategy(Generic[SchemaT]):
schema: type[SchemaT]schema required
The schema defining the structured output format. Supports:
- Pydantic models:
BaseModelsubclasses with field validation - Dataclasses: Python dataclasses with type annotations
- TypedDict: Typed dictionary classes
- JSON Schema: Dictionary with JSON schema specification
LangChain automatically uses ProviderStrategy when you pass a schema type directly to create_agent.response_format and the model supports native structured output:
from pydantic import BaseModel
from langchain.agents import create_agent
class ContactInfo(BaseModel):
"""Contact information for a person."""
name: str = Field(description="The name of the person")
email: str = Field(description="The email address of the person")
phone: str = Field(description="The phone number of the person")
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=ContactInfo # Auto-selects ProviderStrategy
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
})
result["structured_response"]
# ContactInfo(name='John Doe', email='john@example.com', phone='(555) 123-4567')from dataclasses import dataclass
from langchain.agents import create_agent
@dataclass
class ContactInfo:
"""Contact information for a person."""
name: str # The name of the person
email: str # The email address of the person
phone: str # The phone number of the person
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=ContactInfo # Auto-selects ProviderStrategy
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
})
result["structured_response"]
# ContactInfo(name='John Doe', email='john@example.com', phone='(555) 123-4567')from typing_extensions import TypedDict
from langchain.agents import create_agent
class ContactInfo(TypedDict):
"""Contact information for a person."""
name: str # The name of the person
email: str # The email address of the person
phone: str # The phone number of the person
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=ContactInfo # Auto-selects ProviderStrategy
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
})
result["structured_response"]
# {'name': 'John Doe', 'email': 'john@example.com', 'phone': '(555) 123-4567'}from langchain.agents import create_agent
contact_info_schema = {
"type": "object",
"description": "Contact information for a person.",
"properties": {
"name": {"type": "string", "description": "The name of the person"},
"email": {"type": "string", "description": "The email address of the person"},
"phone": {"type": "string", "description": "The phone number of the person"}
},
"required": ["name", "email", "phone"]
}
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=contact_info_schema # Auto-selects ProviderStrategy
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"}]
})
result["structured_response"]
# {'name': 'John Doe', 'email': 'john@example.com', 'phone': '(555) 123-4567'}Provider-native structured output provides high reliability and strict validation because the model provider enforces the schema. Use it when available.
If the provider natively supports structured output for your model choice, it is functionally equivalent to write response_format=ProductReview instead of response_format=ToolStrategy(ProductReview). In either case, if structured output is not supported, the agent will fall back to a tool calling strategy.
Tool calling strategy
For models that don't support native structured output, LangChain uses tool calling to achieve the same result. This works with all models that support tool calling, which is most modern models.
To use this strategy, configure a ToolStrategy:
class ToolStrategy(Generic[SchemaT]):
schema: type[SchemaT]
tool_message_content: str | None
handle_errors: Union[
bool,
str,
type[Exception],
tuple[type[Exception], ...],
Callable[[Exception], str],
]schema required The schema defining the structured output format. Supports:
- Pydantic models:
BaseModelsubclasses with field validation - Dataclasses: Python dataclasses with type annotations
- TypedDict: Typed dictionary classes
- JSON Schema: Dictionary with JSON schema specification
- Union types: Multiple schema options. The model will choose the most appropriate schema based on the context.
tool_message_content
Custom content for the tool message returned when structured output is generated. If not provided, defaults to a message showing the structured response data.
handle_errors
Error handling strategy for structured output validation failures. Defaults to True.
True: Catch all errors with default error templatestr: Catch all errors with this custom messagetype[Exception]: Only catch this exception type with default messagetuple[type[Exception], ...]: Only catch these exception types with default messageCallable[[Exception], str]: Custom function that returns error messageFalse: No retry, let exceptions propagate
from pydantic import BaseModel, Field
from typing import Literal
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ProductReview(BaseModel):
"""Analysis of a product review."""
rating: int | None = Field(description="The rating of the product", ge=1, le=5)
sentiment: Literal["positive", "negative"] = Field(description="The sentiment of the review")
key_points: list[str] = Field(description="The key points of the review. Lowercase, 1-3 words each.")
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=ToolStrategy(ProductReview)
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
})
result["structured_response"]
# ProductReview(rating=5, sentiment='positive', key_points=['fast shipping', 'expensive'])from dataclasses import dataclass
from typing import Literal
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
@dataclass
class ProductReview:
"""Analysis of a product review."""
rating: int | None # The rating of the product (1-5)
sentiment: Literal["positive", "negative"] # The sentiment of the review
key_points: list[str] # The key points of the review
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=ToolStrategy(ProductReview)
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
})
result["structured_response"]
# ProductReview(rating=5, sentiment='positive', key_points=['fast shipping', 'expensive'])from typing import Literal
from typing_extensions import TypedDict
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ProductReview(TypedDict):
"""Analysis of a product review."""
rating: int | None # The rating of the product (1-5)
sentiment: Literal["positive", "negative"] # The sentiment of the review
key_points: list[str] # The key points of the review
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=ToolStrategy(ProductReview)
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
})
result["structured_response"]
# {'rating': 5, 'sentiment': 'positive', 'key_points': ['fast shipping', 'expensive']}from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
product_review_schema = {
"type": "object",
"description": "Analysis of a product review.",
"properties": {
"rating": {
"type": ["integer", "null"],
"description": "The rating of the product (1-5)",
"minimum": 1,
"maximum": 5
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative"],
"description": "The sentiment of the review"
},
"key_points": {
"type": "array",
"items": {"type": "string"},
"description": "The key points of the review"
}
},
"required": ["sentiment", "key_points"]
}
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=ToolStrategy(product_review_schema)
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
})
result["structured_response"]
# {'rating': 5, 'sentiment': 'positive', 'key_points': ['fast shipping', 'expensive']}from pydantic import BaseModel, Field
from typing import Literal, Union
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ProductReview(BaseModel):
"""Analysis of a product review."""
rating: int | None = Field(description="The rating of the product", ge=1, le=5)
sentiment: Literal["positive", "negative"] = Field(description="The sentiment of the review")
key_points: list[str] = Field(description="The key points of the review. Lowercase, 1-3 words each.")
class CustomerComplaint(BaseModel):
"""A customer complaint about a product or service."""
issue_type: Literal["product", "service", "shipping", "billing"] = Field(description="The type of issue")
severity: Literal["low", "medium", "high"] = Field(description="The severity of the complaint")
description: str = Field(description="Brief description of the complaint")
agent = create_agent(
model="gpt-5",
tools=tools,
response_format=ToolStrategy(Union[ProductReview, CustomerComplaint])
)
result = agent.invoke({
"messages": [{"role": "user", "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'"}]
})
result["structured_response"]
# ProductReview(rating=5, sentiment='positive', key_points=['fast shipping', 'expensive'])Custom tool message content
The tool_message_content parameter allows you to customize the message that appears in the conversation history when structured output is generated:
from pydantic import BaseModel, Field
from typing import Literal
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class MeetingAction(BaseModel):
"""Action items extracted from a meeting transcript."""
task: str = Field(description="The specific task to be completed")
assignee: str = Field(description="Person responsible for the task")
priority: Literal["low", "medium", "high"] = Field(description="Priority level")
agent = create_agent(
model="gpt-5",
tools=[],
response_format=ToolStrategy(
schema=MeetingAction,
tool_message_content="Action item captured and added to meeting notes!"
)
)
agent.invoke({
"messages": [{"role": "user", "content": "From our meeting: Sarah needs to update the project timeline as soon as possible"}]
})================================ Human Message =================================
From our meeting: Sarah needs to update the project timeline as soon as possible
================================== Ai Message ==================================
Tool Calls:
MeetingAction (call_1)
Call ID: call_1
Args:
task: Update the project timeline
assignee: Sarah
priority: high
================================= Tool Message =================================
Name: MeetingAction
Action item captured and added to meeting notes!Without tool_message_content, our final ToolMessage would be:
================================= Tool Message =================================
Name: MeetingAction
Returning structured response: {'task': 'update the project timeline', 'assignee': 'Sarah', 'priority': 'high'}Error handling
Models can make mistakes when generating structured output via tool calling. LangChain provides intelligent retry mechanisms to handle these errors automatically.
Multiple structured outputs error
When a model incorrectly calls multiple structured output tools, the agent provides error feedback in a ToolMessage and prompts the model to retry:
from pydantic import BaseModel, Field
from typing import Union
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ContactInfo(BaseModel):
name: str = Field(description="Person's name")
email: str = Field(description="Email address")
class EventDetails(BaseModel):
event_name: str = Field(description="Name of the event")
date: str = Field(description="Event date")
agent = create_agent(
model="gpt-5",
tools=[],
response_format=ToolStrategy(Union[ContactInfo, EventDetails]) # Default: handle_errors=True
)
agent.invoke({
"messages": [{"role": "user", "content": "Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th"}]
})================================ Human Message =================================
Extract info: John Doe (john@email.com) is organizing Tech Conference on March 15th
None
================================== Ai Message ==================================
Tool Calls:
ContactInfo (call_1)
Call ID: call_1
Args:
name: John Doe
email: john@email.com
EventDetails (call_2)
Call ID: call_2
Args:
event_name: Tech Conference
date: March 15th
================================= Tool Message =================================
Name: ContactInfo
Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
Please fix your mistakes.
================================= Tool Message =================================
Name: EventDetails
Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
Please fix your mistakes.
================================== Ai Message ==================================
Tool Calls:
ContactInfo (call_3)
Call ID: call_3
Args:
name: John Doe
email: john@email.com
================================= Tool Message =================================
Name: ContactInfo
Returning structured response: {'name': 'John Doe', 'email': 'john@email.com'}Schema validation error
When structured output doesn't match the expected schema, the agent provides specific error feedback:
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ProductRating(BaseModel):
rating: int | None = Field(description="Rating from 1-5", ge=1, le=5)
comment: str = Field(description="Review comment")
agent = create_agent(
model="gpt-5",
tools=[],
response_format=ToolStrategy(ProductRating), # Default: handle_errors=True
system_prompt="You are a helpful assistant that parses product reviews. Do not make any field or value up."
)
agent.invoke({
"messages": [{"role": "user", "content": "Parse this: Amazing product, 10/10!"}]
})================================ Human Message =================================
Parse this: Amazing product, 10/10!
================================== Ai Message ==================================
Tool Calls:
ProductRating (call_1)
Call ID: call_1
Args:
rating: 10
comment: Amazing product
================================= Tool Message =================================
Name: ProductRating
Error: Failed to parse structured output for tool 'ProductRating': 1 validation error for ProductRating.rating
Input should be less than or equal to 5 [type=less_than_equal, input_value=10, input_type=int].
Please fix your mistakes.
================================== Ai Message ==================================
Tool Calls:
ProductRating (call_2)
Call ID: call_2
Args:
rating: 5
comment: Amazing product
================================= Tool Message =================================
Name: ProductRating
Returning structured response: {'rating': 5, 'comment': 'Amazing product'}Error handling strategies
You can customize how errors are handled using the handle_errors parameter:
Custom error message:
ToolStrategy(
schema=ProductRating,
handle_errors="Please provide a valid rating between 1-5 and include a comment."
)If handle_errors is a string, the agent will always prompt the model to re-try with a fixed tool message:
================================= Tool Message =================================
Name: ProductRating
Please provide a valid rating between 1-5 and include a comment.Handle specific exceptions only:
ToolStrategy(
schema=ProductRating,
handle_errors=ValueError # Only retry on ValueError, raise others
)If handle_errors is an exception type, the agent will only retry (using the default error message) if the exception raised is the specified type. In all other cases, the exception will be raised.
Handle multiple exception types:
ToolStrategy(
schema=ProductRating,
handle_errors=(ValueError, TypeError) # Retry on ValueError and TypeError
)If handle_errors is a tuple of exceptions, the agent will only retry (using the default error message) if the exception raised is one of the specified types. In all other cases, the exception will be raised.
Custom error handler function:
def custom_error_handler(error: Exception) -> str:
if isinstance(error, StructuredOutputValidationError):
return "There was an issue with the format. Try again.
elif isinstance(error, MultipleStructuredOutputsError):
return "Multiple structured outputs were returned. Pick the most relevant one."
else:
return f"Error: {str(error)}"
ToolStrategy(
schema=ToolStrategy(Union[ContactInfo, EventDetails]),
handle_errors=custom_error_handler
)On StructuredOutputValidationError:
================================= Tool Message =================================
Name: ToolStrategy
There was an issue with the format. Try again.On MultipleStructuredOutputsError:
================================= Tool Message =================================
Name: ToolStrategy
Multiple structured outputs were returned. Pick the most relevant one.On other errors:
================================= Tool Message =================================
Name: ToolStrategy
Error: <error message>No error handling:
response_format = ToolStrategy(
schema=ProductRating,
handle_errors=False # All errors raised
)
