« Phase 21 README · Warmup · Deep Dive · Principal Deep Dive · Core Contributor · Staff Notes

Phase 21 — Hitchhiker's Guide

The compressed practitioner tour. Read the WARMUP for the mechanisms; this is the stuff you say in the meeting.

30-second mental model

The OpenAI Agents SDK is four primitives over one loop. The loop lives in Runner: call the model → if it asked for tools, run them and feed results back → if it asked to hand off, switch the active agent → else return the final output; max_turns guards it. Agents are declarative bundles (instructions + tools + handoffs + guardrails + output_type). Tools are typed Python functions (@function_tool derives the schema from the signature). Handoffs are transfer_to_<agent> tools that switch the active agent. Guardrails are cheap tripwire checks around the agent. Sessions are automatic cross-run memory. Understand the loop and the whole SDK falls out.

The things to tattoo on your arm

ThingThe one-liner
the loopmodel → tools → feed back → repeat → final | max_turns
@function_toolschema from the signature; no default ⇒ required
trust boundarymodel proposes a call; on_invoke_tool executes it
tool exceptionfed back as an observation (recoverable)
unknown toolModelBehaviorError (structurally impossible)
RunResultfinal_output, new_items, last_agent
handoffa transfer_to_<agent> tool that switches the active agent
last_agentwho finished — the specialist after a handoff
handoff vs agent-as-toolcontrol transfers vs control returns
guardrailGuardrailFunctionOutput(output_info, tripwire_triggered)
guardrail modelsmall / fast / cheap — it gates the expensive agent
sessionpass session=; history in, new items out, automatically
session_ida tenant boundary, not just a chat key
durabilitywrap the loop in Temporal → crash-safe, resumable

Framework one-liners

  • OpenAI Agents SDK = Agent + Runner (the loop) + handoffs + guardrails + sessions; Python-first, few primitives, built-in tracing.
  • LangGraph = an explicit StateGraph (nodes/edges/typed state + reducers, checkpoints, interrupts). Reach for it when you need to see and persist the control flow.
  • CrewAI = roles + tasks in a crew/process. Reach for it to prototype a role-playing team fast.
  • All of them are while not done: proposal = model(scratchpad); execute with different ergonomics — and they interoperate.

War stories

  • The agent that "handed off" but kept answering. Someone used an agent-as-tool (wrapped Runner.run in a @function_tool) expecting a handoff. Control kept returning to the orchestrator, so the specialist never owned the reply and last_agent was always the triage agent. One line — make it a real handoff — fixed the routing. Know which mechanism you're using.
  • The guardrail that doubled the bill. A team ran their input guardrail on the same big model as the agent "to be accurate." Every request now paid for two big-model calls. Swapping the guardrail to a tiny fast model cut cost back and lost nothing — the guardrail only needed a yes/no.
  • The two agents that ping-ponged forever. Triage handed off to billing, billing handed back to triage, repeat. No max_turns across handoffs, so it burned turns until the timeout. The budget is a guard, and it must count across handoffs, not per agent.
  • The session that leaked across tenants. session_id was the conversation id but not scoped by user, so a shared id surfaced another customer's history. session_id is a security boundary (Phase 13), full stop.
  • The crash that cost $4. A 12-minute research agent died at minute 10 on a deploy; the in-memory loop restarted from zero and re-paid for everything. Wrapping the Runner in Temporal made every turn a durable step — the restart resumed at minute 10.

Vocabulary

Agent (declarative bundle) · Runner (the loop) · @function_tool (schema from signature) · on_invoke_tool (the executor) · RunResult (final_output / new_items / last_agent) · max_turns (loop budget) · Handoff (transfer_to_<agent>, switches agent) · agent-as-tool (control returns) · on_handoff / input_filter (handoff knobs) · Guardrail / tripwire (tripwire_triggered) · Session (get/add/pop/clear) · output_type (structured output) · Tracing (spans) · durable execution (Temporal).

Beginner mistakes

  1. Thinking the framework runs your tools — it doesn't; the model proposes, on_invoke_tool runs.
  2. Confusing a handoff (control transfers) with an agent-as-tool (control returns).
  3. Running guardrails on the expensive model instead of a cheap fast one.
  4. Forgetting max_turns counts across handoffs, so agents can ping-pong.
  5. Treating session_id as a chat key rather than a tenant/security boundary.
  6. Assuming the in-memory loop is durable — it isn't; long-running agents need Temporal.
  7. Reaching for the SDK (or LangGraph, or CrewAI) when the task is a workflow with one fuzzy step (Phase 00) — the most senior move is still "this shouldn't be an agent."