OpenAI Agents SDKopenai-agents 0.22 · Python 3.10+
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
25 small wins to finish your path

Build the support agent

Lesson 0 promised an agent that reads a message, decides whether it needs a tool, hands the hard cases to another agent, refuses what it should not answer, and asks you before it spends money. Here it is, and every piece of it is something you have already built alone.

The desk, as a whole
Guardrail. Off topic questions are refused before the model is called at all.Step 1 of 5

Two tools, one of them dangerous

python
@function_tool
def lookup_order(order_id: str) -> str:
    """Look up the status of an order by its id."""
    return f"Order {order_id} shipped on 3 March."
python
@function_tool(needs_approval=True)
def refund(amount: int) -> str:
    """Refund an amount in rupees to the customer."""
    return f"Refunded {amount} rupees."

Only the second one is marked. Looking something up is safe at any hour; paying money out is not.

The specialists

python
billing = Agent(name="Billing", instructions="Handle refunds. Be brief.",
                model=PretendModel([call("refund", amount=500), "Your refund is on its way."]),
                tools=[refund])

technical = Agent(name="Technical", instructions="Handle logins. Be brief.",
                  model=PretendModel(["Try logging out and back in."]))

The refund tool lives on Billing and nowhere else, so Technical cannot issue one however the conversation goes. That is lesson 13's real argument, in two lines.

The front desk

python
triage = Agent(
    name="Triage",
    instructions="Send the customer to Billing or Technical.",
    tools=[lookup_order],
    handoffs=[billing, technical],
    input_guardrails=[only_support],
)

Everything you have learned, on one object. A tool of its own, two agents it can hand to, and a gate on the way in.

Three turns, one conversation

Example
print("turn 1")
first = await Runner.run(triage, "Where is order A17?", session=session)
print("  ", first.final_output)

print("turn 2")
second = await Runner.run(triage, "I was charged twice, refund me 500", session=session)
print("   waiting on:", second.interruptions[0].raw_item.name)
state = second.to_state()
state.approve(second.interruptions[0])
done = await Runner.run(triage, state, session=session)
print("  ", done.final_output, "| handled by", done.last_agent.name)

print("turn 3")
try:
    await Runner.run(triage, "do my homework", session=session)
except InputGuardrailTripwireTriggered:
    print("   blocked, off topic")

print()
print("remembered", len(await session.get_items()), "items across the conversation")

Read what happened

Turn one was answered by Triage itself. The question named an order, so it used the lookup tool and replied. No handoff, no approval, nothing dramatic.

Turn two went to Billing, which asked for the refund and stopped. The run came back with the request waiting, a person approved it, and only then did the money move. Notice that the answer came from Billing, not Triage.

Turn three never reached a model. The guardrail read it and refused, which cost nothing.

Eleven items across all three turns, in one session. The second turn knew about the first because they share it.

Make it yours

  • Make it real. Lesson 23, one line, and the deciding stops being a script.
  • Make it survive a restart. Give the session a filename, from lesson 19.
  • Make it answerable. Add an output guardrail from lesson 17 so it can never promise a delivery date.
  • Make it typed. Give Triage an output_type from lesson 10 and route on the field rather than on the model's wording.
  • Make it visible. Stream it with lesson 21 so a person watches the handoff happen.

What was left out

Enough exists in the SDK to fill another course, and knowing the names is most of the battle.

Left outWhat it is
Realtime and voice agentsThe same agents over a live audio connection. Four pages of the docs.
SandboxesRunning an agent's code in an isolated environment, local or hosted.
Hosted toolsWeb search, file search and computer use, run on OpenAI's side.
MCP serversTools from an external process rather than functions in your file.
Other session backendsSQLAlchemy, encrypted and advanced SQLite sessions.
LiteLLMOne more way to reach a hundred other providers.
Visualizationdraw_graph, which draws your agents and handoffs.
TestingThe SDK's own helpers for testing agents without a model.
run_demo_loopA one line interactive chat in your terminal, for trying an agent by hand.
Lifecycle hooks and tracing processorsCallbacks on every step, and sending traces somewhere other than OpenAI.
Where you have got to
You now know the part of this SDK that people use every day: agents, tools, the loop, structured answers, context, handoffs, guardrails, approval, sessions and streaming. The documentation will read like a reference from here rather than a wall.
Try it yourself
  • Add a third specialist for deliveries and route to it.
  • Reject the refund instead of approving it and read what the customer is told.
  • Give the session a filename and run the file twice.

This is what real progress feels like.