Pydantic AI v2: Build and Ship Production Python Agents
Pydantic AI v2: type-safe Python agents with structured outputs, DI, and Logfire. Covers v2.0.0 Capabilities, deterministic evals, and LangChain comparison.

Pydantic AI v2: type-safe Python agents with structured outputs, DI, and Logfire. Covers v2.0.0 Capabilities, deterministic evals, and LangChain comparison.

Pydantic AI is a production-grade, open-source Python agent framework that brings type-safe structured outputs, dependency injection, and model-agnostic LLM orchestration to Python automation workflows. Built by the team behind Pydantic, the validation library inside nearly every major Python AI SDK, it crossed 17,969 GitHub stars and 30 million monthly downloads as of June 2026.
The latest release, v2.0.0, shipped June 23, 2026, and introduces the Capabilities API: composable bundles of instructions, tools, hooks, and model settings you attach to any agent.
This guide covers Pydantic AI from first install through production observability, including where it breaks (local models, streaming cancellation) and where it outperforms alternatives (typed outputs, testability, eval pipelines).
RunContext) makes agents unit-testable with TestModel without burning API credits or mocking LLM responses.defer_loading=True.Pydantic AI is a Python-first framework for building AI agents that return validated, typed outputs rather than unstructured strings. The design philosophy, stated in the official docs, is to bring "the FastAPI feeling to GenAI app and agent development."
Samuel Colvin, the creator of both Pydantic and Pydantic AI, observed that virtually every Python AI framework was already built on Pydantic validation, yet none felt as ergonomic as FastAPI. Pydantic AI fills that gap.
The library is MIT-licensed, model-agnostic, and Python-native (requires Python ≥ 3.10). Sequoia Capital led the $12.5M Series A in 2024, bringing total funding to $17.2M. Enterprise customers include Meta, Microsoft, NVIDIA, JPMorgan Chase, Atlassian, Cisco, Duolingo, and NATO.
Interest in Pydantic AI hit its 12-month all-time peak in May 2026 (Google Trends: 100/100) and was still at 89/100 in June 2026, up from a baseline of ~41 in June 2025. That doubled interest tracks a practical shift: teams that shipped LangChain prototypes in 2024-2025 are now refactoring toward production backends that need type guarantees, not prototype chains.
Sebastián Ramírez (@tiangolo) quoted an OpenAI engineer in July 2025: "Pretty much everything operates around FastAPI to create APIs and Pydantic for validation."
If your production stack already runs on FastAPI and Pydantic models, adding Pydantic AI is a pattern extension, not an architecture overhaul.
The v2.0.0 release, published June 23, 2026, adds the Capabilities API, progressive disclosure for large tool libraries, and improved multi-agent ergonomics. The v2.0.0 changelog is the authoritative source for any breaking changes from v1.
Pydantic AI organizes agent behavior around five primitives. Each is a plain Python construct, not a custom DSL.
The Agent class is the primary developer interface: a stateless container bundling instructions, tools, structured output type, dependency type, and model settings.
Component | Role |
|---|---|
Instructions | System prompts, static or dynamic via |
Function tools | Python functions the LLM can call, typed via |
Structured output | Pydantic model defining the required return type |
Dependency type | Typed runtime context for tools and prompts |
LLM model | Default model (overridable per run) |
Capabilities (v2) | Composable bundles of the above |
Agents are stateless: they hold no conversation state between runs. Treat them as global objects. Create one per task type, not one per request.
Instructions vs. system prompts: instructions are the recommended default. When message_history is provided, only the current agent's instructions are sent to the model; prior agents' system prompts are excluded. Use system prompts only when you explicitly want prior context retained across multi-turn workflows.
Structured output is Pydantic AI's core capability. You define a Pydantic model; the framework guarantees a typed Python object back, not a string:
from pydantic import BaseModel
from pydantic_ai import Agent
class CityLocation(BaseModel):
city: str
country: str
agent = Agent('google:gemini-3-flash-preview', output_type=CityLocation)
result = agent.run_sync('Where were the 2024 Olympics held?')
print(result.output)
# CityLocation(city='Paris', country='France')The workflow: Pydantic AI builds a JSON Schema from your model, passes it to the LLM, validates the response, and resends validation errors back to the model for correction, all automatically. If the model can't produce a valid response after retries, it raises an exception. You never write a JSON parse loop.
Pydantic AI's DI system, inspired by FastAPI, supplies data and services to system prompts, tools, and output validators via typed RunContext:
from dataclasses import dataclass
import httpx
from pydantic_ai import Agent, RunContext
@dataclass
class MyDeps:
api_key: str
http_client: httpx.AsyncClient
agent = Agent('openai:gpt-5.2', deps_type=MyDeps)
@agent.tool
async def fetch_data(ctx: RunContext[MyDeps], endpoint: str) -> str:
response = await ctx.deps.http_client.get(
endpoint, headers={'Authorization': ctx.deps.api_key}
)
return response.textThe testing benefit is direct: swap httpx.AsyncClient for a mock without touching the agent or the LLM. Tests run deterministically and don't burn API credits.
Any typed Python function with a docstring becomes a callable tool for the LLM. Pydantic AI auto-generates the JSON schema from type hints. The docstring tells the model when to invoke the tool; the type hints define the interface.
Tool calls (arguments and return values) are automatically traced in Logfire and any OpenTelemetry-compatible backend.
The Capabilities API is the headline feature of v2.0.0 (June 23, 2026). A Capability bundles an agent's instructions, tools, lifecycle hooks, and model settings into one composable unit.
Pydantic described Capabilities on LinkedIn (June 23, 2026): "v2 turns the layer around the agent loop into one thing you compose: the capability."
Progressive disclosure (defer_loading=True) takes this further: a capability's tools, instructions, and hooks load only when the model requests them. For agents with large tool libraries, this keeps context lean and reduces token costs on every run.
Pydantic AI supports five levels of multi-agent complexity, from simplest to most complex:
Because agents are stateless, they can call each other freely without circular dependency concerns. Logfire traces the full chain: which agent handled which step, delegation decisions, latency per agent, and token usage per agent.
Samuel Colvin, initially skeptical of graph-based orchestration, changed his position after seeing real-world use cases:
We've just released @Pydantic AI v0.0.19. This comes with the biggest new feature since we announced PydanticAI — graph support! I was originally cynical about graphs, but I'm now really excited about their use cases, both with GenAI and in general purpose development. Our https://t.co/UHSoF8lyWG
The MCP integration extends reach further. Pydantic AI acts as an MCP client, connecting to any local or remote MCP server. Colvin also added sandboxed Python execution via an MCP server backed by Pyodide:
Weekend work - I've added an MCP server to @pydantic AI to run Python in a @pyodide sandbox. Should allow us to provide a "coding agent" similar to smolagents, except properly sandboxed and therefore safe. https://t.co/UjbH8SHm3n
AG-UI protocol integration streams live agent activity to frontend applications (built with the CopilotKit team), enabling real-time agent status in web UIs without custom WebSocket wiring.
OpenTelemetry tracing is built into Pydantic AI; no extra instrumentation configuration required. Any OTel-compatible backend works. Pydantic Logfire is the tightest integration; a single call instruments the full stack:
import logfire
logfire.instrument_pydantic_ai()What Logfire captures per run:
Logfire pricing (revised January 2026):
Plan | Price | Spans/month |
|---|---|---|
Personal | $0 | |
Team | $49/mo | Higher limits |
Growth | $249/mo | Higher limits |
Enterprise Self-hosted | Custom | Unlimited |
Overage | $2.00/million spans | N/A |
Logfire migrated its backend from Timescale to Apache DataFusion in 2024 for better open-source alignment and query performance. The free tier (10M spans/month) was large enough that teams were running production workloads on it entirely, which prompted the January 2026 pricing revision.
One recurring pain point: the logfire.instrument_pydantic_ai() one-liner is Logfire-specific. Plugging in a custom OTel tracing stack works at the protocol level, but the integration documentation is thinner. Plan for setup friction if your team runs a non-Logfire OTel backend.
Pydantic Evals is a code-first testing framework for evaluating agent behavior against golden datasets:
from pydantic_evals import Case, Dataset
dataset = Dataset(
name='agent_eval',
cases=[
Case(
name='capital_question',
inputs='What is the capital of France?',
expected_output='Paris'
),
]
)
report = dataset.evaluate_sync(my_agent_function)When combined with Logfire, eval results appear in the Logfire UI for visualization and regression tracking. The key design choice is deterministic evaluation against golden datasets, not LLM-as-judge. Colvin at AI Engineer 2026:
"The LLM as a judge is effectively the kind of lunatics running the asylum… if you can have a deterministic eval like this where we're comparing what the result is versus a golden data set, that's much better."
Samuel Colvin in "Agent Optimization with Pydantic AI: GEPA, Evals, Feedback Loops" (AI Engineer, 18:36)
GEPA (Genetic Evaluation-based Prompt Adaptation) iterates eval variants, measures each against a golden dataset, and evolves toward higher-scoring prompts, without touching the model or redeploying. Pydantic posted results on LinkedIn (June 2026):
That 9.7-percentage-point gain on the same model illustrates why systematic evaluation outperforms ad-hoc prompt tweaking. Most teams skip building a golden dataset because the upfront effort feels high.
Start with 20-50 hand-labeled examples from production traces. Colvin's own demo assembled initial labels using Claude Opus, then reviewed them manually; imperfect but immediately useful for GEPA to optimize against.
The clinical RAG deployment built by Vstorm and Schmitt-Thompson Clinical Content illustrates the production ceiling: a four-stage agentic RAG pipeline on Pydantic AI for nurse triage guidelines, validated across 329 clinician-reviewed scenarios with 0% hallucinations. That result required both structured output contracts and Logfire tracing of every step for auditability.
Three failure classes surface repeatedly in production, and none of the top-20 SERP results on Pydantic AI covers them.
An agent returns worse answers after a prompt or model change, but no error is raised, no latency spike appears, and monitoring dashboards stay green. The only signal is user complaints.
Pydantic flagged this on LinkedIn (June 16, 2026):
"Your AI was fine last week. New prompt, new model, answers quietly got worse. Nothing errored. Sound familiar?"
Traditional backend observability was built for crashes and latency spikes, not quality degradation in stateful LLM outputs. The only reliable defense is a continuous eval baseline: run your golden dataset against production on every deploy and page when the score drops below threshold.
Long-running agent loops accumulate context and tool call history in memory. Without explicit message pruning or history caps, a multi-hour agent run hits OOM, especially in multi-agent delegation chains where each sub-agent appends to a shared history object.
The fix is explicit: set max_messages or prune message_history on a rolling window before passing it to the next agent call. Any OTel trace of token counts per run surfaces the growth pattern before it kills the process.
When an agent calls 14+ downstream services and one times out, the default error is opaque: the agent loop exits without specifying which tool call failed. Logfire's per-tool-call traces make attribution explicit: the failed span names the tool, the arguments, and the elapsed time at failure.
Temporal Technologies said on LinkedIn (June 18, 2026): "Strip away runtime, memory, identity, tool access, and observability, and your agent is just a chat completion."
Pydantic AI handles the model interface; Logfire handles the observability; a durable execution layer (Temporal or similar) handles crash recovery and retries. Without all three, timeout attribution becomes a multi-hour grep exercise.
The LangChain guide on Pynions covers that framework's LCEL, RAG, and LangGraph patterns in depth. The distinction for production decisions:
Dimension | Pydantic AI v2.0.0 | LangChain v0.3.x |
|---|---|---|
Abstraction | Python-native, minimal layers | Chain/Graph (LCEL DSL) |
Type safety | First-class (Pydantic v2) | Optional / add-on |
Structured output | Built-in, validated by default | Via output parsers |
Dependency injection | Typed RunContext | Not native |
Async | Native async-first | Supported, not default |
Observability | Logfire + any OTel backend | LangSmith (paid SaaS) |
Ecosystem | Growing (focused) | Massive (600+ integrations) |
Learning curve | Low (plain Python) | Medium-High (custom DSL) |
Best for | Production backends, typed services | Rapid prototyping, broad integrations |
Practitioner verdict from r/LangChain:
"Refactored everything and went with every project to PydanticAI. (And never touched LC/LG since over a year.)"
u/Charming_Support726 in r/LangChain (2026)
Kunal Ganglani's 2026 comparison: "Pydantic AI wins for production systems that need validated, structured outputs; LangChain wins when you need to integrate quickly with a wide ecosystem and prototype fast."
The migration from LangChain to Pydantic AI is a significant rewrite, not a drop-in swap. Different abstractions, different DI model, different observability story. Budget the refactor accordingly.
Framework | Paradigm | Best for |
|---|---|---|
Type-safe Python | Production backends, typed services | |
LangChain / LangGraph | Chain/graph composition | Broad integrations, rapid prototyping |
RAG pipelines | Retrieval-heavy use cases | |
Role-based multi-agent | Team-of-agent simulations | |
Code-first, minimal | Lightweight tasks, local models | |
Provider-specific | OpenAI-first workflows |
Instructor patches the OpenAI client for single-step structured extraction. It is simpler and faster to start with for straightforward extraction tasks.
The friction appears when you add multi-step workflows, tool calls, and dependency injection; that's where Pydantic AI's abstractions earn their weight. Practitioners on Reddit describe an organic migration path: start with Instructor, hit its tool-call ceiling, graduate to Pydantic AI when multi-step agent workflows are needed.
Pydantic AI's structured output relies on tool calling (function calling). Smaller models, including Mistral-7B and Qwen 3.6B, fail at this non-gracefully: function calls cascade into malformed JSON that the Pydantic validator rejects, and the framework has no graceful fallback mode.
One r/LocalLLaMA practitioner described the experience directly:
"I really wanted to like it, but their way to create structured output by 'abusing' tool calling didn't really work with most 'local' models or custom APIs. In the end I stayed with DSPy which is much more flexible."
u/nore_se_kra in r/LocalLLaMA (2025)
Community benchmarks on r/LocalLLaMA show DeepSeek-R1 hitting 100% task success on smolagents vs 40-55% on structured-output-first frameworks. If local inference is your constraint, test your specific model against Pydantic AI's tool calling before committing to the framework.
The Pydantic validation library has ~500 million monthly PyPI downloads. Pydantic AI has ~30 million.
These are separate products. pip install pydantic does not install the agent framework. Endorsements of the validation library (near-universal in Python) are not endorsements of the agent framework.
Building a golden dataset upfront feels expensive. Running one after a silent degradation incident in production costs more.
Start with 20-50 hand-labeled examples from production traces. GEPA can optimize against a small dataset immediately, and a baseline eval catches quality regressions before users surface them.
The logfire.instrument_pydantic_ai() one-liner adds per-tool-call tracing with zero application code changes. Without it, debugging tool call failures, timeout attribution, and token accumulation requires reconstructing context from unstructured logs. Add it before shipping to production, not after the first incident.
Agents are stateless, but message_history passed between multi-agent runs is not. Without explicit pruning or a max_messages cap, delegation chains in long-running workflows accumulate history that grows with every call.
OOM failures in overnight agent runs almost always trace back to unbounded history objects. Set the limit before you ship.

pytest wins on features and developer adoption; unittest ships with Python. The practical story: pytest runs unittest tests natively, so most teams use both.

A complete guide to installing, configuring, and migrating to Ruff — the Rust-powered Python linter and formatter that replaces Flake8, Black, isort, and more a

Learn Polars Python with real code examples and 2026 benchmarks. Filter 14 GB Parquet 11x faster than pandas using lazy evaluation.