Building an AI Agent With the OpenAI Agents SDK
Spent a week hand-rolling retries and tracing? This OpenAI Agents SDK tutorial gets you a tool-using agent — with handoffs and guardrails — in far fewer lines.
# pip install openai-agents (Python 3.10+)
from agents import Agent, Runner
agent = Agent(
name="Assistant",
instructions="You are a concise, helpful assistant.",
)
result = Runner.run_sync(agent, "Explain what an API is in one sentence.")
print(result.final_output)A developer once spent a full week building an agent by hand: a loop, custom retry logic, a homegrown tracing system to see what the model was doing, and a fragile way to pass work to a second specialized agent. It worked, barely. Then they rebuilt it on the OpenAI Agents SDK in an afternoon and got the retries, the tracing, and the handoff for free — which is exactly what this OpenAI Agents SDK tutorial walks you through. The week wasn't wasted — it taught them what the SDK was doing — but it's a vivid lesson in when a framework earns its keep.
The lesson isn't "always use the SDK." It's that once you understand the loop well enough to build it by hand, a good framework stops being a black box and becomes a set of shortcuts you can evaluate honestly — and for retries, tracing, and handoffs, the shortcuts are worth taking.
This OpenAI Agents SDK tutorial gets you to that afternoon version. Copy-paste code, real primitives, no filler.
Quick-Start: OpenAI Agents SDK Tutorial (Copy This Now)
Install the package and run your first agent.
[object Object],
,[object Object], agents ,[object Object], Agent, Runner
agent = Agent(
name=,[object Object],,
instructions=,[object Object],,
)
result = Runner.run_sync(agent, ,[object Object],)
,[object Object],(result.final_output)What this does: it defines an agent (a model plus instructions) and runs it with
Runnerrun_syncRunner.runresult.final_outputTwo things are worth noticing even in this tiny example. You never wrote a loop —
Runner⚡ Pro tip: Start with an agent that has no tools at all, just instructions, and confirm it runs. Getting the install, the import, and the
RunnerUnderstanding the Variables
The SDK is built from a small set of primitives, and knowing them by name makes everything else click.
An Agent is a model plus its instructions and its tools. The Runner is what executes the agent loop — you don't write the loop yourself; the Runner calls the model, runs any tools, and repeats until done. Function tools turn ordinary Python functions into tools the agent can call, generating the schema for you. Handoffs let one agent pass control to another specialist. Guardrails validate inputs or outputs. Sessions persist conversation state across turns. And tracing is built in, so you can see every step without wiring your own logging.
That's the whole surface. There's no giant API to memorize — if you can hold six words in your head, you can hold the SDK. Everything below is just combining these six ideas.
This OpenAI Agents SDK tutorial builds from the raw-loop mental model outward on purpose, so each primitive maps to something you'd otherwise hand-code. The Agent is your model-plus-instructions. The Runner is your loop. Function tools are your dispatch step. Handoffs, guardrails, sessions, and tracing are the parts you'd have spent that hand-rolled week building yourself.
⚡ Pro tip: The Runner is the piece that replaces the hand-rolled loop from a from-scratch agent. If you understand that "the Runner is my loop," the mental map from raw code to the SDK is basically complete.
Step-by-Step: Adding a Tool
The
@function_tool[object Object], agents ,[object Object], Agent, Runner, function_tool
,[object Object],
,[object Object], ,[object Object],(,[object Object],) -> ,[object Object],:
,[object Object],
,[object Object], ,[object Object],
agent = Agent(
name=,[object Object],,
instructions=,[object Object],,
tools=[get_order_status],
)
result = Runner.run_sync(agent, ,[object Object],)
,[object Object],(result.final_output)What this does: the decorator turns
get_order_statusThe
-> str⚡ Pro tip: The docstring becomes the tool description the model sees, so write it as a real instruction — what it does, when to use it, what it returns — not a throwaway line. Everything you know about good tool descriptions applies directly to the docstring.
Pro-Level Variations
Three primitives cover most real needs beyond a single tool-using agent.
Handoffs create multi-agent systems. You define a specialist agent and let a triage agent hand off to it — the SDK manages the transfer. This is the feature that took the week-long hand-roll and made it an afternoon.
billing_agent = Agent(name=,[object Object],, instructions=,[object Object],)
triage = Agent(
name=,[object Object],,
instructions=,[object Object],,
handoffs=[billing_agent],
)What this does: it lets the triage agent pass control to the billing specialist when a question is about billing, without you writing any routing plumbing — the handoff is a first-class primitive.
A subtle but important point about handoffs: each agent keeps its own instructions and tools, so the billing agent can have permissions and a system prompt the triage agent never sees. That isolation is exactly the "boundaries that mean something" test for when multi-agent is worth it — different instructions, different tools, a real separation of duties.
Concretely, teams use these primitives in recognizable shapes. A SaaS support team runs a triage agent that hands billing questions to a billing specialist and technical ones to a docs-searching agent. A fintech team wraps every user-facing agent in input guardrails that reject anything outside financial questions. An e-commerce team uses sessions so a shopping assistant remembers what a customer asked three turns ago. Same six primitives, arranged to fit the job.
⚡ Pro tip: Turn on and actually read the built-in tracing before you build anything complex. Seeing every model call, tool call, and handoff is the capability that most repays the switch from a hand-rolled loop.
Guardrails validate inputs or outputs — rejecting off-topic requests before the agent works, or checking a response before it reaches the user. Sessions persist state so a multi-turn conversation remembers earlier turns. And because tracing is built in, you can inspect every step in the dashboard instead of adding print statements.
⚡ Pro tip: Add guardrails for anything user-facing. An input guardrail that rejects out-of-scope requests up front saves tokens and prevents the agent from wandering into territory it shouldn't handle.
Troubleshooting Common Issues
The agent won't call your tool. Check the docstring — a vague one leaves the model unsure when to use it. Write it as a clear instruction.
Import errors on
from agents import ...openai-agentsYou can't see what the agent did. You don't need custom logging — the built-in tracing shows every step; check the dashboard rather than reinventing observability.
The agent's answers ignore earlier turns. You're not using sessions, so each run starts fresh. Add a session to persist state across turns, or you'll rebuild the conversation context by hand every time.
⚠️ Common mistake: Reaching for handoffs and multiple agents on day one, when a single agent with a few function tools would do the whole job. The SDK makes multi-agent easy, which tempts people into building it prematurely. Start with one agent and tools; add handoffs only when you have a genuine second specialty that needs isolation.
Your Turn
You've now got a tool-using agent on the OpenAI Agents SDK, plus the map of primitives — Agent, Runner, function tools, handoffs, guardrails, sessions, tracing — to grow it. Add a real tool, confirm the trace shows the call, and only reach for handoffs when a second specialty truly earns its own agent.
And lean on the built-in tracing from the start. The single biggest reason the afternoon rebuild beat the week-long hand-roll wasn't the loop — it was seeing every step for free instead of instrumenting it yourself.
The instructions and tool docstrings you write here are the part that most shapes behavior, and they're worth keeping versioned rather than scattered across files. PromptABCD gives you one home to store and reuse agent instructions and tool descriptions, so the docstrings you sharpen for one SDK agent carry straight into the next instead of being rewritten from memory.
Continue Reading
Save the prompts from this post
PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.
