Building Production-Ready AI Agents with Pydantic AI

Overview: Beyond the Chatbot

Building AI applications often involves wrestling with unpredictable text outputs. While Large Language Models (LLMs) like

are brilliant at reasoning, they lack the structural discipline required for production software.
Pydantic AI
solves this by extending the popular
Pydantic
validation library to the world of agents. It allows developers to inject business logic, connect to real-world dependencies like databases, and enforce type-safe outputs that your application can actually trust. This guide demonstrates how to build a healthcare triage assistant that uses these features to assess patient urgency based on live data.

Prerequisites

To follow this tutorial, you should have a solid grasp of Python 3.10+, specifically asynchronous programming with asyncio. You should also be familiar with Type Hinting and the basic concepts of Pydantic data validation. Finally, you will need an OpenAI API Key to power the agent's reasoning.

Building Production-Ready AI Agents with Pydantic AI
Build Production-Ready AI Agents in Python with Pydantic AI

Key Libraries & Tools

  • Pydantic AI: A framework for building robust AI agents with structured validation.
  • Pydantic: Used for defining data models and validating agent outputs.
  • OpenAI GPT-4: The foundational model used for reasoning and natural language processing.
  • Asyncio: Python's standard library for writing concurrent code using the async/await syntax.

Code Walkthrough: The Triage Agent

1. Defining Dependencies and Models

First, we establish the scaffolding. We define what the agent needs to know (dependencies) and what it must return (output model).

from pydantic import BaseModel, Field
from dataclasses import dataclass

@dataclass
class TriageDependencies:
    patient_id: int
    db_conn: "DatabaseConnection"

class TriageOutput(BaseModel):
    response_text: str = Field(description="Message to the patient")
    escalate: bool = Field(description="Whether to escalate to a human")
    urgency: int = Field(ge=1, le=10, description="Urgency level")

2. Initializing the Agent

We initialize the Agent class by specifying the model, dependencies, and the expected output type.

from pydantic_ai import Agent

triage_agent = Agent(
    'openai:gpt-4o',
    deps_type=TriageDependencies,
    result_type=TriageOutput,
    system_prompt="You are a triage assistant assessing patient urgency."
)

3. Injecting Context and Tools

Dynamic prompts and tools allow the agent to fetch real-time data. The @triage_agent.system_prompt decorator lets you pull patient-specific info, while @triage_agent.tool gives the LLM the ability to "call" functions like fetching vitals.

@triage_agent.system_prompt
async def add_patient_name(ctx: RunContext[TriageDependencies]) -> str:
    name = await ctx.deps.db_conn.get_patient_name(ctx.deps.patient_id)
    return f"The patient's name is {name}."

@triage_agent.tool
async def get_vitals(ctx: RunContext[TriageDependencies]) -> str:
    return await ctx.deps.db_conn.get_latest_vitals(ctx.deps.patient_id)

Syntax Notes: RunContext

The RunContext is a pivotal generic type in Pydantic AI. It carries your custom dependencies through the agent's lifecycle, ensuring that your tools and dynamic prompts always have access to your database or API clients without relying on global variables.

Practical Examples

This pattern is ideal for Financial Risk Assessment, where an agent must pull a credit score and return a structured 'approve/deny' decision, or Automated Customer Support, where the agent queries a shipment database to provide precise tracking updates rather than generic hallucinations.

Tips & Gotchas

  • Parenthesis Pitfalls: Code completion tools often struggle with the nested structure of agent definitions; double-check your closing brackets.
  • Graph Complexity: While Pydantic AI supports complex graph-based workflows, start with a single agent. Only move to nodes and edges if your logic is too complex for tools and dynamic prompts.
3 min read