Structured output with output_type
output_type is an Agent argument that makes a run return a typed object instead of a string, by giving the SDK a schema to fill: Agent(output_type=Triage) gives back a Triage.
Last updated: 28 Sep, 2026 · openai-agents 0.22.3
A plain run hands you final_output as text, and text is hard to branch on. If you want to route a ticket by its category, you want a field you can read, not a sentence you have to parse.
The output_type argument
Give the Agent a Pydantic model as its output_type. The SDK then asks the model for JSON that fits, parses it, and puts the object on final_output.
class Triage(BaseModel): # the shape you want back
category: str
urgent: bool
agent = Agent(name="Triage", output_type=Triage, model=model)
# result.final_output is a Triage instance, not a stringThe result shape
Describe the answer as a Pydantic model. Each field is one thing you want back from the model.
from pydantic import BaseModel
class Triage(BaseModel):
category: str # which team the ticket belongs to
urgent: bool # whether it needs a fast replyA stand-in that returns JSON
The canonical shop stand-in returns plain text. For a typed result the model must return JSON that matches the schema, so this lesson uses a tiny JSONModel that returns one line of JSON. _message wraps that text as an assistant message and is shown in the full program.
class JSONModel(Model):
async def get_response(self, *a, **k):
# one assistant message whose text is JSON matching Triage
return ModelResponse(
output=[_message('{"category": "billing", "urgent": true}')],
usage=Usage(), response_id=None)
async def stream_response(self, *a, **k):
raise NotImplementedErrorReading the typed result
Run the agent, then read fields off final_output with dot access. It is a Triage object, so .category is an attribute, not a dictionary key.
agent = Agent(name="Triage", instructions="Classify the ticket.",
output_type=Triage, model=JSONModel())
result = Runner.run_sync(agent, "I was charged twice this month")
print(result.final_output.category) # billingClassifying a ticket into a Triage object
The pieces together. The run returns a Triage, and the three prints read its type and its two fields.
from agents import Agent, Runner, set_tracing_disabled
from agents.models.interface import Model
from agents.items import ModelResponse
from agents.usage import Usage
from openai.types.responses import ResponseOutputMessage, ResponseOutputText
from pydantic import BaseModel
set_tracing_disabled(True)
class Triage(BaseModel):
category: str
urgent: bool
def _message(text):
return ResponseOutputMessage(
id="msg", role="assistant", type="message", status="completed",
content=[ResponseOutputText(text=text, type="output_text", annotations=[])],
)
class JSONModel(Model):
async def get_response(self, *a, **k):
return ModelResponse(
output=[_message('{"category": "billing", "urgent": true}')],
usage=Usage(), response_id=None,
)
async def stream_response(self, *a, **k):
raise NotImplementedError
agent = Agent(name="Triage", instructions="Classify the ticket.",
output_type=Triage, model=JSONModel())
result = Runner.run_sync(agent, "I was charged twice this month")
print(type(result.final_output).__name__)
print(result.final_output.category)
print(result.final_output.urgent)What output_type changed
- final_output is a Triage, not a string:
type(...).__name__printsTriage. - category reads as an attribute:
final_output.categorygivesbillingwith no parsing. - urgent is a real bool: the JSON
truecame back as PythonTrue, because Pydantic coerced it to the field type.
output_type vs a plain string reply
| No output_type | output_type=Triage | |
|---|---|---|
| final_output | a string | a Triage object |
| Reading a field | parse the text yourself | final_output.category |
| Bad shape | you notice late | a validation error at parse time |
| Model must return | any text | JSON matching the schema |
When to ask for structured output
- Routing: read
categoryto pick a team or a next step. - Extraction: pull an order id, a date, and an amount into fields you can store.
Related
- Previous: How the agent runs a tool then answers
- Next: Passing data with RunContextWrapper
- Reference: Agents: output types
- Add a
reason: strfield toTriageand to the JSON the stand-in returns, then print it. - Make
JSONModelreturn'{"category": "billing"}'with nourgentand read the validation error. - Change
urgentin the JSON to"yes"and see whether Pydantic coerces it.
You understood something today that you didn't yesterday.