AIAgentsPydanticPydantic AILogfirePythonProductionType SafetyMCPDurable Execution

Pydantic AI in production 2026: building typed agents that survive the model market

How Pydantic AI grew from a typed wrapper around LLM calls into the default Python agent framework for production teams in 2026. Core primitives, a working agent with typed deps and output, the Logfire and Evals story, durable execution with Temporal and DBOS, and how the framework compares to LangGraph, LangChain, and CrewAI.

By Reactify Solutions16 min read
Pydantic AI in production 2026: building typed agents that survive the model market

For most of 2024 the conversation about Python agent frameworks was a fight between LangChain and a long tail of newcomers. By the middle of 2026 a different framework has quietly become the default pick on new client engagements: Pydantic AI. The repository at github.com/pydantic/pydantic-ai crossed 17,000 stars, the 1.0 release shipped in September 2025 with an API stability commitment, and the same team that built the validation library every other agent framework depends on now ships an end-to-end stack: Pydantic AI for the agent, a Logfire backend for observability, Pydantic Evals for regression testing, and the Pydantic AI Gateway for routing and cost control. The bet is simple: agents are software, and software needs types.

Why typed agents matter

The early agent frameworks treated LLM calls like a chat interface. You sent a string, you got a string, you parsed the string with a regex or a JSON loader and hoped the model held its shape. That worked for demos. It does not work in production. The shape of the output matters because the next step in the workflow depends on it. The shape of the input matters because the model needs to know what tools exist and what they expect. Once a workflow has more than two LLM calls and one tool, the string-in-string-out abstraction collapses under its own weight.

Pydantic AI starts from the other end. Every agent has a typed dependency object the framework passes to every tool. Every tool is a typed Python function. Every output is a Pydantic model the framework validates before it ever reaches your code. If the model returns malformed JSON, the framework re-prompts. If the validator rejects the output, the framework re-prompts. If a tool raises a ModelRetry, the framework re-prompts with the error in context. The application code only sees outputs that already passed the same checks any other piece of Python code in your service would have to pass.

That changes what production looks like. Logs are typed. Tests are typed. The schema is a single source of truth that flows from the agent definition into the FastAPI route, into the database column, into the OpenTelemetry span. Failures get caught at the boundary, not three function calls deeper where the stack trace stops being useful.

The 2026 numbers

  • 17,000+ GitHub stars on the pydantic/pydantic-ai repository as of mid-2026, up from around 6,000 at the start of 2025. The 1.0 release landed in September 2025 with an explicit commitment to a stable surface area until at least V2 in April 2026.
  • The Pydantic library powers the rest of the stack. OpenAI, Anthropic, and Google all use Pydantic for structured-output validation inside their own SDKs. The Pydantic AI team ships the agent layer on top of the validator the rest of the industry already runs.
  • 22+ supported providers out of the box: OpenAI, Anthropic, Gemini, DeepSeek, Grok, Cohere, Mistral, Perplexity; Azure AI Foundry, Bedrock, Vertex, Ollama, LiteLLM, Groq, OpenRouter, Together AI, Fireworks, Cerebras, Hugging Face, GitHub Models, Heroku AI, Vercel AI Gateway, plus regional clouds like Nebius, OVHcloud, Alibaba Cloud, and SambaNova.
  • Four production durable-execution backends. Temporal, DBOS, Prefect, and Restate are all officially supported. The same agent definition runs in-process, on a Temporal worker, or checkpointed to a Postgres-backed DBOS workflow with two-line changes.
  • Thoughtworks moved Pydantic AI to Adopt on the Technology Radar in 2026, citing type-safe structured outputs and production-grade observability as the reasons to default to it for new Python agent work.

Core concepts: Agent, Tool, deps, output_type

Pydantic AI is a small framework. The whole surface area you actually use in production is four primitives. Learn them well and the docs become easy to scan because everything else is built on top.

The Agent is the unit of work. It binds a model, a system prompt, a typed deps schema, a typed output schema, and a set of tools into one object you can call. Agents are generic over deps and output, so the type checker knows what every call returns and what every tool gets passed.

A Tool is a typed Python function the agent can invoke. Pydantic AI generates a JSON schema from the function signature and the docstring, sends that schema to the model in the tool-calling format the provider expects, validates the model’s arguments against the schema, and only then calls the function. If validation fails, the framework raises a ModelRetry and the model gets another attempt with the error message in context.

Deps is the dependency object. It is the Pydantic AI version of a FastAPI dependency: a typed bundle of database connections, HTTP clients, user identity, and feature flags that every tool receives. Deps are how you keep agents testable. In production the deps object holds the live database; in tests it holds a fake one; the agent code does not change.

output_type is the Pydantic model the agent must return. Pydantic AI uses it to drive structured output: the framework sends the schema to the model, parses the response, validates it, and re-prompts on failure. The application code sees a typed object, never a raw string.

Architecture: how Pydantic AI fits in a service

The mental model for a Pydantic AI deployment is the same mental model you already have for FastAPI. The agent is a callable; the deps are dependency-injected; the output is a Pydantic model; the wire format is JSON. Everything else is the surrounding service.

Pydantic AI request path
bash
Client (web, mobile, another service)
        |
        |  POST /support  { "query": "..." }
        v
+----------------------------------------------------+
|  FastAPI route handler                             |
|                                                    |
|  1. Build deps (DB pool, HTTP client, user_id)     |
|  2. Call agent.run(prompt, deps=deps)              |
+----------------------------------------------------+
        |
        v
+----------------------------------------------------+
|  Pydantic AI Agent                                 |
|                                                    |
|  1. Send system prompt + tool schemas to model     |
|  2. Stream tool calls -> validate args -> run tool |
|     - tool can call DB / API / MCP server          |
|     - ModelRetry re-prompts with error in context  |
|  3. Receive structured output                      |
|  4. Validate against output_type (Pydantic model)  |
|  5. Re-prompt on validation failure                |
+----------------------------------------------------+
        |
        v
+----------------------------------------------------+
|  Logfire (OpenTelemetry)                           |
|  - one trace per run, tool calls as child spans    |
|  - cost, tokens, latency, model, errors            |
+----------------------------------------------------+
        |
        v
   Typed Pydantic model back to the route handler

Three details in this flow are worth pulling out because they are the production hooks. The deps object is built per request in the route handler, so the database session, the request id, and the user identity all flow into every tool the agent calls. The validation step on the output is the boundary that catches bad model outputs before they reach the rest of the service. The Logfire span is automatic; if the agent is imported and a Logfire token is configured, every run becomes a trace with tool calls as child spans and token cost as a span attribute.

A working agent with typed deps and output

The snippet below is the shape we use as a starting point on client engagements. A small support agent that looks up a customer’s recent orders, decides whether to escalate, and returns a structured response the caller can act on. Notice that the agent code itself is short; the work is in the type definitions.

support_agent.py
python
from dataclasses import dataclass
from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext

# 1. Deps: everything the tools need to do real work.
#    In tests, swap the pool for a fake. The agent code never changes.
@dataclass
class SupportDeps:
    db_pool: "asyncpg.Pool"
    customer_id: int

# 2. Output: the Pydantic model the agent MUST return.
#    The framework validates and re-prompts on failure.
class SupportResponse(BaseModel):
    reply: str = Field(description="Plain-text reply for the user.")
    sentiment: str = Field(description="positive | neutral | negative")
    escalate: bool = Field(description="True if a human should review.")
    confidence: float = Field(ge=0.0, le=1.0)

# 3. Agent: model, system prompt, deps type, output type.
support_agent = Agent[SupportDeps, SupportResponse](
    "anthropic:claude-sonnet-4.6",
    deps_type=SupportDeps,
    output_type=SupportResponse,
    system_prompt=(
        "You are a careful customer-support agent. "
        "Use the recent_orders tool before answering shipping questions. "
        "If unsure, set escalate=True."
    ),
)

# 4. Tool: typed function. The schema goes to the model automatically.
@support_agent.tool
async def recent_orders(ctx: RunContext[SupportDeps], limit: int = 5) -> list[dict]:
    """Return the customer's most recent orders, newest first."""
    rows = await ctx.deps.db_pool.fetch(
        "SELECT id, total_cents, status, placed_at "
        "FROM orders WHERE customer_id = $1 "
        "ORDER BY placed_at DESC LIMIT $2",
        ctx.deps.customer_id,
        limit,
    )
    return [dict(r) for r in rows]

# 5. Call site: looks like any other FastAPI dependency-injected handler.
async def handle_support(query: str, deps: SupportDeps) -> SupportResponse:
    result = await support_agent.run(query, deps=deps)
    return result.output  # already a typed SupportResponse

Five things in this code carry the production work. The deps dataclass is the seam that makes the agent testable in isolation: in a unit test you build a SupportDeps with a fake pool, in production you build it with the live pool, and nothing else moves. The Pydantic model on output_type becomes the JSON schema the model sees and the validator the framework runs after the model responds, which means sentiment is always one of three values and confidence is always a float between 0 and 1. The tool decorator turns the function signature and the docstring into the tool schema the model receives, so changing the limit default in Python also changes what the model sees without any extra registration. The RunContext gives the tool access to deps without making them a global. And the .output attribute on the result is statically typed as SupportResponse, so the rest of the codebase can use the response without any casting or runtime type guards.

Toolsets, MCP, and reusable capability bundles

A real production agent rarely has just three tools. By the time the workflow is interesting, the agent has a database toolset, a search toolset, a payments toolset, and an MCP connection to an internal tool server. Pydantic AI handles this through Toolsets: composable bundles of tools, hooks, and model settings that an agent can mount the same way a FastAPI router gets mounted on an app.

The MCP integration is first-class. Pydantic AI ships an MCPClient that connects to any MCP server, discovers its tools, and exposes them to the agent as if they were locally defined. The schema flows from the MCP server through the framework to the model with no manual wiring. When the Anthropic, OpenAI, and Microsoft MCP servers ship a new tool, the agent picks it up on the next process start without code changes.

The same Toolset abstraction is what makes the Pydantic AI ecosystem feel like a platform rather than a library. The built-in toolsets for web search and Pydantic-style thinking plug in the same way a custom toolset does. The deferred-tool pattern lets a tool pause the agent loop, return control to the caller, and resume the loop once a human approves or rejects the proposed action: the human-in-the-loop pattern without a separate state machine.

Durable execution: Temporal, DBOS, Prefect, Restate

Agents are long-running by default. A real workflow spans several model calls, several tool calls, and sometimes a human approval that lands hours later. Any of those steps can fail because the model timed out, the database was restarted, the pod was rescheduled, or the deploy went out mid-run. In a stateless agent that means starting the workflow over, paying for the tokens again, and explaining to the user why the same operation now produces a different result. Durable execution solves this by checkpointing the state after every step.

Pydantic AI supports four durable backends out of the box: Temporal, DBOS, Prefect, and Restate. Temporal is the established choice; it runs on its own server, supports replay-based fault tolerance, and treats the agent as a workflow whose state survives any pod restart. DBOS is the 2026 lightweight option; it checkpoints every step to Postgres, so any team that already runs Postgres can pick up durable agents without adding a new piece of infrastructure. The Pydantic team explicitly recommends Postgres-backed DBOS for production deployments, and SQLite for local development.

durable_agent.py
python
# Same agent definition, two lines to make it durable.
from dbos import DBOS
from pydantic_ai.durable_exec.dbos import DBOSAgent

DBOS()                                # initialize DBOS with the connection string
durable = DBOSAgent(support_agent)    # wrap any Pydantic AI agent

# Now every step (model call, tool call) is checkpointed.
# A crash mid-run resumes exactly where it left off, no token loss.
result = await durable.run(
    "Where is my order?",
    deps=SupportDeps(db_pool=pool, customer_id=42),
)

The Pydantic AI design choice that makes this work is that durable execution is a wrapper around the same Agent object you use in unit tests. The agent definition does not change. The tools do not change. The output type does not change. The only difference is that the wrapped agent runs each step through the durable backend, which means the same code ships to dev, staging, and prod without a rewrite when the reliability requirements tighten.

Logfire: observability built for agents

Logfire is the observability product the Pydantic team ships alongside Pydantic AI. It is built on OpenTelemetry, so it ingests traces from any OTel source: FastAPI, the database driver, the Redis client, the background worker. The Pydantic AI integration adds first-class spans for agent runs, tool calls, model calls, token counts, and cost per provider. Every agent.run call becomes one trace with the tool calls as child spans and the structured output as a span attribute the platform can index.

Three Logfire features matter in production. The conversation panel renders the messages, tool calls, and tool results as a readable transcript next to the trace, so debugging an agent run does not require correlating raw OpenTelemetry attributes. The cost view aggregates token spend per agent, per user, and per model, so the finance question of which feature ate the budget has an answer that updates in real time. The MCP server Logfire exposes lets the same agent framework answer questions about its own production behavior: a Pydantic AI agent can call the Logfire MCP server to ask “show me every trace from the last hour where tool calls exceeded five” and act on the answer.

The integration is two lines of code. Import logfire, call logfire.configure(), and the Pydantic AI auto- instrumentation picks up every agent in the process. Logs ship to Logfire Cloud, to a self-hosted Logfire instance, or to any OTel collector you already run.

Pydantic Evals: regression tests for agents

Pydantic Evals is the third leg of the stack. It is a code-first eval framework: you write Python, you define cases, you write evaluators, and you get a structured report you can wire into CI. The point is not to replace manual review; the point is to catch the regression where a prompt change that helped one case broke twenty others, and catch it before the change reaches production.

The shape is familiar to anyone who has used pytest. A Dataset holds the cases. Each Case has inputs, expected outputs, and metadata. An Evaluator scores the run, either with a deterministic check (does the structured output contain the right ticket id) or with an LLM-as-judge call (does the reply read as polite). The framework runs the agent over the cases in parallel, scores every result, and writes a report that can be opened in Logfire for side-by-side comparison across runs. The same Dataset can feed a CI gate and a nightly drift check.

Real-world use cases

Four production stories illustrate why teams moved to Pydantic AI in 2026. Each one tells a different part of the value the framework delivers.

MindsDB is a data-analyst AI company that ran into wall after wall on LangChain: performance issues in agent loops, lack of programmatic control over what the agent did next, and debugging sessions that stretched into days. The team migrated to Pydantic AI with the explicit philosophy of treating agents as software, with structured data validation at every boundary and explicit state management instead of an opaque chain. The migration paid off in two places: tool calls became typed and testable, and the production debugging story shifted from reading chain logs to reading Logfire traces with tool inputs and outputs already validated against schemas.

General Intelligence Company (GIC) moved to Logfire and Pydantic AI to build a live evaluation pipeline for autonomous agents. The reported result was a 150x query-performance improvement against their previous observability stack, with no rate limits on the eval queries and real-time deviation detection that lets the agent self-correct based on its own production telemetry. The pattern is unusual but instructive: the same agent framework powers both the production agent and the eval loop that watches the production agent.

Overjoy replaced LangChain and LangSmith with Pydantic AI and Logfire. The published metrics are worth reading honestly: debugging time per incident fell from half a day to minutes, a 20x cost spike got caught and rolled back before it burned the monthly budget, and the team shipped production-grade AI features at a pace that a lean team would not have been able to sustain on the prior stack. The reported root cause of the speed-up was the single-source schema: the same Pydantic model defined the agent output, the API response, and the Logfire span, so there was no place for the contracts to drift.

Lema AI evaluated several agent frameworks before picking Pydantic AI for their Agentic Risk Engineer, an autonomous system that investigates third-party security with forensic depth. The reasons given were structured output validation, an intuitive API the team could read at a glance, and the Logfire integration for production observability. The point of the story is that production AI work in regulated domains has a high evidence bar, and an agent stack that produces validated structured outputs with a complete trace per run is materially easier to take through a security review than one that returns raw strings.

Pydantic AI vs LangChain, LangGraph, CrewAI

Four frameworks dominate the production Python agent conversation in 2026. They model the same problem in different ways and they fit different teams.

LangChain is the wide-integration play. 70-plus LLM providers, a deep ecosystem of community integrations, and the biggest community for rapid prototyping. The trade-off is the one teams have been living with for two years: many ways to do the same thing, a moving abstraction surface, and runtime types that the Python type checker cannot help you with. For prototypes and exploration the breadth still wins; for production services the cost of the abstraction has caught up.

LangGraph is the graph-orchestration play. Explicit state machines, cyclic graphs, conditional edges, checkpointers, supervisors, and swarm patterns. Where Pydantic AI shines at defining what a single agent does, LangGraph shines at defining how multiple agents interact. The pattern getting the most attention on real production engagements is to use both: Pydantic AI for the individual agent (tools, output schema, validation, model choice) and LangGraph for the orchestration above it (routing between specialist agents, shared state, human-in-the-loop gates).

CrewAI is the role-based multi-agent play. Agents get roles, goals, and backstories; the framework wires them into a crew. The intuitive configuration model is genuinely useful when the workflow maps cleanly onto a team metaphor (a researcher, a writer, an editor). The trade-off is that the metaphor leaks: when the workflow does not actually look like a team, forcing it into roles and backstories adds work rather than saving it.

Pydantic AI is the type-safety play. The smallest surface area of the four, the strongest static typing story, and the deepest integration with the rest of the Pydantic ecosystem (Pydantic for models, FastAPI for APIs, Logfire for observability). In published benchmark comparisons across the four frameworks on equivalent production tasks, Pydantic AI delivered the lowest P95 latency and the lowest error rate under load, with the smallest token consumption per equivalent task. The framework is the right default for new Python agent work when the team values production reliability over prototyping speed.

A rough decision rule that holds on most engagements: Pydantic AI for new production Python agents, LangGraph when the workflow needs explicit multi-agent state, the two together when both are true, CrewAI when the workflow is a literal team of specialists, and LangChain when the primary need is the integration catalog rather than the agent loop.

Advantages and limitations

The Pydantic AI design wins a lot of arguments, but it is not free. An honest list of the trade-offs:

Strengths. Type safety from the model output through the FastAPI route to the database column, which is the single biggest gain over the previous generation of frameworks. A small, learnable surface area: Agent, Tool, deps, output_type, and most of the rest of the docs is built on those four. Provider-neutral by design, so switching from GPT to Claude to a self-hosted DeepSeek model is a string change. First-class observability with Logfire, including cost and tool-call inspection out of the box. First-class durable execution with Temporal or DBOS through a thin wrapper, so the same agent code ships to dev and to a checkpointed production workflow. A code-first eval framework that fits in CI.

Trade-offs. Python only. If the team needs the same agent in TypeScript or Go, Pydantic AI is not the choice (though Logfire ships SDKs for both). The framework is opinionated about typing: if the team is fighting against Pydantic models for the output shape, the framework feels like extra work rather than help. Multi-agent orchestration is not the focus; for workflows that need a real state machine with shared state across agents, LangGraph is still the cleaner pick (and the two compose well). The community is large and growing but still smaller than LangChain’s, which can show up in the long tail of obscure integrations.

When not to use it. A pure-string-in, pure-string-out chat endpoint with no tools and no structured output does not need an agent framework. Below the complexity threshold of one tool plus one structured output, the OpenAI SDK directly is enough. Above it, the type safety pays for itself the first time the model breaks the contract.

Future trends: 2026 and beyond

Four shifts shape the Pydantic AI roadmap and the broader typed-agent space for the next eighteen months.

The agent framework is becoming a stack, not a library. The Pydantic team now ships the agent framework, the observability platform, the eval framework, and an LLM gateway under one roof. The integration story is the product: every layer knows about the layers next to it, and a single Pydantic model carries through all of them. Expect the rest of the agent framework market to converge on the same stack shape over the next year.

Durable execution moves from optional to default. The pattern of wrapping an agent with a DBOS or Temporal adapter has gone from advanced-use-case to standard for any agent that runs longer than a few seconds. The Postgres- backed DBOS option in particular is making durable agents accessible to teams that already run Postgres and do not want to add a Temporal cluster. Expect every credible agent framework in 2027 to ship durable execution as a first-party feature.

Type-safe MCP is the interop story. MCP gave the industry a wire protocol for tool calls; Pydantic AI gives the Python side a typed implementation of that wire protocol. The combination means an MCP server can ship a tool, every Pydantic AI agent in the company can pick it up on the next process start with a typed signature, and the eval framework can test the tool against typed cases. The flywheel only spins faster as more vendors ship MCP servers and more teams ship typed agents that consume them.

Evals become part of the deploy pipeline. Built-in eval frameworks shipped with the agent framework are becoming table stakes in 2026. The expectation by 2027 is that a CI run on an agent repo runs the same eval set the production traffic is scored against, blocks the merge on regressions, and writes the same trace format to the observability backend. Pydantic Evals plus Logfire is the most complete version of that pattern shipping today.

Conclusion: typed agents as the default

The interesting thing about Pydantic AI is how little of it is novel. The Agent is a function. The Tool is a Pydantic- validated callable. The deps are a dataclass. The output is a Pydantic model. The wire format is JSON. None of these pieces are new ideas; what is new is that one framework composes them into a stack that fits the way Python teams already build services. The Pydantic library is already in every credible AI SDK. FastAPI is already the dominant Python service framework. Logfire is OpenTelemetry under the hood. Pydantic AI is the bet that the agent layer belongs in the same vocabulary as the rest of the service.

For new Python agent work in 2026 the argument against starting on Pydantic AI is thin. The migration cost from LangChain is real but the published case studies all point the same direction: less code, fewer regressions, faster debugging, smaller bills. The remaining decisions are how much of the rest of the stack to adopt (Logfire, Evals, the Gateway) and whether to compose with LangGraph for multi-agent orchestration. Both decisions can be made gradually. The first one only has to be made once.

Further reading

ready when you are

Want to build something amazing? Let's bring it to life.

At Reactify Solutions, we turn your ideas into exceptional applications. From concept to deployment, we are here to make your vision a reality.