Skip to main content
← All Articles · · 16 min read

OpenAI Agents SDK vs Claude API vs Gemini: Building Production AI Agents 2026

Complete comparison of OpenAI Agents SDK, Claude API, and Gemini for production AI agents in 2026. Multi-agent workflows, tool use, streaming, cost, tracing, and when to use each.

ai-agentsopenaiclaudegeminillmtypescriptproductiontool-usemulti-agent

AI agents have crossed from research curiosity to production infrastructure. In 2026, three platforms define the landscape for developers building agentic applications: OpenAI Agents SDK, Anthropic’s Claude API, and Google’s Gemini API. Each has a distinct philosophy, architecture, and sweet spot.

This guide is for developers who have moved past “hello world” prompting and want to understand the real tradeoffs when building agents that run workflows, use tools, hand off between agents, and ship in production.


What We Mean by “AI Agents”

Before comparing, let’s align on terminology. In this guide, an “agent” is:

  • An LLM that can use tools (function calling, code execution, web search)
  • An LLM that can make decisions about what to do next based on tool results
  • Optionally, an LLM that can spawn or hand off to other agents

A simple RAG pipeline is not an agent. An LLM that calls your API, reads the result, decides to call a second API based on what it finds, and writes back — that’s an agent.


OpenAI Agents SDK

Released in early 2025, the OpenAI Agents SDK (formerly Swarm) is OpenAI’s opinionated Python framework for multi-agent systems.

Core Concepts

The SDK is built around three primitives: Agents, Handoffs, and Guardrails.

from agents import Agent, Runner, WebSearchTool, function_tool

@function_tool
def get_weather(city: str) -> str:
    """Get current weather for a city."""
    # Your actual implementation
    return f"Weather in {city}: 72°F, sunny"

weather_agent = Agent(
    name="Weather Assistant",
    model="gpt-4o",
    instructions="You help users check weather. Always use the get_weather tool.",
    tools=[get_weather, WebSearchTool()]
)

# Run the agent
result = Runner.run_sync(
    weather_agent,
    "What's the weather in Tokyo and should I bring an umbrella?"
)
print(result.final_output)

Multi-Agent Handoffs

The killer feature of the Agents SDK is first-class handoff support:

from agents import Agent, Runner, handoff

# Specialized agents
research_agent = Agent(
    name="Researcher",
    model="gpt-4o",
    instructions="Research topics thoroughly using web search.",
    tools=[WebSearchTool()]
)

writer_agent = Agent(
    name="Writer",
    model="gpt-4o",
    instructions="Write clear, engaging articles based on research.",
)

# Orchestrator that delegates
orchestrator = Agent(
    name="Orchestrator",
    model="gpt-4o",
    instructions="""
    Coordinate research and writing tasks.
    Use the researcher for fact-finding, then hand off to the writer.
    """,
    handoffs=[
        handoff(research_agent),
        handoff(writer_agent)
    ]
)

result = Runner.run_sync(
    orchestrator,
    "Write a 500-word article about the latest advancements in quantum computing"
)

Handoffs are explicit: the orchestrator calls a transfer_to_researcher tool that the SDK auto-generates from the handoffs list. The context (conversation history) is automatically passed.

Tool Use and Streaming

import asyncio
from agents import Agent, Runner

async def run_with_streaming():
    agent = Agent(
        name="Code Assistant",
        model="gpt-4o",
        instructions="Help with TypeScript code."
    )

    async with Runner.run_streamed(
        agent,
        "Write a TypeScript function to debounce user input"
    ) as stream:
        async for event in stream.stream_events():
            if event.type == "raw_response_event":
                # Handle streaming tokens
                print(event.data.delta, end="", flush=True)
            elif event.type == "run_item_stream_event":
                if event.item.type == "tool_call_item":
                    print(f"\n[Tool call: {event.item.raw_item.name}]")

asyncio.run(run_with_streaming())

Tracing

The Agents SDK has built-in tracing that sends to OpenAI’s dashboard:

from agents import Agent, Runner
from agents.tracing import set_trace_processors
from agents.tracing.processors import ConsoleSpanExporter

# Add console tracing for development
set_trace_processors([ConsoleSpanExporter()])

agent = Agent(name="test", model="gpt-4o", instructions="Be helpful.")
result = Runner.run_sync(agent, "Hello!")
# Prints trace: Agent run → LLM call → response

Cost: OpenAI Agents SDK

GPT-4o: $2.50/1M input tokens, $10.00/1M output tokens
GPT-4o-mini: $0.15/1M input tokens, $0.60/1M output tokens
o3-mini: $1.10/1M input tokens, $4.40/1M output tokens

For multi-agent workflows with handoffs, costs multiply quickly because each handoff includes the full conversation history.


Anthropic Claude API

Claude doesn’t have a dedicated “agents” SDK yet, but the Claude API’s tool use, extended context window, and computer use capabilities make it powerful for agentic workflows — often more so than frameworks that add overhead.

Tool Use (Function Calling)

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const tools = [
  {
    name: "get_weather",
    description: "Get current weather for a location",
    input_schema: {
      type: "object",
      properties: {
        location: { type: "string", description: "City name" },
        unit: { type: "string", enum: ["celsius", "fahrenheit"] }
      },
      required: ["location"]
    }
  },
  {
    name: "search_web",
    description: "Search the web for information",
    input_schema: {
      type: "object",
      properties: {
        query: { type: "string" }
      },
      required: ["query"]
    }
  }
];

async function runAgent(userMessage: string) {
  const messages: Anthropic.Messages.MessageParam[] = [
    { role: "user", content: userMessage }
  ];

  while (true) {
    const response = await client.messages.create({
      model: "claude-opus-4-6",
      max_tokens: 4096,
      tools,
      messages
    });

    if (response.stop_reason === "end_turn") {
      const textContent = response.content.find(b => b.type === "text");
      return textContent?.text;
    }

    if (response.stop_reason === "tool_use") {
      // Add assistant response to messages
      messages.push({ role: "assistant", content: response.content });

      // Process tool calls
      const toolResults: Anthropic.Messages.ToolResultBlockParam[] = [];

      for (const block of response.content) {
        if (block.type === "tool_use") {
          const result = await callTool(block.name, block.input);
          toolResults.push({
            type: "tool_result",
            tool_use_id: block.id,
            content: result
          });
        }
      }

      // Add tool results and continue
      messages.push({ role: "user", content: toolResults });
    }
  }
}

async function callTool(name: string, input: Record<string, unknown>): Promise<string> {
  switch (name) {
    case "get_weather":
      return `Weather in ${input.location}: 72°F, sunny`;
    case "search_web":
      return `Search results for: ${input.query}`;
    default:
      return "Tool not found";
  }
}

Multi-Agent with Claude

Claude doesn’t have native handoff primitives, but you can build multi-agent workflows:

interface AgentConfig {
  name: string;
  systemPrompt: string;
  tools?: typeof tools;
}

class ClaudeAgent {
  constructor(private config: AgentConfig, private client: Anthropic) {}

  async run(input: string): Promise<string> {
    const messages: Anthropic.Messages.MessageParam[] = [
      { role: "user", content: input }
    ];

    // Agent loop
    while (true) {
      const response = await this.client.messages.create({
        model: "claude-opus-4-6",
        max_tokens: 8192,
        system: this.config.systemPrompt,
        tools: this.config.tools,
        messages
      });

      if (response.stop_reason === "end_turn") {
        const text = response.content.find(b => b.type === "text");
        return text?.text ?? "";
      }

      // Handle tool use...
      messages.push({ role: "assistant", content: response.content });
      const results = await this.handleTools(response.content);
      messages.push({ role: "user", content: results });
    }
  }

  private async handleTools(content: Anthropic.Messages.ContentBlock[]) {
    // Tool execution logic
    const results: Anthropic.Messages.ToolResultBlockParam[] = [];
    for (const block of content) {
      if (block.type === "tool_use") {
        const result = await callTool(block.name, block.input as Record<string, unknown>);
        results.push({ type: "tool_result", tool_use_id: block.id, content: result });
      }
    }
    return results;
  }
}

// Orchestrate multiple agents
async function orchestrate(task: string) {
  const researcher = new ClaudeAgent({
    name: "Researcher",
    systemPrompt: "Research topics thoroughly."
  }, client);

  const researchResult = await researcher.run(`Research: ${task}`);

  const writer = new ClaudeAgent({
    name: "Writer",
    systemPrompt: "Write clear articles based on provided research."
  }, client);

  return writer.run(`Write about: ${task}\n\nResearch: ${researchResult}`);
}

Extended Thinking

Claude’s extended thinking mode is uniquely powerful for complex reasoning tasks:

const response = await client.messages.create({
  model: "claude-opus-4-6",
  max_tokens: 16000,
  thinking: {
    type: "enabled",
    budget_tokens: 10000
  },
  messages: [{ role: "user", content: "Analyze this complex system architecture..." }]
});

// Response contains thinking blocks + final answer
for (const block of response.content) {
  if (block.type === "thinking") {
    console.log("Reasoning:", block.thinking);
  } else if (block.type === "text") {
    console.log("Answer:", block.text);
  }
}

Streaming

const stream = await client.messages.stream({
  model: "claude-opus-4-6",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Explain async/await in TypeScript" }]
});

for await (const chunk of stream) {
  if (chunk.type === "content_block_delta" && chunk.delta.type === "text_delta") {
    process.stdout.write(chunk.delta.text);
  }
}

const finalMessage = await stream.finalMessage();
console.log(`\nTokens used: ${finalMessage.usage.input_tokens + finalMessage.usage.output_tokens}`);

Cost: Claude API

claude-opus-4-6: $15/1M input, $75/1M output
claude-sonnet-4-6: $3/1M input, $15/1M output
claude-haiku-4-5: $0.80/1M input, $4/1M output

Claude’s 200K context window is a significant advantage for agents that need to maintain long conversation histories or process large documents.


Google Gemini API

Gemini’s 2025-2026 model family has made Google a serious contender in the agentic space, with the 1M token context window and native multimodal capabilities being standout features.

Basic Tool Use

import { GoogleGenerativeAI, FunctionDeclarationSchemaType } from "@google/generative-ai";

const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);

const tools = [
  {
    functionDeclarations: [
      {
        name: "get_weather",
        description: "Get weather for a city",
        parameters: {
          type: FunctionDeclarationSchemaType.OBJECT,
          properties: {
            location: { type: FunctionDeclarationSchemaType.STRING }
          },
          required: ["location"]
        }
      }
    ]
  }
];

const model = genAI.getGenerativeModel({
  model: "gemini-2.0-flash",
  tools
});

const chat = model.startChat();

async function runGeminiAgent(query: string) {
  let result = await chat.sendMessage(query);

  while (result.response.functionCalls()?.length) {
    const calls = result.response.functionCalls()!;
    const responses = await Promise.all(
      calls.map(async (call) => ({
        functionResponse: {
          name: call.name,
          response: { result: await callTool(call.name, call.args) }
        }
      }))
    );

    result = await chat.sendMessage(responses);
  }

  return result.response.text();
}

Gemini’s 1M Context Advantage

// Process an entire codebase in one context window
const model = genAI.getGenerativeModel({ model: "gemini-2.0-pro" });

const largeContext = await fs.readFile("entire-codebase.txt", "utf-8");

const result = await model.generateContent([
  { text: largeContext },
  { text: "Find all potential security vulnerabilities in this codebase and suggest fixes." }
]);

For agents that need to reason over large corpora — entire codebases, legal documents, research papers — Gemini’s 1M token window is unmatched.

Cost: Gemini API

gemini-2.0-pro: $1.25/1M input (<128K), $5.00/1M input (>128K), $10.00/1M output
gemini-2.0-flash: $0.075/1M input, $0.30/1M output
gemini-2.0-flash-thinking: $0.035/1M input, $3.50/1M output

Gemini Flash is the most cost-effective option for high-throughput agentic workflows.


Feature Comparison

FeatureOpenAI Agents SDKClaude APIGemini API
Native agent framework✅ (Agents SDK)❌ (DIY)❌ (DIY)
Multi-agent handoffs✅ NativeManual orchestrationManual orchestration
Tool use✅✅✅
Streaming✅✅✅
Context window128K200K1M
Extended reasoning✅ (o1/o3)✅ (extended thinking)✅ (thinking)
Built-in tracing✅❌ (need OTEL)❌ (need OTEL)
Computer use❌✅❌
Multimodal✅✅✅ (best-in-class)
Code execution✅ (code interpreter)✅ (beta)✅

Production Considerations

Reliability and Retries

All three platforms can fail. Your agent loop needs retry logic:

async function withRetry<T>(
  fn: () => Promise<T>,
  maxAttempts = 3,
  baseDelay = 1000
): Promise<T> {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxAttempts) throw error;
      // Exponential backoff
      await new Promise(r => setTimeout(r, baseDelay * Math.pow(2, attempt - 1)));
    }
  }
  throw new Error("unreachable");
}

const result = await withRetry(() =>
  client.messages.create({ model: "claude-opus-4-6", /* ... */ })
);

Cost Monitoring

Build cost tracking into your agent from the start:

class CostTracker {
  private totalInputTokens = 0;
  private totalOutputTokens = 0;

  track(usage: { input_tokens: number; output_tokens: number }) {
    this.totalInputTokens += usage.input_tokens;
    this.totalOutputTokens += usage.output_tokens;
  }

  estimateCostCents(model: "opus" | "sonnet" | "haiku"): number {
    const rates = {
      opus: { input: 15, output: 75 },    // per 1M tokens
      sonnet: { input: 3, output: 15 },
      haiku: { input: 0.8, output: 4 }
    };
    const rate = rates[model];
    return (
      (this.totalInputTokens / 1_000_000) * rate.input +
      (this.totalOutputTokens / 1_000_000) * rate.output
    ) * 100; // Convert to cents
  }
}

Observability with OpenTelemetry

For Claude and Gemini (which lack built-in tracing), use OpenTelemetry:

import { trace, SpanStatusCode } from "@opentelemetry/api";

const tracer = trace.getTracer("ai-agent");

async function tracedAgentRun(input: string) {
  return tracer.startActiveSpan("agent.run", async (span) => {
    span.setAttributes({
      "agent.input": input.slice(0, 100),
      "agent.model": "claude-opus-4-6"
    });

    try {
      const result = await runAgent(input);
      span.setStatus({ code: SpanStatusCode.OK });
      return result;
    } catch (error) {
      span.recordException(error as Error);
      span.setStatus({ code: SpanStatusCode.ERROR });
      throw error;
    } finally {
      span.end();
    }
  });
}

When to Choose Each

Choose OpenAI Agents SDK if:

  • You want a batteries-included framework with native multi-agent primitives
  • You’re building Python-first applications
  • OpenAI’s built-in tracing dashboard is valuable for your team
  • You need tight integration with OpenAI’s code interpreter or DALL-E
  • Your team is already comfortable with the OpenAI ecosystem

Choose Claude API if:

  • Your task requires long context (200K tokens for processing large documents)
  • You need extended thinking for complex multi-step reasoning
  • Computer use automation is in scope
  • You want the safest/most instruction-following model for sensitive domains
  • You’re building TypeScript/JavaScript applications (SDK quality is excellent)

Choose Gemini API if:

  • Cost is a primary constraint (Gemini Flash is the most affordable option)
  • You need to process very large contexts (1M tokens for codebases, long documents)
  • Multimodal capabilities (video, audio, images) are central to your application
  • You’re building on Google Cloud and want native integration
  • High-throughput batch processing is your primary use case

The 2026 Landscape

In 2026, the question isn’t “which model is smartest” — all three are capable of production-grade agentic behavior. The question is which ecosystem fits your workflow.

OpenAI Agents SDK is winning the “framework developer” mindset — teams who want something opinionated that just works.

Claude API is winning the “power user” mindset — teams who need maximum context, the best instruction-following, and are comfortable building their own orchestration.

Gemini API is winning the “cost and scale” mindset — teams processing millions of calls per day or needing to handle massive context windows at minimal cost.

For most production applications, the pragmatic advice is: start with one, instrument properly for cost and reliability, and don’t be afraid to route different tasks to different providers. The multi-provider pattern is increasingly common — use Claude for careful reasoning, Gemini Flash for high-volume classification, and GPT-4o for anything that needs OpenAI’s specific ecosystem integrations.

Free Download

50 CLI Commands That Save Hours Every Week

The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.

  • Git mastery (10 cmds)
  • File & search wizardry
  • Docker & DevOps tricks
  • Process & network tools
  • Productivity boosters
  • macOS & Linux install guide

No spam. 1 email/week max. Unsubscribe anytime.

Found this guide useful? Check out our free developer tools.