Skip to content
IRC-CodingIRC-Coding
Pydantic AIAIPythonType SafetyLLMOpenAIAnthropicValidation

Pydantic AI 2.0 Basics: Type-Safe AI Applications

Learn Pydantic AI 2.0 for structured AI outputs, type safety, validation, and LLM integration with OpenAI, Anthropic and local models.

S

schutzgeist

6 min read
Pydantic AI 2.0 Basics: Type-Safe AI Applications

Pydantic AI 2.0 Essentials: Building Type-Safe AI Applications

Pydantic AI 2.0 is a framework for developing AI applications with type safety and validation built in. It enables structured outputs from Large Language Models (LLMs) and integrates seamlessly with Pydantic models.

What is Pydantic AI?

Pydantic AI is a Python library built on top of Pydantic and designed specifically for working with LLMs. It provides:

  • Type-Safe AI Outputs: AI responses are automatically converted to Pydantic models
  • Validation: Inputs and outputs are validated against schemas
  • Multi-Provider Support: OpenAI, Anthropic, Groq, and local models (Ollama, vLLM)
  • Streaming: Real-time streaming of structured data
  • Tool Calling: Built-in support for function calling

Why Use Pydantic AI?

Typical Users

Pydantic AI is ideal for:

  • Python developers who already use Pydantic and value type safety
  • Backend developers who want to integrate structured AI outputs into APIs
  • Data engineers who need AI-powered data extraction and validation
  • AI application developers who require reliable, validated outputs

Who Benefits?

Who gets the most value from Pydantic AI in a project?

  • End users: Receive consistent, validated data instead of unstructured text responses
  • Developers: Save time on validation and error handling
  • Systems: Integration is simpler since outputs are already typed
  • QA teams: Fewer tests needed since validation happens automatically

Project Considerations: When Should You Use Pydantic AI?

Before integrating Pydantic AI into your project, ask yourself these questions:

1. Do you need structured outputs?

  • Yes: Pydantic AI is perfect if you need AI responses in specific formats (JSON, objects, enums)
  • No: If you only need plain text, Pydantic AI might be overkill

2. Are you already using Python and Pydantic?

  • Yes: Pydantic AI integrates seamlessly into your existing ecosystem
  • No: If you don’t use Python, Pydantic AI isn’t suitable (Python-specific)

3. How important is type safety for your project?

  • Critical: Pydantic AI offers compile-time and runtime validation
  • ⚠️ Moderate: Pydantic AI can help, but alternatives like LangChain might suffice
  • Not important: If you don’t need strict typing, simpler alternatives are better

4. Do you plan to support multiple providers?

  • Yes: Pydantic AI makes it simple to switch between OpenAI, Anthropic, Groq, and local models
  • No: If you’re locked into one provider, this isn’t a deciding factor

5. How complex is your AI integration?

  • Simple to moderate: Pydantic AI excels at structured outputs and tool calling
  • ⚠️ Very complex: For sophisticated multi-agent systems, LangChain or LangGraph might be better suited

Decision Matrix

RequirementPydantic AILangChainDirect API
Structured Outputs✅ Optimal⚠️ Possible❌ Manual
Type Safety✅ Native⚠️ Limited❌ None
Simplicity✅ High⚠️ Moderate⚠️ Moderate
Multi-Agent⚠️ Limited✅ Strong❌ No
Provider Agnostic✅ Yes✅ Yes❌ No
Learning Curve🟢 Low🟡 Moderate🟡 Moderate

When NOT to Use Pydantic AI

  • You don’t use Python
  • You only need plain text without structure
  • You require complex multi-agent orchestration (use LangGraph)
  • You want minimal dependencies
  • Your project is small and simple (direct API is enough)

When to Use Pydantic AI

  • You’re already using Python and Pydantic
  • You need reliable, validated AI outputs
  • Type safety matters to you
  • You want the flexibility to switch LLM providers
  • You’re building APIs with AI integration
  • You want tool calling with validated parameters

Real-World Example: Company with API Using Langdock

Within a company, I use FastAPI with Langdock to create a GDPR-compliant environment. The chat works, chatbots work. So why consider Pydantic AI?

Answer: Probably not in your current scenario.

If your chatbot only returns plain text and doesn’t need to produce structured data, Pydantic AI is unnecessary. Langdock and FastAPI already handle GDPR compliance and chat functionality.

When Pydantic AI could still be useful:

Let’s say we expand the scenario slightly:

# Currently: Just plain text
response = "Hello! I can help you."

# With Pydantic AI: Structured outputs
from pydantic import BaseModel
from pydantic_ai import Agent

class SupportTicket(BaseModel):
    category: str  # e.g., "technical", "billing", "hr"
    priority: str  # e.g., "high", "medium", "low"
    description: str
    assigned_to: str | None = None

# AI analyzes the request and returns structured data
ticket = agent.run_sync(
    "My computer won't start, I need help urgently!",
    result_type=SupportTicket
)
# SupportTicket(category='technical', priority='high', description='My computer won\'t start...', assigned_to=None)

Concrete example for your company:

  • Without Pydantic AI: Chatbot returns text → You must parse it to identify categories
  • With Pydantic AI: Chatbot returns a SupportTicket object directly → You can save it to your database without parsing

Takeaway for your scenario:

  • If you only need chat: No Pydantic AI required
  • If you want to extract structured data from chats (tickets, forms, reports): Pydantic AI very useful

Key insight:

A chatbot doesn’t need Pydantic AI. An AI agent usually does. Typical use cases are code assistants, research agents, automations, workflows with multiple tools, or applications that require typed and validated outputs.

Installation

pip install pydantic-ai

For specific providers:

pip install pydantic-ai[openai]  # OpenAI
pip install pydantic-ai[anthropic]  # Anthropic
pip install pydantic-ai[openai,anthropic]  # Both

Basics: Structured Outputs

Simple Example

from pydantic import BaseModel
from pydantic_ai import Agent

class UserResponse(BaseModel):
    name: str
    age: int
    email: str

agent = Agent('openai:gpt-4o')

result = agent.run_sync(
    'Create a user profile for a developer',
    result_type=UserResponse
)

print(result.data)
# UserResponse(name='Max Mustermann', age=28, email='max@example.com')

With System Prompt

from pydantic_ai import Agent, SystemPrompt

agent = Agent(
    'openai:gpt-4o',
    system_prompt=SystemPrompt('You are a helpful assistant for developers.')
)

result = agent.run_sync(
    'Create a profile for a Python developer',
    result_type=UserResponse
)

More Complex Models

Nested Structures

from typing import List
from pydantic import BaseModel

class Skill(BaseModel):
    name: str
    years_experience: int
    level: str  # beginner, intermediate, advanced

class DeveloperProfile(BaseModel):
    name: str
    role: str
    skills: List[Skill]
    github_url: str | None = None
    available_for_hire: bool

agent = Agent('openai:gpt-4o')

result = agent.run_sync(
    'Erstelle ein detailliertes Profil für einen Senior Python-Entwickler',
    result_type=DeveloperProfile
)

Enum Validation

from enum import Enum
from pydantic import BaseModel

class SkillLevel(str, Enum):
    BEGINNER = 'beginner'
    INTERMEDIATE = 'intermediate'
    ADVANCED = 'advanced'
    EXPERT = 'expert'

class Skill(BaseModel):
    name: str
    level: SkillLevel

Multi-Provider Support

OpenAI

from pydantic_ai import Agent

agent = Agent('openai:gpt-4o')
result = agent.run_sync('Hallo Welt!')

Anthropic

agent = Agent('anthropic:claude-3-5-sonnet-20241022')
result = agent.run_sync('Hallo Welt!')

Local Models (Ollama)

agent = Agent('ollama:llama3.2')
result = agent.run_sync('Hallo Welt!')

Groq

agent = Agent('groq:llama-3.1-70b-versatile')
result = agent.run_sync('Hallo Welt!')

Tool Calling

Simple Tool

from pydantic_ai import Agent, Tool

def get_weather(location: str) -> str:
    """Holt das Wetter für einen Ort."""
    # In der Realität: API-Aufruf
    return f'In {location} sind es 22°C.'

agent = Agent('openai:gpt-4o', tools=[Tool(get_weather)])

result = agent.run_sync('Wie ist das Wetter in Berlin?')

With Pydantic Models

from pydantic import BaseModel

class WeatherQuery(BaseModel):
    location: str
    unit: str = 'celsius'

def get_weather(query: WeatherQuery) -> str:
    return f'In {query.location} sind es 22°{query.unit}.'

agent = Agent('openai:gpt-4o', tools=[Tool(get_weather)])

Streaming

Text Streaming

agent = Agent('openai:gpt-4o')

async for chunk in agent.run_stream('Erzähle mir eine Geschichte'):
    print(chunk.content, end='')

Structured Streaming

async for chunk in agent.run_stream(
    'Erstelle ein Benutzerprofil',
    result_type=UserResponse
):
    if chunk.content:
        print(chunk.content, end='')

Error Handling

Validation Errors

from pydantic import ValidationError

try:
    result = agent.run_sync(
        'Erstelle ein Profil',
        result_type=UserResponse
    )
except ValidationError as e:
    print(f'Validierungsfehler: {e}')

Retry Logic

from pydantic_ai import Agent, RetryPolicy

agent = Agent(
    'openai:gpt-4o',
    retry_policy=RetryPolicy(max_retries=3)
)

Best Practices

1. Define Clear Models

# ✅ Good
class UserProfile(BaseModel):
    name: str
    email: str
    age: int

# ❌ Bad
class Response(BaseModel):
    data: dict  # Keine Type-Safety

2. Use System Prompts

agent = Agent(
    'openai:gpt-4o',
    system_prompt=SystemPrompt(
        'Du bist ein technischer Dokumentations-Assistent. '
        'Antworte präzise und strukturiert.'
    )
)

3. Leverage Validation

from pydantic import field_validator

class UserProfile(BaseModel):
    email: str
    
    @field_validator('email')
    def validate_email(cls, v):
        if '@' not in v:
            raise ValueError('Ungültige E-Mail')
        return v

4. Optimize Costs

# Smaller models for simple tasks
agent_simple = Agent('openai:gpt-4o-mini')

# Larger models for complex tasks
agent_complex = Agent('openai:gpt-4o')

Integration with Existing Projects

FastAPI Integration

from fastapi import FastAPI
from pydantic_ai import Agent

app = FastAPI()
agent = Agent('openai:gpt-4o')

@app.post('/generate')
async def generate(prompt: str):
    result = await agent.run(prompt)
    return {'response': result.content}

Async/Await

import asyncio

async def main():
    agent = Agent('openai:gpt-4o')
    result = await agent.run('Hallo Welt!')
    print(result.content)

asyncio.run(main())

Common Mistakes

1. Missing API Keys

import os
from pydantic_ai import Agent

# Set API key
os.environ['OPENAI_API_KEY'] = 'sk-...'

agent = Agent('openai:gpt-4o')

2. Invalid Models

# ✅ Correct
agent = Agent('openai:gpt-4o')

# ❌ Wrong
agent = Agent('openai:gpt-5')  # Modell existiert nicht

3. Missing Type Hints

# ✅ With Type Hints
def get_weather(location: str) -> str:
    return f'Wetter in {location}'

# ❌ Without Type Hints
def get_weather(location):
    return f'Wetter in {location}'

Pydantic AI vs. Alternatives

FeaturePydantic AILangChainLlamaIndex
Type-Safety✅ Native⚠️ Limited⚠️ Limited
Pydantic Integration✅ Full⚠️ Partial⚠️ Partial
Multi-Provider✅ Easy✅ Yes✅ Yes
Streaming✅ Yes✅ Yes✅ Yes
Tool Calling✅ Native✅ Yes✅ Yes
Learning Curve🟢 Low🟡 Medium🟡 Medium

Resources

Back to Blog
Share:

Related Posts