OpenAI Agents SDK in Production: Enterprise Handoff Architecture for Multi-Agent Systems
OpenAI's Agents SDK — the successor to the experimental Swarm library — is now the vendor-blessed way to build multi-agent systems on OpenAI models. Handoffs, guardrails, and structured outputs are first-class primitives, and it is deliberately small enough to reason about. This is a practical look at what the SDK actually gives you, how it fits into an enterprise architecture, and the deployment patterns we use in production.
TL;DR. The OpenAI Agents SDK is a small, opinionated Python (and now TypeScript) library for building agents on OpenAI models. It graduated from the Swarm experiment with four primitives worth learning: Agent, Handoff, Tool, and Guardrail — plus built-in tracing and session management. It is not a heavyweight orchestration framework and does not try to be. In production it fits best when the shape of your work is 'route the user to the right specialist agent, then let that agent act within constrained rails,' and when you want a runtime whose entire behaviour you can hold in your head.
From Swarm to Production SDK
The Agents SDK's lineage matters. OpenAI shipped Swarm as an educational, experimental library — deliberately unmaintained — to demonstrate the handoff pattern for multi-agent systems. Teams liked the pattern enough that Swarm ended up in production against explicit warnings. The Agents SDK is OpenAI's response: the same handoff-first mental model, but with the production concerns Swarm ignored — tracing, guardrails, structured outputs, sessions, proper testing — treated as first-class.
The important thing to internalise: the SDK is small on purpose. It has four primitives, a handful of decorators, and a tracing surface. That is the whole thing. Anything bigger you build sits around it, not inside it. Teams coming from LangChain sometimes read this as under-featured and reach for something heavier; teams coming from having written their own agent loop read it as exactly enough.
The Four Primitives
Agent. An LLM plus instructions, a model, and a set of tools. The agent has a name (which is what handoffs address) and can have structured output via a Pydantic schema. That is essentially the type: an agent is a callable object that takes user input and returns a run result.
Handoff. A special kind of tool call where one agent delegates the rest of the conversation to another agent. The receiving agent inherits the message history and continues from there. This is how the SDK models specialisation: you do not build one giant prompt that tries to be a support agent, a billing agent, and a returns agent; you build three narrower agents and let a triage agent hand off.
Tool. A Python function decorated with @function_tool, exposed to the agent, invoked by the runtime when the model asks. Same shape as tool use in the raw Chat Completions API, but with the schema derived from the function signature and docstring automatically.
Guardrail. A function that inspects an input or output and can halt execution. Input guardrails run before the agent starts; output guardrails run before results reach the user. This is where content policy, PII checks, and jailbreak defence live. Guardrails run in parallel with the agent to keep latency down.
On top of these there are two supporting pieces: Sessions for persistent conversation state, and a Tracing surface that records the full run — every agent invocation, every tool call, every handoff, every guardrail decision — for inspection in the OpenAI dashboard or export to your own observability.
How the OpenAI Agents SDK Compares to the Alternatives
vs. Claude Agent SDK
The two SDKs converge on similar territory from different vendors. Both are opinionated, both give you a tight runtime around a model, both take governance seriously. The centre of gravity differs: OpenAI's SDK puts handoffs and guardrails at the front of the mental model; Claude's SDK puts subagents, hooks, and native MCP integration at the front. If your workflow decomposes cleanly into specialist agents that transfer conversations between them, the OpenAI shape fits naturally. If your workflow is one agent with rich tool use, either fits, and the deciding factor is usually the model. Either way, do not choose 'both' — pick one per product and let the AI gateway handle the model-provider question separately.
vs. LangGraph
LangGraph is a state-machine framework — you model your agent as an explicit graph of nodes and edges, with the graph runtime deciding what runs when. The Agents SDK is the opposite: the LLM decides what runs next, within the rails of tools, handoffs, and guardrails. LangGraph is a better fit when the workflow is well-defined and you need deterministic control over transitions. The Agents SDK is a better fit when you want the model to plan and act, and you are prepared to constrain what it can do through guardrails and tool granularity. Complex production systems sometimes run both — LangGraph as the outer orchestration, an Agents-SDK agent inside specific nodes.
vs. CrewAI and AutoGen
CrewAI models agents as roles in a team and assigns tasks between them; AutoGen models agents as conversational participants and lets them discuss until they converge. Both work, and both introduce more abstraction than the Agents SDK. The SDK's handoff model gives you the specialisation without the metaphor: an agent is a callable, a handoff is a transfer of control, and the trace makes the whole run inspectable. Teams that found CrewAI or AutoGen more surface than they wanted often land on the Agents SDK.
vs. Writing Your Own Loop on Chat Completions
You can build the same shape on the raw Chat Completions API. What you would end up writing looks a lot like the Agents SDK — the SDK captures the answers to about a dozen small design decisions (retry semantics, streaming, session state, guardrail parallelism, structured output validation) that everyone re-decides when they hand-roll. If you have no strong reason to hand-roll, the SDK is engineering time you get back.
The Handoff Pattern: What It Is Actually For
The handoff primitive is the SDK's opinion about how to structure multi-agent systems, and it is worth taking seriously. The shape is: a small triage agent with a broad set of possible handoffs, and specialist agents with tight instructions and narrow tool grants. The triage agent's job is not to solve the user's problem — it is to identify what the user needs and hand off to the specialist who can solve it.
Why this pattern beats one giant prompt: the specialist prompts stay short and unambiguous, tool grants stay minimal, and you can evaluate each specialist independently. It also degrades gracefully — if a new specialist is misbehaving, you disable that handoff and traffic returns to the triage agent's fallback behaviour, without a rewrite.
The pattern breaks down when the specialists need to collaborate mid-conversation rather than transfer control. If your problem is 'a lawyer and an engineer both weigh in on the same question and produce a synthesis,' handoffs are the wrong tool — you want either a parallel-invocation pattern or a proper multi-agent orchestrator like LangGraph or CrewAI. Handoffs assume one active agent at a time.
Guardrails: Where the Production Story Actually Lives
Guardrails are the primitive most teams underestimate. In a demo, guardrails are a nice-to-have; in production they are the difference between a fun prototype and a system you can defend to legal, security, and support.
The SDK's guardrail model is deliberately simple: a guardrail is a function that takes the input (or output) and either passes or raises. Input guardrails run before the agent starts, in parallel with the model call being dispatched. If any input guardrail fails, the run is halted and the failure is returned; the model was never invoked. Output guardrails run before the result reaches the caller, and can halt the response even after generation.
What goes in an input guardrail: prompt-injection detection, PII detection with policy on what to do about it, off-topic detection (so your billing agent does not answer medical questions), rate-limit checks. What goes in an output guardrail: refusal detection, PII leakage, structural checks (does this JSON parse), policy violations. Standard prompt-injection defence still applies here — the SDK gives you the enforcement point but you supply the detection logic, usually a combination of pattern matching for cheap cases and an LLM classifier for the harder ones.
Reference Architecture for an OpenAI Agents SDK Deployment
| Layer | Responsibility | What runs there |
|---|---|---|
| Ingress / gateway | TLS termination, authentication, rate limiting | API gateway (Kong, ingress controller, or a managed API tier) |
| Application service | Session routing, agent invocation, business logic around the SDK | Stateless Python service (FastAPI or similar) hosting the Runner |
| Agent runtime | Agent loop, tool routing, handoffs, guardrails | OpenAI Agents SDK |
| LLM access | Key management, budgets, model routing, cost accounting | AI gateway (LiteLLM or equivalent) |
| Tools | Enterprise system integration | In-process functions for fast internal work; MCP servers for reusable, auditable integrations |
| Session store | Persistent conversation state across turns | Redis or a relational store, depending on retention needs |
| Observability | Traces, prompt versions, evaluations, cost | Langfuse, LangSmith, or an APM vendor's LLM module |
| Governance policy | Rules about what agents may do, in which contexts | Small policy service consulted from guardrails |
A Production-Shaped Agent Setup
# agents.py (sketch — real projects split this across modules)
from agents import Agent, Runner, function_tool, GuardrailFunctionOutput, input_guardrail
from pydantic import BaseModel
# ---- Structured outputs ----
class TicketDecision(BaseModel):
action: str # 'route_billing', 'route_returns', 'answer_directly'
rationale: str
# ---- Tools ----
@function_tool
def lookup_order(order_id: str) -> dict:
"""Look up an order by ID. Read-only."""
return order_service.get(order_id) # your service
@function_tool
def create_refund_request(order_id: str, reason: str) -> dict:
"""Create a refund request. Requires supervisor approval before it is executed."""
return refund_service.create_pending(order_id, reason)
# ---- Input guardrails ----
@input_guardrail
async def block_prompt_injection(ctx, agent, input_text):
# Cheap heuristics first, LLM classifier for anything ambiguous.
verdict = injection_classifier.classify(input_text)
return GuardrailFunctionOutput(
output_info={'score': verdict.score},
tripwire_triggered=verdict.is_injection,
)
@input_guardrail
async def enforce_scope(ctx, agent, input_text):
# This agent handles support. Reject medical, legal, financial advice.
verdict = scope_classifier.classify(input_text)
return GuardrailFunctionOutput(
output_info={'topic': verdict.topic},
tripwire_triggered=not verdict.in_scope,
)
# ---- Specialist agents ----
billing_agent = Agent(
name='billing',
instructions=BILLING_INSTRUCTIONS,
tools=[lookup_order, create_refund_request],
model='gpt-4.1',
)
returns_agent = Agent(
name='returns',
instructions=RETURNS_INSTRUCTIONS,
tools=[lookup_order],
model='gpt-4.1',
)
# ---- Triage agent ----
triage_agent = Agent(
name='triage',
instructions=TRIAGE_INSTRUCTIONS,
handoffs=[billing_agent, returns_agent],
input_guardrails=[block_prompt_injection, enforce_scope],
model='gpt-4.1-mini', # cheaper model for routing
)
# ---- Runtime ----
async def handle_message(session_id: str, user_input: str):
result = await Runner.run(
triage_agent,
input=user_input,
session=session_store.get_or_create(session_id),
)
return result.final_output
Structured Outputs, and Why They Matter More Than You Think
Every agent that returns to a system rather than a human should return a structured output. The SDK supports this natively via Pydantic: give the agent an output_type, and the SDK will enforce it — retrying if the model produces malformed output, or failing loudly if it cannot.
Why this matters: an agent whose output is a validated schema is composable. You can chain it, log it, evaluate it, and route on its fields deterministically. An agent whose output is prose is a black box that needs another LLM call to parse. The engineering cost of defining a Pydantic model is far smaller than the operational cost of parsing free-form text at every downstream step.
The pattern that works: user-facing conversational agents return prose; every other agent — routing, extraction, classification, tool selection, planning — returns a schema. Even 'chat with structured metadata' is a common shape: the visible answer is prose, the metadata (confidence, sources cited, next action) is structured. The SDK supports this cleanly.
Tracing is on by default and it goes to OpenAI. By default the SDK's tracing surface exports traces to the OpenAI platform dashboard. That is fine for many teams; for regulated or sensitive workloads, you either need to opt out of that export and point traces at your own observability, or ensure your OpenAI enterprise agreement covers where trace data lives. Decide this before you deploy, not after.
Where the OpenAI Agents SDK Tends To Be the Right Choice
You are already committed to OpenAI models
The SDK is optimised for the Responses and Chat Completions APIs. If OpenAI is decided and you want a runtime that plays to its shape — including reasoning models, structured outputs, and computer-use — this is the fit.
Your workflow decomposes into specialists with a triage step
The handoff model is opinionated. If your product has a natural pattern of 'figure out what the user needs, then route to the right expert,' the SDK's shape is exactly right and other frameworks are overkill.
You want production primitives — guardrails, tracing, sessions — in the box
You do not have to build the plumbing. Guardrails run in parallel with model calls, sessions persist across turns, and traces cover the whole run. That is a lot of scaffolding you avoid.
You value a runtime you can hold in your head
The SDK is small. You can read the source over a lunch break. For teams that value understanding their production stack top to bottom, that is a real advantage over larger frameworks.
Where It Is Not the Right Choice
If your workflow is a rigid pipeline with occasional model calls, an agent runtime is overkill — a workflow engine calling the OpenAI API directly is simpler. If you need agents that collaborate mid-conversation rather than transfer control, handoffs are the wrong primitive and a proper multi-agent orchestrator is a better fit. If your primary model is not OpenAI, the SDK does support other models via LiteLLM but it is not its centre of gravity — the Claude Agent SDK or a framework-agnostic runtime will feel more natural. And if compliance forbids sending your data to hosted OpenAI endpoints at all, the interesting question is which self-hostable model and runtime you can use, not which vendor SDK.
Seven Decisions Worth Making Before Your First Deploy
- Handoff topology. Draw the agent graph before you write code. A triage agent plus three or four specialists is a proven shape. Twenty specialists behind one triage agent is a warning sign — either merge some, or introduce a second triage layer.
- Model per agent. Use cheaper models for routing and more capable models for specialists that do the actual work. A
gpt-4.1-minitriage agent handing off to agpt-4.1specialist is a common cost pattern that also improves triage latency. - Guardrail placement. Input guardrails belong on the entry point — usually the triage agent, so nothing bypasses them. Output guardrails belong on the specialist that produces user-facing output. Do not sprinkle guardrails across every agent; concentrate them at the edges.
- Structured outputs everywhere except the user-facing surface. Every internal agent should return a Pydantic model. The one exception is the agent that produces the final message to the human, which returns prose.
- Session store. Redis for high-volume, short-retention chat. Postgres for regulated workloads that need long retention and audit. Whatever you pick, decide up front what session-expiry, session-length, and session-privacy look like.
- Tracing destination. OpenAI-hosted for teams comfortable with that data location; disabled and forwarded to your own observability platform for regulated workloads. This is a compliance decision, not a technical one.
- Route through an AI gateway. Point the SDK at LiteLLM or equivalent rather than directly at api.openai.com. You get key management, budgets, virtual keys, and per-tenant cost accounting for free, and you keep the option of moving traffic to a different provider without a rewrite.
How To Approach an OpenAI Agents SDK Pilot
Pilot shape that works: pick one product surface with real traffic and a natural triage pattern — support intake, sales qualification, an internal ops helpdesk. Build one triage agent plus two or three specialist agents. Wire input guardrails on the triage agent — prompt-injection detection and scope enforcement, at minimum. Point the SDK at your AI gateway. Route traces to your observability platform. Run it in shadow mode for a week, then suggested-action mode for another week where the agent proposes and a human accepts, then autonomous for the safest actions only.
At the end you will have concrete numbers on handoff patterns (which specialist gets routed to and how often), guardrail trip rates (how often did we block), cost per conversation, and the specific failure modes that come up in your context. That is the input you actually need to decide the next investment — more specialists, better guardrails, richer tools, or a rethink of the topology.
If you would like help with the architecture — the handoff graph, the guardrail suite, the tracing and evaluation setup, or connecting agents to your existing AI governance framework, gateway, and observability layer — talk to us. We have run this shape across customer-facing, employee-facing, and internal-ops deployments, and the reference architecture is far more repeatable than most first builds assume.
Frequently Asked Questions
Is the OpenAI Agents SDK the same thing as Swarm?
Can we use non-OpenAI models with the Agents SDK?
How does this compare to LangGraph for us?
How do handoffs actually work under the hood?
Where should we put guardrails and how many?
Is tracing safe to leave on for our compliance posture?
How does this fit alongside our AI gateway and observability?
Written By
Inductivee Team
AuthorAgentic AI Engineering Team
The Inductivee engineering team — a remote-first group of multi-agent orchestration specialists, RAG pipeline architects, and data liquidity engineers who have shipped 40+ agentic deployments across 25+ enterprises since 2012. Our writing is grounded in what we actually build, break, and operate in production.
Inductivee is a remote-first agentic AI engineering firm with 40+ production deployments across 25+ enterprises since 2012. Our engineering content is written by active practitioners and technically reviewed before publication. Compliance: SOC2 Type II, HIPAA, GDPR, ISO 27001.
Engineer This With Inductivee
The engineering patterns in this article are what our team builds into production every day. Explore the related service to see how we deliver this capability at enterprise scale.
Agentic Custom Software Engineering
We engineer autonomous agentic systems that orchestrate enterprise workflows and unlock the hidden liquidity of your proprietary data.
ServiceAutonomous Agentic SaaS
Agentic SaaS development and autonomous platform engineering — we build SaaS products whose core loop is powered by LangGraph and CrewAI agents that execute workflows, not just manage them.
Related Articles
Claude Agent SDK in Production: Enterprise Patterns for Building Reliable Anthropic-Powered Agents
Multi-Agent Orchestration: LangChain vs CrewAI vs AutoGen for Enterprise Deployments
Langfuse in Production: Enterprise LLM Observability Architecture for Multi-Model AI Systems
Ready to Build This Into Your Enterprise?
Inductivee engineers agentic systems, RAG pipelines, and enterprise data liquidity solutions. Let's scope your project.
Start a Project