外观
OpenAI Agents SDK
在框架选型总览里,OpenAI Agents SDK 的定位一句话就能说清:抽象最少的那个。它只有极少数几个原语,编排逻辑就是普通 Python 代码,不引入图、不引入 DSL。代价是你放弃了对执行流程的精细控制——这个取舍是否值得,本文最后一节会和 LangGraph 正面比较。
一、定位与沿革:从 Swarm 到生产级 SDK
时间线(均已核实):
- 2024 年:OpenAI 发布实验性项目 Swarm,提出
Agent+handoff两个概念,明确声明"仅用于教育探索,不上生产"。 - 2025-03-11:OpenAI 发布 Responses API、三个内置工具和 Agents SDK,Agents SDK 被定位为 Swarm 的生产级升级版(production-ready upgrade)。同一天发布的还有 Traces 可观测性面板。Swarm 仓库此后归档并重定向到 Agents SDK。
- 2025-03-27:SDK 官宣支持 MCP,可以挂载本地或远程 MCP server。
- 2025-2026 年:高频迭代。截至 2026-08,Python 包
openai-agents最新版本为 v0.22.0(仍为 0.x,官方用 minor 版本号表达"有实质性运行时变更")。期间陆续加入 Sessions(多种后端)、Realtime/Voice agent、Sandbox agent、Programmatic Tool Calling 等能力。默认模型已演进到gpt-5.6-luna(低成本默认)/gpt-5.6-sol(高能力)。
与 Responses API 的关系
这是理解 SDK 的关键一层。Responses API 是 OpenAI 2025-03 推出的新 API 原语,合并了 Chat Completions 的简单和 Assistants API 的工具能力(Assistants API 已宣布弃用,目标 sunset 时间为 2026 年中)。Agents SDK 不是 API,是 API 之上的运行时:
你的代码
│
▼
Agents SDK(Agent / Runner / Guardrail / Session ...) ← 管 Agent Loop、工具分发、护栏、交接
│
▼
Responses API(内置工具 web_search / file_search / computer ...) ← OpenAI 服务端
│
▼
模型(gpt-5.6 系列等)官方文档给的选择标准很直白:
- 想自己掌控 loop、工具分发和状态 → 直接用 Responses API;
- 想让运行时帮你管多轮循环、工具执行、护栏、handoff、session → 用 Agents SDK。
两者不互斥,很多生产系统是混合的:主流程走 SDK,个别低延迟路径直接调 Responses API。
0.x 版本号意味着什么
SDK 至今仍是 0.x。它的迭代节奏非常快(2026 年内几乎每 1-2 周一个 release),minor 版本偶尔会带行为变更(例如 v0.22.0 收紧了 OpenAIProvider 显式 client 的配置契约)。生产使用请锁版本并关注 Releases 页面。
二、核心原语:六个概念撑起整个框架
官方的设计原则是"功能多到值得用,原语少到学得会"。核心就六个:
| 原语 | 一句话 | 对应代码 |
|---|---|---|
| Agent | 带 instructions 和工具的 LLM | Agent(name=..., instructions=..., tools=[...]) |
| Runner | 执行 Agent Loop 的运行时 | Runner.run() / run_sync() / run_streamed() |
| Handoff | Agent 间移交控制权 | handoffs=[other_agent] |
| Guardrail | 输入/输出/工具调用的校验与熔断 | @input_guardrail / @output_guardrail |
| Session | 跨 run 的对话记忆 | SQLiteSession("conv_123") |
| Tracing | 内置可观测性 | 默认开启,无需代码 |
Runner 内部循环大致是:
┌────────────────────────── Runner loop ──────────────────────────┐
│ │
input ─┼─▶ [input guardrails] ─▶ LLM 调用 ─▶ 有 tool call? ──是──▶ 执行工具 ─┐
│ (并行/阻塞) │ │ │
│ │ 否 (结果回填)│
│ ▼ │ │
│ 有 handoff? ──是──▶ 切换当前 Agent ──────────┤
│ │ │ │
│ 否 ▼ │
│ │ [output guardrails] │
│ │ │ │
└─────────────────────────────┴──────────────┴───────────────────────┘
▼
final_outputAgent
python
from agents import Agent, ModelSettings
agent = Agent(
name="客服助手",
instructions="你是售后客服,只回答订单与退款相关问题。",
model="gpt-5.6-sol", # 不传则用默认模型(当前为 gpt-5.6-luna)
model_settings=ModelSettings(temperature=0.2),
tools=[...], # function tool / hosted tool / MCP 工具
handoffs=[...], # 可交接的下游 Agent
)instructions 支持静态字符串,也支持接收 context 的函数(动态注入租户信息、用户画像等,属于上下文工程的常规手法)。Agent 是泛型类 Agent[TContext],配合 RunContextWrapper 做类型化的依赖注入。
Runner
Runner.run(agent, input, ...) 是异步入口;run_sync 是同步包装;run_streamed 返回流式结果,通过 stream_events() 逐事件消费。重要参数:max_turns(防死循环,超了抛 MaxTurnsExceeded)、session、context、run_config。返回值 RunResult 里有 final_output、new_items(本轮产生的全部 item)、last_agent 等。
Handoff:把"移交"做成一等公民
Handoff 是 SDK 最有辨识度的设计。它在底层被建模为一个模型可以调用的工具:模型调用 transfer_to_xxx,Runner 就把后续对话的控制权(含完整历史)切给目标 Agent。这区别于"manager 模式"(主 Agent 调用子 Agent 后收回控制权,SDK 里用 agent.as_tool() 实现)。什么时候用哪种,见多智能体架构。
python
refund_agent = Agent(name="退款专员", instructions="处理退款,必须核对订单号。")
triage_agent = Agent(
name="分诊",
instructions="退款问题转给退款专员,其他问题自己回答。",
handoffs=[refund_agent],
)需要定制(改工具名、传结构化输入、过滤历史)时用 handoff(agent, on_handoff=..., input_type=..., input_filter=...)。
Guardrails:护栏,不是摆设
三类护栏,执行点不同,这是新手最常踩的坑:
- 输入护栏:只在链条中第一个 Agent 收到用户输入时运行。默认与 Agent 并行跑(
run_in_parallel=True),延迟最低;但 tripwire 触发时主模型可能已经烧了一些 token。想省钱或防工具副作用,设run_in_parallel=False让它阻塞执行。 - 输出护栏:只在最后一个 Agent 产出最终结果后运行,不并行。
- 工具护栏:包裹每次 function tool 调用(执行前/后),适合"禁止把 secret 传给外部 API"这类规则。注意它不适用于 handoff 和 hosted tool。
护栏返回 GuardrailFunctionOutput(tripwire_triggered=...),触发后 Runner 抛 InputGuardrailTripwireTriggered / OutputGuardrailTripwireTriggered,你在应用层接住并降级处理。典型做法是用一个便宜小模型当护栏 Agent,与贵的主模型配合。更多模式见安全与护栏。
Sessions:官方版的对话记忆
Session 解决的是多轮对话历史管理——不用手动 result.to_input_list() 往回塞。Runner.run(..., session=session) 之后,Runner 每次运行前自动取历史、运行后自动存新 item。
内置后端已经相当全:SQLiteSession(默认,文件或内存)、AsyncSQLiteSession、RedisSession、SQLAlchemySession、MongoDBSession、DaprSession、OpenAIConversationsSession(历史存 OpenAI 服务端)、OpenAIResponsesCompactionSession(超长对话自动压缩),以及实现 Session 协议(get_items / add_items / pop_item / clear_session 四个方法)的自定义后端。与长期记忆的区别:Session 管的是"工作上下文",跨会话的用户画像、知识沉淀仍需自建。
Tracing
默认开启,零代码。每次 run 自动生成 trace,内部按 span 组织:agent span、generation span(LLM 调用)、function span(工具)、guardrail span、handoff span。默认批量上报到 OpenAI Traces 面板。详见第五节。
三、内置工具:hosted tool 是最大差异化
SDK 的工具分五类,真正独一份的是 hosted tool——工具在 OpenAI 服务端执行,你的代码只声明不实现:
| 工具 | 干什么 | 备注 |
|---|---|---|
WebSearchTool | 联网搜索,带回引用 | 支持 user_location、search_context_size |
FileSearchTool | 检索 OpenAI Vector Store | 托管 RAG,支持 metadata filter 与 rerank |
ComputerTool | GUI/浏览器操作 | 本地执行:模型出动作,你实现 Computer 接口(截图、点击等)回灌 |
CodeInterpreterTool | 沙箱跑代码 | 服务端容器 |
HostedMCPTool | 直连远程 MCP server | 服务端执行 |
ImageGenerationTool | 文生图 | 服务端执行 |
python
from agents import Agent, FileSearchTool, Runner, WebSearchTool
agent = Agent(
name="研究助手",
tools=[
WebSearchTool(), # 联网
FileSearchTool( # 托管 RAG,替代自建检索管线
max_num_results=3,
vector_store_ids=["vs_xxx"],
),
],
)
result = Runner.run_sync(agent, "对比下我们产品文档里 A 方案和今天最新的行业做法")几点判断:
FileSearchTool适合"快速把内部文档接进来"的场景,省掉整条 RAG 管线;但检索逻辑黑盒、数据要上 OpenAI,精细检索需求仍应自建。ComputerTool名义上是 hosted tool 家族,实际动作在你本地执行(官方示例是 Playwright harness)。2026 年的现状:gpt-5.5起 computer use 已 GA,工具载荷从 preview 的computer_use_preview(单 action)迁移到computer(批量 actions);SDK 按实际请求的模型自动选线格式。GA 前 CUA 模型在 OSWorld 上只有 38.1% 成功率,桌面场景务必加人工确认。- 自定义工具用
@function_tool(或 0.19 起的短别名@tool)装饰任意 Python 函数,schema 从类型注解 + docstring 自动生成(基于 Pydantic + griffe)。这是日常开发 90% 的情况。 - MCP 工具:本地 server 用
MCPServerStdio,远程用MCPServerStreamableHttp/MCPServerSse,挂在 Agent 的mcp_servers上即可。
四、完整示例:带护栏、handoff 和 session 的客服分诊
下面这个例子把前面所有原语串起来,语法与 0.2x 版本官方文档一致(pip install openai-agents):
python
import asyncio
from pydantic import BaseModel
from agents import (
Agent, Runner, SQLiteSession, WebSearchTool,
GuardrailFunctionOutput, InputGuardrailTripwireTriggered,
RunContextWrapper, TResponseInputItem,
function_tool, input_guardrail,
)
# ---------- 1. 自定义 function tool ----------
@function_tool
def lookup_order(order_id: str) -> str:
"""根据订单号查询订单状态。
Args:
order_id: 订单号,形如 OD-12345
"""
# 生产里这里调你的订单系统
return f"订单 {order_id}:已发货,预计 3 天内送达"
# ---------- 2. 输入护栏:用小模型拦截无关请求 ----------
class RelevanceCheck(BaseModel):
is_off_topic: bool
reasoning: str
guardrail_agent = Agent(
name="话题检查",
instructions="判断用户是否在问与电商客服无关的问题(如写代码、做数学题)。",
model="gpt-5-mini", # 护栏用便宜模型
output_type=RelevanceCheck,
)
@input_guardrail
async def relevance_guardrail(
ctx: RunContextWrapper[None],
agent: Agent,
input: str | list[TResponseInputItem],
) -> GuardrailFunctionOutput:
result = await Runner.run(guardrail_agent, input, context=ctx.context)
return GuardrailFunctionOutput(
output_info=result.final_output,
tripwire_triggered=result.final_output.is_off_topic,
)
# ---------- 3. 专家 Agent + 分诊 Agent(handoff) ----------
order_agent = Agent(
name="订单专员",
instructions="你只处理订单查询。必须先调用 lookup_order 再回答。",
tools=[lookup_order],
)
research_agent = Agent(
name="商品研究员",
instructions="你负责回答商品对比、选购建议,必要时联网搜索。",
tools=[WebSearchTool()],
)
triage_agent = Agent(
name="分诊客服",
instructions=(
"你是电商客服入口。订单查询转给订单专员,商品选购转给商品研究员,"
"其他问题自己简洁回答。"
),
handoffs=[order_agent, research_agent],
input_guardrails=[relevance_guardrail],
)
# ---------- 4. 带 session 的多轮运行 ----------
async def main():
session = SQLiteSession("user_42", "conversations.db") # 历史落盘
try:
r1 = await Runner.run(triage_agent, "我的订单 OD-12345 到哪了?",
session=session, max_turns=10)
print("客服:", r1.final_output)
# 第二轮自动带上上一轮历史
r2 = await Runner.run(triage_agent, "那顺便帮我看看有没有平替推荐",
session=session, max_turns=10)
print("客服:", r2.final_output)
except InputGuardrailTripwireTriggered:
print("客服: 这个问题超出我的服务范围啦")
if __name__ == "__main__":
asyncio.run(main())跑之前 export OPENAI_API_KEY=sk-...。这个骨架可以直接当生产起点:护栏管边界、handoff 管分工、session 管记忆、tracing 默认就在记录一切。想在此基础上做课程项目,可以参考实战教程的拆解方式。
五、Tracing 与评测集成
可观测性是 SDK 相对其他框架的隐藏优势之一,因为它默认就有:
- 每次 run 包在一个 trace 里(默认名
Agent workflow,可用with trace("名字")自定义并把多次 run 归并到一个 trace)。 - span 覆盖:LLM generation、function tool 调用、guardrail、handoff、语音转写/合成等。
- 免费查看:OpenAI 平台的 Traces 面板。不想把 trace 发给 OpenAI?三个开关:环境变量
OPENAI_AGENTS_DISABLE_TRACING=1、set_tracing_disabled(True)、或RunConfig(tracing_disabled=True)。ZDR(零数据保留)组织不可用 tracing。 - 自定义后端:
add_trace_processor()追加、set_trace_processors()替换。官方文档列出的第三方集成有一长串:Langfuse、LangSmith、Arize Phoenix、Braintrust、Pydantic Logfire、AgentOps、MLflow、Datadog、Langtrace 等二十多家,基本覆盖主流 Agent 可观测性 栈。
评测侧的关系:trace 数据可以沉淀为数据集,接 OpenAI 的 Evals 产品做离线评估,trace 里的真实 LLM 调用记录甚至能用于蒸馏/微调。如果你的组织已有评测管线,第三方 processor 导出的 span 同样能喂给自建 eval——方法论见评测体系与实战评测。
长任务记得 flush
默认的 BatchTraceProcessor 每隔几秒批量上报、进程退出时兜底 flush。在 Celery / FastAPI BackgroundTasks 这类长驻 worker 里,如果要求任务结束立刻能在面板看到 trace,在 trace() 上下文退出后显式调用 flush_traces()。
六、模型中立性与厂商锁定:一半中立,一半绑定
"Agents SDK 能不能不用 OpenAI 模型?"能,但要清楚边界。
中立的部分:
- 任何提供 OpenAI 兼容端点的 provider,三行接入:
python
from agents import Agent, AsyncOpenAI, OpenAIChatCompletionsModel, set_tracing_disabled
set_tracing_disabled(True) # 没有 OpenAI key 时关掉,或单独给 tracing 配 key
client = AsyncOpenAI(base_url="https://你的-provider/v1", api_key="...")
agent = Agent(
name="助手",
model=OpenAIChatCompletionsModel(model="provider-model-name", openai_client=client),
)- 更广的覆盖走第三方适配器(beta):
openai-agents[litellm]的LitellmModel、openai-agents[any-llm]的AnyLLMModel,宣称覆盖 100+ 模型。也能按 Agent 粒度混用模型——分诊用小模型、专家 Agent 用大模型,是常见的成本优化手法。 - Tracing 与模型解耦:模型走别家,trace 仍可用单独的 OpenAI key(
set_tracing_export_api_key)发到 OpenAI 面板,白嫖它的可观测性。
绑定的部分:
- 全部 hosted tool(web search / file search / computer / code interpreter)只在 OpenAI Responses 路径上可用。换 provider 就全没了,得自己补检索和搜索。
- 一批高级特性是 Responses-only:tool search、
ProgrammaticToolCallingTool、服务端 compaction、previous_response_id续链等。走 Chat Completions 适配层时这些字段会被静默丢弃(可用strict_feature_validation=True改成报错)。 - 结构化输出、多模态输入在非 OpenAI provider 上支持参差,官方自己也提醒"换 provider 前要核对功能矩阵"。
结论:SDK 的运行时抽象(loop、handoff、guardrail、session)是模型中立的,但它的火力全开形态绑定 OpenAI 平台。如果你的战略就是 All-in OpenAI,这不是问题;如果要多云/多模型,把 hosted tool 当作"需要替换的依赖"记在架构图上。
七、优缺点与适用场景
| 维度 | 评价 |
|---|---|
| 学习曲线 | 极低。六个原语 + 普通 Python,一下午上手 |
| 代码侵入性 | 低。Agent 是数据类,编排就是函数调用,好测好改 |
| 可观测性 | 默认 tracing + 面板 + 丰富第三方集成,同类里第一梯队 |
| 护栏 | 输入/输出/工具三层护栏是内置一等公民,多数框架要靠外挂 |
| 生态捆绑 | hosted tool、Traces 面板、Evals 全在 OpenAI 平台内,深度用户受益也受限 |
| 控制力 | Runner 的黑盒循环不好定制:想要复杂状态机、分支回退、细粒度 checkpoint 会很别扭 |
| 成熟度 | 0.x、迭代快、偶有行为变更;2026 年核心 API(Agent/Runner/handoff/guardrail)已相当稳定,变动多在边缘模块 |
| 语言 | Python 为主;官方另有 TypeScript 版(@openai/agents),功能基本对齐 |
适合:OpenAI 技术栈上的新业务(客服、研究助手、内容管线);需要快速上线且不想维护编排框架的团队;把 handoff 当核心交互模式的分诊/路由型多智能体系统。
不适合:复杂确定性的工作流编排(状态机、人工审批流、精确重试语义)——那是 LangGraph 的主场;强合规/私有化部署且不能碰 OpenAI 的场景;需要深度定制 loop 行为的研究型项目(那种情况不如自己手写 loop)。
八、与 LangGraph 的取舍
这是选型中最常被问到的一对。两者不是"谁更好",是两种不同的世界观:
| OpenAI Agents SDK | LangGraph | |
|---|---|---|
| 编排范式 | 隐式:Runner 黑盒跑 loop,handoff 去中心化移交 | 显式:你画状态图,节点/边/条件全自己定义 |
| 状态模型 | Session 管对话历史;业务状态自己注入 context | 一等公民的 State schema + checkpointer,可持久化/回放/分叉 |
| 控制粒度 | 粗。loop 内部不可定制,只有 hook 和护栏 | 细。每一步、每次中断、每个 checkpoint 都可控 |
| 上手成本 | 小时级 | 天级(图思维有学习成本) |
| 调试体验 | Traces 面板开箱即用 | LangSmith 同样强,但图结构本身更可解释 |
| 模型绑定 | 中立运行时可换模型,火力全开绑 OpenAI | 完全模型中立 |
| 典型场景 | 对话式、分诊路由、快速上线 | 长周期任务、审批流、多步确定性流程 |
经验法则:
- 先问自己需不需要图。如果你的"工作流"其实就是"分诊 + 几个专家 + 护栏",Agents SDK 一天能上线,LangGraph 是过度设计。
- 一旦出现这些信号,换 LangGraph:需要精确的中断/恢复语义、需要把中间状态存进自己的数据库做审计、流程本身有复杂的分支合流(不是简单的"谁接手")、或者你根本不能依赖 OpenAI。
- 两者可以共存。不少团队用 Agents SDK 做面向用户的对话层,把其中某个重流程节点委托给 LangGraph 子图——框架不是宗教。
别被"官方"二字绑架
Agents SDK 是 OpenAI 官方出品,但"官方"不等于"默认正确"。它的设计深度绑定 Responses API 的能力边界,你享受的便利(hosted tool、tracing 面板)同时就是锁定点。选型时把"如果明天要换掉 OpenAI,我要重写什么"这个问题写下来,答得出来再上生产。
参考资料
- OpenAI: New tools for building agents(2025-03-11 发布公告) —— Responses API、内置工具与 Agents SDK 的原始发布说明
- OpenAI Agents SDK 官方文档 —— 原语、工具、Sessions、Tracing 的权威参考
- openai/openai-agents-python(GitHub) —— 源码与 examples 目录,学 API 最好的材料
- Releases · openai-agents-python —— 版本变更记录,生产升级前必读
- Models 文档:非 OpenAI 模型接入 —— Chat Completions 适配、LiteLLM/Any-LLM 适配器与功能差异
- Guardrails 文档 —— 输入/输出/工具三层护栏的执行语义
- Tracing 文档 —— trace/span 模型与二十余家第三方可观测性集成清单