OpenAI Agents SDK
Overview
OpenAI Agents SDK (formerly Swarm) is OpenAI's official agent development framework. It provides lightweight yet fully functional agent building primitives, including tool calling, agent Handoff, Guardrails, and other core features.
Core Architecture
graph TD
subgraph OpenAI Agents SDK
A[Agent] --> B[Instructions<br/>System Prompt]
A --> C[Tools<br/>Tool Set]
A --> D[Handoffs<br/>Agent Handoff]
A --> E[Guardrails]
end
subgraph Built-in Tools
C --> C1[Web Search]
C --> C2[Code Interpreter]
C --> C3[File Search]
C --> C4[Custom Functions]
end
subgraph Execution
F[Runner] --> G[Agent Loop]
G --> H[LLM Call]
H --> I{Tool Call?}
I -->|Yes| J[Execute Tool]
J --> H
I -->|No| K[Return Result]
end
Basic Usage
Creating an Agent
from openai import agents
# Simplest agent
agent = agents.Agent(
name="Assistant",
instructions="You are a helpful assistant.",
model="gpt-4o"
)
# Run
result = agents.Runner.run_sync(
agent,
messages=[{"role": "user", "content": "Hello!"}]
)
print(result.final_output)
Adding Tools
from openai.agents import Agent, Runner, function_tool
@function_tool
def get_weather(city: str) -> str:
"""Get current weather for a city."""
return f"Weather in {city}: 72F, sunny"
@function_tool
def search_flights(origin: str, destination: str, date: str) -> str:
"""Search for available flights."""
return f"Found 3 flights from {origin} to {destination} on {date}"
travel_agent = Agent(
name="Travel Assistant",
instructions="""You help users plan travel. Use the available
tools to get weather and flight information.""",
tools=[get_weather, search_flights],
model="gpt-4o"
)
Built-in Tools
Web Search
from openai.agents import Agent, WebSearchTool
research_agent = Agent(
name="Researcher",
instructions="Search the web to answer questions with current information.",
tools=[WebSearchTool()],
model="gpt-4o"
)
Code Interpreter
from openai.agents import Agent, CodeInterpreterTool
data_agent = Agent(
name="Data Analyst",
instructions="""You analyze data using Python.
Write and execute code to answer questions.""",
tools=[CodeInterpreterTool()],
model="gpt-4o"
)
File Search (RAG)
from openai.agents import Agent, FileSearchTool
vector_store = client.vector_stores.create(name="knowledge_base")
client.vector_stores.file_batches.upload(
vector_store_id=vector_store.id,
files=["doc1.pdf", "doc2.pdf"]
)
knowledge_agent = Agent(
name="Knowledge Assistant",
instructions="Answer questions based on the uploaded documents.",
tools=[FileSearchTool(vector_store_ids=[vector_store.id])],
model="gpt-4o"
)
Handoff Pattern
Handoff is the core innovation of the Agents SDK, allowing seamless transfer of control between agents.
Basic Handoff
from openai.agents import Agent, handoff
billing_agent = Agent(
name="Billing Specialist",
instructions="You handle billing and payment questions.",
tools=[check_balance, process_payment],
)
technical_agent = Agent(
name="Technical Support",
instructions="You handle technical issues and troubleshooting.",
tools=[check_system_status, restart_service],
)
triage_agent = Agent(
name="Customer Service",
instructions="""You are the first point of contact.
Route to the appropriate specialist:
- Billing questions -> Billing Specialist
- Technical issues -> Technical Support""",
handoffs=[
handoff(billing_agent),
handoff(technical_agent),
],
)
result = Runner.run_sync(
triage_agent,
messages=[{"role": "user", "content": "I can't log into my account"}]
)
Multi-Layer Handoff Architecture
graph TD
A[Triage Agent] -->|Billing issues| B[Billing Agent]
A -->|Technical issues| C[Tech Agent]
A -->|Sales inquiries| D[Sales Agent]
C -->|Network issues| C1[Network Specialist]
C -->|Software issues| C2[Software Specialist]
B -->|Refund requests| B1[Refund Agent]
Guardrails
Guardrails ensure agent behavior stays within safe boundaries.
Input Guardrails
from openai.agents import Agent, InputGuardrail, GuardrailResult
class ContentFilter(InputGuardrail):
"""Filter inappropriate input"""
async def run(self, input_text: str) -> GuardrailResult:
response = await moderation_llm.invoke(
f"Is this input safe and appropriate? '{input_text}'"
)
if "unsafe" in response.lower():
return GuardrailResult(
allow=False,
message="I'm sorry, I can't help with that request."
)
return GuardrailResult(allow=True)
safe_agent = Agent(
name="Safe Assistant",
instructions="You are a helpful assistant.",
input_guardrails=[ContentFilter()],
)
Output Guardrails
class FactCheckGuardrail(OutputGuardrail):
"""Check output factual accuracy"""
async def run(self, output_text: str) -> GuardrailResult:
check = await fact_checker.verify(output_text)
if check.confidence < 0.7:
return GuardrailResult(
allow=False,
message="Let me double-check that information..."
)
return GuardrailResult(allow=True)
Agent Loop Details
Execution Flow
# Simplified Runner internal execution logic
class Runner:
@staticmethod
async def run(agent, messages, max_turns=10):
current_agent = agent
conversation = list(messages)
for turn in range(max_turns):
# 1. Check input guardrails
for guardrail in current_agent.input_guardrails:
result = await guardrail.run(conversation[-1])
if not result.allow:
return RunResult(final_output=result.message)
# 2. Call LLM
response = await llm.create(
model=current_agent.model,
messages=[
{"role": "system", "content": current_agent.instructions},
*conversation
],
tools=current_agent.get_tool_definitions(),
)
# 3. Process response
if response.tool_calls:
for tool_call in response.tool_calls:
if is_handoff(tool_call, current_agent):
current_agent = get_handoff_target(tool_call)
continue
result = await execute_tool(tool_call)
conversation.append(tool_result_message(result))
else:
# 4. Check output guardrails
for guardrail in current_agent.output_guardrails:
result = await guardrail.run(response.content)
if not result.allow:
conversation.append(retry_message(result))
continue
return RunResult(final_output=response.content)
Relationship with Responses API
The Agents SDK is built on top of OpenAI's Responses API:
| Layer | Description |
|---|---|
| Responses API | Low-level API supporting tool calls, streaming output |
| Agents SDK | High-level framework providing Agent abstraction, Handoff, Guardrails |
Streaming Output
from openai.agents import Runner
async def stream_response():
async for event in Runner.run_streamed(
agent,
messages=[{"role": "user", "content": "Write a story"}]
):
if event.type == "text_delta":
print(event.delta, end="", flush=True)
elif event.type == "tool_call":
print(f"\n[Calling tool: {event.tool_name}]")
elif event.type == "handoff":
print(f"\n[Transferring to: {event.target_agent}]")
Best Practices
1. Agent Design Principles
- Single responsibility: Each agent focuses on one domain
- Clear instructions: Instructions should be specific and explicit
- Appropriate tools: Only give agents the tools they need
- Reasonable handoffs: Clearly define handoff conditions
2. Error Handling
from openai.agents import RunResult
result = await Runner.run(agent, messages=messages, max_turns=10)
if result.is_error:
logger.error(f"Agent error: {result.error}")
fallback_response = "I'm having trouble. Let me connect you with a human agent."
3. Cost Control
- Use
gpt-4o-minifor simple routing - Limit
max_turnsto prevent infinite loops - Cache results for common queries
Summary
Core advantages of the OpenAI Agents SDK:
- Simplicity: Minimal concepts, quick to get started
- Handoff pattern: Elegant multi-agent collaboration approach
- Built-in tools: Web Search, Code Interpreter ready out of the box
- Guardrails: Native safety mechanism
- OpenAI integration: Seamless connection with the OpenAI ecosystem