OpenAI SDK: The OpenAI Agents SDK
In part 07 you wrote the tool loop by hand. The Agents SDK ( pip install openai-agents ) packages that loop with a few primitives: an Agent (instructions + tools…
In this post
What this post covers
In part 07 you wrote the tool loop by hand. The Agents SDK (pip install openai-agents) packages that loop with a few primitives: an Agent (instructions +
tools + model), a Runner that runs the loop, handoffs between
specialised agents, guardrails that validate input and output, sessions
for memory, and built-in tracing. It is deliberately small, and still plain Python.
The examples follow the Agents SDK documentation as of September 2026 (from agents import Agent,
Runner, function_tool). The package is not installed in this guide's build environment, so these
blocks were not run. The concepts map directly onto the hand-written loop from part 07, which was.
1. An agent with tools
from agents import Agent, Runner, function_tool
@function_tool
def get_order(order_id: str) -> dict:
"""Look up one order by id. Returns status, delivery date and amount."""
return {"order_id": order_id, "status": "DELIVERED", "delivered_on": "2025-10-12"}
support = Agent(
name="Support",
instructions="You help customers with orders. Look orders up; never guess.",
model="gpt-5.6-luna",
tools=[get_order], # schema generated from the type hints and docstring
)
result = Runner.run_sync(support, "Where is order 1042?")
print(result.final_output)
@function_tool builds the JSON schema from the signature and docstring, so type hints and
docstrings become the prompt. The Runner calls the model, executes requested tools, sends results
back, and stops when the agent produces a final output or a turn limit is reached. That is the same loop as
part 07.
2. Handoffs: a team of specialists
orders = Agent(name="Orders", instructions="Answer order and delivery questions.", tools=[get_order])
billing = Agent(name="Billing", instructions="Handle invoices and refunds. Refunds need confirmation.",
tools=[get_invoice, request_refund])
triage = Agent(name="Triage",
instructions="Route the customer to Orders or Billing. Do not answer yourself.",
handoffs=[orders, billing])
result = Runner.run_sync(triage, "I was charged twice for order 1042")
print(result.last_agent.name, "→", result.final_output)
3. Guardrails
from pydantic import BaseModel
from agents import Agent, GuardrailFunctionOutput, InputGuardrailTripwireTriggered, Runner, input_guardrail
class Relevance(BaseModel):
is_about_our_shop: bool
checker = Agent(name="Relevance check", instructions="Is this about our shop's orders or billing?",
output_type=Relevance, model="gpt-5.6-luna")
@input_guardrail
async def only_shop_questions(ctx, agent, user_input):
verdict = (await Runner.run(checker, user_input, context=ctx.context)).final_output
return GuardrailFunctionOutput(output_info=verdict, tripwire_triggered=not verdict.is_about_our_shop)
support = Agent(name="Support", instructions="...", tools=[get_order],
input_guardrails=[only_shop_questions])
try:
Runner.run_sync(support, "Write my homework essay on the French revolution")
except InputGuardrailTripwireTriggered:
print("Sorry, I can only help with orders and billing.")
A guardrail runs a cheap check (often a small model with a structured output) alongside the main agent, and aborts the run when its tripwire fires. Output guardrails check the final answer before it reaches the user. As part 16 said, guardrails complement least-privilege tools. They do not replace them.
4. Sessions and tracing
from agents import SQLiteSession
session = SQLiteSession("customer-u123", "conversations.db") # memory persisted per conversation id
Runner.run_sync(support, "Hi, I'm Asha. Order 1042 please.", session=session)
Runner.run_sync(support, "And when was it delivered?", session=session) # remembers the order
Every run is traced by default: model calls, tool calls, handoffs and guardrails appear as spans you can inspect in the OpenAI dashboard’s Traces view, or export to your own observability stack. The AI Engineering guide’s parts on OpenTelemetry and Langfuse explain what to do with those traces.
5. When to use an agent framework
Use it when…
You have several tools, multi-step tasks, specialist roles, or you want sessions, guardrails and tracing without writing them yourself.
Skip it when…
One call with structured output solves the problem, or a fixed sequence of steps (a workflow) is enough. Deterministic code is cheaper, faster and easier to test than an autonomous loop.
Recap
- Agent = instructions + tools + model; Runner runs the tool loop until a final output.
@function_toolturns type hints and docstrings into tool schemas.- Handoffs build specialist teams; guardrails validate input and output; sessions remember.
- Tracing is built in; prefer plain workflows when an agent is not needed.
Get the free RAG production checklist
25 checks to run before real users see your RAG feature: exact-match misses, invented citations, prompt injection through documents, quality that slips after a prompt change. Confirm your email and the PDF comes with the welcome email, plus each new experiment.