← Code Recipes
Frameworks & SDKs

Pydantic AI Free code examples in Python

Typed agents where the output is validated against a model, not hoped for.

C# Python Java JavaScript

WHAT THIS PAGE COVERS

Structured output with retries when validation fails.

Model gpt-5.4-mini
Package pydantic-ai
Checked

Model ids change. If a sample returns a 404 on the model, that is the first thing to check against the official docs.

Install

One package. The model string carries its provider, so there is no separate client package to install for the common providers.

pip install pydantic-ai

export OPENAI_API_KEY="sk-your-key"   # macOS / Linux
setx OPENAI_API_KEY "sk-your-key"     # Windows, then open a new shell
An output type, not a prompt asking nicely for JSON

output_type IS THE SCHEMA. Pydantic generates it, the model is constrained by it, and the reply is validated before you see it — so `result.output.severity` is an enum member, not a string you hope is one of three values.

from enum import Enum
from pydantic import BaseModel, Field
from pydantic_ai import Agent

class Severity(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"

class TicketSummary(BaseModel):
    summary: str = Field(description="One sentence, no jargon.")
    error_code: str | None = None
    severity: Severity

agent = Agent(
    "openai:gpt-5.4-mini",
    output_type=TicketSummary,
    instructions="You triage support tickets for industrial pumps.",
)

result = agent.run_sync("The pump stalled and threw code E-4471.")

print(result.output.severity, result.output.error_code)
Tools, with dependencies that are typed too

deps_type gives every tool a typed context. RunContext.deps is your database handle or HTTP client — passed in at run time rather than reached for from a global, which is what makes the agent testable.

from dataclasses import dataclass
from pydantic_ai import RunContext

@dataclass
class Deps:
    db: object          # a real connection in production, a fake in tests

agent = Agent(
    "openai:gpt-5.4-mini",
    deps_type=Deps,
    output_type=TicketSummary,
)

@agent.tool
def lookup_error(ctx: RunContext[Deps], code: str) -> str:
    """Look up what an error code means in the service manual."""
    return ctx.deps.db.fetch_error(code)

result = agent.run_sync(
    "The pump stalled and threw code E-4471.",
    deps=Deps(db=service_manual),
)
ModelRetry — correcting the model instead of failing

THIS IS THE FEATURE. Raising ModelRetry sends the message back to the model as feedback and lets it try again. An exception would end the run; this turns a wrong tool argument into a self-correction.

from pydantic_ai import ModelRetry

@agent.tool
def lookup_error(ctx: RunContext[Deps], code: str) -> str:
    """Look up what an error code means in the service manual."""
    if not code.startswith("E-"):
        # Not an exception. The model reads this and calls again.
        raise ModelRetry(
            f"{code!r} is not an error code. They look like E-4471. "
            "Re-read the ticket and extract the code."
        )

    found = ctx.deps.db.fetch_error(code)
    if found is None:
        raise ModelRetry(f"No manual entry for {code}. Check for a typo.")

    return found

# retries caps the loop — without a cap a stubborn model can spin.
agent = Agent("openai:gpt-5.4-mini", deps_type=Deps,
              output_type=TicketSummary, retries=3)
Validating the whole output, not just its shape

@agent.output_validator runs after the schema check and can raise ModelRetry too. Shape is what Pydantic guarantees; RULES are what this hook is for — and a rule the model broke is worth telling it about.

@agent.output_validator
def severity_needs_a_code(
    ctx: RunContext[Deps], output: TicketSummary
) -> TicketSummary:
    # The type system cannot express "high severity requires a code".
    if output.severity is Severity.HIGH and not output.error_code:
        raise ModelRetry(
            "A high-severity summary must include the error code. "
            "Find it in the ticket or lower the severity."
        )

    return output

result = agent.run_sync(
    "The pump stalled and threw code E-4471.", deps=Deps(db=service_manual))

# By here the output is valid in shape AND in rule.
print(result.output)

QUESTIONS

Which languages are the Pydantic AI examples in?

Python. Every sample runs as pasted and reads its credentials from an environment variable — no key is ever written into the code.

Which package do I install for Pydantic AI?

The samples use pydantic-ai. The install step on each language tab gives the exact command.

Which Pydantic AI model do these examples use?

gpt-5.4-mini, checked against the official documentation on 26 September 2026. Model ids change often; if a call returns a model-not-found error, check the current list in the official docs.

Where is the official Pydantic AI documentation?

https://ai.pydantic.dev/ — it is linked at the bottom of this page, and it is the authority whenever it disagrees with a sample here.

ALSO IN FRAMEWORKS & SDKS

The authority

Official Pydantic AI documentation

Model names, pricing and rate limits change faster than any page here can. When this page and the official docs disagree, the official docs are right.

Read the docs ↗

https://ai.pydantic.dev/

New drops, every week

Free tools & Claude templates, landing regularly.

We keep shipping new free tools, code recipes and Claude Code templates. Follow @freeyantra on Instagram or join the newsletter so you never miss one.

Follow @freeyantra

or get them by email

Double opt-in. Unsubscribe any time. Privacy