【大模型应用开发系列(二)】OpenAI Agents SDK 深度解析:设计、架构、核心模块与技术实践

部署运行你感兴趣的模型镜像

开篇语:为什么 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 的核心属性决定了智能体的行为模式,以下是关键属性的详细解析:

属性名类型作用示例
namestr智能体唯一标识,用于日志记录、任务转移时的身份识别"order_consultant"(订单咨询智能体)
instructionsstr Callable智能体的核心指令,定义 “做什么”“怎么做”,支持静态字符串或动态函数生成
toolslist[Tool]智能体可调用的工具列表,工具需通过@function_tool装饰器或as_tool()生成[order_query_tool, logistics_track_tool](订单查询、物流跟踪工具)
handoffslist[Handoff]智能体可转移任务的目标列表,定义 “何时转移”“转移到哪个智能体”[handoff(refund_agent), handoff(product_agent)](转移到退款、商品智能体)
output_typetype\[Any]None智能体输出的类型约束,支持 Pydantic 模型,确保输出格式统一
model_settingsModelSettingsNone智能体使用的模型参数,如模型名称、温度、最大 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 技术的持续发展,这种 “自治、协作、可扩展” 的智能体系统,将成为企业数字化转型、智能化升级的核心基础设施。

您可能感兴趣的与本文相关的镜像

GPT-oss:20b

GPT-oss:20b

图文对话
Gpt-oss

GPT OSS 是OpenAI 推出的重量级开放模型,面向强推理、智能体任务以及多样化开发场景

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值