openai-agents-python-sdk 源码解析 | 第一篇:认识 OpenAI Agents Python SDK:它解决什么问题
本篇导读
如果你已经用过 OpenAI API,通常会从一个很直接的流程开始:组织 prompt、调用模型、解析返回值。如果任务只是一问一答,这样足够。但当任务开始包含工具调用、多步骤推理、多 Agent 分工、人工审批、会话记忆、流式输出、Trace 排障、MCP 工具生态或沙箱执行时,直接围绕 API 手写流程会很快变得复杂。
OpenAI Agents Python SDK 要解决的核心问题,就是把这些复杂度收敛到一套统一的 Agent 运行时里。开发者通过 Agent 描述“这个智能体是谁、能做什么、有什么边界”,再通过 Runner 执行一次工作流。模型调用、工具执行、handoff、guardrail、session 和 tracing 都被组织在同一条运行链路中。
本系列会从源码角度拆解这个 SDK。第一篇不深入展开每个类的实现细节,而是先建立项目地图:这个项目是什么、为什么需要它、核心模块在哪里、后续应该按什么顺序阅读源码。
这个项目是什么
当前仓库是 openai-agents-python,发布包名是 openai-agents。从 pyproject.toml 可以看到它是一个 Python SDK 项目,要求 Python >=3.10,核心依赖包括:
openai:连接 OpenAI API。pydantic:支持结构化输出、参数 schema、类型校验。websockets:支撑 Realtime 和 WebSocket 场景。mcp:支撑 Model Context Protocol 工具生态。requests、typing-extensions、griffelib:分别支撑基础 HTTP、类型兼容和文档能力。
README 对它的定位非常直接:这是一个用于构建多 Agent 工作流的轻量框架,支持 OpenAI Responses API、Chat Completions API,也能通过 provider 扩展接入其他模型。
更准确地说,它不是一个“聊天机器人模板”,而是一个 Agent runtime SDK。它关注的是以下问题:
- 如何声明一个 Agent 的职责、工具、输出、handoff 和安全边界。
- 如何在一次 run 中协调模型调用、工具调用和 Agent 切换。
- 如何管理上下文、会话历史、流式事件和最终输出。
- 如何让运行过程可观察、可测试、可扩展。
- 如何把 MCP、Realtime、Voice、Sandbox 等能力接入同一套编排模型。
为什么不直接调用模型 API
直接调用模型 API 的典型流程大致是这样:
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-5.4-mini",
input="请总结这段文本。",
)
print(response.output_text)
这段代码很清晰,适合一次性问答。但真实应用很少停留在这里。比如一个客服 Agent 可能需要:
- 识别用户意图。
- 查询订单系统。
- 判断是否需要退款审批。
- 如果是技术问题,交给技术支持 Agent。
- 如果用户输入涉及敏感信息,提前拦截。
- 保存对话历史,下一轮继续处理。
- 在后台记录模型调用、工具调用、耗时和错误。
- 前端需要实时展示模型输出和工具执行进度。
如果全部自己手写,就会出现大量横切逻辑:工具 schema、工具结果回填、错误转换、循环控制、人工审批、上下文裁剪、trace 记录、流式事件合并、重试和取消。这些逻辑和业务代码混在一起后,后续维护成本会快速上升。
Agents SDK 的价值就在这里:它把这些横切逻辑抽象成稳定对象和运行时流程。你仍然需要设计业务 Agent 和工具,但不需要从零维护整套 Agent loop。
最小使用形态
README 中的普通文本 Agent 示例可以简化成下面这样:
from agents import Agent, Runner
agent = Agent(
name="Assistant",
instructions="你是一个简洁、准确的技术助手。",
)
result = Runner.run_sync(agent, "解释什么是 Agent runtime。")
print(result.final_output)
这个例子里出现了两个最重要的对象:
Agent:描述智能体,包括名称、指令、模型、工具、guardrails、handoffs、输出类型等。Runner:执行智能体工作流,把输入交给 Agent,并驱动后续模型调用、工具调用或 handoff。
从使用体验上看,这是一段很短的代码。但从源码视角看,它背后已经进入了完整运行时。
核心对象地图
先把主要概念放在一张表里,后续每篇文章会逐个展开。
| 概念 | 主要源码入口 | 解决的问题 |
|---|---|---|
Agent |
src/agents/agent.py |
声明智能体的指令、工具、handoff、guardrail、输出类型和模型配置 |
Runner |
src/agents/run.py |
执行 Agent 工作流,负责循环、工具调用、handoff、最终输出判断 |
RunConfig |
src/agents/run_config.py |
提供一次 run 的全局配置,例如模型 provider、tracing、tool 行为、sandbox |
RunResult |
src/agents/result.py |
封装最终输出、运行产生的新 item、最后执行的 Agent 等结果 |
Tool |
src/agents/tool.py |
把函数、MCP、Hosted tools、搜索、代码解释器等能力暴露给 Agent |
Handoff |
src/agents/handoffs |
支持 Agent 把任务交给另一个更合适的 Agent |
Guardrail |
src/agents/guardrail.py |
在输入或输出阶段执行安全和业务规则校验 |
Session |
src/agents/memory |
管理自动对话历史和会话状态 |
Tracing |
src/agents/tracing |
记录模型调用、工具调用、guardrail、handoff 等运行过程 |
RealtimeAgent |
src/agents/realtime |
面向低延迟 WebSocket、语音和多模态交互 |
SandboxAgent |
src/agents/sandbox |
面向文件检查、命令执行、补丁应用和长任务工作区 |
这张表是阅读整个项目的导航。后续如果你在源码中迷路,优先回到这张表判断当前文件属于哪个运行阶段。
一次 Agent run 的高层链路
Runner.run 的 docstring 已经把核心循环讲得很清楚。一次工作流会从 starting agent 开始,循环执行,直到出现最终输出、触发 handoff、执行工具后再次进入模型,或遇到异常。
可以把它理解成下面这条链路:
这张图有几个关键点:
Agent不是直接执行者,它更像是“声明对象”。Runner才是运行时入口,负责把 Agent、模型、工具、session 和 tracing 串起来。- 工具调用后通常会再次回到模型,让模型基于工具结果生成最终答案。
- handoff 后会切换当前 Agent,但仍在同一次 run 的控制流里。
- guardrail、session、tracing 是贯穿执行链路的横切能力。
Agent:声明一个智能体
src/agents/agent.py 中的 Agent 是一个 dataclass。源码注释说明得很明确:Agent 是一个配置了 instructions、tools、guardrails、handoffs 等能力的 AI model。
从当前源码看,Agent 的关键字段包括:
instructions:系统指令,可以是字符串,也可以是根据上下文动态生成的函数。prompt:面向 OpenAI Responses API 的 prompt 配置。handoffs:可委派的下游 Agent 或 Handoff 对象。model:当前 Agent 使用的模型或模型名称。model_settings:温度、top_p 等模型参数。input_guardrails:输入阶段的校验规则。output_guardrails:输出阶段的校验规则。output_type:结构化输出类型。tool_use_behavior:工具调用后是否再次调用模型,或直接把工具结果作为最终输出。
这意味着 Agent 不是一个只有 prompt 的薄封装。它把“智能体的职责”和“运行时应该如何对待这个智能体”都描述出来。
一个更接近业务使用的 Agent 可能长这样:
from agents import Agent
support_agent = Agent(
name="SupportAgent",
instructions="你是客服助手,只处理订单、退款和账户问题。",
model="gpt-5.4-mini",
)
后续文章会继续给它增加 tools、handoffs、guardrails 和 session。
Runner:驱动工作流执行
src/agents/run.py 中的 Runner 是 SDK 的执行入口。它提供同步、异步和流式运行方式,其中异步 run 是最核心的形态。
从 Runner.run 的参数可以看出,它不只是接收一个 Agent 和用户输入,还接收很多运行时上下文:
context:传给工具、handoff 和 guardrail 的业务上下文。max_turns:限制最多模型调用轮数,避免无限循环。hooks:生命周期回调。run_config:全局配置。error_handlers:运行时错误处理。previous_response_id/conversation_id:OpenAI Responses API 的会话延续能力。session:SDK 层面的自动历史管理。
这说明 SDK 的执行模型不是“一次输入一次输出”这么简单。它更像一个受控循环,每一轮都可能发生模型调用、工具调用、handoff、guardrail 检查和状态持久化。
Tool:让 Agent 具备行动能力
如果没有工具,Agent 只能基于已有上下文回答问题。工具系统让 Agent 可以查询外部系统、调用函数、检索文件、执行 MCP 工具或访问 Hosted tools。
最常见的入口是 @function_tool:
from agents import Agent, function_tool
@function_tool
def get_order_status(order_id: str) -> str:
# 示例工具:真实场景中这里会查询订单系统。
return f"订单 {order_id} 当前状态为已发货。"
agent = Agent(
name="OrderAgent",
instructions="你负责回答订单状态问题。",
tools=[get_order_status],
)
工具系统背后有三件重要工作:
- 从 Python 函数签名生成模型可理解的参数 schema。
- 在模型请求中暴露工具定义。
- 当模型选择调用工具时,执行 Python 函数并把结果回填给模型。
这也是为什么工具系统需要和 Runner 紧密结合。工具不是孤立函数,而是 Agent loop 的一部分。
Handoff:让 Agent 分工协作
当一个 Agent 不应该处理所有事情时,就需要 handoff。比如客服系统里,入口 Agent 可以负责意图识别,然后把任务交给不同专家 Agent:
BillingAgent:处理账单和支付。RefundAgent:处理退款策略。TechSupportAgent:处理技术问题。
Handoff 和工具调用的区别在于:
- 工具调用是“当前 Agent 调用一个能力”。
- Handoff 是“当前 Agent 把控制权交给另一个 Agent”。
这两者都能实现模块化,但适用边界不同。工具适合明确、可执行的动作;handoff 适合职责切换和上下文交接。
Guardrail:把安全和业务边界放进运行时
Guardrail 负责在输入或输出阶段做校验。它不是 prompt 里的“请不要做某事”,而是运行时里的明确检查。
典型场景包括:
- 输入中包含不允许处理的主题,直接拦截。
- 输出不符合结构化格式,触发错误。
- 工具输入缺少必要字段,禁止执行。
- 工具输出包含敏感信息,不允许返回给用户。
这类逻辑如果只靠 prompt,稳定性很难保证。放入 guardrail 后,它就变成了可测试、可追踪、可维护的运行时边界。
Session 与 Memory:让多轮对话可持续
很多 Agent 应用不是单轮问答,而是多轮任务。SDK 提供 session 和 memory 相关抽象,用于自动管理会话历史。
当前项目里相关代码主要分布在:
src/agents/memorysrc/agents/extensions/memorysrc/agents/run_internal/session_persistence.py
内置和扩展能力覆盖 SQLite、SQLAlchemy、Redis、MongoDB、Dapr、加密 session 等。对于业务系统来说,这部分很关键,因为历史状态一旦管理不好,就会出现重复写入、上下文错乱、重试不一致或隐私数据残留等问题。
Tracing:让 Agent 运行过程可观察
Agent 工作流一旦包含工具和 handoff,排障就不能只看最终输出。你需要知道:
- 模型被调用了几次。
- 每次模型调用用了哪个 Agent。
- 模型是否选择了工具。
- 工具执行耗时多久。
- 是否发生 handoff。
- 哪个 guardrail 拦截了请求。
- session 是否保存成功。
src/agents/tracing 就是为这些问题服务的。Tracing 不只是调试辅助,它也是生产化 Agent 应用的基础设施。后续文章会专门展开 trace、span、processor 和敏感信息处理。
三种主要运行形态
README 中给出了三种入门方式,它们代表 SDK 的三类典型使用形态。
普通文本 Agent
这是最常见的形态。适合普通问答、结构化输出、工具调用、多 Agent 编排和后台任务。
核心入口:
agents.Agentagents.Runnerexamples/basic
Realtime Agent
Realtime Agent 面向低延迟交互,尤其是 WebSocket、语音和多模态场景。它的核心对象是:
RealtimeAgentRealtimeRunnerRealtimeSession
相关代码位于 src/agents/realtime,示例位于 examples/realtime。
Sandbox Agent
Sandbox Agent 面向长任务工作区。它可以让 Agent 在受控环境里检查文件、运行命令、应用补丁或保留 workspace state。
相关代码位于:
src/agents/sandboxsrc/agents/extensions/sandboxexamples/sandbox
如果你把这个 SDK 用在代码分析、数据处理、仓库巡检或自动修复任务上,Sandbox Agent 会是很重要的一条线。
仓库结构怎么读
先看顶层目录:
| 路径 | 作用 |
|---|---|
src/agents |
SDK 核心实现 |
examples |
官方示例,适合从使用方式切入 |
docs |
MkDocs 文档源码 |
tests |
测试套件,适合理解行为边界 |
pyproject.toml |
包配置、依赖、ruff、mypy、pytest、coverage 配置 |
Makefile |
常用开发命令 |
.agents/references |
维护者视角的架构参考 |
.agents/skills |
本仓库自动化工作流说明 |
再看 src/agents 内部:
| 路径 | 建议阅读时机 |
|---|---|
agent.py |
第一优先级,理解 Agent 声明模型 |
run.py |
第一优先级,理解 Runner 入口 |
run_internal |
第二优先级,理解运行时拆分 |
tool.py |
写工具前阅读 |
guardrail.py / tool_guardrails.py |
做安全和业务校验前阅读 |
handoffs |
做多 Agent 编排前阅读 |
memory / extensions/memory |
做多轮对话和持久化前阅读 |
models |
需要切换模型或 provider 时阅读 |
mcp |
接入 MCP server 时阅读 |
tracing |
做排障、审计和可观测性时阅读 |
realtime |
做低延迟交互和语音应用时阅读 |
voice |
做语音输入输出 pipeline 时阅读 |
sandbox |
做长任务、文件操作和命令执行时阅读 |
推荐源码阅读路线
不要从 src/agents/__init__.py 一路顺序读完整个包。这个文件主要是公共 API re-export,适合查“SDK 暴露了什么”,但不适合作为理解运行时的第一入口。
更实用的阅读顺序是:
README.md:确认项目定位和三类运行形态。examples/basic:先跑通最小 Agent。src/agents/agent.py:理解 Agent 可以声明什么。src/agents/run.py:理解 Runner 如何启动一次 run。src/agents/run_internal/run_loop.py:理解内部循环。src/agents/tool.py:理解工具怎么声明和执行。src/agents/items.py:理解模型输出、工具调用和 handoff 如何被表示。src/agents/result.py:理解最终结果如何组织。tests中对应模块测试:用测试确认行为边界。
这个顺序的好处是先建立外部使用视角,再进入运行时内部,不容易被细节打散。
从应用视角看 SDK 的分层
可以把项目分成四层:
| 层级 | 代表模块 | 说明 |
|---|---|---|
| 用户声明层 | Agent、Tool、Guardrail、Handoff |
开发者声明工作流能力 |
| 运行编排层 | Runner、run_internal |
驱动模型调用、工具调用、handoff 和结果收敛 |
| 模型适配层 | models、extensions/models |
屏蔽 Responses、Chat Completions 和第三方 provider 差异 |
| 能力扩展层 | mcp、memory、tracing、realtime、voice、sandbox |
支撑真实应用中的外部工具、状态、可观测和复杂交互 |
这个分层不是源码中的强制目录边界,而是理解项目时的工程视角。后续源码解析会不断回到这四层,判断某个类到底承担哪一层职责。
本篇小结
OpenAI Agents Python SDK 的核心不是“帮你少写几行调用模型的代码”,而是提供一套可组合、可观察、可扩展的 Agent runtime。
第一篇需要记住四个结论:
Agent是声明对象,描述智能体的职责和能力。Runner是执行入口,负责驱动完整 Agent loop。- Tools、Handoffs、Guardrails、Sessions、Tracing 是围绕模型调用的关键运行时能力。
- Realtime、Voice、Sandbox 是 SDK 面向复杂交互和长任务场景的扩展形态。
下一篇会进入实际编码:从环境准备开始,运行第一个文本 Agent,并观察 RunResult 中到底包含哪些信息。
实践任务
读完本篇后,建议完成三个动作:
- 打开
README.md,确认三种官方入门示例:普通文本 Agent、Realtime Agent、Sandbox Agent。 - 打开
src/agents/agent.py,找到Agentdataclass,浏览字段和字段注释。 - 打开
src/agents/run.py,找到Runner.run,阅读 docstring 中关于运行循环的描述。
如果你能用自己的话解释“为什么 Agent 不是 Runner,Runner 也不是 Model Provider”,就已经具备继续阅读后续源码的基础。
更多推荐


所有评论(0)