返回专栏
Agent SDK/02 · OpenAI/2.3

OpenAI Agents SDK

Agents SDK 的设计目标是原语极少:Agent、Agents as tools / Handoffs、Guardrails,在此之上提供 Sessions、Tracing、Sandbox agents 和 Realtime/Voic…

预计阅读
32分钟
全文字数
3,817字
资料截至
2026-10-09
Agent SDK · OpenAI2.3
版本与时效声明
  • 本文基于 openai-agents(Python)0.23.1(仓库 commit 26345c1,2026-10-08)。它依赖 openai 3.x:从 0.21.0 开始要求 openai v31。JS/TS 版 @openai/agents 的最新版本为 0.19.0,本文不展开。
  • 该 SDK 采用 0.Y.Z 的版本号:Y 增加时可能包含破坏性变更,官方建议需要稳定的项目锁定 Y1。
  • 文中代码已用 pyright 对照 0.23.1 做过类型检查;没有 API Key,因此没有实际运行。
  • 资料截至 2026-10-09,本文不随官方同步更新,请对照 官方文档(另有中文版)和 Releases 使用。
本文要点
  1. Agents SDK 的设计目标是原语极少:Agent、Agents as tools / Handoffs、Guardrails,在此之上提供 Sessions、Tracing、Sandbox agents 和 Realtime/Voice2。
  2. Runner 就是 手写循环 的工程化版本:调用模型 → 有最终输出就结束 / 有 handoff 就切换 agent / 有工具调用就执行,然后继续3。
  3. 多 Agent 协作有两种模式:Manager(agents as tools,由中心 agent 掌控)和 Handoffs(把控制权转交给专家 agent)4。
  4. 生产级能力包括:护栏(input/output/tool)、人工审批(needs_approval,加上 RunState 暂停与恢复)、默认开启的 Tracing、四种状态策略,以及与持久化执行框架(Temporal、DBOS 等)的集成。

1. 定位#

  • 分层位置:L3 Agent Loop。加上 Sandbox Agents 后覆盖一部分 L4。整个 SDK 运行在你自己的进程里。
  • 它前身是 OpenAI 的实验项目 Swarm,官方称它是 Swarm 的“生产级升级版”2。
  • OpenAI 模型默认通过 Responses API 调用2,也可以接入其他厂商(见第 13 节)。
  • 设计原则有两条2:功能要多到值得使用、原语要少到容易学会;开箱即用,同时每一步都可以定制。

什么时候直接用 Responses API,什么时候用 Agents SDK#

直接使用 Responses API使用 Agents SDK
想自己掌控循环、工具分发和状态希望由运行时管理轮次、工具执行、护栏、handoff 和会话
流程短,主要就是拿模型的返回agent 需要产出成果,或者要跨多个步骤协作
—需要真实的工作区,或者需要可恢复的执行(Sandbox agents)

以上取自官方文档2。两者可以混用:用 SDK 管理工作流,底层路径照样直接调用 Responses。

Agents SDK

Agent

instructions 静态或动态

tools

handoffs

output_type 结构化输出

guardrails

hooks

Runner

run

run_sync

run_streamed

RunConfig

Tools

function tools

hosted tools

agents as tools

MCP servers

Codex tool 实验性

多 Agent

Handoffs

Manager 模式

安全

Guardrails

Human in the loop

状态

to_input_list

Sessions

conversation_id

previous_response_id

可观测

Tracing

进阶

Sandbox agents

Realtime 语音

Agents SDK

Agent

instructions 静态或动态

tools

handoffs

output_type 结构化输出

guardrails

hooks

Runner

run

run_sync

run_streamed

RunConfig

Tools

function tools

hosted tools

agents as tools

MCP servers

Codex tool 实验性

多 Agent

Handoffs

Manager 模式

安全

Guardrails

Human in the loop

状态

to_input_list

Sessions

conversation_id

previous_response_id

可观测

Tracing

进阶

Sandbox agents

Realtime 语音


2. 安装与 Hello World#

Terminal window
pip install openai-agents # 官方示例同时给出 uv add openai-agents 的写法
export OPENAI_API_KEY=sk-...
from agents import Agent, Runner
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
result = Runner.run_sync(agent, "写一首关于递归的俳句。")
print(result.final_output)

这是官方 Hello World 的中文化版本2。


3. Agent:定义一个智能体#

属性是否必填说明
name是便于人类阅读的名字
instructions否,但强烈建议填写系统提示词,也可以是一个函数(动态指令)
model / model_settings否使用哪个模型,以及 temperature、tool_choice 等参数
tools否可调用的工具
handoffs否可以把对话转交给的专家 agent
mcp_servers否提供工具的 MCP 服务器
input_guardrails / output_guardrails否输入、输出护栏
output_type否结构化输出的类型,比如 Pydantic 模型
hooks否生命周期回调
tool_use_behavior否工具结果是交回模型继续处理,还是直接作为最终结果
reset_tool_choice否工具调用后把 tool_choice 重置为 auto,默认开启,用来防止死循环

完整列表见官方文档5。下面是一个带工具和结构化输出的完整示例:

import asyncio
from pydantic import BaseModel
from agents import Agent, Runner
from agents.decorators import tool
@tool
def get_weather(city: str) -> str:
"""查询城市今天的天气。
Args:
city: 城市名,例如 北京。
"""
return {"北京": "晴,25°C", "上海": "小雨,22°C"}.get(city, "暂无数据")
class WeatherReport(BaseModel):
city: str
summary: str
suggestion: str
agent = Agent(
name="天气助手",
instructions="先调用工具查询天气,再给出是否适合户外运动的建议。",
tools=[get_weather],
output_type=WeatherReport, # 让模型输出结构化结果(底层使用 Structured Outputs)
)
async def main() -> None:
result = await Runner.run(agent, "北京今天适合跑步吗?")
report = result.final_output_as(WeatherReport)
print(report.summary, "|", report.suggestion)
# new_items 记录了本次运行产生的全部条目:工具调用、工具结果、消息……
print([item.type for item in result.new_items])
if __name__ == "__main__":
asyncio.run(main())
@tool 和 @function_tool 是同一个东西

0.23 的文档统一使用 from agents.decorators import tool。源码里 tool 就是 function_tool 的别名6。SDK 会自动完成三件事7:

  • 函数名成为工具名;
  • docstring 成为工具描述,其中各参数的说明成为参数描述(用 griffe 解析);
  • 函数签名经过 Pydantic 转换成 JSON Schema。

动态指令与 Context(依赖注入)#

Agent 对 context 类型是泛型的。context 是一个依赖注入容器:你把对象传给 Runner.run(..., context=...),SDK 会把它递给每一个 agent、工具和 handoff 回调5。

import asyncio
from dataclasses import dataclass
from agents import Agent, RunContextWrapper, Runner
from agents.decorators import tool
@dataclass
class UserContext:
name: str
is_vip: bool
def dynamic_instructions(ctx: RunContextWrapper[UserContext], agent: Agent[UserContext]) -> str:
level = "VIP" if ctx.context.is_vip else "普通"
return f"用户名是 {ctx.context.name},是{level}用户。请礼貌地回答。"
@tool
def get_points(ctx: RunContextWrapper[UserContext]) -> str:
"""查询当前用户的积分。"""
return f"{ctx.context.name} 当前有 1200 积分" # 真实场景下这里会查数据库
agent = Agent[UserContext](name="客服", instructions=dynamic_instructions, tools=[get_points])
async def main() -> None:
result = await Runner.run(agent, "我有多少积分?", context=UserContext(name="小王", is_vip=True))
print(result.final_output)
asyncio.run(main())

context 不会发送给模型,它只是本地依赖的“背包”。工具函数的第一个参数如果声明为 RunContextWrapper,就能拿到 context。


4. Runner:循环是怎么运行的#

三种运行方式3:

方法同步还是异步返回值
Runner.run()异步RunResult
Runner.run_sync()同步(内部调用 run)RunResult
Runner.run_streamed()异步、流式RunResultStreaming

官方对循环的描述3:

最终输出

(类型符合 output_type

且没有工具调用)

handoff

工具调用

超过 max_turns

Runner.run(agent, input)

调用当前 agent 的 LLM

判断输出

结束,返回 RunResult

切换当前 agent,更新输入

执行工具,追加结果

抛出 MaxTurnsExceeded

max_turns=None 可关闭上限

最终输出

(类型符合 output_type

且没有工具调用)

handoff

工具调用

超过 max_turns

Runner.run(agent, input)

调用当前 agent 的 LLM

判断输出

结束,返回 RunResult

切换当前 agent,更新输入

执行工具,追加结果

抛出 MaxTurnsExceeded

max_turns=None 可关闭上限

  • 输入可以是:一个字符串(当作用户消息)、一组 Responses 格式的 input item,或者一个 RunState(用于从暂停处恢复)3。
  • 一次 Runner.run 对应聊天里的一个逻辑回合:其中可能有多个 agent 先后运行、多次调用 LLM、执行多次工具,最后产出一个输出3。

RunConfig:全局覆盖#

RunConfig 可以在不修改 agent 定义的情况下改变一次运行的行为3:

类别常用字段
模型model、model_provider、model_settings
护栏与 handoffinput_guardrails、output_guardrails、handoff_input_filter、nest_handoff_history(beta)
输入整形call_model_input_filter(在每次调用模型前修改输入,比如裁剪历史)
Tracingtracing_disabled、workflow_name、trace_id、group_id
工具执行tool_execution(例如 max_function_tool_concurrency)、tool_not_found_behavior、tool_error_formatter

5. 工具(Tools)#

SDK 支持五类工具7:

类别执行位置代表
托管工具(Hosted)OpenAI 服务端WebSearchTool、FileSearchTool、CodeInterpreterTool、HostedMCPTool、ImageGenerationTool、ToolSearchTool、ProgrammaticToolCallingTool
本地运行时工具你的环境ComputerTool、ApplyPatchTool;ShellTool 可以在本地或托管容器中运行
函数工具你的进程@tool 装饰的任意 Python 函数
Agents as tools你的进程agent.as_tool(...)
Codex 工具(实验性)本地 Codex CLIcodex_tool(...),把有明确边界的工作区任务委派给 Codex
本地 Shell 和 ApplyPatch 默认不需要审批

本地的 ShellTool 和 ApplyPatchTool 默认 needs_approval=False。官方特别说明:SDK 的审批机制并不提供沙箱,隔离和权限必须由你的执行器自己保证7。

5.1 MCP 集成的四种方式#

需求推荐方案
让 OpenAI 服务端代为调用一个公网可达的 MCP 服务器HostedMCPTool:整个工具调用往返都在 OpenAI 那边完成
连接你自己运行的 Streamable HTTP 服务器MCPServerStreamableHttp
连接旧式的 HTTP+SSE 服务器MCPServerSse
启动一个本地子进程,通过 stdio 通信MCPServerStdio

以上取自官方文档8。下面是 stdio 方式的例子:

import asyncio
from pathlib import Path
from agents import Agent, Runner
from agents.mcp import MCPServerStdio
async def main() -> None:
docs_dir = Path(__file__).parent / "docs"
async with MCPServerStdio(
name="Filesystem Server via npx",
params={
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", str(docs_dir)],
},
) as server: # 上下文管理器负责启动、关闭子进程
agent = Agent(
name="文档助手",
instructions="使用 MCP 提供的文件工具回答问题。",
mcp_servers=[server],
)
result = await Runner.run(agent, "列出你能访问的文件。")
print(result.final_output)
asyncio.run(main())
使用 MCP 前先信任 server

MCP 工具会接触模型上下文中的数据,并以你提供的凭据执行操作。官方建议:只连接可信的 server,使用最小权限的凭据,token 放在 header 而不是 URL 里,敏感操作要求审批8。


6. 多 Agent 编排:Manager 与 Handoffs#

模式怎么工作适合什么场景
Agents as tools(Manager)管理者 agent 始终掌控对话,通过 Agent.as_tool() 调用专家需要由一个 agent 负责最终答案、汇总多个专家的输出,或者在一处统一施加护栏
Handoffs分诊 agent 把对话路由给专家,专家成为本回合剩余部分的当前 agent希望专家直接回复用户、保持提示词聚焦

以上取自官方文档4。

Handoffs 模式

transfer_to_退款专家

直接回复

用户

分诊 agent

退款专家

Manager 模式(agents as tools)

调用工具

调用工具

结果

结果

最终答案

用户

客服总管

订票专家

退款专家

Handoffs 模式

transfer_to_退款专家

直接回复

用户

分诊 agent

退款专家

Manager 模式(agents as tools)

调用工具

调用工具

结果

结果

最终答案

用户

客服总管

订票专家

退款专家

6.1 Handoffs#

import asyncio
from agents import Agent, Runner
history_tutor = Agent(
name="History Tutor",
handoff_description="负责历史问题的专家",
instructions="清晰、简洁地回答历史问题。",
)
math_tutor = Agent(
name="Math Tutor",
handoff_description="负责数学问题的专家",
instructions="一步一步讲解数学题,并给出例题。",
)
triage = Agent(
name="Triage Agent",
instructions="把每个作业问题路由给合适的专家。",
handoffs=[history_tutor, math_tutor],
)
async def main() -> None:
result = await Runner.run(triage, "美国第一任总统是谁?")
print(result.final_output)
print("回答者:", result.last_agent.name) # 实际给出回答的 agent
asyncio.run(main())
  • 对模型来说,handoff 就是一个工具。转交给名为 Refund Agent 的 agent 时,工具名是 transfer_to_refund_agent9。
  • 用 handoff() 函数可以进一步定制:on_handoff 回调、input_type(让模型在转交时附带结构化参数,例如转交原因)、input_filter(过滤传给下一个 agent 的历史)、is_enabled(动态开关)9。
  • 默认情况下,接手的 agent 能看到完整的对话历史5。

6.2 Agents as tools#

import asyncio
from agents import Agent, Runner
spanish = Agent(name="Spanish agent", instructions="把用户的消息翻译成西班牙语")
french = Agent(name="French agent", instructions="把用户的消息翻译成法语")
orchestrator = Agent(
name="orchestrator",
instructions="你是翻译助手,使用给你的工具完成翻译;需要多种语言时就调用多个工具。",
tools=[
spanish.as_tool(tool_name="translate_to_spanish", tool_description="翻译成西班牙语"),
french.as_tool(tool_name="translate_to_french", tool_description="翻译成法语"),
],
)
async def main() -> None:
result = await Runner.run(orchestrator, "把 'Hello, how are you?' 翻译成西班牙语和法语。")
print(result.final_output)
asyncio.run(main())

6.3 用代码编排#

官方同样推荐用代码来编排,这样结果更可预测,速度和成本也更可控4:

  • 用结构化输出分类,再根据类别选择下一个 agent;
  • 把多个 agent 串成流水线(研究 → 大纲 → 写作 → 评审 → 修改);
  • 用 while 循环反复“执行加评估”,直到评估通过;
  • 用 asyncio.gather 并行运行多个互不依赖的 agent。

7. 护栏(Guardrails)#

类型在哪里运行说明
输入护栏只在链条中的第一个 agent 上运行默认与 agent 并行执行(run_in_parallel=True),延迟最低,但触发时 agent 可能已经消耗了 token。设为 run_in_parallel=False 则改为阻塞执行,先检查再运行
输出护栏只在产出最终输出的 agent 上运行agent 完成后执行
工具护栏每一次受保护的函数工具调用执行前检查输入,执行后检查输出

以上取自官方文档10。

import asyncio
from pydantic import BaseModel
from agents import (
Agent,
GuardrailFunctionOutput,
InputGuardrailTripwireTriggered,
RunContextWrapper,
Runner,
TResponseInputItem,
)
from agents.decorators import input_guardrail
class HomeworkCheck(BaseModel):
is_math_homework: bool
reasoning: str
# 用一个便宜、快速的 agent 做检查
guardrail_agent = Agent(
name="Guardrail check",
instructions="判断用户是不是在让你代写数学作业。",
output_type=HomeworkCheck,
)
@input_guardrail
async def math_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_as(HomeworkCheck).is_math_homework,
)
support_agent = Agent(
name="Customer support agent",
instructions="你是客服,帮助客户解决问题。",
input_guardrails=[math_guardrail],
)
async def main() -> None:
try:
await Runner.run(support_agent, "帮我解一下 2x + 3 = 11")
except InputGuardrailTripwireTriggered:
print("触发了“代写作业”护栏,请求被拦截")
asyncio.run(main())

这个例子改编自官方示例10。触发护栏后,SDK 会抛出 InputGuardrailTripwireTriggered 或 OutputGuardrailTripwireTriggered 并立即停止执行。


8. 状态与 Sessions#

四种跨轮次保存状态的策略如下。官方强调一段对话只选一种3。

策略状态存放在哪下一轮传什么
result.to_input_list()你的应用内存上一轮的 list 加上新的用户消息
session=...你的存储,由 SDK 负责读写同一个 session 实例
conversation_idOpenAI Conversations API同一个 ID,加上新一轮的输入
previous_response_idOpenAI Responses APIresult.last_response_id,加上新一轮的输入
import asyncio
from agents import Agent, Runner, SQLiteSession
async def main() -> None:
agent = Agent(name="Assistant", instructions="回答要非常简洁。")
session = SQLiteSession("conversation_123") # 默认是内存数据库;也可以传入文件路径持久化
r1 = await Runner.run(agent, "金门大桥在哪个城市?", session=session)
print(r1.final_output) # 旧金山
r2 = await Runner.run(agent, "它在哪个州?", session=session) # 自动带上之前的历史
print(r2.final_output) # 加利福尼亚
asyncio.run(main())

Session 的行为:每次运行之前,自动取出历史拼到输入前面;运行之后,自动保存新产生的所有条目11。内置实现包括:OpenAI Conversations、Responses compaction、SQLite(同步和异步)、Redis、SQLAlchemy、Dapr、MongoDB、Advanced SQLite、加密 Session 等11。

注意

Session 不能和 conversation_id、previous_response_id 在同一次运行中混用3。


9. 流式事件#

import asyncio
from agents import Agent, ItemHelpers, Runner
from agents.decorators import tool
@tool
def how_many_jokes() -> int:
"""返回要讲的笑话数量。"""
return 3
async def main() -> None:
agent = Agent(
name="Joker",
instructions="先调用 how_many_jokes,再讲对应数量的笑话。",
tools=[how_many_jokes],
)
result = Runner.run_streamed(agent, input="讲几个笑话")
async for event in result.stream_events():
if event.type == "raw_response_event":
continue # 原始的 token 级事件,这里忽略
elif event.type == "agent_updated_stream_event":
print(f"当前 agent:{event.new_agent.name}")
elif event.type == "run_item_stream_event":
if event.item.type == "tool_call_item":
print("-- 调用了工具")
elif event.item.type == "tool_call_output_item":
print(f"-- 工具输出:{event.item.output}")
elif event.item.type == "message_output_item":
print(ItemHelpers.text_message_output(event.item))
asyncio.run(main())
事件类型粒度用途
RawResponsesStreamEventtoken 级,包装 Responses 的原始事件逐字显示文本
RunItemStreamEvent条目级,name 取值如 message_output_created、tool_called、tool_output、handoff_requested 等显示进度
AgentUpdatedStreamEvent当前 agent 变化时触发(例如发生了 handoff)显示由哪个 agent 在处理

以上取自官方文档12。

必须把 stream_events() 迭代到结束,这次运行才算完成。最后一个 token 显示出来之后,SDK 可能还在保存 session 或者压缩历史12。


10. 人工审批(Human in the loop)#

模型Runner你的应用模型Runner你的应用Runner.run(agent, "取消订单 42")调用模型工具调用 cancel_order(42)运行暂停:result.interruptions = [ToolApprovalItem]state = result.to_state(),交给人审批state.approve(item),然后 Runner.run(agent, state)执行 cancel_order(42)带上工具结果继续调用最终回复RunResult
模型Runner你的应用模型Runner你的应用Runner.run(agent, "取消订单 42")调用模型工具调用 cancel_order(42)运行暂停:result.interruptions = [ToolApprovalItem]state = result.to_state(),交给人审批state.approve(item),然后 Runner.run(agent, state)执行 cancel_order(42)带上工具结果继续调用最终回复RunResult
import asyncio
from agents import Agent, Runner
from agents.decorators import tool
@tool(needs_approval=True) # 也可以传一个异步函数,按参数逐次判断是否需要审批
async def cancel_order(order_id: int) -> str:
"""取消订单。"""
return f"订单 {order_id} 已取消"
agent = Agent(name="Support agent", instructions="处理工单,必要时取消订单。", tools=[cancel_order])
async def main() -> None:
result = await Runner.run(agent, "请取消我的订单 42")
while result.interruptions: # 有待审批的工具调用
state = result.to_state() # RunState 可以序列化,跨进程、跨天后再恢复都可以
for item in result.interruptions:
ok = input(f"批准 {item.name}({item.arguments})?[y/N] ") == "y"
if ok:
state.approve(item)
else:
state.reject(item)
result = await Runner.run(agent, state) # 从暂停处继续
print(result.final_output)
asyncio.run(main())
  • 审批的范围是整次运行,不只限于当前 agent。handoff 之后的 agent、Agent.as_tool() 嵌套运行里的工具,审批请求都会出现在外层运行的 interruptions 里13。
  • RunState 支持 to_json()、to_string() 序列化,因此可以“暂停后过一天再批”13。

11. Tracing#

  • 默认开启。会记录 LLM 生成、工具调用、handoff、护栏以及自定义事件,可以在 OpenAI Dashboard 的 Traces 页面查看14。
  • 结构:Trace 对应一次端到端的工作流,由多个 Span 组成。默认的 span 类型包括 agent_span、generation_span、function_span、guardrail_span、handoff_span 等14。
  • 关闭方法有三种:设置环境变量 OPENAI_AGENTS_DISABLE_TRACING=1、调用 set_tracing_disabled(True)、或者设置 RunConfig(tracing_disabled=True)14。
  • 注意:采用零数据保留(ZDR)策略的组织无法使用 tracing14。
from agents import Agent, Runner, trace
agent = Agent(name="Assistant", instructions="简洁回答。")
with trace(workflow_name="多轮对话示例", group_id="thread_123"): # 把多次运行归到同一个 trace 下
first = Runner.run_sync(agent, "金门大桥在哪个城市?")
second = Runner.run_sync(
agent, first.to_input_list() + [{"role": "user", "content": "在哪个州?"}]
)
print(second.final_output)

12. Sandbox Agents:让 Agent 拥有真实的工作区#

Sandbox Agents 给模型一个持久化的工作区:可以检索文档、编辑文件、运行命令、生成产物,并且能从保存的沙箱状态继续工作15。

  • 在普通的 Agent 加 Runner 用法之上,再增加三样东西:
    • Manifest:声明工作区里有哪些文件和目录;
    • capabilities:沙箱原生的能力,例如文件、shell、skills、memory、compaction;
    • SandboxRunConfig:指定在哪里运行,即沙箱客户端15。
  • 沙箱客户端可以是 Unix 本地(UnixLocalSandboxClient)、Docker、或者托管的沙箱提供方15。
本地沙箱的隔离能力有限

Linux 上的 UnixLocalSandboxClient 不提供任何 OS 级隔离;macOS 上用 sandbox-exec 限制文件系统,但不隔离网络。不可信的命令应该放到 Docker 或托管沙箱里执行15。


13. 模型与提供方#

  • 没有指定模型时,默认使用 gpt-5.6-luna,并设置 reasoning.effort="none"、verbosity="low",面向成本敏感、调用量大的场景。需要最强能力时,可以显式设置 model="gpt-5.6-sol"16。
  • 用环境变量 OPENAI_DEFAULT_MODEL 可以修改全局默认模型,用 RunConfig(model=...) 可以修改单次运行的模型16。
  • 接入其他厂商的方式:
    • 对于兼容 OpenAI 接口的端点,用 set_default_openai_client(AsyncOpenAI(base_url=..., api_key=...));
    • 也可以用 MultiProvider 按模型名前缀路由,或者使用 LiteLLM、any-llm 扩展。
    • 很多厂商还不支持 Responses API,这种情况下示例里改用 Chat Completions 模型16。

14. 生产化:持久化执行与异常#

持久化执行:官方列出了与 Dapr、Temporal、Restate、DBOS 的集成。它们可以支撑长时间运行、可以从故障中恢复、支持人工审批的 agent3。

主要异常3:

异常什么时候抛出
MaxTurnsExceeded超过 max_turns
ModelBehaviorError模型输出非法,例如 JSON 格式错误,或者 Responses 返回 failed / incomplete
ModelTimeoutError / ToolTimeoutError模型调用或工具调用超时
InputGuardrailTripwireTriggered / OutputGuardrailTripwireTriggered护栏被触发
UserErrorSDK 用法错误

另外,error_handlers 参数可以把 max_turns、model_refusal、invalid_final_output 这三类错误转换成可控的兜底输出,而不是直接抛异常3。


15. 实现视角:它为什么“轻”#

  • 公开概念只有 Agent、Runner、Tool、Handoff、Guardrail 少数几个,其余能力(Session、Tracing、Sandbox)都是可选的。
  • 与 Responses API 的关系:Runner 每一轮都会把“当前 agent 的 instructions 加历史 item 加工具定义”组装成一次 Responses 请求。工具调用的 item 也采用 Responses 的格式,所以 to_input_list() 的结果可以直接作为下一次 Responses 请求的输入3。
  • 笔者理解:Agents SDK 处在“只有循环”和“完整 harness”之间。普通 Agent 本身没有文件系统和 shell 工具,需要的话要显式添加 Sandbox Agents 或本地运行时工具。这一点与 3.3 Claude Agent SDK 默认就带一整套编码工具形成鲜明对比。

小结#

  • Agents SDK 的核心公式是:Agent(指令 + 工具 + handoffs + 护栏)加 Runner(循环)。
  • 多 Agent 优先考虑两种模式:需要统一出口时用 Manager,需要专家直接面对用户时用 Handoffs。
  • 生产化相关的能力:Guardrails、needs_approval 加 RunState、Tracing、Sessions,以及持久化执行集成。
  • 如果连“运行在哪里、断线后怎么恢复”都不想操心,可以看 2.4 OpenAI Agents API。

相关笔记#

参考资料#

注释与出处#

  1. openai/openai-agents-python,docs/release.md(0.Y.Z 版本策略,以及 0.21.0 要求 openai v3),https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/release.md ↩ ↩2

  2. openai/openai-agents-python,docs/index.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/index.md ↩ ↩2 ↩3 ↩4 ↩5 ↩6

  3. openai/openai-agents-python,docs/running_agents.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/running_agents.md ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12

  4. openai/openai-agents-python,docs/multi_agent.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/multi_agent.md ↩ ↩2 ↩3

  5. openai/openai-agents-python,docs/agents.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/agents.md ↩ ↩2 ↩3

  6. openai/openai-agents-python,src/agents/decorators.py(“tool is an alias for function_tool”),https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/src/agents/decorators.py ↩

  7. openai/openai-agents-python,docs/tools.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/tools.md ↩ ↩2 ↩3

  8. openai/openai-agents-python,docs/mcp.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/mcp.md ↩ ↩2

  9. openai/openai-agents-python,docs/handoffs.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/handoffs.md ↩ ↩2

  10. openai/openai-agents-python,docs/guardrails.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/guardrails.md ↩ ↩2

  11. openai/openai-agents-python,docs/sessions/index.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/sessions/index.md ↩ ↩2

  12. openai/openai-agents-python,docs/streaming.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/streaming.md ↩ ↩2

  13. openai/openai-agents-python,docs/human_in_the_loop.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/human_in_the_loop.md ↩ ↩2

  14. openai/openai-agents-python,docs/tracing.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/tracing.md ↩ ↩2 ↩3 ↩4

  15. openai/openai-agents-python,docs/sandbox_agents.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/sandbox_agents.md ↩ ↩2 ↩3 ↩4

  16. openai/openai-agents-python,docs/models/index.md,https://github.com/openai/openai-agents-python/blob/26345c1e45ebede8e2fc9b0bc7341dedab5e01fc/docs/models/index.md ↩ ↩2 ↩3

输入关键词开始搜索。多个关键词用空格分隔。