开篇语:为什么 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-4litellm/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 基于 Pythondataclass实现,默认属性不可变(需显式声明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,克隆出不同instructionsmodel_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()方法是最基础的执行方式,适用于 “不需要实时反馈、等待全量结果” 的场景(如批量处理任务、生成完整报告)。其核心逻辑是:

  1. 初始化执行上下文:创建RunContextWrapper,封装 Agent 配置、用户输入、会话信息、执行状态;
  2. 调用模型生成决策:通过ModelProvider调用指定模型,传入 Agent 指令、用户输入、历史上下文,模型返回 “是否调用工具”“调用哪个工具” 的决策;
  3. 执行工具调用:若模型决策为调用工具,Runner 会自动校验工具参数(基于@function_tool定义的类型约束),执行工具并获取结果;
  4. 循环迭代:将工具结果回传给模型,模型基于新信息生成下一轮决策(如继续调用工具、生成最终回复),直到模型决定停止;
  5. 返回执行结果:将最终回复、工具调用记录、执行日志封装为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装饰器的工作流程可分为四步,开发者无需手动干预:

  1. 函数签名解析:解析函数的参数名、参数类型、返回类型,生成工具的 “参数结构元数据”(如 “本金:float 类型,必填”“利率:float 类型,默认 4.5”);
  2. 文档字符串提取:支持 Google、NumPy、Sphinx 三种注释风格,自动提取参数说明、返回说明、示例、异常信息,生成工具的 “描述元数据”(如 “计算贷款月供金额,使用等额本息还款方式”);
  3. 类型校验规则生成:基于函数的类型提示(如Literal["1d", "1w"]Pydantic模型),生成参数校验规则,确保调用时参数类型正确、范围合法;
  4. 工具对象封装:将上述元数据与函数逻辑封装为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_interestequal_principal”)。
② 复杂类型支持:应对结构化、多维度的参数需求

Tool 模块不仅支持基础类型(strintfloat),还支持复杂类型,满足实际业务中结构化、多维度的参数需求:

  • 泛型类型:如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
    }

复杂类型的优势

  • 结构化参数更清晰:避免 “参数过多导致调用混乱” 的问题(如 “用户信息” 无需拆分为nameagephone等多个参数,只需传入一个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-4litellm/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 实现类:多模型的 “路由中枢”

MultiProviderModelProvider 接口的核心实现,它管理多个模型提供商,通过 “前缀路由” 实现模型的精准调用。根据文档内容,其核心逻辑如下:

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_idtrace_id 关联同一执行流程的所有环节,形成清晰的调用树(如“智能体执行”为根 Span,“模型调用”“工具执行”为子 Span);

2.信息完整:记录每个环节的时间、状态、元数据(如模型调用的 model_name、工具执行的 argumentsresult),便于问题定位;

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
  }
}

通过日志分析,开发者可快速定位问题:

  • statusfailed,查看 metadata.error 了解失败原因(如 “订单 API 调用失败”);
  • duration 过长,检查 metadata.tool_call_count 与工具执行耗时,优化性能瓶颈;
  • 若智能体未调用工具,查看模型调用 Span 的 metadata.promptmetadata.response,调整指令。

(3)模块价值总结:让生产环境 “可观测”

Tracing 系统的核心价值在于:

  • 问题排查:全链路日志记录,快速定位 “模型调用失败”“工具参数错误”“任务转移异常” 等问题;
  • 性能优化:分析各环节耗时(如模型调用耗时、工具执行耗时),识别并优化瓶颈;
  • 合规审计:记录用户交互、工具调用等关键操作,满足合规要求(如金融、医疗领域);
  • 趋势分析:聚合日志数据,分析智能体执行成功率、工具调用频率等指标,持续优化系统。

3. Session 系统:智能体的 “记忆中枢”

在对话场景中,智能体需要 “记住” 用户的历史交互(如用户之前提供的订单号、偏好设置),避免重复询问。Session 系统通过 “会话存储” 技术,管理对话历史与状态,解决 “上下文丢失” 的痛点,是提升用户体验的关键。

(1)模块设计理念:让智能体 “记住” 上下文

Session 系统的设计理念是 “持久化对话状态,保持交互连贯性”,核心目标包括:

  • 状态持久化:存储用户与智能体的历史对话(如用户输入、智能体回复、工具调用结果),支持跨请求复用;
  • 接口统一:定义标准化的会话操作接口(如 “获取历史”“添加记录”“清空会话”),适配不同存储介质(如 SQLite、Redis);
  • 轻量灵活:支持本地存储(开发环境)与分布式存储(生产环境),兼顾易用性与扩展性;
  • 隐私保护:支持会话过期、敏感信息脱敏,保障用户数据安全。

根据文档内容,Session 系统的核心是 Session 抽象协议与具体实现(如 SqliteSessionRedisSession),前者定义接口,后者适配不同存储介质。

(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 技术的持续发展,这种 “自治、协作、可扩展” 的智能体系统,将成为企业数字化转型、智能化升级的核心基础设施。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐