10.01

Agent, Runner, and the first run

Lesson 01 of 5 · 4:15
skillmaxing
Python with AI
Lesson 01
Agent, Runner, and the first run
0:00 / 4:151x
Notes

You have written the agent loop by hand, so the OpenAI Agents SDK will not feel like magic. It is the same loop with names on the parts. An Agent is the configuration: a name, instructions, the tools it may call, the agents it may hand off to, and optionally the type of its final answer. A Runner is the loop: it takes an agent and an input, calls the model, runs tools, and returns a result when the model produces a final answer. Nothing you learned in module nine is wasted; the SDK just stops you rewriting it per project.

The package is openai-agents and it imports as agents. It talks to OpenAI models by default, so it needs OPENAI_API_KEY in the environment.

uv add openai-agents
export OPENAI_API_KEY=sk-...

The first run

from agents import Agent, Runner

relay = Agent(
    name="Relay",
    instructions=(
        "You are Relay, the support assistant for a small SaaS. "
        "Answer briefly. Ask one clarifying question when the request "
        "is ambiguous. Never promise refunds or credits."
    ),
)

result = Runner.run_sync(relay, "How do I change my billing email?")
print(result.final_output)

Run it with uv run main.py and you get a sentence back. Runner.run_sync is the blocking entry point for scripts and tests. Inside a FastAPI route, or anywhere an event loop is already running, use the async form: result = await Runner.run(relay, message). Both return the same result object, and final_output is the model's final text, or a typed object if you asked for one.

There are no tools yet, so this run is one model call. It is still worth doing once, because everything you add in the next lessons changes what happens inside the runner, not how you call it.

What the Runner does for you

Map it onto lesson 09.01 and every line of the hand-written loop has a home.

What the Runner does
Figure 1What the Runner doesThe same loop as lesson 09.01, with the message bookkeeping done for you.
Hand-written loop Agents SDK
The for loop with max_turns Runner.run with max_turns= (default 10)
TOOLS list and run_tool dispatcher tools=[...] on the Agent; the schema is derived from the function
Appending assistant and tool messages Done inside the runner; the result exposes them
stop_reason != "tool_use" The runner detects a final answer, or a handoff, or a tool call
Returning text result.final_output
Session save and load session= (lesson 10.04)

The one thing to internalise is that an Agent is data. Creating one makes no network call. You can build agents at import time, keep them in a module, and pass the same instance to thousands of runs. Per-request state, such as which customer is talking, does not belong on the agent; it goes into the run.

The Agent constructor's main parameters:

Parameter What it is
name A label used in traces and handoffs
instructions The system prompt
tools Function tools and hosted tools (lesson 10.02)
handoffs Other agents this one may transfer to (lesson 10.03)
output_type A Pydantic model the final answer must match

Typed output with output_type

Relay's first real use of the SDK is not chat; it is classification. Every incoming message gets a category and an urgency before anything else happens, and a typed output makes that a function call with a schema rather than a string to parse.

from typing import Literal
from pydantic import BaseModel
from agents import Agent, Runner

class Triage(BaseModel):
    category: Literal["billing", "technical", "account", "other"]
    urgency: Literal["low", "normal", "high"]
    summary: str

triage = Agent(
    name="Triage",
    instructions="Classify the customer's message. The summary is one sentence.",
    output_type=Triage,
)

result = Runner.run_sync(triage, "I was charged twice this month and need it fixed today")
t: Triage = result.final_output
print(t.category, t.urgency)   # billing high

final_output is now a Triage instance, validated before it reaches you. If the model produces something that does not fit the schema, the runner surfaces that as an error rather than handing you a half-parsed dict. This is structured output from lesson 08.05 wired into the loop, and it is the pattern for any agent whose answer is consumed by code rather than read by a person.

Engineering note. Decide per agent whether its answer is for a person or for code, and give every code-facing agent an output_type. Free text that "usually" contains a category is the most common source of silent breakage in agent systems, and it is avoidable at the cost of one Pydantic model.

Where it goes wrong

Runner.run_sync inside FastAPI. An event loop is already running, and run_sync will refuse or block it. Use await Runner.run(...) in async code, always.

Per-request state on the agent. Building a new Agent per request with the customer's details baked into instructions works, but it makes the agent impossible to cache, trace, or test in isolation. Keep the agent static and pass per-run context to the runner.

Missing API key. The first run fails with an authentication error. Set OPENAI_API_KEY or load it through pydantic-settings from .env as in lesson 04.04.

Instructions that describe the company, not the job. Three paragraphs about the product and one line about what to do produce chatty, unfocused answers. Lead with the behaviour: what to answer, what to ask, what never to promise.

Treating max_turns as a limit that will not be hit. It will, and when it is the runner raises. Catch it in the route and return a graceful message, exactly as in lesson 09.01.

Try it

Install the SDK in your Relay project, write the triage agent above, and run it against ten support messages you make up, printing category and urgency for each. Then change urgency to Literal["low", "high"] only, rerun, and note how the messages you had marked normal are reassigned. That is a live demonstration of the schema shaping the answer.

Next: function tools and hosted tools, where lookup_customer, search_help_articles, and escalate_ticket become @tool functions and the runner starts calling them.

Free preview

Continue with the complete track

Keep your progress and unlock the surrounding lessons, exercises, and complete learning path.

Unlock the complete track
124 online