Pydantic Ai Agents

Written by

in

Pydantic AI Agents: A Developer’s Guide to Building Type-Safe LLM Applications

Disclosure: This post contains one or more links to providers we have a real, registered affiliate/referral relationship with. We may earn a commission at no extra cost to you if you sign up through them.

Building reliable applications on top of large language models is hard when every response comes back as an unstructured string. Pydantic AI agents solve this by combining the validation guarantees of Pydantic with a lightweight agent framework, so you get structured, type-checked outputs instead of free-form text you have to parse by hand. This guide walks through what pydantic AI agents are, how to set one up, and how to run one in production alongside the rest of your DevOps stack.

What Are Pydantic AI Agents?

Pydantic AI is a Python agent framework built by the team behind the Pydantic validation library. The core idea behind pydantic AI agents is straightforward: you define the shape of the data you expect back from a model using a Pydantic model, and the framework handles prompting, tool calls, and retries until the model produces output that actually matches that schema.

This matters because most agent failures in production aren’t about the model being “wrong” in some abstract sense — they’re about downstream code choking on a malformed JSON blob or a missing field. Pydantic AI agents push that validation earlier in the pipeline, catching schema mismatches before they ever reach your business logic.

A few properties distinguish pydantic AI agents from writing raw API calls yourself:

  • Structured output is enforced via Pydantic models, not hoped for via prompt engineering alone.
  • Tool/function calling is declared with regular Python type hints, so IDEs and static checkers understand your agent’s capabilities.
  • Dependency injection lets you pass database connections, API clients, or config objects into an agent run without global state.
  • The framework is model-agnostic — it supports OpenAI, Anthropic, Gemini, and other providers behind a common interface.
  • If you’ve already read our guide on how to create an AI agent, the concepts here will feel familiar, but Pydantic AI adds a stricter contract layer on top of the usual prompt-tool-response loop.

    Why Type Safety Matters for Agents

    LLM output is inherently probabilistic. Without a validation layer, a single malformed field can crash a downstream service or silently corrupt a database row. Pydantic AI agents catch these problems at the boundary — the moment the model’s response is parsed — rather than three function calls later when a KeyError shows up in production logs.

    Setting Up a Pydantic AI Agents Project

    Getting a basic pydantic AI agents project running takes only a few steps. You’ll need Python 3.9 or newer and an API key for whichever model provider you plan to use.

    Install the package with pip:

    python3 -m venv .venv
    source .venv/bin/activate
    pip install pydantic-ai
    export OPENAI_API_KEY="sk-your-key-here"

    A minimal agent definition looks like this:

    from pydantic import BaseModel
    from pydantic_ai import Agent
    
    class SupportTicket(BaseModel):
        summary: str
        priority: str
        requires_human: bool
    
    agent = Agent(
        "openai:gpt-4o",
        output_type=SupportTicket,
        system_prompt="Classify the incoming support message.",
    )
    
    result = agent.run_sync("My payment failed twice and I need a refund now.")
    print(result.output.priority)

    Notice that result.output is a validated SupportTicket instance, not a raw string. If the model returns something that doesn’t match the schema, Pydantic AI will retry the call with corrective feedback before giving up — you don’t have to write that retry logic yourself.

    Managing Secrets and Environment Variables

    Never hardcode API keys directly into your agent code. If you’re deploying pydantic AI agents inside Docker containers, keep credentials out of the image entirely and inject them at runtime. Our guide on Docker Compose secrets covers the mechanics of keeping API keys out of version control, and the companion piece on managing Docker Compose environment variables is useful once you have multiple agents each needing their own provider keys.

    Core Concepts: Models, Tools, and Dependencies

    Three building blocks show up in almost every pydantic AI agents implementation: the model, the tools it can call, and the dependencies it needs to do its job.

    Defining Tools

    Tools are just Python functions decorated and registered with the agent. Pydantic AI inspects the function signature — including type hints — to build the schema the model uses to call it correctly.

    from pydantic_ai import RunContext
    
    @agent.tool
    def lookup_order(ctx: RunContext[None], order_id: str) -> dict:
        """Fetch order details from the internal order service."""
        return order_service.get(order_id)

    Because the tool signature is strongly typed, the model can’t invent arguments that don’t exist, and your IDE will flag a mismatch before you even run the code.

    Dependency Injection

    Rather than reaching for global variables or os.environ calls inside a tool function, pydantic AI agents support passing a typed dependency object into every run. This keeps agent code testable — you can swap in a mock database client for unit tests without touching the agent’s core logic. This is the same discipline recommended in our broader post on building AI agents: keep external state out of your agent’s core reasoning loop wherever possible.

    Structured Output and Validation

    The single biggest reason teams adopt pydantic AI agents over a bare LLM API call is output validation. Instead of asking a model to “return JSON” and hoping for the best, you declare a Pydantic model and the framework enforces it.

    from pydantic import BaseModel, Field
    from typing import Literal
    
    class Invoice(BaseModel):
        invoice_number: str
        amount_due: float = Field(gt=0)
        currency: Literal["USD", "EUR", "GBP"]
        line_items: list[str]

    If the model tries to return a negative amount_due or a currency outside the allowed set, Pydantic’s own validation (documented in full at the Pydantic docs) rejects it, and the agent framework can automatically re-prompt with the validation error attached. This closes the loop between “the model said something” and “the model said something we can actually trust in our system” without you writing manual try/except blocks around every field.

    Handling Validation Failures Gracefully

    Not every retry succeeds. Production pydantic AI agents should have an explicit fallback path — logging the raw model output, flagging the run for human review, or falling back to a simpler prompt — rather than letting an unhandled ValidationError propagate up and take down a request handler.

    Deploying Pydantic AI Agents in Production

    Once an agent works locally, the next step is running it reliably as a service. Most teams containerize their pydantic AI agents alongside the rest of their application stack using Docker Compose, which keeps the agent process, any supporting database, and a reverse proxy defined in one place.

    services:
      agent-api:
        build: .
        environment:
          - OPENAI_API_KEY=${OPENAI_API_KEY}
        ports:
          - "8000:8000"
        restart: unless-stopped
      redis:
        image: redis:7
        restart: unless-stopped

    A few practical considerations when running pydantic AI agents on a real server rather than a laptop:

  • Set explicit request timeouts on model calls — a hung upstream provider shouldn’t hang your whole service.
  • Log both the input prompt and the validated output for every agent run, so failures are debuggable after the fact.
  • Rate-limit agent endpoints separately from the rest of your API, since LLM calls are far more expensive per request than a typical database read.
  • Run health checks against a lightweight synthetic prompt, not a full production-scale agent call, to avoid burning API quota on every liveness probe.
  • If you’re standing up a new VPS specifically to host an agent workload, a provider like DigitalOcean gives you a straightforward path to a Docker-ready box without managing bare-metal hardware yourself. For teams already comfortable with container orchestration, the setup patterns are the same ones covered in our AI agent framework deployment guide.

    Scaling Beyond a Single Instance

    As traffic grows, a single container running pydantic AI agents will hit concurrency limits imposed by your model provider’s rate limits before it hits CPU limits — LLM calls are I/O-bound, not compute-bound. Horizontal scaling with a queue in front of the agent (Redis or a message broker) is usually more effective than vertical scaling a single instance, since it lets you smooth out bursts without exceeding provider-side rate ceilings.

    Testing and Observability

    Because pydantic AI agents produce validated, typed output, they’re actually easier to unit test than typical LLM integrations. You can assert against real fields instead of doing brittle string matching on raw text.

    def test_ticket_classification():
        result = agent.run_sync("The app crashes every time I open it.")
        assert isinstance(result.output, SupportTicket)
        assert result.output.priority in {"low", "medium", "high"}

    For production observability, capture per-run metadata: token usage, latency, retry counts, and validation failure rates. A rising validation-failure rate over time is often the earliest signal that a model provider changed behavior, or that your schema needs to loosen a constraint that turned out to be too strict for real-world inputs. If your agents run behind a broader automation pipeline, the monitoring patterns described in our SEO automation platform guide — polling intervals, fail-soft subprocess isolation, alert deduplication — translate directly to agent workloads even though the domain is different.


    Recommended: Ready to put this into practice? DigitalOcean is a tool we use for exactly this, and we have a real, disclosed affiliate relationship with them.

    FAQ

    Is Pydantic AI the same thing as the Pydantic validation library?
    No. Pydantic (the library) handles data validation and parsing. Pydantic AI is a separate agent framework, built by the same team, that uses Pydantic models to define and enforce structured output from LLM calls.

    Do pydantic AI agents work with providers other than OpenAI?
    Yes. The framework is designed to be model-agnostic and supports multiple providers behind a common Agent interface, so you can swap models without rewriting your tool definitions or output schemas.

    What happens if the model can’t produce output matching my schema?
    Pydantic AI agents will typically retry with the validation error fed back into the prompt as corrective context. If retries are exhausted, the framework raises an exception you can catch and handle explicitly — it does not silently return invalid data.

    Can I use pydantic AI agents without exposing them as a web API?
    Yes. Agents can run synchronously as part of a script, a scheduled job, or a CLI tool. Wrapping one in a FastAPI or similar service is only necessary if other systems need to call it over HTTP.

    Conclusion

    Pydantic AI agents bring the same discipline that made Pydantic popular for API validation — explicit schemas, clear error messages, fail-fast behavior — to the much less predictable world of LLM output. Instead of parsing raw text and hoping for the best, you get typed, validated results with automatic retry logic built in. For teams already running Python services in Docker, adding pydantic AI agents to the stack is a relatively small operational lift: the same deployment, logging, and secrets-management practices you already use elsewhere apply directly. Start with a single, narrowly-scoped agent and a strict output schema, verify it under real traffic, and expand from there rather than trying to build one agent that handles everything at once.

    Comments

    Leave a Reply

    Your email address will not be published. Required fields are marked *