Agents vs. a Single API Call — When You Need One

Ma Mahalakshmi V Updated 16 Sep 2026
7 min read ·Lesson 47 of 224

What Every Previous Unit Had in Common

Every example from Unit 1 through Unit 10 shared one structural property: your own application code was in control of the loop. You decided when to call client.responses.create(), you decided what to do with the result, and if the result included a function call (Unit 8) or a built-in tool call (Unit 9), your code — or the platform, for a built-in tool — handled it and fed the outcome back in. The model never decided on its own to keep working after handing control back to you; every round trip through the loop was something your code explicitly orchestrated.

This works well for a bounded number of well-understood steps. It becomes noticeably more work as a task grows in one specific way: when the number of steps needed to complete it isn't known in advance, when several distinct roles or areas of specialization need to hand a task to each other, or when the task benefits from a model reasoning about its own next step — "have I gathered enough information yet?", "should I hand this off to a specialist?" — rather than your application code making that decision on the model's behalf every single time.

What an Agent Actually Is

An agent, in the sense this unit uses the term, is a configuration bundling together a model, a set of instructions, and a set of tools it's allowed to use — paired with a runtime that manages the loop of calling the model, executing any tool calls it requests, and feeding the results back in, repeating until the agent produces a final answer, without your application code manually orchestrating each round trip.

from agents import Agent, Runner

triage_agent = Agent(
    name="Triage Agent",
    instructions="Help the user with general questions about their account.",
    model="gpt-5.6-terra",
)

result = Runner.run_sync(triage_agent, "What's the status of my last order?")
print(result.final_output)

Note: The exact package name, import paths, and class and method names for the Agents SDK can change across versions. Confirm the current package name, installation instructions, and API surface against the current official documentation before relying on these specifics in production code.

Two things are worth noticing here, in contrast to every prior unit's client.responses.create() pattern. First, there is no explicit loop in this code at all — Runner.run_sync() is what manages calling the model, checking whether it produced a final answer or requested a tool, and continuing until a final answer is reached; Unit 8's run_conversation_with_tools() function, which you wrote and controlled yourself, is functionally replaced here by code the SDK provides. Second, an Agent is a reusable, named configuration — you define it once, with its instructions and model, and can run it against many different inputs, rather than building the whole request from scratch every time.

Why Not Just Use client.responses.create() With a Bigger Loop?

Nothing about function calling or built-in tools (Units 8 and 9) technically requires the Agents SDK — every one of this course's earlier projects built exactly this kind of multi-step, multi-tool interaction using client.responses.create() directly, with your own code managing the loop. The Agents SDK doesn't add a capability that wasn't otherwise possible; it removes boilerplate and adds structure for a specific category of application: one involving multiple distinct roles that need to hand work to each other, safety checks that need to run consistently across every step, or a genuinely unpredictable number of steps where writing your own loop-with-a-safety-cap (as Unit 8, Lesson 3's max_rounds did) starts to feel like reimplementing something that already exists.

Aspectclient.responses.create() with your own loop (Units 8-9)The Agents SDK (this unit)
Who manages the tool-call loopYour application code, explicitlyThe SDK's Runner, automatically
Multiple specialized rolesBuilt manually — separate functions, separate prompts, your own routing logicBuilt in — separate Agent instances with a handoff mechanism between them
Consistent safety checks across stepsApplied manually wherever you remember to add themGuardrails apply consistently through the SDK's runtime
Visibility into what happened during a runWhatever you build yourself (Unit 8's log_tool_selection_for_test_questions(), for instance)Built-in tracing (Lesson 6)
Best suited forA well-defined, bounded interaction with tools you fully controlA task better modeled as multiple cooperating roles, or with a genuinely open-ended number of steps

Neither column is a strictly better way to build with the platform; they're suited to different shapes of problem. A single well-defined task — Unit 8's weather assistant, Unit 9's research assistant — is entirely reasonable to build directly against client.responses.create(), and doing so gives you full, direct control over every step. A system that needs several distinct roles cooperating, consistent safety enforcement across an unpredictable number of steps, or built-in observability into a complex run is where the Agents SDK's structure starts to pay for itself.

When You Actually Need an Agent, Rather Than a Direct API Call

Reach for the Agents SDK specifically when a task exhibits one or more of these properties: multiple distinct roles that benefit from being modeled as separate agents handing work to each other (a triage agent routing to a billing specialist or a technical specialist, covered in Lesson 4), a genuinely unpredictable number of steps where a fixed max_rounds safety cap (Unit 8, Lesson 3) feels like an awkward substitute for letting the runtime manage iteration itself, a need for safety checks — input validation, output review — that must apply consistently regardless of which specific tool or model call is happening at any given moment (Lesson 5), or a need to inspect and debug exactly what happened across a complex, multi-step interaction after the fact (Lesson 6).

Conversely, a single, well-scoped task with a known, bounded set of tools — extracting structured data from a document (Unit 6), answering a question using a fixed set of built-in tools (Unit 9) — has no particular need for the Agents SDK's additional structure. Building it directly against client.responses.create(), as this course did throughout Units 1 through 10, remains the simpler and entirely sufficient choice. This unit's remaining lessons build up the Agents SDK's core pieces — defining an agent, giving it tools, handoffs between agents, guardrails, and tracing — culminating in a project (Lesson 7) that genuinely needs several of these properties at once, which is where the SDK's value becomes concrete rather than abstract.

Seeing the Equivalence Directly

It's worth making the equivalence between the two approaches concrete rather than taking it on faith. A simplified version of what Runner.run_sync() does internally looks structurally identical to the tool-calling loop Unit 8, Lesson 3 built by hand.

import json

def run_agent_loop_by_hand(client, instructions: str, tools: list[dict], available_functions: dict, user_input: str, max_rounds: int = 5):
    input_messages = [{"role": "user", "content": user_input}]

    for _ in range(max_rounds):
        response = client.responses.create(
            model="gpt-5.6-terra",
            instructions=instructions,
            input=input_messages,
            tools=tools,
        )

        function_calls = [item for item in response.output if item.type == "function_call"]
        if not function_calls:
            return response.output_text

        for call in function_calls:
            arguments = json.loads(call.arguments)
            result = available_functions[call.name](**arguments)
            input_messages.append(call)
            input_messages.append({
                "type": "function_call_output",
                "call_id": call.call_id,
                "output": json.dumps(result),
            })

    return "Max rounds reached without a final answer."

This is, structurally, the same max_rounds-bounded loop Unit 8, Lesson 3 introduced: call the model, check for function calls, execute them, feed the results back in, repeat until a final answer or a safety cap is reached. Runner.run_sync() performs this same sequence of steps internally; the Agents SDK's value isn't a different underlying mechanism, it's not having to write and maintain this loop yourself, plus the additional structure (handoffs, guardrails, tracing) layered on top of it in the lessons that follow.

Common Mistakes

Reaching for the Agents SDK for a task that's really just a single well-defined tool-calling interaction, taking on additional structure and a new dependency for a problem Unit 8's direct client.responses.create() loop already solves cleanly.

Assuming the Agents SDK adds a fundamentally new capability, rather than understanding it as a structured way to build the same kind of multi-step, multi-tool interaction this course has already been building directly since Unit 8.

Building a single monolithic agent to handle a task that's naturally several distinct roles, missing the opportunity to model it as multiple cooperating agents with clear handoffs, which Lesson 4 covers directly.

Best Practices

Default to a direct client.responses.create() call for a single, well-scoped task, reserving the Agents SDK for tasks that genuinely need multiple cooperating roles, consistent cross-step safety enforcement, or built-in observability.

Recognize the specific properties that make the Agents SDK worth its added structure — multiple roles, an unpredictable number of steps, cross-cutting safety requirements, or a need for detailed run inspection — rather than adopting it as a default framework for every tool-using task.

Treat what you already know about tool calling (Unit 8) and built-in tools (Unit 9) as directly transferable, since the Agents SDK builds on the same underlying model behavior this course has already covered in depth, just with a different runtime managing the loop.

0 Comments

Reviewed before they appear

No comments yet.

OpenAI SDK
Introduction to the OpenAI SDK Setting Up Python Creating an API Key Your First Call — client.responses.create() and response.output_text Understanding Billing, Credits, and What a Request Costs Why Responses Replaced Chat Completions Anatomy of a Request: model, input, and instructions Anatomy of a Response: The Typed output Array, Not Just Text Roles: User, Assistant, and Developer/System Choosing a Model, and Reading the Models Page Instead of Memorizing Names Instructions vs. Input Writing Prompts That Get Consistent Results Few-Shot Examples Reasoning Models and the reasoning Parameter Debugging a Prompt That Misbehaves Why Streaming Matters for User Experience stream=True and Iterating Over Events Handling the Event Types You Actually Care About Background Mode for Long-Running Jobs Project — Add Live Streaming to Your Chatbot The Problem With Parsing Free Text JSON Schema and Strict Mode Pydantic Models With the SDK's Parse Helpers Handling Refusals and Validation Failures Project — A Resume-to-JSON Extractor Working With input_image input_file, PDFs, and the Files API Image Generation Speech-to-Text and Text-to-Speech Project: A PDF Question-Answering Script What Function Calling Is Defining a Tool Schema The Full Loop Multiple Tools Errors, Timeouts, and Untrusted Arguments Project: A Weather Assistant Web Search File Search and Vector Stores Code Interpreter Remote MCP Servers and Connectors Project: A Research Assistant What an Embedding Is, Without the Maths Generating and Storing Embeddings Similarity Search From Scratch Hosted Vector Stores vs. Rolling Your Own A Small RAG App Over a Folder of Notes Agents vs. a Single API Call — When You Need One pip install openai Giving Agents Tools Handoffs and Multi-Agent Triage Guardrails and Approvals Tracing and Observing What Your Agent Did A Multi-Agent Support Desk Error Codes and What Each One Means Retries, Timeouts, and Backoff Rate Limits and Spend Limits Prompt Caching and Cost Optimisation The Batch API for Bulk Work Async Clients and Concurrency Moderation and Safety Best Practices Designing the App Backend With FastAPI Streaming to a Simple Frontend Deploying and a Cost/Safety Checklist Why Web Search Is Useful for Current Information Using the Web Search Tool with the Responses API Configuring Search Behavior for Application Use Cases Understanding Citations and Source Attribution Where to Go Next Building a Research Assistant with Web Search Combining Web Search with Structured Outputs Handling Conflicting or Low-Quality Web Sources Reducing Unsupported Claims with Grounded Generation Testing Freshness-Sensitive AI Answers Production Considerations for Web-Grounded Applications Understanding File Search and Retrieval-Augmented Generation Creating and Organizing Vector Stores Uploading Documents for Retrieval Connecting Vector Stores to Responses API Requests Designing Document Metadata and Filtering Strategies Building a PDF Question-Answering Application Improving Retrieval Quality With Better Document Preparation Handling Missing Evidence and Retrieval Failures Combining File Search With Web Search Building a Production Knowledge-Base Assistant What the Code Interpreter Tool Is Designed For Running Python-Based Analysis Through the OpenAI SDK Uploading Datasets for Analysis Analyzing CSV and Spreadsheet Data Generating Charts and Data Summaries Handling Generated Files and Downloadable Artifacts Building a Data-Analysis Assistant Combining Code Execution with Structured Outputs Validating Generated Calculations and Results Security and Sandbox Considerations for Code Execution Understanding Multimodal Input with the OpenAI SDK Sending Images to a Model Image Analysis from URLs and Uploaded Files Extracting Text and Information from Screenshots Building an Image-Question-Answering Application Combining Image Input with Structured Output Analyzing Multiple Images in One Request Handling Image Quality and Input Limitations Designing Multimodal Prompts for Reliable Results Building a Practical Vision-Powered Python Application Understanding Speech-to-Text and Text-to-Speech Workflows Transcribing Audio with the OpenAI SDK Working with Uploaded Audio Files Handling Timestamps and Transcription Metadata Building a Meeting Transcription Workflow Generating Spoken Responses from Text Handling Long Audio and Processing Failures Combining Audio with Text and Tool Calling Building an End-to-End Python Voice Application What Embeddings Are and When to Use Them Generating Embeddings With the OpenAI API Preparing Text for Embedding Comparing Vectors With Cosine Similarity Building a Simple Semantic Search Engine in Python Storing Embeddings in a Database Metadata Filtering for Semantic Search Chunking Strategies for Better Retrieval Evaluating Semantic Search Quality Building a Document Similarity Application When Batch Processing Makes Sense Designing Large-Volume AI Processing Pipelines Using Asynchronous Python with the OpenAI SDK Running Concurrent Requests Safely Controlling Concurrency and Avoiding Rate Limits Tracking Batch Job Progress Handling Partial Failures in Bulk Workloads Retrying Failed Items Without Duplicating Successful Work Designing Resumable AI Processing Jobs Building a Production Batch-Processing Pipeline Batch Processing Makes Sense Large-Scale AI Processing Pipelines Async Python with OpenAI SDK Safe Concurrent Requests Concurrency & Rate Limits Batch Progress Tracking Partial Failure Handling Safe Retry Handling Resumable AI Jobs Production Batch Pipeline System–User Data Separation Reusable App Instructions Prompt Templates & Variables Extraction & Classification Prompts Summarization & Transformation Prompts Explicit Output Requirements Prompt Version Management Prompt Testing & Evaluation Reusable Python Prompt Library API Key Security Secure API Key Storage Secure Secret Management Prompt Injection Prevention Trusted vs. Untrusted Content Tool Argument Validation Sensitive Data Handling Secure Logging AI Action Authorization Production AI Security Checklist Why AI Applications Need Evaluation Beyond Unit Tests Unit Testing OpenAI SDK Integration Code Mocking API Responses in Python Tests Testing Structured Outputs Against Schemas Testing Tool-Calling Workflows Building a Small Evaluation Dataset Measuring Accuracy, Consistency, and Failure Rates Regression Testing Prompts and Model Changes Human Evaluation Versus Automated Evaluation Creating a Repeatable Evaluation Pipeline AI Request Monitoring Token Cost Management Usage Metrics Design Reducing Model Calls Prompt & Context Optimization Model Selection & Optimization AI Caching Strategies Interactive Latency Optimization Usage Dashboards & Budget Alerts Performance & Cost Checklist Every API Call Starts Fresh Fixing API Statelessness Server-Side Conversation Memory Limits of Response Chaining What We're Building Conversation Memory Challenges Preparing an OpenAI SDK Application for Deployment Environment-Specific Configuration for Development and Production Deploying a Python AI Service with Docker Container Health Checks and Startup Configuration Managing Secrets in Cloud Deployments Background Workers for Long-Running AI Tasks Queues and Asynchronous Job Architectures Scaling AI Workloads Horizontally Monitoring Production Incidents and Failures Production Deployment Checklist for OpenAI SDK Applications Reusable OpenAI Service Classes AI Client Dependency Injection Typed AI Responses Python Configuration Management AI Request Decorators Centralized AI Error Handling Clean SDK Abstractions Reusable OpenAI Utilities Internal AI Python Libraries SDK Integration Maintenance Production AI Chatbot Document Q&A System Web Research Assistant Customer Support Agent AI Data Analysis Assistant Image Analysis App Meeting Transcription & Summary Semantic Document Search Multi-Tool AI Agent Production OpenAI SDK App Why "It Looked Fine When I Tested It" Isn't Enough Timing Note Status Note Pre-Decision Status Note Current Availability Note
Ask about this post
AI Ask about this post

Ask questions about Agents vs. a Single API Call — When You Need One and get answers drawn from it.

Signed-in readers only.