你有没有想过:不用 LangChain 那种层层封装,也能几行 Python 拼出一个会调工具、能交接、还带安全护栏的多智能体系统?OpenAI Agents SDK 就是干这件事的——它是 OpenAI 官方的轻量级多智能体框架(前身是开源实验项目 Swarm,现已"转正"为正式 SDK)。本教程从装环境到跑通一个"客服分流 + 工具查询 + 护栏拦截"的可运行示例,每一步都给你完整代码,跟着敲就行。
一、先搞懂: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
建议先建个虚拟环境,避免污染全局 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 提交到 Git!把 export OPENAI_API_KEY=... 写进 .env 并加进 .gitignore,或者用 python-dotenv 加载,比硬编码安全得多。
Step 2:第一个最小 Agent(Runner.run_sync)
不写工具、不做编排,先确认链路通:
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)
用 @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)
真实客服里,一个问题可能要转给不同部门。用 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)
有些输入不该进模型(如含邮箱 / 手机等隐私,或明显越权)。用 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
把上面几块拼起来,就是一个最小的"分流 + 工具 + 护栏"系统。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 限制循环次数防失控。这些都只需改几行配置。常见问题速查
| 现象 | 大概率原因 & 解决 |
|---|---|
| 报错 AuthenticationError | OPENAI_API_KEY 没设或失效,重设后重试 |
| 工具没被调用 | docstring 写得太含糊,或 instructions 没说"必要时调用工具" |
| handoff 没生效 | 忘了在父 Agent 挂 handoffs,或 handoff_description 太模糊 |
| 护栏一触发就崩溃 | 没用 try / except 接住 tripwire 异常 |