← Back to hub

Structured output & validation

Status CoveredUpdated 28 Jun 2026

In one line

Make the model return clean data (JSON or a typed object) you can use straight away, then check it is valid.

Key idea

Set response_format on create_agent. The clean data lands in result["structured_response"]. No parsing of free text.

You define the shape with a schema. Options:

Two strategies

StrategyHowWhen
ProviderStrategyUses the provider's native structured output.Most reliable. Works with OpenAI, Anthropic (Claude), xAI, Gemini.
ToolStrategyUses tool calling to force the shape.Any model that supports tool calling (most models).

If you pass a schema type directly, LangChain auto-picks: ProviderStrategy when the model supports it natively, else ToolStrategy.

Tool-calling models (ToolStrategy)

This is the path for any model that can call tools. Field rules like ge, le, and Literal are what get validated.

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="1-5", ge=1, le=5)
    sentiment: Literal["positive", "negative"]
    key_points: list[str]

agent = create_agent(model="claude-sonnet-4-6", tools=[], response_format=ToolStrategy(ProductReview))
result = agent.invoke({"messages": [{"role":"user","content":"Analyze: Great, 5/5, fast shipping but pricey"}]})
print(result["structured_response"])
# ProductReview(rating=5, sentiment='positive', key_points=['fast shipping','expensive'])

Validating model outputs (auto-retry)

Models slip up. With ToolStrategy, a bad value fails Pydantic validation, the agent feeds the exact error back, and the model tries again. Example: the rule is 1-5, the model returns 10, it gets corrected to 5. You control this with handle_errors:

handle_errorsEffect
True (default)Catch all errors, retry with a default message.
"your message"Retry with your fixed message.
ValueError / tupleRetry only on these errors; raise the rest.
a functionReturn a custom message per error type.
FalseNo retry. Raise the error.
ToolStrategy(schema=ProductReview, handle_errors="Give a rating from 1 to 5 and a short comment.")
Tip. Put your real rules in the schema (ge, le, Literal, required fields). That is your validation. handle_errors decides what happens when a rule is broken.

Validating tool calls & tool outputs

Two layers:

from collections.abc import Callable
from langchain.agents.middleware import wrap_tool_call
from langchain.messages import ToolMessage
from langchain.tools.tool_node import ToolCallRequest

@wrap_tool_call
def handle_tool_errors(request: ToolCallRequest, handler: Callable[[ToolCallRequest], ToolMessage]) -> ToolMessage:
    try:
        return handler(request)
    except Exception as e:
        return ToolMessage(content=f"Tool error, try again. ({e})", tool_call_id=request.tool_call["id"])

agent = create_agent(model="claude-sonnet-4-6", tools=[...], middleware=[handle_tool_errors])

Validating a raw model (no agent)

Outside an agent, bind a schema straight to a model:

model_with_structure = model.with_structured_output(ProductReview)
review = model_with_structure.invoke("...")   # validated object

Watch out

Links

My notes

Add your own findings here as you learn.