If you've been building with the raw OpenAI Chat Completions API, you've probably written the same boilerplate loop over and over: send messages, check for tool_calls, invoke the tool, append the result, call the API again. The OpenAI Agents SDK (released March 2025) was designed to eliminate exactly that ceremony. It introduces a higher-level Agent object that manages the loop, tools, and multi-agent routing for you.
This post builds a working two-agent pipeline — a Researcher that looks up information and hands off to a Writer that summarises it — in under 60 lines of Python.
Everything in the Agents SDK is built on three objects:
| Primitive | What it does |
|---|---|
Agent | Defines an LLM + instructions + a list of tools it can use |
Runner | Executes an agent (or a chain of agents) and manages the loop |
Tool | A Python function decorated with @function_tool that the agent can call |
That's deliberately minimal. If you know what those three things do, you can build almost anything.
pip install openai-agents
export OPENAI_API_KEY="sk-..."The SDK uses gpt-4.1 by default. You can override per-agent with model="gpt-4o-mini" if you want to cut costs during development.
A tool is just a Python function with a docstring. The SDK uses the docstring as the tool description and infers the JSON schema from the type annotations.
from agents import function_tool
@function_tool
def search_web(query: str) -> str:
"""Search the web and return a short summary of the top result."""
# In production, call a real search API (Tavily, Brave, etc.)
# For this demo we return a stub.
return f"Top result for '{query}': AI agents are autonomous systems that use LLMs to reason and act."That's it — no JSON schema to write, no manual registration.
from agents import Agent
researcher = Agent(
name="Researcher",
instructions="You are a research assistant. Use the search_web tool to find information, then hand off to the Writer agent with a concise summary of what you found.",
tools=[search_web],
model="gpt-4.1",
)
writer = Agent(
name="Writer",
instructions="You are a technical writer. Take the research summary you receive and write a clear, 3-sentence explanation suitable for a developer blog.",
model="gpt-4.1",
)A handoff tells the Researcher that it can transfer control to the Writer once it has finished gathering information. It appears in the agent's tool list just like a regular tool.
from agents import handoff
researcher.tools.append(handoff(writer))When the Researcher calls transfer_to_Writer (the handoff tool the SDK auto-generates), the Runner passes control and accumulated context to the Writer agent.
import asyncio
from agents import Runner
async def main():
result = await Runner.run(
researcher,
input="What are AI agents and why do developers care about them?",
)
print(result.final_output)
asyncio.run(main())The Runner handles the full agentic loop:
search_web, gets a resulttransfer_to_Writer handoffresult.final_output contains the Writer's responseGuardrails let you validate input before it reaches the agent. A common use case is blocking off-topic requests:
from agents import input_guardrail, GuardrailFunctionOutput, RunContextWrapper
from pydantic import BaseModel
class TopicCheck(BaseModel):
is_on_topic: bool
reason: str
@input_guardrail
async def topic_guardrail(
ctx: RunContextWrapper, agent: Agent, input: str
) -> GuardrailFunctionOutput:
"""Only allow questions about AI and software development."""
result = await Runner.run(
Agent(
name="TopicChecker",
instructions="Decide if the user's question is about AI or software development. Respond with JSON.",
output_type=TopicCheck,
),
input=input,
context=ctx.context,
)
check: TopicCheck = result.final_output
return GuardrailFunctionOutput(
output_info=check,
tripwire_triggered=not check.is_on_topic,
)
# Add the guardrail to the researcher
researcher = Agent(
name="Researcher",
instructions="...",
tools=[search_web, handoff(writer)],
input_guardrails=[topic_guardrail],
)If tripwire_triggered is True, the SDK raises an InputGuardrailTripwireTriggered exception before the agent even runs.
| Raw Chat Completions | Agents SDK | |
|---|---|---|
| Tool loop | Manual (you write it) | Automatic |
| Multi-agent routing | Manual | Handoffs |
| Tracing | None built-in | Built-in trace per run |
| Input validation | Manual | Guardrails |
| Best for | Full control, simple one-shot calls | Agentic workflows, multi-step tasks |
Use raw Chat Completions when you need fine-grained control over every API call (e.g., streaming partial tokens into a UI). Reach for the Agents SDK the moment your workflow involves tool calls, retries, or multiple agents handing off to each other.
Tomorrow we'll put the two SDKs side by side: the same weather-lookup agent built with the Anthropic Claude SDK and with the OpenAI Agents SDK, with a line-by-line comparison of how tool schemas, invocation flows, and error handling differ.