Skip to content

OpenAI Agents SDK vs Pydantic AI: A Minimal Agent Comparison

Research date: Aug 7, 2026. This is a practical comparison of the current Python SDKs, not a benchmark. Examples intentionally show the smallest useful unit and omit API-key setup, error handling, authorization, and production observability.


Short answer

Both libraries turn an LLM into a tool-using control loop:

  1. receive input and the available tools;
  2. ask the model what to do;
  3. execute any selected tools;
  4. return tool results to the model; and
  5. stop when the model emits a final answer or a run limit is reached.

The difference is where each SDK puts its emphasis:

Question OpenAI Agents SDK Pydantic AI
Smallest call Runner.run_sync(agent, prompt) agent.run_sync(prompt)
Main idea A managed agent loop with tools, handoffs, sessions, tracing, and OpenAI integration A type-safe Python layer for models, dependencies, tools, structured output, and explicit workflows
Tool declaration @tool @agent.tool or @agent.tool_plain
Local state RunContextWrapper.context RunContext.deps
Conversation continuity Built-in sessions or explicit result history Caller supplies message_history
Multi-agent control LLM-directed handoffs or code-directed composition Mostly code-directed composition; optional pydantic-graph for explicit FSMs
MCP client Native stdio, SSE, Streamable HTTP, and hosted MCP options MCPToolset, backed by FastMCP client transports

Choose OpenAI Agents SDK when an OpenAI-first managed runtime, handoffs, hosted tools, or built-in session implementations are the center of the design. Choose Pydantic AI when typed dependencies and outputs, provider flexibility, and application-owned workflow control matter most.


1. The minimal agent

OpenAI Agents SDK

from agents import Agent, Runner

agent = Agent(
    name="Assistant",
    instructions="Answer concisely.",
)

result = Runner.run_sync(agent, "What is a prime number?")
print(result.final_output)

Agent describes one model-facing worker; Runner executes the loop and exposes the final output. The basic package is installed with pip install openai-agents and requires Python 3.10 or newer. See the OpenAI Agents SDK overview.

Pydantic AI

from pydantic_ai import Agent

agent = Agent(
    "openai:gpt-5.2",
    instructions="Answer concisely.",
)

result = agent.run_sync("What is a prime number?")
print(result.output)

Pydantic AI puts the runner on the agent object. The model string selects a provider and model; the same API can use other supported model providers. Install with pip install pydantic-ai. See Pydantic AI's model overview.

Minimal-feature takeaway: these examples are both a model call wrapped in an agent loop. Neither becomes meaningfully “agentic” until it has tools, instructions, state, or a workflow.


2. Capability: a function tool

A capability is an action the model may request but cannot perform by merely generating text. A tool should be narrow, validated, authorized by the host application, and safe to retry.

OpenAI Agents SDK

from agents import Agent, Runner
from agents.decorators import tool

@tool
def lookup_temperature(city: str) -> str:
    """Return the current temperature for a city."""
    return f"{city}: 21°C"

agent = Agent(
    name="Weather assistant",
    instructions="Use the weather tool when a city is named.",
    tools=[lookup_temperature],
)

print(Runner.run_sync(agent, "What is the temperature in Taipei?").final_output)

The @tool decorator derives a tool schema from the Python signature and docstring. Tools can receive a RunContextWrapper for local application context; that context is not sent to the model. The SDK also provides hosted OpenAI tools and can expose one agent as another agent's tool. See tools and context.

Pydantic AI

from pydantic_ai import Agent

agent = Agent(
    "openai:gpt-5.2",
    instructions="Use the weather tool when a city is named.",
)

@agent.tool_plain
def lookup_temperature(city: str) -> str:
    """Return the current temperature for a city."""
    return f"{city}: 21°C"

print(agent.run_sync("What is the temperature in Taipei?").output)

Use @agent.tool instead when the function needs RunContext[DepsType]; @agent.tool_plain is for a context-free function. Pydantic AI uses annotations and docstrings to build the tool definition and can validate structured output with Pydantic. See tools and dependencies.

Important: a tool is not permission. The SDK tells the model that it can ask to use a function; the application still owns credentials, authorization, input validation, rate limits, and side-effect approval.


3. What “skills” means here

“Skill” is overloaded. It may mean:

  1. a callable capability (a tool);
  2. a reusable instruction package, commonly SKILL.md; or
  3. code, assets, and scripts that an agent can access in a workspace.

For the basic APIs above, the closest equivalent is a tool or a collection of tools—not a learned capability stored inside the model.

Skill form OpenAI Agents SDK Pydantic AI
Reusable callable behavior Function tools, hosted tools, or an agent exposed with Agent.as_tool() Functions and toolsets
Instruction package Available in the beta sandbox Skills capability, which materializes a skill library into its sandbox workspace Available through the pydantic-ai-harness Skills capability, which exposes deferred SKILL.md instructions
Bundled scripts/assets Sandbox filesystem capabilities can use them after materialization The harness skill loader is instruction-oriented; application tools must execute any files deliberately

Thus, “the agent uses a skill” normally means the model selected a tool or loaded a relevant instruction package during a run. It does not mean the agent permanently learned a new ability. The practical distinction matters: tools are executable host-controlled interfaces; instruction skills are prompt context; sandbox skills can additionally make files available to controlled execution.

For the SDK-specific workspace implementations, see OpenAI sandbox skills and Pydantic AI harness skills.


4. MCP: tools supplied by another process or service

Model Context Protocol (MCP) standardizes how a client discovers and invokes tools from a server. It is an integration boundary, not an authorization system. Treat every MCP server as code with access to its configured credentials and data.

OpenAI Agents SDK

import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStdio

async def main():
    async with MCPServerStdio(
        params={"command": "python", "args": ["weather_mcp_server.py"]}
    ) as server:
        agent = Agent(
            name="Weather assistant",
            instructions="Use the MCP weather tool when needed.",
            mcp_servers=[server],
        )
        result = await Runner.run(agent, "Weather in Taipei?")
        print(result.final_output)

asyncio.run(main())

The SDK supports stdio (MCPServerStdio), SSE, Streamable HTTP, and an OpenAI-hosted MCP tool option. The context manager owns the server connection lifecycle. See MCP integration.

Pydantic AI

import asyncio
from pydantic_ai import Agent
from pydantic_ai.mcp import MCPToolset

async def main():
    toolset = MCPToolset("http://localhost:8000/mcp")
    agent = Agent("openai:gpt-5.2", toolsets=[toolset])

    async with agent:
        result = await agent.run("Weather in Taipei?")
        print(result.output)

asyncio.run(main())

MCPToolset accepts a URL, a local Python or Node script, a FastMCP transport/client, or an in-process FastMCP server. Entering the agent context opens its toolsets and exiting closes them. See Pydantic AI's MCP client guide.

MCP takeaway: both SDKs turn discovered remote tools into tools visible to the model. That is useful for interoperability, but it broadens the trust boundary. Use trusted servers, narrowly scoped credentials, allowlists, approval for destructive operations, and audit logs.


5. Is an agent multiprocessing?

No. An agent is a control pattern; multiprocessing is an execution strategy.

Both SDKs are compatible with normal Python concurrency. For independent work, start several asynchronous runs and combine their results:

answers = await asyncio.gather(
    Runner.run(research_agent, question_a),
    Runner.run(research_agent, question_b),
)

The equivalent Pydantic AI form is await asyncio.gather(agent_a.run(...), agent_b.run(...)). This is useful when tasks are independent. It does not automatically make the result better: it increases token use, tool calls, failure modes, and coordination work.

For dependent work, use a deliberate workflow:

Pattern OpenAI Agents SDK Pydantic AI
One specialist completes a task, then another consumes its output Application code chains result.final_output Application code chains result.output
Manager delegates but keeps the final response Agent.as_tool() A delegate agent wrapped as a tool
Model chooses the next specialist handoff() transfers the active agent Usually modeled explicitly by the application rather than as a default handoff mechanism
Deterministic branch/retry/approval flow Host application code Host application code or typed pydantic-graph nodes
Independent parallel research asyncio.gather(Runner.run(...)) asyncio.gather(agent.run(...))

See OpenAI multi-agent orchestration and Pydantic AI multi-agent applications.


6. What an agent is—and is not

The closest general metaphor is a probabilistic workflow runner with a language-model planner:

  • The model proposes the next action or final response from the current context.
  • The runner applies deterministic rules: call a tool, append its result, switch agents, enforce limits, or stop.
  • The host application supplies durable state, permissions, tools, and business workflow.

This explains several tempting but incomplete metaphors:

Metaphor Accurate part Why it is incomplete
State generator Each step produces a new trace: messages, tool calls, outputs, and possibly an active agent Most next-state transitions are proposed by a non-deterministic model, not a fixed transition table
State machine A carefully designed workflow can be an FSM A default agent loop is not an FSM: tool selection and routing are model decisions. Pydantic AI's optional pydantic-graph is the explicit FSM layer
Categorizer An agent can classify input and route work Classification is only one possible model action; tools can read, write, calculate, and delegate
Database Sessions and message histories can persist facts and traces The agent is neither the source of truth nor a query engine with database guarantees; persistent data belongs in a database or other system of record
Event queue Tool calls and streamed events can be emitted and processed asynchronously A queue transports work durably; an agent decides work. Combine an agent with a queue for durable asynchronous jobs

State and memory are separate design choices

In the OpenAI Agents SDK, a run has loop state and can use a session= implementation such as SQLiteSession; it can also resume durable work through RunState. Local context passed to a run remains application-only. See sessions and running agents.

Pydantic AI agents are intentionally stateless and reusable. The caller continues a conversation by passing message_history=result.all_messages() to a later run. For an explicit typed state machine, use pydantic-graph and put state in its graph run context. See message history and graphs.

The production architecture is therefore usually:

request / queue event → application workflow + durable store
                       → agent run (instructions + model + permitted tools)
                       → validated result / next durable event

The agent contributes flexible interpretation and planning. Deterministic systems remain responsible for correctness boundaries, durable state, access control, retries, and auditability.


Decision guide

  • Pick OpenAI Agents SDK for an OpenAI-centered runtime with built-in tracing, sessions, hosted tools, or model-directed handoffs.
  • Pick Pydantic AI for provider flexibility, typed dependencies and outputs, and application-defined control flow.
  • Use MCP when a tool boundary should work across compatible clients and servers; do not use it merely to call a local Python function.
  • Use an explicit graph or ordinary application code when exact workflow states, approvals, retries, and transitions matter.
  • Use a database for durable facts, an event queue for durable asynchronous work, and an agent to interpret unstructured input and choose among authorized actions.

References