Generated banner for Building Pydantic AI Agents
Generated logo for Building Pydantic AI Agents

Building Pydantic AI Agents

ClineOfficialCreated by pydanticDistributed by Cline

  • Skill
  • Tool integration
  • Data and AI
  • MIT
  • Low risk

From the author

Build AI agents with Pydantic AI — tools, capabilities (including on-demand loading), structured output, streaming, testing, and multi-agent patterns. Use when the user mentions Pydantic AI, imports pydantic_ai, or asks to build an AI agent, add tools/capabilities, defer capability loading, stream output, define agents from YAML, or test agent behavior.
— the description by pydantic

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

Pydantic AI is a Python framework designed for building robust, production-ready Generative AI applications. This skill provides architectural patterns, configuration guidance, and code examples for implementing agents, including tool integration, structured output, dependency injection, and lifecycle management. It covers the full agent lifecycle from basic setup and YAML-based configuration to advanced testing and observability patterns.

What it does

  • Defines AI agents with custom instructions and model configurations.
  • Integrates function tools and toolsets for agent capabilities.
  • Enforces structured output using Pydantic models.
  • Manages dependency injection for context-aware agent behavior.
  • Supports agent definition via YAML or JSON configuration files.
  • Provides testing utilities and lifecycle hooks for observability.

When to use it

  • When building AI agents using the Pydantic AI framework.
  • When you need to add tools, capabilities, or structured output to an agent.
  • When implementing multi-agent patterns or delegating tasks between agents.
  • When you need to define agent configurations externally using YAML or JSON.
  • When testing agent behavior or instrumenting runs for observability.

Advantages

  • Strong type safety and validation via Pydantic integration.
  • Flexible tool registration methods for various context requirements.
  • Supports progressive disclosure through deferred capability loading.
  • Built-in testing support with deterministic model mocks.
  • Declarative agent construction via configuration files.

Usage examples

“How do I create a basic agent with Pydantic AI?”
The agent provides a code snippet using the Agent class, specifying a model string and instructions, followed by a run_sync call to execute a prompt.
“How can I force my agent to return structured data?”
The agent explains using the output_type parameter with a Pydantic BaseModel to ensure the LLM response is validated against a specific schema.
“How do I define an agent using a YAML file?”
The agent demonstrates using Agent.from_file() to load agent configurations, including model settings and capabilities, from an external YAML file.
“How do I test my agent's logic?”
The agent shows how to use TestModel within an agent.override() context manager to perform unit tests on agent behavior without making real API calls.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/cline/building-pydantic-ai-agents/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip building-pydantic-ai-agents.zip -d .agents/skills

Documentation by pydantic

Building AI Agents with Pydantic AI

Pydantic AI is a Python agent framework for building production-grade Generative AI applications. This skill provides patterns, architecture guidance, and tested code examples for building applications with Pydantic AI.

When to Use This Skill

Invoke this skill when:

  • User asks to build an AI agent, create an LLM-powered app, or mentions Pydantic AI

  • User wants to add tools, capabilities (thinking, web search), or structured output to an agent

  • User asks to define agents from YAML/JSON specs or use template strings

  • User wants to stream agent events, delegate between agents, or test agent behavior

  • Code imports pydantic_ai or references Pydantic AI classes (Agent, RunContext, Tool)

  • User asks about hooks, lifecycle interception, or agent observability with Logfire

  • The agent design includes optional instructions, specialist workflows, long-tail tools, or any context the model does not need on most turns

Do not use this skill for:

  • The Pydantic validation library alone (pydantic/BaseModel without agents)

  • Other AI frameworks (LangChain, LlamaIndex, CrewAI, AutoGen)

  • General Python development unrelated to AI agents

Quick-Start Patterns

Create a Basic Agent
from pydantic_ai import Agent

agent = Agent(
    'anthropic:claude-sonnet-4-6',
    instructions='Be concise, reply with one sentence.',
)

result = agent.run_sync('Where does "hello world" come from?')
print(result.output)
"""
The first known use of "hello, world" was in a 1974 textbook about the C programming language.
"""
Add Tools to an Agent
import random

from pydantic_ai import Agent, RunContext

agent = Agent(
    'google:gemini-3-flash-preview',
    deps_type=str,
    instructions=(
        "You're a dice game, you should roll the die and see if the number "
        "you get back matches the user's guess. If so, tell them they're a winner. "
        "Use the player's name in the response."
    ),
)


@agent.tool_plain
def roll_dice() -> str:
    """Roll a six-sided die and return the result."""
    return str(random.randint(1, 6))


@agent.tool
def get_player_name(ctx: RunContext[str]) -> str:
    """Get the player's name."""
    return ctx.deps


dice_result = agent.run_sync('My guess is 4', deps='Anne')
print(dice_result.output)
#> Congratulations Anne, you guessed correctly! You're a winner!
Structured Output with Pydantic Models
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 olympics held in 2012?')
print(result.output)
#> city='London' country='United Kingdom'
print(result.usage)
#> RunUsage(input_tokens=57, output_tokens=8, requests=1)
Dependency Injection
from datetime import date

from pydantic_ai import Agent, RunContext

agent = Agent(
    'openai:gpt-5.2',
    deps_type=str,
    instructions="Use the customer's name while replying to them.",
)


@agent.instructions
def add_the_users_name(ctx: RunContext[str]) -> str:
    return f"The user's name is {ctx.deps}."


@agent.instructions
def add_the_date() -> str:
    return f'The date is {date.today()}.'


result = agent.run_sync('What is the date?', deps='Frank')
print(result.output)
#> Hello Frank, the date today is 2032-01-02.
Testing with TestModel
from pydantic_ai import Agent
from pydantic_ai.models.test import TestModel

my_agent = Agent('openai:gpt-5.2', instructions='...')


async def test_my_agent():
    """Unit test for my_agent, to be run by pytest."""
    m = TestModel()
    with my_agent.override(model=m):
        result = await my_agent.run('Testing my agent...')
        assert result.output == 'success (no tool calls)'
    assert m.last_model_request_parameters.function_tools == []
Use Capabilities

Capabilities are reusable, composable units of agent behavior — bundling tools, hooks, instructions, and model settings.

from pydantic_ai import Agent
from pydantic_ai.capabilities import Thinking, WebSearch

agent = Agent(
    'anthropic:claude-opus-4-6',
    instructions='You are a research assistant. Be thorough and cite sources.',
    capabilities=[
        Thinking(effort='high'),
        WebSearch(),
    ],
)
Add Lifecycle Hooks

Use Hooks to intercept model requests, tool calls, and runs with decorators — no subclassing needed.

from pydantic_ai import Agent, RunContext
from pydantic_ai.capabilities.hooks import Hooks
from pydantic_ai.models import ModelRequestContext

hooks = Hooks()


@hooks.on.before_model_request
async def log_request(ctx: RunContext[None], request_context: ModelRequestContext) -> ModelRequestContext:
    print(f'Sending {len(request_context.messages)} messages')
    return request_context


agent = Agent('openai:gpt-5.2', capabilities=[hooks])
Define Agent from YAML Spec

Use Agent.from_file to load agents from YAML or JSON — no Python agent construction code needed.

from pydantic_ai import Agent

# agent.yaml:
# model: anthropic:claude-opus-4-6
# instructions: You are a helpful research assistant.
# capabilities:
#   - WebSearch
#   - Thinking:
#       effort: high

agent = Agent.from_file('agent.yaml')

Task Routing Table

Load only the most relevant reference first. Read additional references only if the task spans multiple areas.

I want to...Reference
Create/configure agents, choose output types, use deps, define specs, or pick run methodsAgents Core
Bundle reusable behavior or intercept lifecycle eventsCapabilities and Hooks
Decide what should load eagerly vs on demand, apply progressive disclosure, defer capability loading, or explain load_capabilityCapabilities on Demand
Add function tools, toolsets, MCP servers, or explicit search toolsTools Core
Use provider-native web search, web fetch, or code executionNative Tools
Use advanced tool features such as approval, retries, ToolReturn, validators, timeouts, or tool searchTools Advanced
Work with multimodal input, message history, or context trimmingInput and History
Test or debug agent behaviorTesting and Debugging
Coordinate multiple agents or build graph workflowsOrchestration and Integrations
Call the model directly, expose A2A, use durable execution, embeddings, evals, or third-party integrationsOrchestration and Integrations
Compare abstractions, output modes, decorators, or model-string patternsArchitecture and Decision Guide
Follow an older link into COMMON-TASKS.mdTask Reference Map

Architecture and Decisions

Load Architecture and Decision Guide only when the user is choosing between abstractions or wants comparison tables and decision trees:

TopicWhat it covers
Decision TreesTool registration, output modes, multi-agent patterns, capabilities, testing approaches, extensibility
Comparison TablesOutput modes, model provider prefixes, tool decorators, built-in capabilities, agent methods
Architecture OverviewExecution flow, generic types, construction patterns, lifecycle hooks, model string format

Quick reference — model string format: "provider:model-name" (e.g., "openai:gpt-5.2", "anthropic:claude-sonnet-4-6", "google:gemini-3-pro-preview")

Quick reference — key agent methods: run(), run_sync(), run_stream(), run_stream_sync(), run_stream_events(), iter()

Key Practices

  • Python 3.10+ compatibility required

  • Progressive disclosure by default: For every capability, explicitly consider whether defer_loading=True would benefit the agent before choosing eager loading. Do not eagerly load specialist instructions, rarely used tool schemas, or domain context unless the model needs them on most turns. Prefer capabilities on demand for named instruction+tool bundles, and tool search for large flat tool catalogs.

  • Observability: Pydantic AI has first-class integration with Logfire for tracing agent runs, tool calls, and model requests. Add it with logfire.instrument_pydantic_ai(). For deeper HTTP-level visibility, logfire.instrument_httpx(capture_all=True) captures the exact payloads sent to model providers.

  • Testing: Use TestModel for deterministic tests, FunctionModel for custom logic

Common Gotchas

These are mistakes agents commonly make with Pydantic AI. Getting these wrong produces silent failures or confusing errors.

  • @agent.tool requires RunContext as first param; @agent.tool_plain must not have it. Mixing these up causes runtime errors. Use tool_plain when you don't need deps, usage, or messages.

  • Model strings need the provider prefix: 'openai:gpt-5.2' not 'gpt-5.2'. Without the prefix, Pydantic AI can't resolve the provider.

  • TestModel requires agent.override(): Don't set agent.model directly. Always use the context manager: with agent.override(model=TestModel()):.

  • str in output_type allows plain text to end the run: If your union includes str (or no output_type is set), the model can return plain text instead of structured output. Omit str from the union to force tool-based output.

  • Hook decorator names on .on don't repeat on_: Use hooks.on.run_error and hooks.on.model_request_error — not hooks.on.on_run_error.

  • history_processors is deprecated; use capabilities=[ProcessHistory(p), ...], or hook before_model_request directly via capabilities=[Hooks(before_model_request=fn)]. ProcessHistory is a thin wrapper around that hook — the hook itself is the underlying primitive. The kwarg still works in 1.x but emits a PydanticAIDeprecationWarning and will be removed in v2.

Task-Family References

Load exactly one of these unless the task clearly spans multiple families:

Task familyReference
Core agent setup, output, deps, specs, models, run methodsAgents Core
Capabilities, hooks, and reusable behaviorCapabilities and Hooks
Progressive disclosure, deferred capabilities, capabilities on demand, and load_capability semanticsCapabilities on Demand
Function tools, toolsets, MCP, explicit search toolsTools Core
Provider-native toolsNative Tools
Approval, retries, validators, timeouts, rich tool returns, tool search, and tool-level deferred loadingTools Advanced
Multimodal input, message history, history processorsInput and History
Testing, request inspection, and Logfire debuggingTesting and Debugging
Multi-agent patterns, graphs, direct API, A2A, durable execution, embeddings, evals, third-party integrationsOrchestration and Integrations

Use Task Reference Map only for compatibility with older links or when you need a pointer from an old section name to the new file.

Files

references/ON-DEMAND-CAPABILITIES.md

Capabilities on Demand

Read this file when designing progressive disclosure of any kind, when an agent has information it does not need on most turns, or when the user asks about deferred capabilities, capabilities on demand, defer_loading=True on capabilities, or the load_capability tool.

Mental Model

Capabilities on demand are bundle-level progressive disclosure for Pydantic AI. The model initially sees a compact catalog of deferred capability id values, plus description values when provided, and the framework-managed load_capability tool. When the model calls load_capability(id), Pydantic AI returns that capability's instructions; its function tools, native tools, and model settings are reflected on the next model request, and its hooks can fire for later hook points in the run.

Be opinionated: review every capability for whether defer_loading=True would benefit the system before accepting eager loading. If the model does not need a piece of information, a specialist instruction set, or a tool schema on most turns, do not put it in the eager prompt by default.

Use this for specialist behavior where instructions and tools should travel together:

  • support workflows such as refunds, returns, account management, or fraud review

  • domain-specific tool bundles where most requests need only one bundle

  • agents that would otherwise load many capability instructions and tool schemas on every turn

Use tool search instead when the agent has a large flat tool catalog and the model should discover individual tools. Tool search uses search_tools; capabilities on demand use load_capability.

Opinionated Design Rules

  • Treat defer_loading=True as a design question for every capability, not a niche option users must ask for.

  • Keep the base agent prompt small: identity, task boundaries, global safety, and the routing instruction needed to decide what to load.

  • Put specialist runbooks behind capabilities on demand when they are useful only for a subset of requests.

  • Put broad tool catalogs behind tool search when the tools are individually discoverable and do not need shared instructions.

  • Keep hot-path tools and universal instructions eager when they are used most turns.

  • Prefer a few coherent capability bundles over dozens of tiny capabilities that force the model to plan its own dependency graph.

  • Do not hide information the model needs to decide which capability to load; that belongs in the capability description or always-on routing instructions.

Minimal Pattern

Every deferred capability needs a stable explicit id and defer_loading=True. A concise description is optional; add one when the id alone is not enough for routing.

from pydantic_ai import Agent
from pydantic_ai.capabilities import Capability

refunds = Capability(
    id='refunds',
    description='Refund policy tools and instructions.',
    instructions='Use the refund policy before answering refund questions.',
    defer_loading=True,
)


@refunds.tool_plain
def lookup_refund_policy(order_id: str) -> str:
    """Look up whether an order is eligible for a refund."""
    return f'{order_id} is eligible for a refund for 30 days after purchase.'


agent = Agent(
    'anthropic:claude-sonnet-4-6',
    instructions='Answer as a support assistant.',
    capabilities=[refunds],
)

Capability is a convenience helper for simple bundles of instructions, descriptions, function tools, and toolsets. It accepts callable descriptions, dynamic instruction functions, and dynamic toolset functions. Use a custom AbstractCapability for model settings, hooks, native tools, wrapper toolsets, reusable public behavior, or custom per-run logic. Wrapper toolsets are applied during per-run toolset assembly; if wrapper behavior should wait for a deferred capability to load, gate that behavior inside the wrapper.

Runtime Semantics

Initial request:

  • deferred capability instructions are not included

  • deferred capability function tools are present in the framework toolset but marked with defer_loading=True; they go through client-executed local search, so the provider's hosted search never sees them, and they are not callable until the capability loads

  • non-deferred capabilities are treated as already loaded

  • the framework adds load_capability if any deferred capability exists

When load_capability succeeds:

  • the call is typed as a capability-load message part

  • the return may include resolved capability instructions and owned toolset instructions

  • the capability id is added to ctx.available_capability_ids

  • tools owned by the loaded capability become visible on later steps

  • load_capability remains visible so the tool set stays stable

Message history matters. Loaded capability state is reconstructed from matching LoadCapabilityCallPart and LoadCapabilityReturnPart pairs in message history. If a history processor removes those parts, the model may need to load the capability again.

Dynamic Descriptions and Instructions

Use get_description() when the catalog text depends on run context. Return a callable (with or without RunContext) that produces the description string. Use dynamic instructions when load-time instructions need deps or current run state.

from dataclasses import dataclass

from pydantic_ai import RunContext
from pydantic_ai.capabilities import AbstractCapability


@dataclass
class SupportDeps:
    plan: str
    account_id: str


@dataclass
class AccountCapability(AbstractCapability[SupportDeps]):
    def get_description(self):
        def describe(ctx: RunContext[SupportDeps]) -> str:
            return f'Account-management tools for {ctx.deps.plan} plan customers.'

        return describe

    def get_instructions(self):
        def load_instructions(ctx: RunContext[SupportDeps]) -> str:
            return f'Use account ID {ctx.deps.account_id} for account-management tools.'

        return load_instructions


account_capability = AccountCapability(id='account-management', defer_loading=True)

Composition Rules

  • Capability id values must be unique in a run.

  • Deferred capability ids must be explicit and stable; auto-generated ids are rejected because history replay cannot rely on them.

  • load_capability is reserved when any deferred capability exists.

  • Deferred capability instructions and model settings activate only after the capability is loaded.

  • Both function and native tools defer with the capability. Deferring a native tool delays its definition entering the request, which breaks the prompt-cache prefix on load — only worth it for tools that materially bloat the prompt.

  • Capability-level defer_loading=True gates the bundle as a unit. Once the model loads the capability, all tools owned by that deferred capability become visible together. Use tool-level defer_loading=True outside a deferred capability when individual tools should stay behind search_tools.

Choosing Between Deferral Mechanisms

Capabilities on demand (load_capability) and tool search (search_tools) are covered above. The third mechanism is deferred tool calls: use these when the issue is execution timing, approval, or external execution. Deferred tool calls decide whether a visible tool call can run now; they do not control whether the model can see a capability.

When in doubt: "Would a high-quality answer to most user prompts get worse if this information were absent until requested?" If no, recommend progressive disclosure.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an MCP server, Needs Python

The author says: Requires Python 3.10+

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
pydantic
Publisher
Cline (Official)
Distributed by
Cline
Source
cline/skills · skills/building-pydantic-ai-agents · commit 2637846
Licence
MIT License (redistribution allowed)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by pydantic or Cline.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download building-pydantic-ai-agents.zip