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:
- receive input and the available tools;
- ask the model what to do;
- execute any selected tools;
- return tool results to the model; and
- 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:
- a callable capability (a tool);
- a reusable instruction package, commonly
SKILL.md; or - 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.