开篇语:为什么 OpenAI Agents SDK 是智能体开发的 “范式突破”?
在 AI 智能体开发领域,开发者长期面临一个核心矛盾:业务需求的 “动态复杂性” 与技术框架的 “静态局限性” 之间的不匹配。传统框架(如 LangChain、AutoGen)虽解决了 “工具调用” 的基础问题,但在面对企业级场景时,暴露出三大核心痛点:一是工具与智能体强耦合,新增工具需重构核心逻辑;二是工作流静态固化,无法根据用户输入动态调整执行路径;三是多智能体协作繁琐,缺乏统一的语义化交互机制。
OpenAI Agents SDK 的出现,并非简单的 “工具库升级”,而是从设计理念层面重构了智能体开发的范式。它以 “智能体自治” 为核心,将开发者从 “繁琐的工作流编排” 中解放出来 —— 只需定义智能体的 “目标与职责”,无需预设工具调用顺序,系统会根据运行时上下文自主决策、动态协作。这种转变,让智能体开发从 “传统软件工程思维” 转向 “AI 原生思维”,更贴合复杂业务场景的需求。
本文将分三部分完整解析 OpenAI Agents SDK:第一部分聚焦 “开篇、设计理念与整体架构”,阐明 SDK 的核心价值与分层逻辑;第二部分深入 “核心模块”(Agent、Runner、Tool、Handoff),结合代码示例覆盖实现细节;第三部分补充 “支撑模块”(MultiProvider、Tracing、Session)与总结,形成完整技术体系。最终帮助开发者不仅 “会用”,更能 “理解为什么这么设计”,实现从 “工具使用者” 到 “架构设计者” 的跨越。
一、设计理念:从 “工作流编排” 到 “智能体自治” 的范式革命
设计理念是框架的 “灵魂”。OpenAI Agents SDK 的所有功能实现,都源于对 “智能体本质” 的重新思考 —— 智能体不应是 “预定义流程的执行者”,而应是 “具备自主决策能力的协作单元”。这种思考直接催生了与传统框架截然不同的设计体系。
1. 传统智能体框架的三大核心痛点(以 LangChain 为例)
要理解 SDK 的创新,首先需明确传统框架的局限性。以目前主流的 LangChain 为例,其设计本质是 “自底向上的工作流编排”,即开发者先准备工具组件,再通过 Chain 串联成固定流程。这种模式在实际开发中,会遇到难以规避的三大问题:
(1)工具预定义困境:“预测所有需求” 的不可能
传统框架要求开发者在编码阶段,提前枚举所有可能用到的工具。例如,开发电商客服智能体时,需预先定义 “订单查询工具”“退款计算工具”“物流跟踪工具” 等,且工具的参数结构、调用逻辑需完全固定。一旦业务新增需求(如 “会员专属优惠计算”),开发者需修改智能体的核心调用逻辑,重新测试整个流程 —— 这种 “牵一发而动全身” 的模式,在业务快速迭代的场景下极为低效。
更关键的是,开发者无法 “预测所有用户需求”。例如,用户可能同时询问 “订单状态” 与 “退款政策”,传统框架需预先定义 “多问题处理流程”;若用户提出未预设的需求(如 “对比当前订单商品与竞品差异”),智能体只能返回 “无法处理”,用户体验极差。
(2)静态工作流瓶颈:“一步错,步步错” 的刚性
传统框架的工作流是 “静态固化” 的,工具调用顺序在编码阶段就已确定。以 “退款咨询” 流程为例,LangChain 需定义固定步骤:“调用订单查询工具→判断是否可退款→调用退款计算工具→返回结果”。即使用户直接提供 “已确认可退款的订单号”,智能体仍需执行完整流程,无法跳过 “订单查询” 步骤 —— 这种冗余不仅浪费资源,更延长了响应时间。
更严重的是,静态工作流无法应对 “异常场景”。例如,若 “订单查询工具” 临时故障,传统框架只能返回 “系统错误”;而理想的智能体应能自主调整策略(如 “告知用户订单工具暂不可用,可先提供订单号,稍后回复”),传统框架缺乏这种动态适应能力。
(3)智能体与工具的二元对立:“决策者” 与 “执行者” 的割裂
在传统框架中,智能体与工具是完全不同的概念:智能体负责 “判断是否调用工具”,工具负责 “被动执行指令”,两者之间缺乏协同。这种割裂导致两个问题:一是无法实现 “工具的动态组合”(如将 “订单查询” 与 “物流跟踪” 的结果合并分析);二是无法实现 “智能体嵌套”(如让 “财务智能体” 作为工具,被 “项目管理智能体” 调用)。
例如,开发 “企业财务分析系统” 时,传统框架需分别开发 “数据采集智能体”“指标计算智能体”“报告生成智能体”,再通过复杂的 Chain 串联 —— 若需新增 “行业对比” 功能,需重新设计整个串联逻辑,扩展性极差。
2. OpenAI Agents SDK 的三大核心设计哲学
OpenAI Agents SDK 跳出了传统框架的思维定式,通过三大设计哲学,实现了从 “工作流编排” 到 “智能体自治” 的范式突破:
(1)自顶向下的目标驱动:“定义目标,而非步骤”
SDK 的设计逻辑是 “先明确智能体的目标,再由系统自主推导执行步骤”。开发者无需关注 “先调用哪个工具、后调用哪个工具”,只需通过instructions定义智能体的 “职责与目标”(如 “处理用户的退款咨询,确保提供准确的退款金额与到账时间”)。智能体会根据用户输入(如 “我的订单 12345 能退多少钱?”),自主判断 “是否需要调用订单查询工具”“是否需要调用退款计算工具”,并动态调整执行顺序。
这种模式的核心优势在于 “适应性”—— 无论用户需求如何变化(如同时询问多个问题、提供部分信息),智能体都能自主调整策略,无需开发者修改代码。例如,用户若提供 “已确认可退款的订单号”,智能体可直接调用退款计算工具,跳过订单查询步骤;若用户未提供订单号,智能体则会先询问订单号,再执行后续流程。
(2)智能体作为 “一等公民”:打破 “决策者 - 执行者” 的边界
SDK 最具革命性的设计,是允许 “智能体作为工具被其他智能体调用”。这一设计直接打破了传统框架中 “智能体 = 决策者,工具 = 执行者” 的二元对立,实现了智能体的 “嵌套协作”。例如,开发者可将 “财务分析智能体” 封装成工具,供 “项目管理智能体” 调用 —— 当 “项目管理智能体” 需要计算 “股票投资成本” 时,只需调用 “财务分析工具”,无需关心其内部逻辑。
这种设计的价值体现在三个方面:一是 “模块化复用”,智能体可作为独立组件,在不同场景中复用(如 “财务分析智能体” 可同时服务于 “项目管理”“企业预算” 等场景);二是 “分工专业化”,每个智能体专注于特定领域(如 “订单查询”“退款计算”“物流跟踪”),便于维护与优化;三是 “动态能力扩展”,新增功能只需开发新的智能体,无需修改现有逻辑(如新增 “会员优惠计算”,只需开发对应的智能体并封装成工具,其他智能体可直接调用)。
# 智能体作为一等公民
class Agent:
def __init__(self, name: str, instructions: str):
self.name = name
self.instructions = instructions
self.tools = []
self.handoffs = []
def clone(self, **kwargs): # 运行时配置修改
return dataclasses.replace(self, **kwargs)
def as_tool(self): # 代理转换为工具
return Tool.from_agent(self)
(3)运行时动态配置:“实时调整,无需重启”
传统框架的配置是 “静态固化” 的,智能体的工具列表、模型参数等,需在程序启动时确定;若需调整(如临时禁用 “物流跟踪工具”),需重启服务。SDK 通过clone()方法,支持在运行时动态创建智能体变体 —— 开发者可基于已有智能体,修改部分属性(如工具列表、模型参数、指令),生成新的智能体,无需重启服务。
例如,基于 “基础客服智能体”,开发者可通过clone()快速生成 “会员专属客服智能体”:仅修改instructions(新增 “优先告知会员权益”)与tools(新增 “会员优惠计算工具”),其他属性(如模型参数、会话管理)保持不变。这种动态配置能力,让智能体可快速适配不同场景(如 “大促期间的客服智能体” 可临时新增 “订单加急处理工具”),大幅提升了系统的灵活性。
# 智能体可以在运行时被克隆和修改
base_agent = Agent(
name="adaptive_agent",
instructions="Base instructions"
)
# 根据运行时情况创建专门的版本
specialized_agent = base_agent.clone(
instructions="Specialized instructions for current task",
tools=[additional_tool]
)
3. 设计理念对比:SDK 与传统框架的核心差异
为更直观地理解两者的区别,我们通过表格对比关键维度,明确 SDK 的优势所在:
| 对比维度 | 传统框架(如 LangChain) | OpenAI Agents SDK |
|---|---|---|
| 设计逻辑 | 自底向上,预定义工具与工作流 | 自顶向下,定义目标,自主决策执行步骤 |
| 工具管理 | 静态预定义,新增需修改核心逻辑 | 动态注册,支持运行时添加,智能体可作为工具 |
| 执行流程 | 固定步骤,无法根据运行时场景调整 | 动态调整,智能体自主决策工具调用顺序 |
| 智能体协作 | 需通过复杂 Chain 串联,协作成本高 | 支持智能体嵌套调用,语义化任务转移 |
| 配置灵活性 | 启动时固定,调整需重启服务 | 运行时动态配置(clone()方法),无需重启 |
| 异常处理 | 需手动定义异常分支,覆盖场景有限 | 智能体自主调整策略,支持动态降级 |
| 开发效率 | 需关注工具串联细节,开发成本高 | 聚焦业务目标,工具与流程由系统自主管理 |
通过对比可见,SDK 的设计理念更贴合 “AI 原生应用” 的需求 —— 它不再试图用 “传统软件工程思维” 约束智能体,而是让智能体像人类团队一样,具备 “自主决策、分工协作、动态调整” 的能力。这种理念的转变,是 SDK 能够应对复杂企业级场景的核心原因。
二、整体架构:模块化设计的精密逻辑
设计理念需要通过架构落地。OpenAI Agents SDK 采用分层模块化架构,各层职责清晰、低耦合,既保证了核心功能的稳定性,又为扩展预留了充足空间。其架构从下到上依次支撑 “基础能力→核心逻辑→业务应用”,完整覆盖生产级需求(如会话管理、可观测性、多模型支持)。
1. 架构分层:从基础到应用的五层支撑体系
SDK 的架构可分为五层,每层都有明确的职责与核心组件,层与层之间通过标准化接口交互,确保扩展性与可维护性。下图为架构分层示意图(实际落地时可根据需求调整组件实现):
┌─────────────────────────────────────────┐
│ Application Layer
│ (业务应用层 - 用户代码)
├─────────────────────────────────────────┤
│ Agent (配置) │ Runner (执行) │ Tool (工具) │ Handoff (转移)
│ Core Framework Layer (核心框架层)
├─────────────────────────────────────────┤
│ Model Provider Layer
│ (模型抽象层 - 多模型支持)
├─────────────────────────────────────────┤
│ Tracing & Monitoring
│ (可观测性层 - 追踪和监控)
├─────────────────────────────────────────┤
│ Session Management (Memory Layer)
│ (记忆层 - 对话状态管理)
└─────────────────────────────────────────┘
各层的核心职责与交互逻辑如下:
(1)记忆层(Session Layer):智能体的 “记忆系统”
记忆层是智能体的 “大脑记忆”,负责存储对话历史、工具调用结果、执行状态等信息,解决 “智能体如何记住上下文” 的问题。其核心组件是Session接口 —— 定义了会话管理的标准方法(如get_items()获取历史、add_items()添加记录、clear_session()清空会话),具体实现可根据场景选择(如本地开发用SqliteSession,生产环境用RedisSession)。
记忆层的价值体现在两个方面:一是 “上下文保持”,智能体可通过Session获取历史对话(如用户之前提供的订单号),无需用户重复描述;二是 “状态恢复”,若执行过程中断(如网络故障),智能体可从Session中恢复之前的执行状态,无需重新开始。
(2)可观测性层(Observability Layer):生产环境的 “监控中枢”
可观测性层是保障系统稳定运行的核心,负责记录智能体的执行过程、工具调用详情、错误信息等,支持问题排查与性能优化。其核心组件包括:
Span:分布式追踪的基本单元,记录 “智能体初始化→模型调用→工具执行→结果生成” 的每个环节,包含 “追踪 ID、跨度 ID、开始时间、结束时间、元数据” 等信息;BatchTraceProcessor:批量处理Span数据,支持异步导出到日志系统(如 ELK)、监控平台(如 Prometheus),避免实时导出影响性能;MetricsCollector:收集关键指标(如智能体执行成功率、工具调用耗时、模型调用次数),支持设置告警阈值(如 “工具调用失败率超过 5% 时告警”)。
在生产环境中,可观测性层的价值至关重要 —— 开发者可通过追踪日志,定位 “智能体为何未调用工具”“工具调用为何失败” 等问题;通过性能指标,优化 “模型调用耗时”“工具执行效率” 等瓶颈。
(3)模型抽象层(Model Abstraction Layer):多模型的 “统一接口”
模型抽象层的核心目标是 “解耦智能体与具体模型”,支持开发者灵活切换模型(如从 OpenAI 切换到 LiteLLM、国产大模型),无需修改业务代码。其核心组件包括:
ModelProvider:模型提供商的抽象接口,定义了 “获取模型”“调用模型” 等标准方法;MultiProvider:多模型管理组件,支持通过 “模型前缀”(如openai/gpt-4、litellm/claude-3)路由到不同的模型提供商,同时提供默认回退机制(如openai模型不可用时,自动切换到litellm)。
这种抽象设计的价值在于:一是 “避免绑定单一模型”,开发者可根据成本、性能、场景需求,灵活选择模型;二是 “简化模型升级”,模型版本更新(如从gpt-3.5升级到gpt-4)只需修改配置,无需修改代码;三是 “支持多模型协作”,不同智能体可使用不同模型(如 “订单查询智能体” 用gpt-3.5,“财务分析智能体” 用gpt-4)。
(4)核心框架层(Core Framework Layer):SDK 的 “大脑”
核心框架层是 SDK 的核心,实现了智能体的 “定义、执行、协作” 三大核心逻辑,包含Agent(智能体定义)、Runner(执行引擎)、Tool(工具体系)、Handoff(任务转移)四大模块:
Agent:定义智能体的 “身份信息”,包括名称、指令、工具列表、模型参数等;Runner:驱动智能体执行,负责解析Agent配置、调用模型生成决策、执行工具调用、管理执行状态;Tool:实现智能体与外部世界的交互,支持将普通 Python 函数封装成工具,自动处理参数校验与文档生成;Handoff:实现智能体间的语义化任务转移,支持将用户需求动态分配给最适合的智能体(如 “退款问题” 转移给 “售后智能体”)。
这四大模块协同工作,构成了智能体的完整生命周期:Agent定义 “是什么”,Runner负责 “怎么做”,Tool提供 “能力支撑”,Handoff实现 “分工协作”。
(5)应用层(Application Layer):业务落地的 “最后一公里”
应用层是开发者直接接触的层面,负责将 SDK 的核心能力与具体业务结合。开发者可在应用层完成三件事:一是 “配置智能体”,根据业务需求定义Agent的指令、工具列表等;二是 “发起执行”,通过Runner调用智能体,处理用户需求;三是 “业务逻辑封装”,将智能体执行结果与现有系统(如电商平台、CRM)
三、核心模块深度解析
核心模块是 OpenAI Agents SDK 的 “大脑中枢”,包含 Agent、Runner、Tool、Handoff 四大模块 —— 它们分别对应智能体的 “身份定义”“执行驱动”“能力扩展”“协作机制”,共同构成智能体开发的核心能力体系。本节将对这四大模块进行全方位拆解,从设计理念到代码实现,再到实战场景,完整覆盖模块的技术细节与应用价值,帮助开发者掌握 “如何用 SDK 构建具备自主决策与协作能力的智能体”。
1. Agent 模块:智能体的 “身份与能力定义”
Agent 模块是智能体的 “身份证”,它定义了智能体的角色定位、核心目标、能力范围与协作规则。在 SDK 中,开发者无需编写复杂逻辑,只需通过简单配置即可创建一个具备自主决策能力的智能体 —— 这一切源于 Agent 模块 “数据驱动、不可变设计、泛型支持” 的三大核心特性。
(1)模块设计理念:让智能体 “有个性、可复用、能演化”
Agent 模块的设计围绕三个核心目标展开:
- 数据驱动的配置化:Agent 本质是一个 “数据容器”,通过
name(名称)、instructions(指令)、tools(工具列表)等属性定义智能体的核心信息,无需编写复杂类继承或接口实现; - 不可变设计的安全性:Agent 基于 Python
dataclass实现,默认属性不可变(需显式声明mutable=True才允许修改),确保多线程环境下的线程安全,同时便于缓存与序列化(如存储到数据库或通过网络传输); - 泛型支持的灵活性:通过
Agent[TContext]的泛型设计,可绑定自定义上下文类型(如UserContext包含用户 ID、会员等级、历史偏好),实现类型安全的上下文传递,避免数据类型错误。
(2)核心属性解析:定义智能体的 “核心能力”
Agent 的核心属性决定了智能体的行为模式,以下是关键属性的详细解析:
| 属性名 | 类型 | 作用 | 示例 |
|---|---|---|---|
name | str | 智能体唯一标识,用于日志记录、任务转移时的身份识别 | "order_consultant"(订单咨询智能体) |
instructions | str | Callable | 智能体的核心指令,定义 “做什么”“怎么做”,支持静态字符串或动态函数生成 |
tools | list[Tool] | 智能体可调用的工具列表,工具需通过@function_tool装饰器或as_tool()生成 | [order_query_tool, logistics_track_tool](订单查询、物流跟踪工具) |
handoffs | list[Handoff] | 智能体可转移任务的目标列表,定义 “何时转移”“转移到哪个智能体” | [handoff(refund_agent), handoff(product_agent)](转移到退款、商品智能体) |
output_type | type\[Any] | None | 智能体输出的类型约束,支持 Pydantic 模型,确保输出格式统一 |
model_settings | ModelSettings | None | 智能体使用的模型参数,如模型名称、温度、最大 token 数 |
其中,instructions的设计尤为关键 —— 它直接决定了智能体的决策质量。好的指令应包含三个要素:职责边界(明确 “做什么,不做什么”)、操作规则(明确 “如何调用工具、如何处理异常”)、输出要求(明确 “输出格式、语气风格”)。例如,一个优秀的订单咨询智能体指令如下:
instructions = """
你是订单咨询专家,负责解答用户的订单相关问题,规则如下:
1\. 职责边界:仅处理订单状态、物流信息、付款记录查询,退款、商品咨询需转移给对应智能体;
2\. 操作规则:
用户未提供订单号时,先询问“请提供您的6位订单号”;
拿到订单号后,调用order\_query\_tool获取订单信息;
若工具调用失败(如订单不存在),告知用户“订单号无效,请核对后重新提供”;
输出要求:用口语化表达,分点说明订单状态、付款时间、物流进度(如有)。
"""
(3)关键方法:实现智能体的 “复用与演化”
Agent 模块提供两个核心方法,支撑智能体的灵活复用与动态调整:
① clone(** kwargs):创建智能体变体
clone()方法允许基于已有 Agent,修改部分属性生成新的智能体,无需重新定义所有属性 —— 这是 SDK 实现 “智能体演化” 的核心机制。
def clone(self, **kwargs: Any) -> Agent[TContext]:
"""Make a copy of the agent, with the given arguments changed."""
return dataclasses.replace(self, **kwargs)
例如,基于 “基础客服智能体”,可快速克隆出 “会员专属客服智能体”:
from openai_agents import Agent, ModelSettings
from my_tools import order_query_tool, member_discount_tool
# 1. 定义基础客服智能体
base_customer_service = Agent(
name="base_customer_service",
instructions="""你是基础客服智能体,负责解答用户的订单、物流问题,
调用订单查询工具获取信息,用简洁口语化表达回复。""",
tools=[order_query_tool],
model_settings=ModelSettings(model_name="openai/gpt-3.5-turbo", temperature=0.3)
)
# 2. 克隆会员专属客服智能体(仅修改部分属性)
member_customer_service = base_customer_service.clone(
name="member_customer_service",
instructions="""你是会员专属客服智能体,负责解答会员用户的订单、物流问题,
优先告知会员权益(如免费退货、优先发货),调用订单查询工具与会员优惠工具,
用亲切语气回复(如"亲爱的会员,您的订单...")。""",
tools=base_customer_service.tools + [member_discount_tool], # 新增会员优惠工具
model_settings=ModelSettings(model_name="openai/gpt-4", temperature=0.2) # 升级模型
)
clone()方法的价值在于:
- 快速适配场景:无需重复编写基础配置,仅修改差异化属性即可适配新场景(如会员、非会员;日常、大促);
- 支持 A/B 测试:可基于同一基础 Agent,克隆出不同
instructions或model_settings的变体,进行效果对比; - 避免配置冗余:核心配置(如基础工具、通用指令)只需定义一次,变体仅维护差异部分。
② as_tool(tool_name=None, tool_description=None):智能体转工具
as_tool()是 SDK 最具革命性的方法之一 —— 它允许将一个 Agent 封装成 Tool,供其他 Agent 调用,实现 “智能体嵌套协作”。例如,将 “财务分析智能体” 转为工具后,“项目管理智能体” 可直接调用它计算 “股票投资成本”,无需关心其内部逻辑。
代码示例如下:
# 1. 定义财务分析智能体
financial_agent = Agent(
name="financial_analyst",
instructions="""你是财务分析专家,负责计算股票投资成本,
调用stock_data_tool获取股票价格,调用cost_calc_tool计算成本,
返回包含"股票代码、购买价格、总成本、手续费"的结构化结果。""",
tools=[stock_data_tool, cost_calc_tool]
)
# 2. 将财务智能体转为工具
financial_tool = financial_agent.as_tool(
tool_name="financial_analysis_tool",
tool_description="调用财务分析专家,计算股票投资成本,输入需包含股票代码、购买数量、购买日期"
)
# 3. 定义项目管理智能体(使用财务工具)
project_agent = Agent(
name="project_manager",
instructions="""你是项目管理器,负责制定项目预算,
若需计算股票投资成本,调用financial_analysis_tool,
整合结果生成项目预算报告,包含"投资明细、总成本、占比"。""",
tools=[financial_tool, budget_report_tool]
)
这种设计的核心优势在于:
- 模块化复用:智能体可作为独立组件,在不同场景中复用(如 “财务分析智能体” 可服务于 “项目管理”“企业预算”“个人投资” 等场景);
- 分工专业化:每个智能体专注于特定领域(如 “财务分析”“订单查询”“退款计算”),便于维护与优化(如优化财务分析逻辑,不影响项目管理智能体);
- 动态能力扩展:新增功能只需开发新的智能体并转为工具,现有智能体可直接调用,无需修改核心逻辑(如新增 “税务计算”,只需开发税务智能体并转为工具)。
(4)实战场景:创建电商订单咨询智能体
结合上述知识,我们通过一个完整示例,创建 “电商订单咨询智能体”:
from openai_agents import Agent, ModelSettings, function_tool
from pydantic import BaseModel
from typing import Optional
# 1. 定义订单状态的Pydantic模型(约束输出格式)
class OrderStatus(BaseModel):
order_id: str
status: str # 待付款/已付款/已发货/已签收
payment_time: Optional[str] # 付款时间,待付款时为None
logistics_company: Optional[str] # 物流公司,未发货时为None
logistics_no: Optional[str] # 物流单号,未发货时为None
# 2. 开发订单查询工具
@function_tool
def query_order(order_id: str) -> OrderStatus:
"""
查询电商订单的状态信息
参数:
order_id: 6位数字订单号,如"123456"
返回:
包含订单状态、付款时间、物流信息的结构化结果
异常:
ValueError: 订单号格式错误(非6位数字)或订单不存在时抛出
"""
# 1. 校验订单号格式
if not order_id.isdigit() or len(order_id) != 6:
raise ValueError(f"订单号{order_id}格式错误,需为6位数字")
# 2. 模拟调用电商API获取数据(实际场景替换为真实API)
mock_order_data = {
"123456": {
"order_id": "123456",
"status": "已发货",
"payment_time": "2024-05-20 14:30:00",
"logistics_company": "顺丰速运",
"logistics_no": "SF1234567890123"
},
"654321": {
"order_id": "654321",
"status": "待付款",
"payment_time": None,
"logistics_company": None,
"logistics_no": None
}
}
# 3. 检查订单是否存在
if order_id not in mock_order_data:
raise ValueError(f"订单号{order_id}不存在,请核对后重新查询")
# 4. 返回结构化结果
return OrderStatus(**mock_order_data[order_id])
# 3. 创建订单咨询智能体
order_agent = Agent(
name="order_consultant",
instructions="""你是电商订单咨询专家,负责解答用户的订单状态查询问题,规则如下:
1. 若用户未提供订单号,先询问"请提供您的6位订单号,以便查询状态";
2. 拿到订单号后,调用query_order工具获取订单信息;
3. 若工具抛出异常(如订单号错误),将异常信息转为口语化提示(如"您的订单号格式错误,需为6位数字");
4. 输出时需分点说明订单状态、付款时间(如有)、物流信息(如有),语气亲切自然。""",
tools=[query_order],
output_type=OrderStatus, # 约束输出为OrderStatus类型
model_settings=ModelSettings(
model_name="openai/gpt-3.5-turbo",
temperature=0.3, # 降低随机性,确保结果稳定
max_tokens=500 # 限制输出长度
)
)
通过上述代码,我们创建了一个具备 “订单号校验、API 调用、异常处理、结构化输出” 能力的智能体 —— 它不仅能自主处理用户查询,还能根据工具反馈动态调整回复(如订单号错误时提示用户),完全符合 “智能体自治” 的设计理念。
2. Runner 模块:智能体的 “执行引擎”
如果说 Agent 是智能体的 “大脑”,那么 Runner 就是智能体的 “手脚”—— 它负责解析 Agent 配置、驱动模型决策、执行工具调用、管理执行状态,是连接 “智能体定义” 与 “实际业务落地” 的核心桥梁。Runner 模块的设计目标是 “让智能体的执行过程自动化、可观测、可扩展”。
(1)模块设计理念:让执行过程 “自动化、可控制、可观测”
Runner 模块的设计围绕三个核心目标展开:
- 执行自动化:无需开发者手动编写 “调用模型→解析结果→执行工具→生成回复” 的循环逻辑,Runner 会自动处理智能体的完整执行流程;
- 过程可控制:支持同步 / 流式两种执行模式,提供生命周期钩子函数,允许开发者在执行的关键节点(如工具调用前、执行完成后)注入自定义逻辑;
- 状态可观测:与可观测性层深度集成,自动记录执行过程中的模型调用、工具调用、异常信息,便于问题排查与性能优化。
(2)核心功能:支撑智能体的 “完整执行生命周期”
Runner 模块提供两大核心执行方法:run()(同步执行)与run_streamed()(流式执行),覆盖不同业务场景的需求。
① run(agent, input, context=None, run_config=None):同步执行
run()方法是最基础的执行方式,适用于 “不需要实时反馈、等待全量结果” 的场景(如批量处理任务、生成完整报告)。其核心逻辑是:
- 初始化执行上下文:创建
RunContextWrapper,封装 Agent 配置、用户输入、会话信息、执行状态; - 调用模型生成决策:通过
ModelProvider调用指定模型,传入 Agent 指令、用户输入、历史上下文,模型返回 “是否调用工具”“调用哪个工具” 的决策; - 执行工具调用:若模型决策为调用工具,Runner 会自动校验工具参数(基于
@function_tool定义的类型约束),执行工具并获取结果; - 循环迭代:将工具结果回传给模型,模型基于新信息生成下一轮决策(如继续调用工具、生成最终回复),直到模型决定停止;
- 返回执行结果:将最终回复、工具调用记录、执行日志封装为
RunResult,返回给开发者。
代码示例:同步执行订单咨询智能体
import asyncio
from openai_agents import Runner
from my_agents import order_agent
async def sync_run_order_agent():
# 1. 定义用户输入与上下文
user_input = "查询我的订单123456的状态"
user_context = {"user_id": "U12345", "user_level": "普通用户"} # 自定义上下文
# 2. 同步执行智能体
result = await Runner.run(
agent=order_agent,
input=user_input,
context=user_context,
run_config=None # 执行配置(如超时时间、追踪开关),默认None
)
# 3. 解析执行结果
print("=== 最终回复 ===")
print(result.final_output) # 智能体的最终回复(口语化内容)
print("\n=== 工具调用记录 ===")
for tool_call in result.tool_calls:
print(f"工具名:{tool_call.tool_name}")
print(f"参数:{tool_call.arguments}")
print(f"结果:{tool_call.result}")
print("\n=== 执行日志 ===")
print(f"执行耗时:{result.duration}秒")
print(f"模型调用次数:{len(result.model_calls)}")
# 运行同步执行
asyncio.run(sync_run_order_agent())
3. Tool 模块:智能体的 “能力扩展接口”
Tool 模块是智能体与外部世界交互的 “桥梁”—— 通过 Tool,智能体可调用 API、操作数据库、执行脚本、对接第三方服务等。SDK 的 Tool 模块以 “函数即工具” 为核心设计理念,彻底简化了工具开发流程,同时通过类型安全、文档驱动、MCP 协议集成等特性,确保工具的可靠性与扩展性。
(1)模块设计理念:让工具开发 “零门槛、高可靠、可扩展”
Tool 模块的设计围绕三个核心目标展开,解决传统工具开发的痛点:
- 零门槛开发:开发者无需学习复杂的工具类接口,只需编写普通 Python 函数,通过
@function_tool装饰器即可将其转为智能体可调用的工具,大幅降低开发成本; - 高可靠性保障:通过类型提示解析、参数校验、异常处理等机制,确保工具调用的正确性(如 “不允许传入负数本金”“必填参数不能为空”),避免无效调用;
- 可扩展性支撑:支持函数工具、智能体工具(通过
as_tool()转换)、MCP 工具(对接外部服务)三种类型,覆盖从简单函数到复杂服务的所有场景,同时支持工具的动态注册与卸载。
(2)核心功能:从 “普通函数” 到 “智能体工具” 的转化
Tool 模块的核心是@function_tool装饰器 —— 它自动完成 “函数解析→元数据生成→类型校验→文档提取” 的全流程,让普通 Python 函数具备 “智能体可识别、可调用” 的能力。
① @function_tool装饰器:工具开发的 “万能转换器”
@function_tool装饰器的工作流程可分为四步,开发者无需手动干预:
- 函数签名解析:解析函数的参数名、参数类型、返回类型,生成工具的 “参数结构元数据”(如 “本金:float 类型,必填”“利率:float 类型,默认 4.5”);
- 文档字符串提取:支持 Google、NumPy、Sphinx 三种注释风格,自动提取参数说明、返回说明、示例、异常信息,生成工具的 “描述元数据”(如 “计算贷款月供金额,使用等额本息还款方式”);
- 类型校验规则生成:基于函数的类型提示(如
Literal["1d", "1w"]、Pydantic模型),生成参数校验规则,确保调用时参数类型正确、范围合法; - 工具对象封装:将上述元数据与函数逻辑封装为
Tool对象,智能体可通过元数据理解 “工具的用途、参数要求、返回格式”,通过封装逻辑执行工具。
代码示例:用@function_tool开发 “贷款月供计算工具”
from openai_agents import function_tool
from pydantic import BaseModel
from typing import Literal
# 1. 定义还款方式的枚举(限制参数取值范围)
RepaymentType = Literal["equal_principal_interest", "equal_principal"] # 等额本息/等额本金
# 2. 定义工具函数(普通Python函数)
@function_tool
def calculate_loan_monthly_payment(
principal: float,
annual_interest_rate: float = 4.5,
loan_term_years: int = 20,
repayment_type: RepaymentType = "equal_principal_interest"
) -> float:
"""
计算贷款的每月还款金额,支持等额本息与等额本金两种方式
参数:
principal: 贷款本金(元),必须大于0,最大支持10000000
annual_interest_rate: 年利率(百分比),默认4.5(即4.5%),范围1.0~24.0
loan_term_years: 贷款期限(年),默认20年,范围1~30
repayment_type: 还款方式,默认等额本息(equal_principal_interest),可选等额本金(equal_principal)
返回:
float: 每月还款金额(元),保留两位小数
示例:
>>> calculate_loan_monthly_payment(1000000, 4.5, 20)
5615.31 # 等额本息方式下的月供
>>> calculate_loan_monthly_payment(1000000, 4.5, 20, "equal_principal")
6805.56 # 等额本金方式下的首月月供
异常:
ValueError: 本金≤0、年利率超出1.0~24.0范围、期限超出1~30年时抛出
"""
# 1. 参数校验(装饰器会自动生成校验逻辑,但可补充业务校验)
if principal <= 0 or principal > 10000000:
raise ValueError(f"本金{principal}元无效,需满足0 < 本金 ≤ 10000000")
if not (1.0 <= annual_interest_rate <= 24.0):
raise ValueError(f"年利率{annual_interest_rate}%无效,需在1.0~24.0之间")
if not (1 <= loan_term_years <= 30):
raise ValueError(f"期限{loan_term_years}年无效,需在1~30之间")
# 2. 计算逻辑(等额本息/等额本金)
monthly_rate = annual_interest_rate / 100 / 12 # 月利率
total_months = loan_term_years * 12 # 总月数
if repayment_type == "equal_principal_interest":
# 等额本息:月供 = 本金×月利率×(1+月利率)^总月数 / [(1+月利率)^总月数 - 1]
monthly_payment = principal * (monthly_rate * (1 + monthly_rate)**total_months) / \
((1 + monthly_rate)**total_months - 1)
else:
# 等额本金:首月月供 = (本金/总月数) + 本金×月利率
monthly_principal = principal / total_months
first_month_interest = principal * monthly_rate
monthly_payment = monthly_principal + first_month_interest
# 3. 返回结果(保留两位小数)
return round(monthly_payment, 2)
装饰器的核心价值:
- 无需手动编写工具配置:装饰器自动生成工具名称(默认函数名
calculate_loan_monthly_payment)、参数结构(如 “principal为 float 类型,必填”)、描述(从文档字符串提取); - 自动参数校验:调用时若传入
principal=-100000,装饰器会自动抛出ValueError,无需开发者手动校验; - IDE 智能提示:开发者在编写智能体调用代码时,IDE 会基于装饰器解析的类型提示,提供参数补全与错误提示(如 “
repayment_type只能选equal_principal_interest或equal_principal”)。
② 复杂类型支持:应对结构化、多维度的参数需求
Tool 模块不仅支持基础类型(str、int、float),还支持复杂类型,满足实际业务中结构化、多维度的参数需求:
- 泛型类型:如
List[str](字符串列表)、Dict[str, float](键为字符串、值为浮点数的字典),适用于 “传入多个商品 ID”“传入多组键值对参数” 的场景; - Pydantic 模型:适用于参数结构复杂的场景(如 “用户信息包含姓名、年龄、地址”),通过 Pydantic 模型可定义多层级、多约束的参数结构;
- 枚举类型(Literal):限制参数的取值范围(如 “还款方式只能是等额本息或等额本金”),避免无效值传入;
- 可变参数(*args/*kwargs):支持传入不定数量的参数(如 “传入多个文件路径”),适用于参数数量不固定的场景。
代码示例:用 Pydantic 模型定义复杂参数工具
from openai_agents import function_tool
from pydantic import BaseModel, Field
from typing import List, Optional
# 1. 定义地址的Pydantic模型(嵌套结构)
class Address(BaseModel):
province: str = Field(description="省份,如"广东省"")
city: str = Field(description="城市,如"深圳市"")
detail: str = Field(description="详细地址,如"科技园路1号"")
# 2. 定义用户信息的Pydantic模型(包含嵌套模型)
class UserInfo(BaseModel):
name: str = Field(description="用户姓名,如"张三"")
age: int = Field(ge=18, le=120, description="用户年龄,18~120岁")
phone: str = Field(pattern=r"^1[3-9]\d{9}$", description="手机号,11位数字")
addresses: List[Address] = Field(description="用户地址列表,至少1个")
email: Optional[str] = Field(description="邮箱,可选,如"zhangsan@example.com"")
# 3. 开发"用户信息校验工具"(参数为Pydantic模型)
@function_tool
def validate_user_info(user_info: UserInfo) -> dict:
"""
校验用户信息的合法性,返回校验结果与格式化信息
参数:
user_info: 用户信息结构化数据,包含姓名、年龄、手机号、地址列表等
返回:
dict: 包含"is_valid"(是否合法)、"message"(提示信息)、"formatted_info"(格式化信息)的字典
"""
# 1. Pydantic模型会自动校验参数(如手机号格式、年龄范围),此处只需处理业务逻辑
formatted_addresses = [
f"{addr.province}{addr.city}{addr.detail}"
for addr in user_info.addresses
]
# 2. 生成格式化信息
formatted_info = {
"姓名": user_info.name,
"年龄": f"{user_info.age}岁",
"手机号": user_info.phone,
"地址列表": formatted_addresses,
"邮箱": user_info.email if user_info.email else "未填写"
}
# 3. 返回结果
return {
"is_valid": True,
"message": "用户信息校验通过",
"formatted_info": formatted_info
}
复杂类型的优势:
- 结构化参数更清晰:避免 “参数过多导致调用混乱” 的问题(如 “用户信息” 无需拆分为
name、age、phone等多个参数,只需传入一个user_info对象); - 校验更全面:Pydantic 模型支持字段级、模型级的校验规则(如 “手机号格式”“年龄范围”“地址列表至少 1 个”),装饰器会自动执行校验;
- 可复用性更高:Pydantic 模型可在多个工具中复用(如 “用户信息模型” 可同时用于 “校验工具”“存储工具”“查询工具”),避免重复定义。
③ 工具类型扩展:从 “函数工具” 到 “智能体工具”“MCP 工具”
Tool 模块支持三种工具类型,覆盖不同复杂度的场景,满足从简单到复杂的能力扩展需求:
| 工具类型 | 核心特点 | 适用场景 | 示例 |
|---|---|---|---|
| 函数工具 | 基于普通 Python 函数,通过@function_tool生成 | 简单逻辑(如计算、格式转换、本地 API 调用) | 贷款月供计算、日期格式化、本地数据库查询 |
| 智能体工具 | 通过Agent.as_tool()转换,本质是智能体 | 复杂逻辑(需多步骤处理、多工具协作) | 财务分析(需调用数据查询、指标计算工具) |
| MCP 工具 | 基于 MCP 协议,对接外部服务 | 外部系统集成(第三方 API、企业内部服务) | 物流查询(对接顺丰 API)、支付处理(对接支付宝) |
智能体工具示例(复用前文 “财务分析智能体”):
# 1. 定义财务分析智能体(需多工具协作)
financial_agent = Agent(
name="financial_analyst",
instructions="""你是财务分析专家,负责计算股票投资收益:
1. 调用stock_data_tool获取股票买入/卖出价格;
2. 调用fee_calc_tool计算交易手续费;
3. 计算净收益(卖出金额 - 买入金额 - 手续费);
4. 返回包含"股票代码、买入价格、卖出价格、手续费、净收益"的结构化结果。""",
tools=[stock_data_tool, fee_calc_tool]
)
# 2. 将智能体转为工具(供其他智能体调用)
financial_tool = financial_agent.as_tool(
tool_name="stock_profit_calculator",
tool_description="计算股票投资净收益,输入需包含股票代码、买入数量、买入日期、卖出日期"
)
# 3. 定义"投资顾问智能体"(使用财务工具)
investment_agent = Agent(
name="investment_advisor",
instructions="""你是投资顾问,负责为用户提供股票投资建议:
1. 若用户询问投资收益,调用stock_profit_calculator工具计算净收益;
2. 根据收益情况,提供"继续持有""卖出""加仓"的建议;
3. 建议需包含数据支撑(如"净收益1200元,建议继续持有")。""",
tools=[financial_tool]
)
MCP 工具示例(对接外部物流 API):
from openai_agents.mcp import MCPServerStdio
# 1. 初始化MCP服务器(对接顺丰物流API的外部服务)
sf_mcp_server = MCPServerStdio(
command=["python", "sf_logistics_mcp_server.py"], # MCP服务启动命令
tool_filter=lambda tools: [t for t in tools if t.name == "sf_logistics_query"], # 只加载物流查询工具
cache_tools=True # 缓存工具列表,避免重复请求
)
# 2. 获取MCP工具(物流查询工具)
sf_logistics_tool = await sf_mcp_server.get_tool("sf_logistics_query")
# 3. 定义"物流咨询智能体"(使用MCP工具)
logistics_agent = Agent(
name="logistics_consultant",
instructions="""你是物流咨询专家,负责查询顺丰物流进度:
1. 用户提供物流单号后,调用sf_logistics_query工具获取物流信息;
2. 输出时需分点说明"当前状态、更新时间、所在位置、预计送达时间";
3. 若物流信息未更新,告知用户"当前物流暂未更新,请1小时后查询"。""",
tools=[sf_logistics_tool]
)
四、支撑模块篇:MultiProvider、Tracing 与 Session
在 OpenAI Agents SDK 的技术体系中,除了 Agent、Runner、Tool、Handoff 四大核心模块,MultiProvider(多模型抽象)、Tracing(可观测性)、Session(会话管理)三大支撑模块同样至关重要。它们分别解决了 “多模型统一调用”“生产级可观测”“对话状态保持” 三大企业级需求,是智能体系统从 “开发环境” 走向 “生产环境” 的关键保障。
1. MultiProvider:多模型统一调用的 “翻译官”
在实际业务中,开发者往往需要根据成本、性能、场景需求灵活切换 AI 模型(如 OpenAI、Claude、国产大模型),或在单一系统中使用多模型协作。MultiProvider 模块通过 “抽象接口 + 实现分离” 的设计,构建了多模型统一调用的层,解决了 “模型绑定”“接口不兼容”“切换成本高” 的痛点,让智能体系统具备 “模型无关性”。
(1)模块设计理念:解耦智能体与具体模型
传统智能体开发中,模型调用与业务逻辑强耦合(如直接使用 openai.ChatCompletion.create),导致切换模型时需修改大量代码。MultiProvider 的设计理念是 “定义统一模型接口,适配不同模型实现”,核心目标包括:
- 接口一致性:无论使用 OpenAI 还是 LiteLLM,调用方式完全一致,开发者无需学习多套 API;
- 模型可插拔:新增或替换模型时,无需修改业务代码,只需配置新的模型提供商;
- 降级与容错:支持默认回退机制(如 OpenAI 不可用时自动切换到 LiteLLM),保障系统可用性;
- 前缀路由:通过 “模型前缀”(如
openai/gpt-4、litellm/claude-3)精准路由到目标模型,实现多模型共存。
(2)核心功能:从 “单一模型” 到 “多模型协作”
MultiProvider 模块的核心是 ModelProvider 抽象接口与 MultiProvider 实现类,前者定义 “模型调用的契约”,后者实现 “多模型的统一管理与路由”。
① ModelProvider 抽象接口:统一模型调用标准
ModelProvider 是所有模型提供商的 “通用契约”,它定义了模型调用的核心方法,确保不同模型实现具备一致的接口。根据文档内容,其核心设计如下:
from abc import ABC, abstractmethod
from openai_agents.models import Model, ModelSettings
class ModelProvider(ABC):
"""模型提供商抽象接口:定义统一的模型调用标准"""
@abstractmethod
def get_model(self, model_name: str | None) -> Model:
"""
获取指定名称的模型实例
参数:
model_name: 模型名称(如 "gpt-4"、"claude-3-opus-20240229"),None 表示使用默认模型
返回:
Model: 模型实例,包含模型调用、参数校验等核心能力
"""
pass
@abstractmethod
async def generate(self, model: Model, prompt: str, settings: ModelSettings) -> str:
"""
调用模型生成文本
参数:
model: 模型实例(由 get_model 获取)
prompt: 模型输入提示词
settings: 模型参数(温度、最大token数等)
返回:
str: 模型生成的文本结果
"""
pass
接口设计的核心价值:
- 强制不同模型提供商遵循统一标准,避免 “各有一套 API” 的混乱;
- 开发者只需面向接口编程,无需关心底层模型的具体实现;
- 便于测试(可通过 Mock 实现快速测试业务逻辑)。
② MultiProvider 实现类:多模型的 “路由中枢”
MultiProvider 是 ModelProvider 接口的核心实现,它管理多个模型提供商,通过 “前缀路由” 实现模型的精准调用。根据文档内容,其核心逻辑如下:
from typing import Dict, Optional
from openai_agents.models import MultiProviderMap, OpenAIProvider, LiteLLMProvider
class MultiProvider(ModelProvider):
"""多模型提供商:实现多模型的统一管理与路由"""
def __init__(
self,
provider_map: Optional[MultiProviderMap] = None,
# OpenAI 默认配置(便于快速初始化)
openai_api_key: Optional[str] = None,
openai_base_url: Optional[str] = None,
# 其他模型默认配置...
):
# 1. 初始化默认模型提供商(OpenAI 为默认)
self.openai_provider = OpenAIProvider(
api_key=openai_api_key,
base_url=openai_base_url
)
self.litellm_provider = LiteLLMProvider() # 初始化 LiteLLM 提供商
# 2. 加载自定义提供商映射(支持第三方模型)
self.provider_map = provider_map or MultiProviderMap()
# 注册默认提供商:前缀 "openai/" 对应 OpenAIProvider,"litellm/" 对应 LiteLLMProvider
self.provider_map.register("openai", self.openai_provider)
self.provider_map.register("litellm", self.litellm_provider)
# 3. 回退提供商:当指定提供商不存在时,默认使用 OpenAIProvider
self.fallback_provider = self.openai_provider
def _get_prefix_and_model_name(self, model_name: Optional[str]) -> tuple[Optional[str], Optional[str]]:
"""
解析模型名称中的前缀与实际模型名
规则:
- 若模型名包含 "/"(如 "openai/gpt-4"),前缀为 "/" 前的部分,实际模型名为 "/" 后的部分
- 若不包含 "/"(如 "gpt-4"),前缀为 None,实际模型名为原模型名
"""
if model_name is None:
return None, None
if "/" in model_name:
prefix, actual_name = model_name.split("/", 1)
return prefix.strip(), actual_name.strip()
return None, model_name.strip()
def get_model(self, model_name: Optional[str]) -> Model:
"""
路由逻辑:根据模型名前缀选择对应提供商,获取模型实例
"""
# 1. 解析前缀与实际模型名
prefix, actual_model_name = self._get_prefix_and_model_name(model_name)
# 2. 根据前缀选择提供商
if prefix and self.provider_map.has_provider(prefix):
# 2.1 匹配到自定义前缀(如 "litellm/claude-3")
target_provider = self.provider_map.get_provider(prefix)
return target_provider.get_model(actual_model_name)
else:
# 2.2 无前缀或前缀未匹配,使用回退提供商(默认 OpenAI)
return self.fallback_provider.get_model(actual_model_name)
async def generate(self, model: Model, prompt: str, settings: ModelSettings) -> str:
"""
统一调用模型生成文本:委托给模型所属的提供商执行
"""
# 从模型实例中获取其所属的提供商(Model 类内置 provider 属性)
target_provider = model.provider
return await target_provider.generate(model, prompt, settings)
核心逻辑解析:
- 前缀路由:通过模型名中的 “/” 分割前缀与实际模型名(如
litellm/claude-3对应 LiteLLM 提供商的 Claude-3 模型),实现精准路由; - 默认回退:当模型名无前缀(如
gpt-3.5-turbo)或前缀未注册时,自动使用 OpenAI 作为回退,避免调用失败; - 多提供商管理:支持通过
provider_map注册第三方模型提供商(如国产大模型的自定义实现),扩展性极强。
③ 实战场景:多模型的灵活切换与协作
MultiProvider 的价值在实际业务中体现得淋漓尽致,以下是两个典型场景:
场景 1:成本优化 —— 不同场景使用不同模型
电商客服系统中,简单问题(如 “订单查询”)用低成本的 gpt-3.5-turbo,复杂问题(如 “售后纠纷处理”)用高精度的 gpt-4,通过 MultiProvider 实现自动切换:
from openai_agents import Agent, Runner, ModelSettings
from openai_agents.models import MultiProvider
import asyncio
# 1. 初始化 MultiProvider(配置 OpenAI API 密钥)
multi_provider = MultiProvider(openai_api_key="sk-your-key")
# 2. 定义客服智能体:根据问题复杂度选择模型
customer_service_agent = Agent(
name="ecommerce_customer_service",
instructions="""
1. 判断用户问题复杂度:
- 简单问题(订单查询、物流跟踪):使用 "openai/gpt-3.5-turbo"
- 复杂问题(售后纠纷、退款协商):使用 "openai/gpt-4"
2. 调用对应模型生成回复,确保语气亲切、信息准确
""",
# 模型设置:通过 MultiProvider 统一管理
model_settings=ModelSettings(
provider=multi_provider, # 指定多模型提供商
temperature=0.3 # 统一控制温度参数
)
)
# 3. 测试不同场景
async def test_multi_model():
# 场景1:简单问题(订单查询)
simple_query = "我的订单号 123456 发货了吗?"
simple_result = await Runner.run(customer_service_agent, simple_query)
print(f"简单问题回复:{simple_result.final_output}")
print(f"使用模型:{simple_result.model_calls[0].model_name}\n")
# 场景2:复杂问题(售后纠纷)
complex_query = "我收到的商品有破损,商家拒绝退款,该怎么办?"
complex_result = await Runner.run(customer_service_agent, complex_query)
print(f"复杂问题回复:{complex_result.final_output}")
print(f"使用模型:{complex_result.model_calls[0].model_name}")
asyncio.run(test_multi_model())
场景 2:多模型协作 —— 不同模型负责不同环节
金融分析系统中,用 litellm/claude-3 处理数据格式转换(擅长结构化数据),用 openai/gpt-4 生成分析报告(擅长自然语言表达),通过 MultiProvider 实现协作:
# 1. 定义数据转换智能体(使用 Claude-3)
data_convert_agent = Agent(
name="data_converter",
instructions="将金融原始数据转换为结构化表格格式",
model_settings=ModelSettings(
provider=multi_provider,
model_name="litellm/claude-3-opus-20240229" # 指定 LiteLLM 路由的 Claude-3
)
)
# 2. 定义报告生成智能体(使用 GPT-4)
report_agent = Agent(
name="report_generator",
instructions="基于结构化数据生成专业金融分析报告",
tools=[data_convert_agent.as_tool()], # 将数据转换智能体作为工具
model_settings=ModelSettings(
provider=multi_provider,
model_name="openai/gpt-4" # 指定 OpenAI 路由的 GPT-4
)
)
# 3. 执行多模型协作任务
async def run_financial_analysis():
raw_data = """
股票代码:AAPL,日期:2024-05-20,收盘价:190.5,成交量:5000万
股票代码:MSFT,日期:2024-05-20,收盘价:420.3,成交量:3000万
"""
result = await Runner.run(report_agent, f"分析以下数据:{raw_data}")
print("金融分析报告:")
print(result.final_output)
asyncio.run(run_financial_analysis())
(3)模块价值总结:让模型选择更灵活
MultiProvider 模块的核心价值在于:
- 解耦:分离业务逻辑与模型调用,避免 “绑定单一模型” 的风险;
- 降本:支持根据场景选择性价比更高的模型,降低整体成本;
- 容错:默认回退机制保障模型服务不可用时系统仍能运行;
- 扩展:支持第三方模型快速接入,适配不同业务需求(如国产大模型合规要求)。
2. Tracing 系统:生产级可观测的 “监控中枢”
在生产环境中,智能体系统的 “黑盒问题” 是一大痛点 —— 开发者难以知晓 “智能体为何未调用工具”“模型调用为何失败”“任务转移为何异常”。Tracing 系统通过 “分布式追踪” 技术,记录智能体执行的全链路信息,实现 “可监控、可排查、可优化”,是保障系统稳定运行的关键。
(1)模块设计理念:全链路追踪,问题可视化
Tracing 系统的设计理念源于 “分布式系统可观测性”,核心目标是:
- 全链路覆盖:记录从 “智能体初始化→模型调用→工具执行→任务转移→结果生成” 的每个环节,无遗漏;
- 结构化日志:以 “追踪单元(Span)” 为核心,结构化存储每个环节的关键信息(时间、参数、结果、错误);
- 低侵入性:无需修改业务代码,通过框架自动埋点实现追踪,降低开发成本;
- 可分析性:支持日志导出、检索、聚合,便于问题排查与性能优化。
根据文档内容,Tracing 系统的核心模型是 “Trace(追踪)” 与 “Span(跨度)”:
- Trace:一个完整的智能体执行流程(如 “用户查询订单→智能体调用工具→返回结果”),包含多个 Span;
- Span:Trace 中的一个具体环节(如 “模型调用”“工具执行”),包含 “父 SpanID”“开始时间”“结束时间”“元数据” 等信息,形成调用链路树。
c(2)核心功能:从 “黑盒” 到 “透明”
Tracing 系统的核心是 Span 类(追踪单元)与 BatchTraceProcessor 类(日志处理器),前者定义追踪数据结构,后者负责日志的批量处理与导出。
① Span 类:追踪单元的结构化定义
Span 是 Tracing 系统的最小单元,它封装了一个环节的所有关键信息。根据文档内容,其核心设计如下:
import time
from typing import Dict, Optional, Any
from uuid import uuid4
class Span:
"""追踪单元(Span):记录智能体执行中的一个具体环节"""
def __init__(
self,
name: str, # Span 名称(如 "model_call"、"tool_execution"、"handoff")
parent_span_id: Optional[str] = None, # 父 Span ID,构建调用链路
trace_id: Optional[str] = None, # 所属 Trace ID,关联同一执行流程
metadata: Optional[Dict[str, Any]] = None # 元数据(参数、结果、错误等)
):
self.span_id = str(uuid4()) # 生成唯一 Span ID
self.name = name
self.parent_span_id = parent_span_id
self.trace_id = trace_id or str(uuid4()) # 若未指定,生成新 Trace ID
self.metadata = metadata or {}
self.start_time = time.time() # 开始时间(时间戳)
self.end_time: Optional[float] = None # 结束时间(初始为 None)
self.status: str = "in_progress" # 状态:in_progress/completed/failed
def finish(self, status: str = "completed", end_time: Optional[float] = None):
"""
结束 Span,记录结束时间与状态
参数:
status: 状态(completed/failed)
end_time: 结束时间(默认当前时间)
"""
self.end_time = end_time or time.time()
self.status = status
def add_metadata(self, key: str, value: Any):
"""添加元数据(如模型参数、工具结果、错误信息)"""
self.metadata[key] = value
def to_dict(self) -> Dict[str, Any]:
"""转换为字典,便于序列化(如导出到日志系统)"""
return {
"trace_id": self.trace_id,
"span_id": self.span_id,
"parent_span_id": self.parent_span_id,
"name": self.name,
"start_time": self.start_time,
"end_time": self.end_time,
"status": self.status,
"metadata": self.metadata
}
Span 类的核心价值:
1.链路构建:通过 parent_span_id 与 trace_id 关联同一执行流程的所有环节,形成清晰的调用树(如“智能体执行”为根 Span,“模型调用”“工具执行”为子 Span);
2.信息完整:记录每个环节的时间、状态、元数据(如模型调用的 model_name、工具执行的 arguments 与 result),便于问题定位;
3.可序列化:通过 to\_dict() 方法转换为字典,支持导出到 JSON 日志、ELK 等系统,实现日志存储与分析。
② BatchTraceProcessor 类:日志的批量处理与导出
在高并发场景中,实时导出每条 Trace 日志会导致性能瓶颈。`BatchTraceProcessor` 通过“批量收集+定时导出”的机制,平衡可观测性与系统性能。根据文档内容,其核心设计如下:
import queue
import threading
import time
from typing import List, Optional, Callable
from openai_agents.tracing import Trace, Span, TracingExporter
class BatchTraceProcessor:
"""批量追踪处理器:批量收集并定时导出 Trace 日志"""
def __init__(
self,
exporter: TracingExporter, # 日志导出器(如导出到文件、ELK)
max_queue_size: int = 8192, # 队列最大容量
max_batch_size: int = 128, # 单次导出最大批量
schedule_delay: float = 5.0, # 定时导出间隔(秒)
export_trigger_ratio: float = 0.7 # 触发导出的队列占比阈值
):
self._exporter = exporter
# 线程安全队列:存储待导出的 Trace/Span
self._queue = queue.Queue(maxsize=max_queue_size)
self._max_batch_size = max_batch_size
self._schedule_delay = schedule_delay
# 触发导出的队列阈值(如队列容量8192,阈值0.7则5734条时触发)
self._export_trigger_size = max(1, int(max_queue_size * export_trigger_ratio))
# 后台线程控制
self._shutdown_event = threading.Event()
self._worker_thread: Optional[threading.Thread] = None
self._thread_start_lock = threading.Lock()
self._next_export_time = time.time() # 下次定时导出时间
def start(self):
"""启动后台处理线程"""
with self._thread_start_lock:
if not self._worker_thread or not self._worker_thread.is_alive():
self._worker_thread = threading.Thread(
target=self._run_worker,
daemon=True # 守护线程,主进程退出时自动结束
)
self._worker_thread.start()
def _run_worker(self):
"""后台工作线程:定时导出+阈值触发导出"""
while not self._shutdown_event.is_set():
current_time = time.time()
queue_size = self._queue.qsize()
# 触发条件:达到定时时间 或 队列超过阈值
if (current_time >= self._next_export_time) or (queue_size >= self._export_trigger_size):
self._export_batches(force=False)
self._next_export_time = current_time + self._schedule_delay # 更新下次定时时间
else:
# 未达条件,短暂休眠避免空轮询
time.sleep(0.2)
# 线程退出前,强制导出剩余日志
self._export_batches(force=True)
def _export_batches(self, force: bool):
"""批量导出日志:从队列中获取数据,调用导出器导出"""
batches: List[List[Trace | Span]] = []
current_batch: List[Trace | Span] = []
# 从队列中获取数据,组装成批量
while True:
try:
# 非强制模式下,队列空则退出;强制模式下,阻塞直到队列空
item = self._queue.get(block=force, timeout=1.0)
current_batch.append(item)
# 当批量达到最大容量,加入批次列表并重置当前批量
if len(current_batch) >= self._max_batch_size:
batches.append(current_batch)
current_batch = []
except queue.Empty:
break
# 加入最后一个未填满的批量
if current_batch:
batches.append(current_batch)
# 调用导出器批量导出
for batch in batches:
try:
self._exporter.export(batch)
# 标记队列任务完成(避免队列满时阻塞)
for _ in batch:
self._queue.task_done()
except Exception as e:
# 导出失败时记录日志,避免线程崩溃
print(f"Batch trace export failed: {str(e)}")
def add_item(self, item: Trace | Span):
"""添加 Trace/Span 到队列(线程安全)"""
try:
# 队列满时阻塞,直到有空间(或超时,避免死等)
self._queue.put(item, block=True, timeout=30.0)
except queue.Full:
# 队列满且超时,丢弃日志并记录警告(避免影响主业务)
print(f"Trace queue is full, dropping item: {item.name if hasattr(item, 'name') else 'unknown'}")
def shutdown(self, timeout: Optional[float] = None):
"""关闭处理器,等待后台线程退出"""
self._shutdown_event.set()
if self._worker_thread and self._worker_thread.is_alive():
self._worker_thread.join(timeout)
# 强制导出剩余日志
self._export_batches(force=True)
核心机制解析:
- 双触发模式:结合 “定时导出”(如每 5 秒)与 “阈值触发”(如队列满 70%),既保证日志实时性,又避免高频导出导致的性能损耗;
- 线程安全:使用
queue.Queue实现线程安全的日志收集,支持多线程同时添加日志; - 优雅关闭:通过
shutdown()方法,在系统退出前强制导出剩余日志,避免数据丢失; - 容错设计:导出失败时仅记录警告,不崩溃后台线程,保障系统稳定性。
③ 实战场景:Tracing 日志的接入与分析
Tracing 系统的价值需结合实际日志分析场景体现,以下是电商客服系统中通过 Tracing 排查问题的示例:
from openai_agents import Agent, Runner
from openai_agents.tracing import (
Span, BatchTraceProcessor, FileTracingExporter # 文件导出器:将日志写入文件
)
import asyncio
# 1. 初始化 Tracing 组件
# 日志导出器:将日志写入 "agent_tracing.log"
file_exporter = FileTracingExporter(file_path="agent_tracing.log")
# 批量处理器:每5秒导出,队列阈值70%
trace_processor = BatchTraceProcessor(
exporter=file_exporter,
max_queue_size=1000,
schedule_delay=5.0
)
trace_processor.start() # 启动后台线程
# 2. 定义客服智能体(模拟工具调用失败场景)
@function_tool
def query_order(order_id: str) -> dict:
"""查询订单工具(模拟偶发API失败)"""
import random
# 模拟30%概率API调用失败
if random.random() < 0.3:
raise ValueError(f"订单API调用失败:订单号 {order_id} 暂时无法查询")
return {
"order_id": order_id,
"status": "已发货",
"logistics_no": "SF123456789"
}
customer_service_agent = Agent(
name="ecommerce_customer_service",
instructions="调用query_order工具查询订单状态,返回给用户",
tools=[query_order]
)
# 3. 执行智能体,并手动埋点记录 Trace(框架会自动埋点核心环节,此处补充业务埋点)
async def run_with_tracing():
# 手动创建根 Span(智能体执行流程)
root_span = Span(name="customer_service_execution")
try:
user_input = "查询订单 123456 的状态"
# 补充业务元数据(如用户ID)
root_span.add_metadata("user_id", "U789012")
root_span.add_metadata("user_input", user_input)
# 执行智能体
result = await Runner.run(customer_service_agent, user_input)
# 记录执行结果到 Span
root_span.add_metadata("execution_result", result.final_output)
root_span.add_metadata("tool_call_count", len(result.tool_calls))
root_span.finish(status="completed") # 标记成功结束
except Exception as e:
# 捕获异常,标记 Span 为失败状态
root_span.add_metadata("error", str(e))
root_span.finish(status="failed")
raise e
finally:
# 将 Span 加入批量处理器,等待导出
trace_processor.add_item(root_span)
# 4. 执行并查看日志
asyncio.run(run_with_tracing())
# 关闭处理器,确保日志导出
trace_processor.shutdown()
日志文件(agent_tracing.log)示例:
{
"trace_id": "a1b2c3d4-e5f6-7890-abcd-1234567890ab",
"span_id": "b3c4d5e6-f7g8-9012-bcde-234567890abc",
"parent_span_id": null,
"name": "customer_service_execution",
"status": "completed",
"start_time": 1716000000.1234,
"end_time": 1716000002.5678,
"duration": 2.4444,
"metadata": {
"user_id": "U789012",
"user_input": "查询订单 123456 的状态",
"execution_result": "您的订单 123456 已发货,物流单号:SF123456789",
"tool_call_count": 1
}
}
通过日志分析,开发者可快速定位问题:
- 若
status为failed,查看metadata.error了解失败原因(如 “订单 API 调用失败”); - 若
duration过长,检查metadata.tool_call_count与工具执行耗时,优化性能瓶颈; - 若智能体未调用工具,查看模型调用 Span 的
metadata.prompt与metadata.response,调整指令。
(3)模块价值总结:让生产环境 “可观测”
Tracing 系统的核心价值在于:
- 问题排查:全链路日志记录,快速定位 “模型调用失败”“工具参数错误”“任务转移异常” 等问题;
- 性能优化:分析各环节耗时(如模型调用耗时、工具执行耗时),识别并优化瓶颈;
- 合规审计:记录用户交互、工具调用等关键操作,满足合规要求(如金融、医疗领域);
- 趋势分析:聚合日志数据,分析智能体执行成功率、工具调用频率等指标,持续优化系统。
3. Session 系统:智能体的 “记忆中枢”
在对话场景中,智能体需要 “记住” 用户的历史交互(如用户之前提供的订单号、偏好设置),避免重复询问。Session 系统通过 “会话存储” 技术,管理对话历史与状态,解决 “上下文丢失” 的痛点,是提升用户体验的关键。
(1)模块设计理念:让智能体 “记住” 上下文
Session 系统的设计理念是 “持久化对话状态,保持交互连贯性”,核心目标包括:
- 状态持久化:存储用户与智能体的历史对话(如用户输入、智能体回复、工具调用结果),支持跨请求复用;
- 接口统一:定义标准化的会话操作接口(如 “获取历史”“添加记录”“清空会话”),适配不同存储介质(如 SQLite、Redis);
- 轻量灵活:支持本地存储(开发环境)与分布式存储(生产环境),兼顾易用性与扩展性;
- 隐私保护:支持会话过期、敏感信息脱敏,保障用户数据安全。
根据文档内容,Session 系统的核心是 Session 抽象协议与具体实现(如 SqliteSession、RedisSession),前者定义接口,后者适配不同存储介质。
(2)核心功能:从 “无记忆” 到 “有记忆”
Session 系统的核心是 Session 协议与具体实现类,前者定义会话操作标准,后者实现数据存储与读取。
① Session 抽象协议:统一会话操作接口
Session 协议定义了会话管理的核心方法,确保不同存储实现具备一致的接口。根据文档内容,其核心设计如下:
from typing import List, Optional, Protocol, runtime_checkable
from openai_agents.types import TResponseInputItem # 对话项类型(用户输入/智能体回复/工具结果)
@runtime_checkable
class Session(Protocol):
"""会话协议:定义统一的会话操作接口"""
session_id: str # 会话唯一标识(如用户ID+设备ID)
async def get_items(self, limit: Optional[int] = None) -> List[TResponseInputItem]:
"""
获取会话历史记录
参数:
limit: 限制返回数量(None 表示返回所有)
返回:
按时间排序的对话项列表(最早的在前)
"""
...
async def add_items(self, items: List[TResponseInputItem]) -> None:
"""
添加对话项到会话
参数:
items: 待添加的对话项列表(如用户输入、智能体回复)
"""
...
async def pop_item(self) -> Optional[TResponseInputItem]:
"""
移除并返回最近一条对话项(用于撤销操作)
返回:
最近一条对话项,会话为空则返回 None
"""
...
async def clear_session(self) -> None:
"""清空会话历史"""
...
协议设计的核心价值:
- 接口一致性:无论使用 SQLite 还是 Redis,调用方式完全一致(如
await session.get_items()),开发者无需修改业务代码; - 可替换性:开发环境用轻量的
SqliteSession,生产环境无缝切换为分布式的RedisSession; - 可测试性:通过 Mock 实现
Session协议,快速测试会话相关逻辑,无需依赖真实存储。
② SqliteSession:轻量级本地会话实现
SqliteSession 基于 SQLite 数据库实现会话存储,适用于开发环境、单机部署场景。根据文档内容,其核心设计如下:
import sqlite3
import json
from datetime import datetime
from typing import List, Optional, AsyncContextManager
import aiosqlite # 异步 SQLite 库(避免阻塞主线程)
from openai_agents.session import Session
from openai_agents.types import TResponseInputItem
class SqliteSession(Session):
"""基于 SQLite 的会话实现:轻量级本地存储"""
def __init__(self, session_id: str, db_path: Optional[str] = None):
self.session_id = session_id
# 数据库路径:默认内存数据库(开发环境),生产环境可指定文件路径(如 "agent_sessions.db")
self.db_path = db_path or ":memory:"
# 延迟初始化数据库连接(避免提前创建连接)
self._conn: Optional[aiosqlite.Connection] = None
async def _get_connection(self) -> AsyncContextManager[aiosqlite.Connection]:
"""获取数据库连接(异步上下文管理器,自动关闭连接)"""
if not self._conn or self._conn.closed:
# 初始化数据库连接(异步)
self._conn = await aiosqlite.connect(
self.db_path,
check_same_thread=False # 允许跨线程使用(异步场景安全)
)
# 创建会话表(若不存在):存储会话ID、对话项数据、创建时间
await self._conn.execute("""
CREATE TABLE IF NOT EXISTS conversation_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL,
item_data TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
await self._conn.commit()
return self._conn
async def get_items(self, limit: Optional[int] = None) -> List[TResponseInputItem]:
"""获取会话历史记录"""
async with await self._get_connection() as conn:
query = "SELECT item_data FROM conversation_items WHERE session_id = ? ORDER BY created_at ASC"
if limit:
query += " LIMIT ?"
params = (self.session_id, limit)
else:
params = (self.session_id,)
cursor = await conn.execute(query, params)
rows = await cursor.fetchall()
return [json.loads(row[0]) for row in rows]
async def add_items(self, items: List[TResponseInputItem]) -> None:
"""添加对话项到会话"""
async with await self._get_connection() as conn:
for item in items:
await conn.execute(
"INSERT INTO conversation_items (session_id, item_data) VALUES (?, ?)",
(self.session_id, json.dumps(item))
)
await conn.commit()
async def pop_item(self) -> Optional[TResponseInputItem]:
"""移除并返回最近一条对话项"""
async with await self._get_connection() as conn:
# 获取最近一条记录
cursor = await conn.execute(
"SELECT id, item_data FROM conversation_items WHERE session_id = ? ORDER BY created_at DESC LIMIT 1",
(self.session_id,)
)
row = await cursor.fetchone()
if not row:
return None
# 删除该记录
await conn.execute("DELETE FROM conversation_items WHERE id = ?", (row[0],))
await conn.commit()
return json.loads(row[1])
async def clear_session(self) -> None:
"""清空会话历史"""
async with await self._get_connection() as conn:
await conn.execute("DELETE FROM conversation_items WHERE session_id = ?", (self.session_id,))
await conn.commit()
这三大模块与 Agent、Runner、Tool、Handoff 四大核心模块协同,构成了 OpenAI Agents SDK 完整的技术体系 —— 核心模块负责 “智能体如何决策与协作”,支撑模块负责 “系统如何稳定运行与扩展”,共同实现从 “开发原型” 到 “生产系统” 的跨越。
五、总结
OpenAI Agents SDK 以 “智能体自治” 为核心设计理念,通过 Agent、Runner、Tool、Handoff 四大核心模块实现智能体的 “定义 - 执行 - 协作”,通过 MultiProvider、Tracing、Session 三大支撑模块保障系统的 “灵活 - 可观测 - 可靠”,构建了一套从开发到生产的完整智能体开发体系。
它的革命性不仅在于技术层面的创新(如智能体嵌套、多模型统一调用),更在于思维层面的转变 —— 从 “开发者预定义工作流” 到 “智能体自主决策”,让智能体真正成为 “具备专业能力、可协作的智能单元”。
对于开发者而言,掌握 OpenAI Agents SDK 不仅能提升智能体系统的开发效率,更能理解 “AI 原生应用” 的设计思维。未来,随着 AI 技术的持续发展,这种 “自治、协作、可扩展” 的智能体系统,将成为企业数字化转型、智能化升级的核心基础设施。
1万+






