OpenAI Agents SDK
Build AI applications with text agents, voice agents (realtime), multi-agent workflows, tools, guardrails, and human-in-the-loop patterns.
Quick Start
npm install @openai/agents zod@4
npm install @openai/agents-realtime # Voice agents
export OPENAI_API_KEY="your-key"
Runtimes: Node.js 22+, Deno, Bun, Cloudflare Workers (experimental)
Core Concepts
Agents: LLMs with instructions + tools
import { Agent } from '@openai/agents';
const agent = new Agent({ name: 'Assistant', tools: [myTool], model: 'gpt-4o-mini' });
Tools: Functions with Zod schemas
import { tool } from '@openai/agents';
import { z } from 'zod';
const weatherTool = tool({
name: 'get_weather',
parameters: z.object({ city: z.string() }),
execute: async ({ city }) => `Weather in ${city}: sunny`,
});
Handoffs: Multi-agent delegation
const triageAgent = Agent.create({ handoffs: [specialist1, specialist2] });
Guardrails: Input/output validation
const agent = new Agent({ inputGuardrails: [detector], outputGuardrails: [filter] });
Structured Outputs: Type-safe responses
const agent = new Agent({ outputType: z.object({ sentiment: z.enum(['positive', 'negative']) }) });
Text Agents
Basic: const result = await run(agent, 'What is 2+2?')
Streaming:
const stream = await run(agent, 'Tell me a story', { stream: true });
for await (const event of stream) {
if (event.type === 'raw_model_stream_event') process.stdout.write(event.data?.choices?.[0]?.delta?.content || '');
}
Multi-Agent Handoffs
const billingAgent = new Agent({ name: 'Billing', handoffDescription: 'For billing questions', tools: [refundTool] });
const techAgent = new Agent({ name: 'Technical', handoffDescription: 'For tech issues', tools: [ticketTool] });
const triageAgent = Agent.create({ name: 'Triage', handoffs: [billingAgent, techAgent] });
Guardrails
Input: Validate before processing
const guardrail: InputGuardrail = {
execute: async ({ input }) => ({ tripwireTriggered: detectHomework(input) })
};
const agent = new Agent({ inputGuardrails: [guardrail] });
Output: Filter responses (PII detection, content safety)
Human-in-the-Loop
const refundTool = tool({ name: 'process_refund', requiresApproval: true, execute: async ({ amount }) => `Refunded $${amount}` });
let result = await runner.run(input);
while (result.interruption?.type === 'tool_approval') {
result = await promptUser(result.interruption) ? result.state.approve(result.interruption) : result.state.reject(result.interruption);
}
Realtime Voice Agents
Create:
import { RealtimeAgent } from '@openai/agents-realtime';
const voiceAgent = new RealtimeAgent({
voice: 'alloy', // alloy, echo, fable, onyx, nova, shimmer
model: 'gpt-4o-realtime-preview',
tools: [weatherTool],
});
Browser Session:
import { RealtimeSession } from '@openai/agents-realtime';
const session = new RealtimeSession(voiceAgent, { apiKey: sessionApiKey, transport: 'webrtc' });
await session.connect();
CRITICAL: Never send OPENAI_API_KEY to browser! Generate ephemeral session tokens server-side.
Voice Handoffs: Voice/model must match across agents (cannot change during handoff)
Templates:
templates/realtime-agents/realtime-agent-basic.tstemplates/realtime-agents/realtime-session-browser.tsxtemplates/realtime-agents/realtime-handoffs.ts
References:
references/realtime-transports.md- WebRTC vs WebSocket
Framework Integration
Cloudflare Workers (experimental):
export default {
async fetch(request: Request, env: Env) {
process.env.OPENAI_API_KEY = env.OPENAI_API_KEY;
const agent = new Agent({ name: 'Assistant', model: 'gpt-4o-mini' });
const result = await run(agent, (await request.json()).message);
return Response.json({ response: result.finalOutput, tokens: result.usage.totalTokens });
}
};
Limitations: No voice agents, 30s CPU limit, 128MB memory
Next.js: app/api/agent/route.ts → POST handler with run(agent, message)
Templates: cloudflare-workers/, nextjs/
Error Handling (9+ Errors Prevented)
1. Zod Schema Type Errors
Error: Type errors with tool parameters.
Workaround: Define schemas inline.
// ❌ Can cause type errors
parameters: mySchema
// ✅ Works reliably
parameters: z.object({ field: z.string() })
Source: GitHub #188
2. MCP Tracing Errors
Error: "No existing trace found" with MCP servers.
Workaround:
import { initializeTracing } from '@openai/agents/tracing';
await initializeTracing();
Source: GitHub #580
3. MaxTurnsExceededError
Error: Agent loops infinitely.
Solution: Increase maxTurns or improve instructions:
const result = await run(agent, input, {
maxTurns: 20, // Increase limit
});
// Or improve instructions
instructions: `After using tools, provide a final answer.
Do not loop endlessly.`
4. ToolCallError
Error: Tool execution fails.
Solution: Retry with exponential backoff:
for (let attempt = 1; attempt <= 3; attempt++) {
try {
return await run(agent, input);
} catch (error) {
if (error instanceof ToolCallError && attempt < 3) {
await sleep(1000 * Math.pow(2, attempt - 1));
continue;
}
throw error;
}
}
5. Schema Mismatch
Error: Output doesn't match outputType.
Solution: Use stronger model or add validation instructions:
const agent = new Agent({
model: 'gpt-4o', // More reliable than gpt-4o-mini
instructions: 'CRITICAL: Return JSON matching schema exactly',
outputType: mySchema,
});
All Errors: See references/common-errors.md
Template: templates/shared/error-handling.ts
Orchestration Patterns
LLM-Based: Agent decides routing autonomously (adaptive, higher tokens)
Code-Based: Explicit control flow with conditionals (predictable, lower cost)
Parallel: Promise.all([run(agent1, text), run(agent2, text)]) (concurrent execution)
Debugging
process.env.DEBUG = '@openai/agents:*'; // Verbose logging
const result = await run(agent, input);
console.log(result.usage.totalTokens, result.history.length, result.currentAgent?.name);
❌ Don't use when:
- Simple OpenAI API calls (use
openai-apiskill instead) - Non-OpenAI models exclusively
- Production voice at massive scale (consider LiveKit Agents)
Production Checklist
- [ ] Set
OPENAI_API_KEYas environment secret - [ ] Implement error handling for all agent calls
- [ ] Add guardrails for safety-critical applications
- [ ] Enable tracing for debugging
- [ ] Set reasonable
maxTurnsto prevent runaway costs - [ ] Use
gpt-4o-miniwhere possible for cost efficiency - [ ] Implement rate limiting
- [ ] Log token usage for cost monitoring
- [ ] Test handoff flows thoroughly
- [ ] Never expose API keys to browsers (use session tokens)
Token Efficiency
Estimated Savings: ~60%
| Task | Without Skill | With Skill | Savings | |------|---------------|------------|---------| | Multi-agent setup | ~12k tokens | ~5k tokens | 58% | | Voice agent | ~10k tokens | ~4k tokens | 60% | | Error debugging | ~8k tokens | ~3k tokens | 63% | | Average | ~10k | ~4k | ~60% |
Errors Prevented: 9 documented issues = 100% error prevention
Templates Index
Text Agents (8):
agent-basic.ts- Simple agent with toolsagent-handoffs.ts- Multi-agent triageagent-structured-output.ts- Zod schemasagent-streaming.ts- Real-time eventsagent-guardrails-input.ts- Input validationagent-guardrails-output.ts- Output filteringagent-human-approval.ts- HITL patternagent-parallel.ts- Concurrent execution
Realtime Agents (3):
9. realtime-agent-basic.ts - Voice setup
10. realtime-session-browser.tsx - React client
11. realtime-handoffs.ts - Voice delegation
Framework Integration (4):
12. worker-text-agent.ts - Cloudflare Workers
13. worker-agent-hono.ts - Hono framework
14. api-agent-route.ts - Next.js API
15. api-realtime-route.ts - Next.js voice
Utilities (2):
16. error-handling.ts - Comprehensive errors
17. tracing-setup.ts - Debugging
References
agent-patterns.md- Orchestration strategiescommon-errors.md- 9 errors with workaroundsrealtime-transports.md- WebRTC vs WebSocketcloudflare-integration.md- Workers limitationsofficial-links.md- Documentation links
Official Resources
- Docs: https://openai.github.io/openai-agents-js/
- GitHub: https://github.com/openai/openai-agents-js
- npm: https://www.npmjs.com/package/@openai/agents
- Issues: https://github.com/openai/openai-agents-js/issues
Version: SDK v0.3.7 Last Verified: 2026-01-09 Skill Author: Jeremy Dawes (Jezweb) Production Tested: Yes