Getting started with OpenAI Agents SDK: a practical guide

coding入门25 分钟阅读2026/7/11

Last month, I was building a customer support automation system. I started with a simple script that called the OpenAI chat API, wrapped it in a while loop, and bolted on some function calls. It worked—kind of. But managing the conversation state, handling tool execution, and orchestrating multiple specialized bots quickly turned into a tangled mess of conditional logic. When I heard OpenAI released the Agents SDK, I was skeptical. Did we really need another framework? After spending a weekend rebuilding my support system with it, I'm convinced the answer is yes. Here's how I got started, mistakes and all.

The Problem: Chat APIs Aren't Agents

Here's the thing I had to learn the hard way: a chat completion endpoint is not an agent. When I was using the standard openai.chat.completions.create(), every tool call, every handoff to a different "persona," every guardrail check—I had to wire all of it manually. My code looked like a state machine built by someone who didn't plan ahead.

The OpenAI Agents SDK solves this by giving you a proper abstraction. An agent isn't just a prompt; it's a configured entity with a name, instructions, a model, optional tools, and the ability to hand off to other agents. The SDK handles the agent loop—the cycle of calling the model, executing tools, feeding results back, and deciding when to stop—so you don't have to.

Step 1: Installation and Setup

First, I created a clean virtual environment and installed the SDK:

mkdir support-agents && cd support-agents
python -m venv venv
source venv/bin/activate
pip install openai-agents

You'll need your OpenAI API key set as an environment variable:

export OPENAI_API_KEY="sk-your-key-here"

I initially made the mistake of trying pip install openai-agents-sdk—that doesn't exist. The package name is just openai-agents. Small thing, but it cost me ten minutes of confusion.

Step 2: Creating Your First Agent

Let's start simple. I wanted a single agent that acts as a refund specialist:

from agents import Agent, Runner

refund_agent = Agent(
    name="Refund Specialist",
    instructions="""You are a refund processing specialist for an e-commerce store.
    Help customers with refund requests. Always ask for the order number first.
    Be empathetic but follow policy: refunds are only available within 30 days of purchase.
    If a customer has a question about shipping or product details, say you'll transfer them.""",
    model="gpt-4o"
)

That's it. No system message dictionary, no message array construction. You define who the agent is and what it does, and the SDK handles the rest.

Now, to actually run it:

result = await Runner.run(refund_agent, "Hi, I bought a jacket two weeks ago and it doesn't fit. Can I get a refund?")

print(result.final_output)

The first time I ran this, I got an error because I used Runner.run() without await—it's an async function. You either need to run it in an async context or use Runner.run_sync() for simpler scripts:

# For quick scripts and testing
result = Runner.run_sync(refund_agent, "Hi, I bought a jacket two weeks ago and it doesn't fit. Can I get a refund?")

print(result.final_output)

The output was surprisingly good right out of the gate:

I'm sorry to hear the jacket doesn't fit! I'd be happy to help you with a refund.
Could you please provide your order number so I can look up your purchase details?

The agent followed its instructions—asked for the order number first, was empathetic, and stayed in its lane.

Step 3: Giving Your Agent Tools

Instructions alone only get you so far. Real agents need to do things. I wanted my refund agent to actually check order status, so I added a tool:

from agents import Agent, Runner, function_tool

@function_tool
def check_order(order_number: str) -> str:
    """Check the status and date of an order by its order number."""
    # In production, this would hit your database
    fake_orders = {
        "ORD-1234": {"date": "2025-04-10", "status": "delivered", "item": "Winter Jacket"},
        "ORD-5678": {"date": "2025-02-01", "status": "delivered", "item": "Running Shoes"},
    }
    order = fake_orders.get(order_number)
    if order:
        return f"Order {order_number}: {order['item']}, status: {order['status']}, purchased on {order['date']}"
    return f"Order {order_number} not found."

refund_agent = Agent(
    name="Refund Specialist",
    instructions="""You are a refund processing specialist for an e-commerce store.
    Help customers with refund requests. Always ask for the order number first.
    Use the check_order tool to verify the order and purchase date.
    Refunds are only available within 30 days of purchase. Today's date is 2025-05-15.
    If the order is eligible, confirm the refund. If not, explain the policy clearly.""",
    model="gpt-4o",
    tools=[check_order]
)

A couple of things I learned the hard way here. First, the docstring matters—the model reads it to decide when to use the tool. Write it clearly. Second, the type hints on the function parameters are required. The SDK uses them to generate the tool schema automatically. Skip them, and you'll get a confusing validation error.

Let's test it:

result = Runner.run_sync(refund_agent, "I want a refund for order ORD-1234")

print(result.final_output)

Output:

I've checked your order. Order ORD-1234 was for a Winter Jacket, purchased on April 10, 2025.
Since your purchase was within the 30-day refund window, you're eligible for a refund!
I've confirmed your refund. You should see the funds back on your original payment method within 5-7 business days.

Now try an ineligible order:

result = Runner.run_sync(refund_agent, "I want a refund for order ORD-5678")

print(result.final_output)

Output:

I've looked up your order. Order ORD-5678 was for Running Shoes, purchased on February 1, 2025.
Unfortunately, our refund policy only allows refunds within 30 days of purchase, and your order
was placed over 30 days ago. I'm unable to process a refund for this order. Is there anything
else I can help you with?

The agent used the tool, checked the date against the policy, and responded appropriately. No manual if/else chains from me.

Step 4: Adding Multiple Agents with Handoffs

This is where the SDK really shines. My support system needed more than just refund handling. I wanted a triage agent that could route customers to the right specialist:

from agents import Agent, Runner, function_tool

# Tools
@function_tool
def check_order(order_number: str) -> str:
    """Check the status and date of an order by its order number."""
    fake_orders = {
        "ORD-1234": {"date": "2025-04-10", "status": "delivered", "item": "Winter Jacket"},
        "ORD-5678": {"date": "2025-02-01", "status": "delivered", "item": "Running Shoes"},
    }
    order = fake_orders.get(order_number)
    if order:
        return f"Order {order_number}: {order['item']}, status: {order['status']}, purchased on {order['date']}"
    return f"Order {order_number} not found."

@function_tool
def search_faq(query: str) -> str:
    """Search the FAQ for answers to common questions."""
    faq = {
        "shipping": "Standard shipping takes 3-5 business days. Express shipping takes 1-2 business days.",
        "returns": "Returns are accepted within 30 days of purchase with original tags attached.",
        "sizing": "Our sizing runs true to standard US sizes. Check the size chart on each product page.",
    }
    for key, value in faq.items():
        if key in query.lower():
            return value
    return "No FAQ match found. Please transfer to a human agent."

# Specialists
refund_agent = Agent(
    name="Refund Specialist",
    instructions="""You handle refund requests. Always verify the order using check_order first.
    Refunds only within 30 days. Today is 2025-05-15. Be empathetic and clear about policy.""",
    model="gpt-4o",
    tools=[check_order],
)

faq_agent = Agent(
    name="FAQ Agent",
    instructions="""You answer general questions about shipping, returns policy, and sizing.
    Use the search_faq tool to find answers. If you can't find an answer, say you'll transfer to a human.""",
    model="gpt-4o",
    tools=[search_faq],
)

# Triage - the front door
triage_agent = Agent(
    name="Support Triage",
    instructions="""You are the first point of contact for customer support.
    Determine what the customer needs and transfer them to the right specialist:
    - Refund requests -> Refund Specialist
    - General questions about shipping, sizing, or returns policy -> FAQ Agent
    If you're unsure, ask a clarifying question before transferring.""",
    model="gpt-4o",
    handoffs=[refund_agent, faq_agent],
)

The handoffs parameter is the key. It tells the triage agent which other agents it can transfer to. The SDK automatically creates handoff tools that the model can call.

Running it:

result = Runner.run_sync(triage_agent, "How long does shipping usually take?")

print(result.final_output)

What happens behind the scenes: the triage agent recognizes this as a FAQ question, hands off to the FAQ agent, the FAQ agent uses the search tool, and you get the answer. The result object also tracks the full history, so you can see the handoff path:

for event in result.all_model_responses:
    print(f"Agent: {event.agent_name}, Action: {event.type}")

Step 5: Inspecting What Actually Happened

One thing that tripped me up early: understanding the agent's decision-making. The SDK provides tracing that saved me hours of debugging:

from agents import trace

with trace("support-session-1"):
    result = Runner.run_sync(triage_agent, "I want a refund for ORD-5678")

This logs the full execution trace, including which agent was active at each step, what tools were called, and why handoffs occurred. You can view these in the OpenAI dashboard. Without this, I was flying blind when an agent made an unexpected handoff.

Practical Tips and Honest Limitations

After building with this SDK for a few weeks, here's what I wish someone had told me upfront:

Tips:

  • Start with a single agent and get it working before adding handoffs. Multi-agent systems are harder to debug than you think.
  • Be extremely specific in your instructions. Vague instructions lead to vague behavior, and with agents, that means wrong tool calls and confused handoffs.
  • Use Runner.run_sync() for development and testing. Switch to async for production.
  • Always include type hints and docstrings on your tool functions. The SDK generates tool schemas from them, and missing or wrong types cause silent failures.
  • Set model="gpt-4o-mini" for testing to save money. The behavior is close enough for iteration, and you can switch to gpt-4o for production.

Limitations:

  • The SDK is new and the documentation is still sparse. I hit a few edge cases with handoff chains (agent A hands off to B, which hands off to C) that weren't well documented.
  • Tool execution is local and synchronous by default. If you need to call external APIs with retries, timeouts, or async behavior, you'll need to handle that yourself inside the tool function.
  • There's no built-in memory or conversation persistence. Each Runner.run() call is independent. For multi-turn conversations, you need to manage the conversation history yourself and pass it back in.
  • The guardrails system exists but is basic. If you need sophisticated output validation or content filtering, plan to build additional layers.

The OpenAI Agents SDK isn't perfect, but it solves the core problem I had: turning a messy hand-rolled agent loop into something structured and maintainable. For my support automation project, it cut my codebase in half and made the agent behavior far more predictable. If you're building anything beyond a simple chatbot—especially systems with multiple specialized roles and tool use—it's worth your time to learn.

相关 Agent

C

光标编辑器

AI驱动的代码编辑器,支持智能补全和对话。

了解更多 →