Building Pydantic AI Agents
ClineOfficialCreated by pydanticDistributed by Cline
- Skill
- Tool integration
- Data and AI
- MIT
- Low risk
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
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/cline/building-pydantic-ai-agents/downloadUnzip 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_aior 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/BaseModelwithout 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 methods | Agents Core |
| Bundle reusable behavior or intercept lifecycle events | Capabilities and Hooks |
Decide what should load eagerly vs on demand, apply progressive disclosure, defer capability loading, or explain load_capability | Capabilities on Demand |
| Add function tools, toolsets, MCP servers, or explicit search tools | Tools Core |
| Use provider-native web search, web fetch, or code execution | Native Tools |
Use advanced tool features such as approval, retries, ToolReturn, validators, timeouts, or tool search | Tools Advanced |
| Work with multimodal input, message history, or context trimming | Input and History |
| Test or debug agent behavior | Testing and Debugging |
| Coordinate multiple agents or build graph workflows | Orchestration and Integrations |
| Call the model directly, expose A2A, use durable execution, embeddings, evals, or third-party integrations | Orchestration and Integrations |
| Compare abstractions, output modes, decorators, or model-string patterns | Architecture and Decision Guide |
Follow an older link into COMMON-TASKS.md | Task 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:
| Topic | What it covers |
|---|---|
| Decision Trees | Tool registration, output modes, multi-agent patterns, capabilities, testing approaches, extensibility |
| Comparison Tables | Output modes, model provider prefixes, tool decorators, built-in capabilities, agent methods |
| Architecture Overview | Execution 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=Truewould 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
TestModelfor deterministic tests,FunctionModelfor custom logic
Common Gotchas
These are mistakes agents commonly make with Pydantic AI. Getting these wrong produces silent failures or confusing errors.
@agent.toolrequiresRunContextas first param;@agent.tool_plainmust not have it. Mixing these up causes runtime errors. Usetool_plainwhen 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.TestModelrequiresagent.override(): Don't setagent.modeldirectly. Always use the context manager:with agent.override(model=TestModel()):.strin output_type allows plain text to end the run: If your union includesstr(or nooutput_typeis set), the model can return plain text instead of structured output. Omitstrfrom the union to force tool-based output.Hook decorator names on
.ondon't repeaton_: Usehooks.on.run_errorandhooks.on.model_request_error— nothooks.on.on_run_error.history_processorsis deprecated; usecapabilities=[ProcessHistory(p), ...], or hookbefore_model_requestdirectly viacapabilities=[Hooks(before_model_request=fn)].ProcessHistoryis a thin wrapper around that hook — the hook itself is the underlying primitive. The kwarg still works in 1.x but emits aPydanticAIDeprecationWarningand will be removed in v2.
Task-Family References
Load exactly one of these unless the task clearly spans multiple families:
| Task family | Reference |
|---|---|
| Core agent setup, output, deps, specs, models, run methods | Agents Core |
| Capabilities, hooks, and reusable behavior | Capabilities and Hooks |
Progressive disclosure, deferred capabilities, capabilities on demand, and load_capability semantics | Capabilities on Demand |
| Function tools, toolsets, MCP, explicit search tools | Tools Core |
| Provider-native tools | Native Tools |
| Approval, retries, validators, timeouts, rich tool returns, tool search, and tool-level deferred loading | Tools Advanced |
| Multimodal input, message history, history processors | Input and History |
| Testing, request inspection, and Logfire debugging | Testing and Debugging |
| Multi-agent patterns, graphs, direct API, A2A, durable execution, embeddings, evals, third-party integrations | Orchestration 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
- ATTRIBUTION.mdAdded by AutoGineer878 B
- SKILL.md12.1 KiB
- references/AGENTS-CORE.md4.1 KiB
- references/ARCHITECTURE.md11.9 KiB
- references/CAPABILITIES-AND-HOOKS.md3.5 KiB
- references/COMMON-TASKS.md4.8 KiB
- references/INPUT-AND-HISTORY.md3.9 KiB
- references/NATIVE-TOOLS.md2.6 KiB
- references/ON-DEMAND-CAPABILITIES.md7.4 KiB
- references/ORCHESTRATION-AND-INTEGRATIONS.md3.8 KiB
- references/TESTING-AND-DEBUGGING.md2.0 KiB
- references/TOOLS-ADVANCED.md4.0 KiB
- references/TOOLS-CORE.md2.8 KiB
references/CAPABILITIES-AND-HOOKS.md
Capabilities and Hooks
Read this file when the user wants reusable agent behavior, provider-adaptive tools, or lifecycle interception.
Add Capabilities to an Agent
Capabilities bundle reusable behavior and compose automatically.
from pydantic_ai import Agent
from pydantic_ai.capabilities import Thinking, WebSearch
agent = Agent(
'anthropic:claude-opus-4-6',
capabilities=[
Thinking(effort='high'),
WebSearch(),
],
)Provider-adaptive capabilities to reach for first:
ThinkingWebSearchWebFetchImageGenerationMCP
Use capabilities when the user wants behavior that should survive model/provider changes.
Enable Thinking Across Providers
Use the unified Thinking capability or the thinking model setting.
from pydantic_ai import Agent
from pydantic_ai.capabilities import Thinking
agent = Agent('anthropic:claude-opus-4-6', capabilities=[Thinking(effort='high')])
agent = Agent('anthropic:claude-opus-4-6', model_settings={'thinking': 'high'})Supported effort values:
TrueFalse'minimal''low''medium''high''xhigh'
Intercept Agent Lifecycle with Hooks
Use Hooks for decorator-based lifecycle interception.
from pydantic_ai import Agent, RunContext, ToolDefinition
from pydantic_ai.capabilities import ValidatedToolArgs
from pydantic_ai.capabilities.hooks import Hooks
from pydantic_ai.messages import ToolCallPart
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
@hooks.on.before_tool_execute(tools=['send_email'])
async def audit_tool(
ctx: RunContext[None],
*,
call: ToolCallPart,
tool_def: ToolDefinition,
args: ValidatedToolArgs,
) -> ValidatedToolArgs:
print(f'Executing {call.tool_name}')
return args
agent = Agent('openai:gpt-5.2', capabilities=[hooks])Important hook families:
run-level hooks
node-level hooks
model-request hooks
tool-validation hooks
tool-execution hooks
event-stream hooks
Use hooks when the user wants observability, auditing, or light interception without adding a new abstraction.
Build a Custom Capability
Subclass AbstractCapability when the user wants reusable behavior that combines tools, hooks, instructions, or model settings into one package.
Reach for a custom capability when:
the same bundle should be reused across multiple agents
Hooksalone is not enoughthe behavior should be installable or declarative
Keep custom capabilities focused. If the user only needs one tool or one hook, do not introduce a capability.
For every capability, consider whether defer_loading=True would improve the system by keeping instructions and tool schemas out of the eager context. Keep it eager only when the model benefits from that capability on most turns, when its hooks/settings must always apply, or when deferral would make capability selection unreliable.
Defer Capability Loading
For capabilities on demand, load Capabilities on Demand. Use it when the user mentions deferred capabilities, capability progressive disclosure, defer_loading=True on a capability, or load_capability; also use it proactively when an agent design includes optional instructions, specialist workflows, long-tail tools, or context the model does not need on most turns.
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