Zenaique

Why the OpenAI Agents SDK keeps Agent, Handoff, and Runner as its only three primitives

Flashcard·Easy·4.0 · 0·~30s·Asked atCopy AiIntelMoveworks
Attempt it
TL;DR

Three primitives cover the 'a few agents that hand off to each other' case beautifully and stay out of the way; anything beyond it you build yourself or pick a different framework.

Memory aid
Sign in to see the mnemonic that makes this stick.
Easy to grasp

Imagine the kitchen aisle. Some gadget brands sell you 40 different specialised tools (an avocado slicer, a strawberry huller, a banana peeler). Others sell you a really good knife and trust you with the rest. The Agents SDK is the really good knife brand. It says: most agent work is one agent doing the job until it hands the task to a teammate; we will make that one motion clean and let you bring your own kitchen for everything else. The trade is honest. You give up shelves of specialised gadgets in exchange for fewer ways to cut yourself.

Concept explanation~2 min read

Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.

API surface area is an architectural choice. Frameworks that ship dozens of abstractions are betting that no single shape covers enough of the workload to justify a minimal API. Frameworks that ship three are betting the opposite: that one shape covers most of the workload and the win is in making that shape beautiful.

The OpenAI Agents SDK takes the second bet. It evolved from the experimental Swarm project (2024) into a production-grade SDK with deliberate minimalism: Agent, Handoff, Runner. Understanding why this choice was made tells you when to reach for it and when to pick something with a larger surface.

One-line summary: the SDK bets that most multi-agent work is a few agents handing off to each other, and that betting on that shape produces a better day to day experience than covering every topology. Everything follows from that bet.

The three primitives and what each one carries

Agent

An Agent is a system prompt plus a tool list plus a list of other agents it is allowed to hand off to. The handoff list is an ACL on transfers: an agent can only invoke handoffs to peers explicitly named in its config. This bounds the handoff graph to what you have declared, which is a real safety property compared to free-form peer chat.

Agents can be reused across workflows because they are just configuration objects. A billing agent is the same agent whether you call it from a triage agent in workflow A or directly in workflow B.

Handoff

A Handoff is a typed peer transfer. Under the hood, the SDK exposes a synthetic tool to the model named transfer_to_<agent_name> for each allowed peer. When the model calls it, the runtime intercepts the call, packages the transferred state (the conversation and any context object), and swaps the active agent.

The critical design decision is that handoff lives in the model's native action space. There is no separate side channel for transfers; they are tool calls. That means transfers show up uniformly in tracing, in token accounting, and in the model's own planning, which makes the agent's behaviour interpretable in the same lens you already use for tool use.

Runner

Runner.run (or run_sync) is the loop. It calls the current agent's model with the running conversation, parses the response, executes any tool calls and handoff calls in order, and repeats until the model emits no further tool calls (the agent decided it is done) or a max_turns cap fires. Streaming is supported with the same shape.

The loop is single active agent by design. At any moment one agent owns the turn; handoffs swap which agent owns it, but never run two in parallel. This is the architectural opinion that makes the rest of the API work.

Why a minimal API is the right choice for this workload
What you give up, and what to use instead
Where the SDK sits in the 2026 framework landscape
Sign in to unlock the full deep dive.

Situations where this technique stops working.

Sign in to see when this approach fails.

2–4 min · Everything important, quickly.

Sign in to see the quick scan of the deep dive.
python
from agents import Agent, Runner

billing = Agent(name="billing",
    instructions="Handle billing questions.",
    tools=[lookup_invoice, issue_refund])

tech = Agent(name="tech",
    instructions="Handle technical issues.",
    tools=[search_kb, escalate_to_oncall])

triage = Agent(name="triage",
    instructions="Route the user to the right specialist.",
    handoffs=[billing, tech])

result = Runner.run_sync(triage, "My invoice looks wrong")
print(result.final_output)

Real products, models, and research that use this idea.

  • OpenAI's own documentation example builds a triage agent that hands off to billing, refund, and tech-support specialists.
  • Klarna and Stripe-style customer-support routing fits the agent plus handoff pattern cleanly.
Sign in to see more production examples.

What an interviewer would ask next. Try answering before peeking at the approach.

QHow would you add structured shared state across multiple agents in the SDK?
A

Pass a context object through Runner.run that each agent's tools can read and write. It is not a reducer-backed graph, but for shared-counter, shared-cache, or running-budget patterns it is enough. For anything more structured, lift to LangGraph state.

2 more follow-ups an interviewer would ask next. Sign in to reveal them.

Red flags & common mistakes

The phrases that signal junior thinking. Click to expand.

Most common mistake

Expecting the SDK to cover parallel fan-out, durable resume, or graph topologies. It explicitly does not; that is the design centre, not a gap.

Sign in to see all red flags and common mistakes.

60 second bullets to scan on the way to the call.

  • What each of Agent, Handoff, and Runner is and what it does

  • Why Handoff being a tool call (not a side channel) matters for tracing

Sign in to unlock the revision sheet.

Primary sources. Browse if you want the original framing.

Similar questions

Same topic, related formats. Practice these next.

4 curated
Next question
Why AutoGen 0.4 makes TerminationCondition a first class primitive instead of leaving it to convention
Flashcard·Medium