0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
27 small wins to finish your pathNext lesson →

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.

python
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 string

The result shape

Describe the answer as a Pydantic model. Each field is one thing you want back from the model.

python
from pydantic import BaseModel

class Triage(BaseModel):
    category: str   # which team the ticket belongs to
    urgent: bool    # whether it needs a fast reply

A 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.

python
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 NotImplementedError

Reading 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.

python
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)   # billing

Classifying 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.

Example
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__ prints Triage.
  • category reads as an attribute: final_output.category gives billing with no parsing.
  • urgent is a real bool: the JSON true came back as Python True, because Pydantic coerced it to the field type.

output_type vs a plain string reply

No output_typeoutput_type=Triage
final_outputa stringa Triage object
Reading a fieldparse the text yourselffinal_output.category
Bad shapeyou notice latea validation error at parse time
Model must returnany textJSON matching the schema

When to ask for structured output

  • Routing: read category to pick a team or a next step.
  • Extraction: pull an order id, a date, and an amount into fields you can store.
Watch out. The model has to return JSON that fits the schema. If a field is missing or has the wrong type, the parse raises a validation error rather than handing back a half-filled object, so a flaky model breaks the run instead of the field.
Try it yourself
  • Add a reason: str field to Triage and to the JSON the stand-in returns, then print it.
  • Make JSONModel return '{"category": "billing"}' with no urgent and read the validation error.
  • Change urgent in the JSON to "yes" and see whether Pydantic coerces it.

You understood something today that you didn't yesterday.