OpenAI Agents SDK: Handoffs, Guardrails, Tracing

> OpenAI Agents SDK is the lightweight multi-agent framework built on the Responses API. Five primitives: Agent, Handoff, Guardrail, Session, Tracing. Handoffs are tools named transfer_to_. Guardrails trip on input or output. Tracing is on by default.

Type: Learn + Build

Languages: Python (stdlib)

Prerequisites: Phase 14 · 01 (Agent Loop), Phase 14 · 06 (Tool Use)

Time: ~75 minutes

Learning Objectives

The Problem

Agents that cannot delegate cleanly end up stuffing everything into one prompt. Agents without guardrails ship PII, policy-violating output, or loop forever. OpenAI's SDK codifies the three primitives that make multi-agent work tractable.

The Concept

Five primitives

  1. Agent. LLM + instructions + tools + handoffs.
  2. Handoff. Delegation to another agent. Represented to the model as a tool named transfer_to_.
  3. Guardrail. Validation on input (first agent only), output (last agent only), or tool invocation (per function tool).
  4. Session. Automatic conversation history across turns.
  5. Tracing. Built-in spans for LLM generations, tool calls, handoffs, guardrails.

Handoffs as tools

The model sees transfer_to_billing_agent in its tool list. Calling it signals the runtime to:

  1. Copy the conversation context (or collapse it via nest_handoff_history beta).
  2. Initialize the target agent with its instructions.
  3. Continue the run with the target agent.

This is the supervisor pattern (Lesson 13 / Lesson 28) productized.

Guardrails

Three flavors:

Mode:

Tripwires raise InputGuardrailTripwireTriggered / OutputGuardrailTripwireTriggered.

Tracing

On by default. Every LLM generation, tool call, handoff, and guardrail emits a span. OPENAI_AGENTS_DISABLE_TRACING=1 opts out. add_trace_processor(processor) fans spans to your own backend alongside OpenAI's.

Sessions

Session stores conversation history in a backend (SQLite, Redis, custom). Runner.run(agent, input, session=session) auto-loads and appends.

Where this pattern goes wrong

Build It

code/main.py implements the SDK shape in stdlib:

Run it:

python3 code/main.py

The trace shows two successful handoffs, one input guardrail trip, and a span tree mirroring what the real SDK emits.

Use It

Ship It

outputs/skill-agents-sdk-scaffold.md scaffolds an Agents SDK app with a triage agent, handoffs, input/output/tool guardrails, session store, and a trace processor.

Exercises

  1. Add a handoff hop counter: refuse after N transfers. Trace the behavior.
  2. Implement nest_handoff_history as an option — collapse prior messages into one summary before transferring.
  3. Write a blocking output guardrail. Compare latency on prompts that would trip it vs ones that pass.
  4. Wire add_trace_processor to a JSON logger. What shape does it emit per span?
  5. Read the SDK docs. Port your stdlib toy to openai-agents-python. What did you model wrong?

Key Terms

Term What people say What it actually means
Agent "LLM + instructions" Agent type in the SDK; owns tools and handoffs
Handoff "Transfer" Tool the model calls to delegate to another agent
Guardrail "Policy check" Validation on input / output / tool invocation
Tripwire "Guardrail trip" Exception raised when guardrail rejects
Session "History store" Conversation memory persisted between runs
Tracing "Spans" Built-in observability over LLM + tool + handoff + guardrail
Blocking guardrail "Sequential check" Guardrail runs first; no token waste on trip
Parallel guardrail "Concurrent check" Guardrail runs alongside; lower latency, wastes tokens on trip

Further Reading