实战 📋 6 个步骤 第 597 / 599 篇

用 OpenAI Agents SDK 搭你的第一个多 Agent 系统(工具调用 + handoff 交接 + guardrails 护栏)

OpenAI Agents SDK 是 OpenAI 官方的轻量级多智能体框架(原 Swarm 转正),用 Agent / Runner / function_tool / handoff / guardrail 几个原语就能拼出会调工具、能交接、带安全护栏的 Agent 系统。本教程从装环境到跑通一个"客服分流 + 工具查询 + 护栏拦截"的可运行示例,每一步都有完整代码。

2026.11.04· 18 分钟阅读· 约 1700 字· 🤖 OpenAI Agents SDK / 🔧 工具调用

你有没有想过:不用 LangChain 那种层层封装,也能几行 Python 拼出一个会调工具、能交接、还带安全护栏的多智能体系统?OpenAI Agents SDK 就是干这件事的——它是 OpenAI 官方的轻量级多智能体框架(前身是开源实验项目 Swarm,现已"转正"为正式 SDK)。本教程从装环境到跑通一个"客服分流 + 工具查询 + 护栏拦截"的可运行示例,每一步都给你完整代码,跟着敲就行。

🧩 本教程适合:会用一点 Python、想用官方 SDK 快速写出生产级 Agent 的开发者。你只需要 Python 3.9+ 和一个 OpenAI API Key(文内会教你怎么拿)。

一、先搞懂:Agents SDK 在解决什么问题

传统做法里,要让 Agent "会调工具、能协作、有兜底",往往要自己写一堆胶水代码。Agents SDK 把这件事收敛成四个原语:

原语一句话作用你写什么
Agent一个带人设和能力的"员工"name / instructions / tools / handoffs / guardrails
Runner驱动 Agent 跑起来的"调度员"Runner.run(agent, input)
handoff把对话交给另一个专家 Agent在 Agent 上挂 handoffs=[...]
guardrail跑前 / 跑后做安全检查InputGuardrail / OutputGuardrail

它和 LangChain / CrewAI 不一样在哪?Agents SDK 刻意做得很"薄":几乎没有新的抽象,Agent 就是 LLM + 工具 + 指令,handoff 就是一次特殊的工具调用。好处是你完全掌控执行流程,调试时能直接看到每一步;代价是高级编排(如持久化状态机)要你自己搭。

Step 1:安装与准备 API Key

1 装包并配置密钥

建议先建个虚拟环境,避免污染全局 Python:

# 创建并激活虚拟环境(Windows 用 .venv\Scripts\activate)
python -m venv .venv
source .venv/bin/activate

# 安装官方 SDK
pip install openai-agents

# 配置 OpenAI API Key(Linux / macOS)
export OPENAI_API_KEY="sk-..."
💡 没有 Key?去 platform.openai.com 注册后,在 API Keys 页面创建一个。新账号通常有试用额度,本教程的示例跑几遍完全够用。

千万别把 Key 提交到 Git!把 export OPENAI_API_KEY=... 写进 .env 并加进 .gitignore,或者用 python-dotenv 加载,比硬编码安全得多。

Step 2:第一个最小 Agent(Runner.run_sync)

2 三行跑起一个 Agent

不写工具、不做编排,先确认链路通:

from agents import Agent, Runner

agent = Agent(
    name="助理",
    instructions="你是一个简洁友好的助手。",
    model="gpt-4o",
)

# run_sync 是同步写法,适合脚本 / 笔记本;异步用 await Runner.run(...)
result = Runner.run_sync(agent, "用一句话解释什么是 AI Agent。")
print(result.final_output)
🔑 Runner.run_sync() 内部其实就是 asyncio.run(Runner.run(...))。在 async def main() 里请用 await Runner.run(agent, input),避免事件循环冲突。

Step 3:给 Agent 加工具(@function_tool)

3 让 Agent 能查真实数据

用 @function_tool 把一个普通函数变成 Agent 可调用的工具。函数的 docstring 就是给 LLM 看的"使用说明书",务必写清楚参数和用途:

from agents import Agent, Runner, function_tool

@function_tool
def get_weather(city: str) -> str:
    """查询指定城市的天气。Args: city: 城市名,如 'Tokyo'。"""
    # 真实项目里这里换成你的天气 API
    return f"{city} 今天晴,22℃,适合出门。"

agent = Agent(
    name="天气助手",
    instructions="你帮助用户查询天气,必要时调用 get_weather 工具。",
    model="gpt-4o",
    tools=[get_weather],
)

result = Runner.run_sync(agent, "东京天气怎么样?")
print(result.final_output)

工具没有类型或 docstring 会失灵。SDK 靠函数的类型注解和 docstring 自动生成工具 schema。参数名、类型、说明三者要对得上,否则模型可能传错参。

Step 4:多 Agent 交接(handoffs)

4 让"前台"把问题转给"专家"

真实客服里,一个问题可能要转给不同部门。用 handoffs 让一个调度 Agent 把对话交给专家 Agent——控制权转移,但上下文保留:

from agents import Agent, Runner

billing = Agent(
    name="账单专员",
    instructions="你处理账单和退款问题,给出清晰具体的答复。",
    model="gpt-4o",
    handoff_description="处理账单、发票、退款相关问题",
)
tech = Agent(
    name="技术支持",
    instructions="你解决技术故障,先问清楚现象再给方案。",
    model="gpt-4o",
    handoff_description="处理技术故障、报错、连接问题",
)
triage = Agent(
    name="总机",
    instructions="判断用户需要账单还是技术支持,然后交给对应专员。",
    model="gpt-4o",
    handoffs=[billing, tech],
)

result = Runner.run_sync(triage, "我上个月被重复扣款了,怎么退?")
print(result.final_output)
print("实际由:", result.last_agent.name, "答复")
💡 handoff_description 是写给"调度 Agent"看的路由说明,写得越清楚,分流越准。想知道最后是谁答的?用 result.last_agent.name 即可。

Step 5:加输入护栏(InputGuardrail)

5 在 Agent 处理前先拦一道

有些输入不该进模型(如含邮箱 / 手机等隐私,或明显越权)。用 InputGuardrail 在跑之前做检查,触发时直接抛异常中断:

from agents import Agent, Runner, InputGuardrail, GuardrailFunctionOutput

async def check_pii(ctx, agent, input_text):
    has_email = "@" in input_text
    return GuardrailFunctionOutput(
        output_info={"has_email": has_email},
        tripwire_triggered=has_email,   # True 则中断
    )

guarded = Agent(
    name="安全助手",
    instructions="你回答常识问题。",
    model="gpt-4o",
    input_guardrails=[InputGuardrail(guardrail_function=check_pii)],
)

try:
    r = Runner.run_sync(guarded, "我的邮箱是 [email protected],帮我写首诗")
    print(r.final_output)
except Exception as e:
    print("护栏触发,已拦截:", e)

tripwire 抛的是异常,不是返回值。必须用 try / except 接住 InputGuardrailTripwireTriggered,否则程序会直接崩。护栏是"安全层",不是"提示词层"——它比在 prompt 里写"不要怎么做"可靠得多。

Step 6:跑通完整示例并看 Tracing

6 组装并观察每一步

把上面几块拼起来,就是一个最小的"分流 + 工具 + 护栏"系统。SDK 内置 Tracing,跑起来后去 OpenAI 控制台的 Trace 面板就能看到每次工具调用和交接:

import asyncio
from agents import (
    Agent, Runner, function_tool,
    InputGuardrail, GuardrailFunctionOutput,
)

@function_tool
def get_order_status(order_id: str) -> str:
    """查询订单状态。Args: order_id: 订单号。"""
    return f"订单 {order_id}:已发货,预计明天送达。"

async def no_prompt_injection(ctx, agent, text):
    return GuardrailFunctionOutput(
        output_info={},
        tripwire_triggered=("忽略" in text and "之前" in text),
    )

support = Agent(name="客服", instructions="回答订单问题。",
                model="gpt-4o", tools=[get_order_status])
triage = Agent(name="总机", instructions="把订单问题交给客服。",
              model="gpt-4o", handoffs=[support],
              input_guardrails=[InputGuardrail(guardrail_function=no_prompt_injection)])

async def main():
    r = await Runner.run(triage, "帮我查下订单 A123 到哪了?")
    print(r.final_output)

asyncio.run(main())
# 打开 platform.openai.com 的 Traces 页面,可看到本次运行的完整调用链
🚀 下一步可以做什么:① 用 WebSearchTool() 接联网搜索;② 用 SQLiteSession 给 Agent 加跨轮记忆;③ 用 max_turns 限制循环次数防失控。这些都只需改几行配置。

常见问题速查

现象大概率原因 & 解决
报错 AuthenticationErrorOPENAI_API_KEY 没设或失效,重设后重试
工具没被调用docstring 写得太含糊,或 instructions 没说"必要时调用工具"
handoff 没生效忘了在父 Agent 挂 handoffs,或 handoff_description 太模糊
护栏一触发就崩溃没用 try / except 接住 tripwire 异常
← 返回教程中心