Structured output with response_format
response_format is a create_agent option that asks the agent for an answer shaped like a Pydantic class and returns it as structured_response, so your code reads fields instead of parsing a sentence.
Last updated: 27 Sep, 2026 · LangChain 1.4
The shop's ticket system wants two fields for every question, the order id and its status, not a sentence to pick apart. You describe the shape with a Pydantic class.
The response_format option
agent = create_agent(model, tools=[...], response_format=Ticket) # Ticket is a Pydantic class
result = agent.invoke(inputs)
result["structured_response"] # a Ticket instance, ready for codeDefining the Ticket class
Define the shape as a Pydantic class with the fields your code wants.
from pydantic import BaseModel
class Ticket(BaseModel):
"""A support ticket for one order."""
order_id: str
status: strGiven a schema, create_agent picks a strategy. If the model supports structured output natively, as OpenAI's and Anthropic's do, it asks the provider for it. Otherwise it uses ToolStrategy: it adds one more tool, named after the class, and requires the model to call a tool on every turn. The arguments of that final call are the answer.
Pick one to watch it run, step by step.
Making the model fill Ticket
This small stand-in model has no native support, so it gets the extra tool and needs to know what to do with it. When it would answer in words and a tool called Ticket is bound, it splits its answer into an order id and a status and calls that tool instead.
from langchain.messages import AIMessage
from shop_model import ShopModel
class TicketModel(ShopModel):
def decide(self, messages):
reply = super().decide(messages)
if reply.tool_calls or "Ticket" not in [t.name for t in self.tools]:
return reply
order_id, status = reply.text.rstrip(".").split(" ", 1)
args = {"order_id": order_id, "status": status}
return AIMessage("", tool_calls=[{"name": "Ticket", "args": args, "id": "call_ticket"}])Wiring in response_format
Wire the class in with response_format.
from langchain.agents import create_agent
from ticket import Ticket
from ticket_model import TicketModel
from tools import lookup_order
agent = create_agent(TicketModel(), tools=[lookup_order], response_format=Ticket)The structured answer on one question
Ask one question and read both the structured response and the messages behind it.
result = agent.invoke({"messages": [{"role": "user", "content": "Where is my order A17?"}]})
print(repr(result["structured_response"]))
for message in result["messages"]:
print(f"{message.type:<6} {message.text or message.tool_calls[0]['name']}")What structured_response held
structured_responseis aTicketobject, ready for code to use.- The conversation shows how it got there: the lookup, then a call to the
Tickettool, then a tool message that ends the loop by returning the ticket. - Because this stand-in model has no native structured output,
create_agentadded theTickettool and required a tool call each turn; the arguments of the final call became the answer.
A sentence vs response_format
| Plain answer | response_format | |
|---|---|---|
| What you get back | Text in result["messages"] | A Ticket in result["structured_response"] |
| Your code then | Parses the sentence | Reads fields |
| If a field is missing | You notice late | The class can require it |
When to use structured output
- Feeding an answer into another system: a ticket, a row in a database, a form.
- Any time code, not a person, reads what the agent produced.
ToolStrategy the class name becomes a tool name, so the model must call it to finish. If your prompt or model never calls that tool, the run has no structured_response to return.Related
- Previous: Runtime context: who is asking
- Next: Validation errors, and the retry
- Reference: Structured output
- Add a field
customer: str = "unknown"toTicketand print the response again. - Ask about B22 and read the status the ticket gets.
- Print
result["structured_response"].model_dump()to get a plain dictionary.
Little by little, you're building something great.