Pass a Pydantic model (or a JSON schema as a dict) as response_model and the agent returns the validated object in response.parsed.
response.content keeps the raw JSON text.

How it works

Without tools, the schema is sent as the provider’s native structured output (response_format), and the answer is validated against the model. With tools, the schema is offered to the model as one more tool, final_result. The model gathers what it needs with its other tools, then ends the loop by calling final_result: its arguments are the answer. Native structured output is not sent while tools are on offer, because many OpenAI-compatible servers (vLLM, DeepSeek, Ollama, Scaleway…) apply the JSON grammar from the first token, so the model could never call a tool and would invent the data instead.
A model that answers with plain JSON text instead of calling final_result is accepted too.

Validation and retries

An answer that does not validate is sent back to the model with the validation errors, up to three times; when tools are on offer, the retry forces a final_result call. If the answer still does not validate, parsed is None and content keeps the raw text, so check before using it:

On a model call

Outside an agent, set response_format on the LLM client itself:

For sub-agents

An orchestrator can ask a sub-agent for a JSON report of a given shape by passing an output_schema in its spawn_agent call; the report comes back as JSON.