OpenAI Agents SDK in Python:深度解析openai-agents-python

文章目录

  • openai-agents-python
  • 一、关于 OpenAI Agents SDK
  • 核心概念:
  • 二、开始使用
  • 三、Hello world 示例
  • 四、Handoffs 示例
  • 五、函数示例
  • 六、代理循环
  • 最终输出
  • 七、常见代理模式
  • 八、追踪
  • 九、开发(仅当您需要编辑 SDK/示例时需要)
  • 致谢
  • Index
  • 一、关于 OpenAI Agents SDK
  • 为什么使用代理 SDK
  • 二、安装使用
  • 1、安装
  • 2、Hello world 示例
  • 快速入门
  • 一、创建项目和工作环境
  • 1、创建环境
  • 2、激活虚拟环境
  • 3、安装 Agents SDK
  • 4、设置 OpenAI API 密钥
  • 二、创建您的第一个智能体
  • 三、添加更多代理
  • 四、定义你的handoffs
  • 五、运行代理编排
  • 六、添加一个 guardrail
  • 七、把所有东西放在一起
  • 八、查看您的 traces
  • Agents
  • 一、关于 Agents
  • 二、基本配置
  • 三、Context
  • 四、输出类型
  • 五 、Handoffs
  • 六、动态指令
  • 七、生命周期事件(hooks)
  • 八、Guardrails
  • 九、克隆/复制代理
  • 运行代理
  • 一、运行代理
  • 二、代理循环( agent loop)
  • 四、流媒体
  • 五、运行配置
  • 六、对话/聊天线程
  • 七、异常
  • 工具
  • 一、关于 工具
  • 二、Hosted 工具
  • 三、函数工具
  • 自定义函数工具
  • 自动参数和文档字符串解析
  • 四、Agents as tools
  • 五、处理函数工具中的错误
  • Guardrails
  • 一、关于 Guardrails
  • 二、输入 guardrail
  • 三、输出 guardrail
  • 四、Tripwires
  • 五、实现一个 guardrail
  • 模型
  • 一、关于 模型
  • 二、混合和匹配模型
  • 使用其他 LLM 提供商
  • 配置 SDK
  • 一、API key 和 客户端
  • 二、跟踪
  • 三、调试日志
  • 日志中的敏感数据
  • 上下文管理
  • 一、关于上下文
  • 二、本地上下文
  • Agent/LLM 上下文
  • Handoffs
  • 一、关于 Handoffs
  • 二、创建Handoffs
  • 1、基本用法
  • 2、通过 `handoff()` 函数自定义Handoffs
  • 三、Handoffs 输入
  • 四、输入过滤器
  • 五、推荐提示
  • 多代理编排
  • 一、关于 编排
  • 二、通过 LLM 进行编排
  • 三、通过代码进行编排
  • Results
  • 一、关于 Results
  • 二、最终输出
  • 三、下一次回合的输入
  • 四、最后的代理
  • 五、新项目
  • 六、其他信息
  • 1、Guardrail 结果
  • 2、原始响应
  • 3、原始输入
  • Streaming
  • 一、关于 Streaming
  • 二、原始响应事件
  • 三、运行项目事件和代理事件
  • Tracing
  • 一、关于 Tracing
  • 二、Traces 和 spans
  • 三、默认跟踪
  • 四、高级跟踪
  • 五、创建追踪
  • 六、创建span
  • 七、敏感数据
  • 八、自定义跟踪处理器

  • openai-agents-python

    一、关于 OpenAI Agents SDK

    The OpenAI Agents SDK 是一个轻量级但功能强大的框架,用于构建多智能体工作流程。

  • 官网:https://openai.github.io/openai-agents-python/
  • github : https://github.com/openai/openai-agents-python

  • 核心概念:

    1. Agents: 配置有指令、工具、约束和Handoffs 的 LLMs
    2. Handoffs : 允许代理将控制权转移到其他代理以执行特定任务
    3. Guardrails: 可配置的输入和输出验证安全检查
    4. Tracing: 内置代理运行跟踪,允许您查看、调试和优化工作流程

    探索 示例 目录以查看 SDK 的实际应用,并阅读我们的 文档 获取更多详细信息。

    值得注意的是,我们的 SDK 与任何支持 OpenAI Chat Completions API 格式的模型提供商兼容。


    二、开始使用

    1、设置你的 Python 环境

    python -m venv env
    source env/bin/activate
    

    2、安装 Agents SDK

    pip install openai-agents
    

    三、Hello world 示例

    from agents import Agent, Runner
    
    agent = Agent(name="Assistant", instructions="You are a helpful assistant")
    
    result = Runner.run_sync(agent, "Write a haiku about recursion in programming.")
    print(result.final_output)
    
    # Code within the code,
    # Functions calling themselves,
    # Infinite loop's dance.
    

    (如果运行此代码,请确保您已设置 OPENAI_API_KEY 环境变量)


    四、Handoffs 示例

    from agents import Agent, Runner
    import asyncio
    
    spanish_agent = Agent(
        name="Spanish agent",
        instructions="You only speak Spanish.",
    )
    
    english_agent = Agent(
        name="English agent",
        instructions="You only speak English",
    )
    
    triage_agent = Agent(
        name="Triage agent",
        instructions="Handoff to the appropriate agent based on the language of the request.",
        handoffs=[spanish_agent, english_agent],
    )
    
    async def main():
        result = await Runner.run(triage_agent, input="Hola, ¿cómo estás?")
        print(result.final_output)
        # ¡Hola! Estoy bien, gracias por preguntar. ¿Y tú, cómo estás?
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    五、函数示例

    import asyncio
    
    from agents import Agent, Runner, function_tool
    
    @function_tool
    def get_weather(city: str) -> str:
        return f"The weather in {city} is sunny."
    
    agent = Agent(
        name="Hello world",
        instructions="You are a helpful agent.",
        tools=[get_weather],
    )
    
    async def main():
        result = await Runner.run(agent, input="What's the weather in Tokyo?")
        print(result.final_output)
        # The weather in Tokyo is sunny.
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    六、代理循环

    当你调用 Runner.run() 时,我们运行一个循环,直到我们得到最终输出。

    1. 我们调用 LLM,使用代理上的模型和设置以及消息历史。
    2. LLM 返回一个响应,其中可能包括工具调用。
    3. 如果响应有一个最终输出(下面将对此进行更多说明),我们返回它并结束循环。
    4. 如果响应包含Handoffs ,我们将代理设置为新的代理并返回步骤 1。
    5. 我们处理工具调用(如果有)并追加工具响应消息。然后我们回到步骤 1。

    有一个 max_turns 参数可用于 限制循环执行的次数。


    最终输出

    最终输出是代理在循环中产生的最后一件事。

    1. 如果你为代理设置了 output_type,最终的输出就是当 LLM 返回该类型的内容时。我们使用 结构化输出 来实现这一点。
    2. 如果没有 output_type(即纯文本响应),那么第一个 没有任何工具调用 或 移交的 LLM 响应 将被视为最终输出。

    因此,代理循环的心理模型是:

    1. 如果当前代理有 output_type,循环将一直运行,直到代理产生 与该类型匹配的 结构化输出。
    2. 如果当前代理没有 output_type,循环将一直运行,直到当前代理 产生一条 不包含任何工具调用/Handoffs 的消息。

    七、常见代理模式

    Agent SDK的设计非常灵活,允许您对广泛的LLM工作流建模,包括确定性流、迭代循环等。
    请参见 examples/agent_patterns 中的示例。


    八、追踪

    代理 SDK 自动追踪您的代理运行,使跟踪和调试代理的行为变得容易。设计上支持可扩展性,支持自定义跨度 以及广泛的第三方目标,包括 Logfire,AgentOps 和 Braintrust。
    有关如何自定义或禁用追踪的更多详细信息,请参阅 追踪。


    九、开发(仅当您需要编辑 SDK/示例时需要)

    0、确保你已经安装了 uv

    uv --version
    

    1、安装依赖

    make sync
    

    2、(After making changes) 代码风格检查/测试

    make tests  # run tests
    make mypy   # run typechecker
    make lint   # run linter
    

    致谢

    我们想感谢开源社区的优秀工作,特别是:

  • Pydantic (数据验证) 和 PydanticAI (高级代理框架)
  • MkDocs
  • Griffe
  • uv 和 ruff
  • 我们致力于继续构建 Agents SDK 作为开源框架,以便社区中的其他人 可以在此基础上 扩展我们的方法。


    Index


    一、关于 OpenAI Agents SDK

    OpenAI 代理 SDK 允许您在一个轻量级、易于使用的包中构建代理人工智能应用,具有非常少的抽象。它是我们之前针对代理的实验 Swarm 的生产就绪升级。代理 SDK 有一个非常小的原语集:

  • Agent,这些是配备了指令和工具的LLM
  • Handoffs,允许代理将特定任务委派给其他代理
  • Guardrails,它使代理的输入能够得到验证
  • 结合 Python,这些原语强大到 足以表达工具和代理之间复杂的关联,并允许你在没有陡峭的学习曲线的情况下 构建现实世界的应用程序。
    此外,该 SDK 随带内置的 跟踪 功能,让你可以可视化并调试你的代理流程,以及评估它们甚至微调模型以适应你的应用程序。


    为什么使用代理 SDK

    SDK 有两个驱动设计原则:

    1. 充足的功能使其值得一用,但基础元素又足够少,使其易于学习。
    2. 开箱即用效果极佳,但您可以自定义具体会发生什么。

    以下是 SDK 的主要功能:

  • 代理循环:内置的代理循环,用于调用工具、将结果发送到LLM,并循环直到LLM完成。
  • Python-first: 使用内置的语言特性来编排和链式调用代理,而不是需要学习新的抽象概念。
  • 手续移交:一个强大的功能,用于在多个代理之间进行协调和委派。
  • 防 guardrail :并行运行输入验证和检查,如果检查失败则提前中断。
  • 函数工具:将任何Python函数转换为工具,具有自动模式生成和Pydantic驱动的验证。
  • 跟踪:内置跟踪功能,让您可视化、调试和监控您的流程,以及使用 OpenAI 的评估、微调和蒸馏工具套件。

  • 二、安装使用

    1、安装

    pip install openai-agents
    

    2、Hello world 示例

    from agents import Agent, Runner
    
    agent = Agent(name="Assistant", instructions="You are a helpful assistant")
    
    result = Runner.run_sync(agent, "Write a haiku about recursion in programming.")
    print(result.final_output)
    
    # Code within the code,
    # Functions calling themselves,
    # Infinite loop's dance.
    

    (如果运行此程序,请确保设置 OPENAI_API_KEY 环境变量)

    export OPENAI_API_KEY=sk-...
    

    快速入门


    一、创建项目和工作环境

    1、创建环境

    您只需这样做一次。

    mkdir my_project
    cd my_project
    python -m venv .venv
    

    2、激活虚拟环境

    每次启动新的终端会话时都执行此操作。

    source .venv/bin/activate
    

    3、安装 Agents SDK

    pip install openai-agents # or `uv add openai-agents`, etc
    

    4、设置 OpenAI API 密钥

    如果您还没有,请按照这些说明创建一个 OpenAI API 密钥。

    export OPENAI_API_KEY=sk-...
    

    二、创建您的第一个智能体

    代理通过指令、一个名称和可选的配置(例如 model_config)来定义。

    from agents import Agent
    
    agent = Agent(
        name="Math Tutor",
        instructions="You provide help with math problems. Explain your reasoning at each step and include examples",
    )
    

    三、添加更多代理

    附加代理 可以以相同的方式 进行定义。handoff_descriptions 为确定转接路由 提供额外的上下文。

    from agents import Agent
    
    history_tutor_agent = Agent(
        name="History Tutor",
        handoff_description="Specialist agent for historical questions",
        instructions="You provide assistance with historical queries. Explain important events and context clearly.",
    )
    
    math_tutor_agent = Agent(
        name="Math Tutor",
        handoff_description="Specialist agent for math questions",
        instructions="You provide help with math problems. Explain your reasoning at each step and include examples",
    )
    

    四、定义你的handoffs

    在每一个代理上,您可以定义一个出站交班选项清单,代理可以从中选择以决定如何在其任务上取得进展。

    triage_agent = Agent(
        name="Triage Agent",
        instructions="You determine which agent to use based on the user's homework question",
        handoffs=[history_tutor_agent, math_tutor_agent]
    )
    

    五、运行代理编排

    让我们检查工作流程是否运行,以及分级代理是否正确地在两个专家代理之间进行路由。

    from agents import Runner
    
    async def main():
        result = await Runner.run(triage_agent, "What is the capital of France?")
        print(result.final_output)
    

    六、添加一个 guardrail

    您可以为输入或输出 定义自定义的Guardrail 来运行。

    from agents import GuardrailFunctionOutput, Agent, Runner
    from pydantic import BaseModel
    
    class HomeworkOutput(BaseModel):
        is_homework: bool
        reasoning: str
    
    guardrail_agent = Agent(
        name="Guardrail check",
        instructions="Check if the user is asking about homework.",
        output_type=HomeworkOutput,
    )
    
    async def homework_guardrail(ctx, agent, input_data):
        result = await Runner.run(guardrail_agent, input_data, context=ctx.context)
        final_output = result.final_output_as(HomeworkOutput)
        return GuardrailFunctionOutput(
            output_info=final_output,
            tripwire_triggered=not final_output.is_homework,
        )
    

    七、把所有东西放在一起

    让我们将所有内容整合起来,并运行整个工作流程,使用Handoffs 和输入 guardrail 。

    from agents import Agent, InputGuardrail, GuardrailFunctionOutput, Runner
    from pydantic import BaseModel
    import asyncio
    
    class HomeworkOutput(BaseModel):
        is_homework: bool
        reasoning: str
    
    guardrail_agent = Agent(
        name="Guardrail check",
        instructions="Check if the user is asking about homework.",
        output_type=HomeworkOutput,
    )
    
    math_tutor_agent = Agent(
        name="Math Tutor",
        handoff_description="Specialist agent for math questions",
        instructions="You provide help with math problems. Explain your reasoning at each step and include examples",
    )
    
    history_tutor_agent = Agent(
        name="History Tutor",
        handoff_description="Specialist agent for historical questions",
        instructions="You provide assistance with historical queries. Explain important events and context clearly.",
    )
    
    async def homework_guardrail(ctx, agent, input_data):
        result = await Runner.run(guardrail_agent, input_data, context=ctx.context)
        final_output = result.final_output_as(HomeworkOutput)
        
        return GuardrailFunctionOutput(
            output_info=final_output,
            tripwire_triggered=not final_output.is_homework,
        )
    
    triage_agent = Agent(
        name="Triage Agent",
        instructions="You determine which agent to use based on the user's homework question",
        handoffs=[history_tutor_agent, math_tutor_agent],
        input_guardrails=[
            InputGuardrail(guardrail_function=homework_guardrail),
        ],
    )
    
    async def main():
        result = await Runner.run(triage_agent, "who was the first president of the united states?")
        print(result.final_output)
    
        result = await Runner.run(triage_agent, "what is life")
        print(result.final_output)
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    八、查看您的 traces

    为了回顾你的代理运行期间发生的事情,请导航到OpenAI仪表板的跟踪查看器以查看你的代理运行的跟踪信息。


    Agents

    一、关于 Agents

    https://github.com/openai/openai-agents-python/blob/main/docs/agents.md

    代理是您应用的核心构建块。代理是一个大型语言模型(LLM),配置了指令和工具。


    二、基本配置

    您将配置的代理最常见的属性是:

  • instructions:也称为开发者消息或系统提示。
  • model: 要使用的 LLM,以及可选的 model_settings 来配置模型调整参数,如 temperaturetop_p 等。
  • tools: 代理可以使用的工具,以实现其任务。

  • from agents import Agent, ModelSettings, function_tool
    
    def get_weather(city: str) -> str:
        return f"The weather in {city} is sunny"
    
    agent = Agent(
        name="Haiku agent",
        instructions="Always respond in haiku form",
        model="o3-mini",
        tools=[function_tool(get_weather)],
    )
    

    三、Context

    代理在其 context 类型上是通用的。
    上下文是一个依赖注入工具:它是一个你创建并传递给 Runner.run() 的对象,该对象被传递给每个代理、工具、Handoffs 等,并作为依赖和状态的抓袋为代理运行提供服务。
    你可以提供任何 Python 对象作为上下文。

    @dataclass
    class UserContext:
      uid: str
      is_pro_user: bool
    
      async def fetch_purchases() -> list[Purchase]:
         return ...
    
    agent = Agent[UserContext](
        ...,
    )
    

    四、输出类型

    默认情况下,代理生成纯文本(即 str)输出。如果您希望代理生成特定类型的输出,可以使用 output_type 参数。

    一个常见的选择是使用 Pydantic 对象,但我们支持任何可以被 Pydantic TypeAdapter 包装的类型 – dataclasses, lists, TypedDict 等。

    from pydantic import BaseModel
    from agents import Agent
    
    class CalendarEvent(BaseModel):
        name: str
        date: str
        participants: list[str]
    
    agent = Agent(
        name="Calendar extractor",
        instructions="Extract calendar events from text",
        output_type=CalendarEvent,
    )
    

    !!! note : 当你传递一个 output_type 时,这告诉模型使用 结构化输出 而不是常规的纯文本响应。


    五 、Handoffs

    Handoffs 是代理可以委托的 子代理。

    您提供一份 Handoffs 列表,代理可以选择在相关情况下委托给它们。

    这是一个强大的模式,允许编排模块化、专业化的代理,它们擅长单一任务

    在 Handoffs 文档中了解更多信息。

    from agents import Agent
    
    booking_agent = Agent(...)
    refund_agent = Agent(...)
    
    triage_agent = Agent(
        name="Triage agent",
        instructions=(
            "Help the user with their questions."
            "If they ask about booking, handoff to the booking agent."
            "If they ask about refunds, handoff to the refund agent."
        ),
        handoffs=[booking_agent, refund_agent],
    )
    

    六、动态指令

    在大多数情况下,你可以在创建代理时提供指令。
    然而,你还可以通过一个函数提供动态指令。
    该函数将接收代理和上下文,并必须返回提示。接受常规和 async 函数。

    def dynamic_instructions(
        context: RunContextWrapper[UserContext], agent: Agent[UserContext]
    ) -> str:
        return f"The user's name is {context.context.name}. Help them with their questions."
    
    agent = Agent[UserContext](
        name="Triage agent",
        instructions=dynamic_instructions,
    )
    

    七、生命周期事件(hooks)

    有时,您想观察一个代理的生命周期。

    例如,您可能想在某些事件发生时记录事件或预取数据。

    您可以通过 hooks 属性将钩子连接到代理生命周期。

    继承 AgentHooks[agents.lifecycle.AgentHooks] 类 ,并覆盖您感兴趣的方法。


    八、Guardrails

    守卫(Guardrails)允许你 在代理运行的同时对用户输入进行检查/验证。

    例如,你可以筛选用户输入的相关性。更多内容请参阅 Guardrails文档。


    九、克隆/复制代理

    通过在代理上使用 clone() 方法,您可以复制一个代理,并且可以任意更改任何您喜欢的属性。

    pirate_agent = Agent(
        name="Pirate",
        instructions="Write like a pirate",
        model="o3-mini",
    )
    
    robot_agent = pirate_agent.clone(
        name="Robot",
        instructions="Write like a robot",
    )
    

    运行代理

    一、运行代理

    您可以通过 [Runner][agents.run.Runner] 类运行代理。您有以下3种选择:

    1、[Runner.run()][agents.run.Runner.run],它异步运行并返回一个[RunResult][agents.result.RunResult]。

    2、[Runner.run_sync()][agents.run.Runner.run_sync],这是一个同步方法,底层只是运行 .run()

    3、[Runner.run_streamed()][agents.run.Runner.run_streamed],它异步运行并返回一个 [RunResultStreaming][agents.result.RunResultStreaming] 。 它以流模式调用 LLM,并将接收到的那些事件以流的形式发送给您。


    from agents import Agent, Runner
    
    async def main():
        agent = Agent(name="Assistant", instructions="You are a helpful assistant")
    
        result = await Runner.run(agent, "Write a haiku about recursion in programming.")
        print(result.final_output)
        # Code within the code,
        # Functions calling themselves,
        # Infinite loop's dance.
    

    阅读更多内容请参阅结果指南。


    二、代理循环( agent loop)

    当你在 Runner 中使用 run 方法时,你需要传入一个起始代理和输入。输入可以是一个字符串(这被视为用户消息),或者是一个输入项列表,这些是 OpenAI 响应 API 中的项目。
    跑步者随后运行一个循环:

    1. 我们为当前代理调用 LLM,并使用当前输入。
    2. LLM生成其输出。
      1. 如果 LLM 返回一个 final_output,循环结束并返回结果。
      2. 如果LLM进行Handoffs ,我们更新当前代理和输入,并重新运行循环。
      3. 如果LLM产生工具调用,我们运行这些工具调用,添加结果,并重新运行循环。
    3. 如果我们超过传递的 max_turns,我们将引发一个 [MaxTurnsExceeded][agents.exceptions.MaxTurnsExceeded] 异常。

    注意:关于LLM输出是否被视为“最终输出”的规则是,它产生具有所需类型的文本输出,并且没有工具调用。


    四、流媒体

    流式处理允许你在LLM运行时接收流式事件。一旦流式处理完成,[RunResultStreaming][agents.result.RunResultStreaming] 将包含关于运行的完整信息,包括所有产生的新输出。

    你可以调用.stream_events()来获取流式事件。更多内容请参阅流式处理指南。


    五、运行配置

    run_config 参数允许您为代理运行配置一些全局设置:

  • [model][agents.run.RunConfig.model]: 允许设置一个全局的LLM模型以使用,无论每个Agent有什么模型
  • [model_provider][agents.run.RunConfig.model_provider]: 用于查找模型名称的模型提供者,默认为 OpenAI.
  • [model_settings][agents.run.RunConfig.model_settings]: 覆盖特定代理的设置。例如,您可以设置全局的 temperaturetop_p
  • [input_guardrails][agents.run.RunConfig.input_guardrails], [output_guardrails][agents.run.RunConfig.output_guardrails]: 包含在所有运行中的输入或输出防 guardrail 的列表。
  • [handoff_input_filter][agents.run.RunConfig.handoff_input_filter]: 一个全局输入过滤器,用于所有移交,如果移交本身还没有一个。输入过滤器允许您编辑发送到新代理的输入。有关更多详细信息,请参阅[Handoff.input_filter][agents.handoffs.Handoff.input_filter]中的文档。
  • [tracing_disabled][agents.run.RunConfig.tracing_disabled]: 允许您在整个运行中禁用 tracing。
  • [trace_include_sensitive_data][agents.run.RunConfig.trace_include_sensitive_data]: 配置是否在跟踪中包含可能敏感的数据,例如 LLM 和工具调用的输入/输出。
  • [workflow_name][agents.run.RunConfig.workflow_name], [trace_id][agents.run.RunConfig.trace_id], [group_id][agents.run.RunConfig.group_id]: 设置运行的可追踪工作流程名称、跟踪 ID 和跟踪组 ID。我们建议至少设置 workflow_name。会话 ID 是一个可选字段,允许您在多个运行之间链接跟踪。
  • [trace_metadata][agents.run.RunConfig.trace_metadata]: 包含在所有跟踪中的元数据。

  • 六、对话/聊天线程

    调用任何运行方法可能会导致一个或多个代理运行(从而触发一个或多个 LLM 调用),但这在聊天对话中代表一个单一的逻辑回合。例如:

    1. 用户回合:用户输入文本
    2. 运行器运行:第一个代理调用LLM,运行工具,将任务Handoffs 给第二个代理,第二个代理运行更多工具,然后生成输出。

    在代理运行结束时,您可以选择向用户展示什么内容。

    例如,您可能展示代理生成的每个新项目,或者只展示最终输出。

    无论哪种方式,用户可能会随后提出一个后续问题,在这种情况下,您可以再次调用 run 方法。

    您可以使用基础方法 RunResultBase.to_input_list() 获取下一轮的输入。

    async def main():
        agent = Agent(name="Assistant", instructions="Reply very concisely.")
    
        with trace(workflow_name="Conversation", group_id=thread_id):
            # First turn
            result = await Runner.run(agent, "What city is the Golden Gate Bridge in?")
            print(result.final_output)
            # San Francisco
    
            # Second turn
            new_input = output.to_input_list() + [{"role": "user", "content": "What state is it in?"}]
            result = await Runner.run(agent, new_input)
            print(result.final_output)
            # California
    

    七、异常

    SDK 在某些情况下会引发异常。完整的列表在 [agents.exceptions] 中。概述如下:

  • [AgentsException][agents.exceptions.AgentsException] 是在 SDK 中引发的所有异常的基类。
  • [MaxTurnsExceeded][agents.exceptions.MaxTurnsExceeded] 在运行超过传入运行方法的 max_turns 时抛出。
  • [ModelBehaviorError][agents.exceptions.ModelBehaviorError] 在模型产生无效输出时被触发,例如格式不正确的JSON或使用不存在的工具。
  • [UserError][agents.exceptions.UserError] 当您(使用SDK编写代码的人)使用SDK时出错时会被抛出。
  • [InputGuardrailTripwireTriggered][agents.exceptions.InputGuardrailTripwireTriggered], [OutputGuardrailTripwireTriggered][agents.exceptions.OutputGuardrailTripwireTriggered] 在触发 guardrail 时被抛出。

  • 工具

    https://github.com/openai/openai-agents-python/blob/main/docs/tools.md

    一、关于 工具

    工具让Agent 执行操作:比如获取数据、运行代码、调用外部API,甚至使用电脑。在Agent SDK中,有三种工具类别:

  • Hosted tools(本机工具):这些工具在 LLM 服务器上与 AI 模型一起运行。OpenAI 提供检索、网络搜索和计算机使用作为主机工具。
  • Function calling(函数调用):这些允许您将任何Python函数用作工具。
  • Agents as tools(Agent 作为工具):这允许您将 Agent 作为工具使用,允许Agent 调用其他Agent,而不需要将其传递给它们。

  • 二、Hosted 工具

    OpenAI 在使用 OpenAIResponsesModel 时提供了一些内置工具:

  • The [WebSearchTool][agents.tool.WebSearchTool] 让Agent 搜索网络。
  • The [FileSearchTool][agents.tool.FileSearchTool] 允许从您的 OpenAI 向量存储中检索信息。
  • The [ComputerTool][agents.tool.ComputerTool] 允许自动化计算机使用任务。

  • from agents import Agent, FileSearchTool, Runner, WebSearchTool
    
    agent = Agent(
        name="Assistant",
        tools=[
            WebSearchTool(),
            FileSearchTool(
                max_num_results=3,
                vector_store_ids=["VECTOR_STORE_ID"],
            ),
        ],
    )
    
    async def main():
        result = await Runner.run(agent, 
        "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?")
        print(result.final_output)
    

    三、函数工具

    您可以使用任何Python函数作为工具。Agent SDK将自动设置该工具:

  • 工具的名称将是 Python 函数的名称(或者您也可以提供一个名称)
  • 工具描述将从函数的文档字符串中获取(或您可以提供描述)
  • 函数输入的架构 自动从函数的参数 创建
  • 每个输入的描述 来源于函数的文档字符串,除非已禁用

  • 我们使用 Python 的 inspect 模块来提取函数签名,同时使用 griffe 来解析文档字符串,以及 pydantic 用于创建模式。

    import json
    
    from typing_extensions import TypedDict, Any
    
    from agents import Agent, FunctionTool, RunContextWrapper, function_tool
    
    class Location(TypedDict):
        lat: float
        long: float
    
    @function_tool  # (1)!
    async def fetch_weather(location: Location) -> str:
        # (2)!
        """Fetch the weather for a given location.
    
        Args:
            location: The location to fetch the weather for.
        """
        # In real life, we'd fetch the weather from a weather API
        return "sunny"
    
    @function_tool(name_override="fetch_data")  # (3)!
    def read_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str:
        """Read the contents of a file.
    
        Args:
            path: The path to the file to read.
            directory: The directory to read the file from.
        """
        # In real life, we'd read the file from the file system
        return "<file contents>"
    
    agent = Agent(
        name="Assistant",
        tools=[fetch_weather, read_file],  # (4)!
    )
    
    for tool in agent.tools:
        if isinstance(tool, FunctionTool):
            print(tool.name)
            print(tool.description)
            print(json.dumps(tool.params_json_schema, indent=2))
            print()
    
    

    1. 你可以将任何 Python 类型作为函数的参数,函数可以是同步的或异步的。
    2. 如果存在,文档字符串用于捕获描述和参数描述。
    3. 函数可以可选地接受 context(必须是第一个参数)。你还可以设置覆盖项,如工具名称、描述、要使用的文档字符串样式等。
    4. 你可以将装饰过的函数传递给工具列表。

    ??? 注意 “展开以查看输出”

    fetch_weather
    Fetch the weather for a given location.
    {
    "$defs": {
      "Location": {
        "properties": {
          "lat": {
            "title": "Lat",
            "type": "number"
          },
          "long": {
            "title": "Long",
            "type": "number"
          }
        },
        "required": [
          "lat",
          "long"
        ],
        "title": "Location",
        "type": "object"
      }
    },
    "properties": {
      "location": {
        "$ref": "#/$defs/Location",
        "description": "The location to fetch the weather for."
      }
    },
    "required": [
      "location"
    ],
    "title": "fetch_weather_args",
    "type": "object"
    }
    
    fetch_data
    Read the contents of a file.
    {
    "properties": {
      "path": {
        "description": "The path to the file to read.",
        "title": "Path",
        "type": "string"
      },
      "directory": {
        "anyOf": [
          {
            "type": "string"
          },
          {
            "type": "null"
          }
        ],
        "default": null,
        "description": "The directory to read the file from.",
        "title": "Directory"
      }
    },
    "required": [
      "path"
    ],
    "title": "fetch_data_args",
    "type": "object"
    }
    

    自定义函数工具

    有时候,您可能不想使用Python函数作为工具。
    如果您更愿意,可以直接创建一个[FunctionTool][agents.tool.FunctionTool]。您需要提供:

  • name
  • description
  • params_json_schema, 这是指定参数的 JSON 模式
  • on_invoke_tool,它是一个异步函数,接收上下文和作为 JSON 字符串的参数,必须以字符串的形式返回工具输出。

  • from typing import Any
    
    from pydantic import BaseModel
    
    from agents import RunContextWrapper, FunctionTool
    
    def do_some_work(data: str) -> str:
        return "done"
    
    class FunctionArgs(BaseModel):
        username: str
        age: int
    
    async def run_function(ctx: RunContextWrapper[Any], args: str) -> str:
        parsed = FunctionArgs.model_validate_json(args)
        return do_some_work(data=f"{parsed.username} is {parsed.age} years old")
    
    tool = FunctionTool(
        name="process_user",
        description="Processes extracted user data",
        params_json_schema=FunctionArgs.model_json_schema(),
        on_invoke_tool=run_function,
    )
    

    自动参数和文档字符串解析

    如前所述,我们自动解析函数签名 以提取工具的模式,并解析文档字符串 以提取工具和各个参数的描述。关于这一点的一些注意事项:

    1、签名解析是通过 inspect 模块完成的。
    我们使用类型注解来理解参数的类型,并动态构建一个 Pydantic 模型来表示整体模式。
    它支持大多数类型,包括 Python 原始类型、Pydantic 模型、TypedDicts 以及更多。

    2、我们使用 griffe 来解析文档字符串。
    支持的文档字符串格式有 googlesphinxnumpy

    我们尝试自动检测文档字符串格式,但这是一种尽力而为的做法,您在调用 function_tool 时可以明确设置它。

    您还可以通过将 use_docstring_info 设置为 False 来禁用文档字符串解析。

    代码用于模式提取位于[agents.function_schema]。


    四、Agents as tools

    在某些工作流程中,您可能希望有一个中心Agent 来协调一组专业Agent ,而不是传递控制权。

    您可以通过将Agent 建模为工具来实现这一点。

    from agents import Agent, Runner
    import asyncio
    
    spanish_agent = Agent(
        name="Spanish agent",
        instructions="You translate the user's message to Spanish",
    )
    
    french_agent = Agent(
        name="French agent",
        instructions="You translate the user's message to French",
    )
    
    orchestrator_agent = Agent(
        name="orchestrator_agent",
        instructions=(
            "You are a translation agent. You use the tools given to you to translate."
            "If asked for multiple translations, you call the relevant tools."
        ),
        tools=[
            spanish_agent.as_tool(
                tool_name="translate_to_spanish",
                tool_description="Translate the user's message to Spanish",
            ),
            french_agent.as_tool(
                tool_name="translate_to_french",
                tool_description="Translate the user's message to French",
            ),
        ],
    )
    
    async def main():
        result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.")
        print(result.final_output)
    

    五、处理函数工具中的错误

    当你通过 @function_tool 创建一个函数工具时,你可以传递一个 failure_error_function。这是一个在工具调用崩溃时向 LLM 提供错误响应的函数。

  • 默认情况下(即如果你没有传递任何内容),它将运行一个 default_tool_error_function,该函数告诉LLM发生了错误。
  • 如果你传递了自己的错误函数,它将运行那个函数,并将响应发送到LLM。
  • 如果你显式地传递 None,那么任何工具调用错误将会被重新抛出供你处理。
    这可能是由于模型生成了无效的 JSON 而导致的 ModelBehaviorError,或者是由于你的代码崩溃而导致的 UserError 等。

  • 如果您是手动创建一个 FunctionTool 对象,那么您必须在 on_invoke_tool 函数内部处理错误。


    Guardrails

    https://github.com/openai/openai-agents-python/blob/main/docs/guardrails.md

    一、关于 Guardrails

    Guardrails 与您的代理并行运行,使您能够对用户输入进行检查和验证。
    例如,想象您有一个使用非常智能(因此慢/昂贵)的模型 来帮助处理客户请求的代理。
    您不希望 恶意用户 请求模型 帮助他们完成数学作业。
    因此,您可以运行一个 使用快速/便宜模型的 Guardrails。

    如果 Guardrails 检测到恶意使用,它可以立即引发错误,这会阻止 昂贵模型运行 并为您 节省时间/金钱。
    有两种 guardrails :

    1. 输入guardrails 运行在初始用户输入上
    2. 输出 guardrails 运行在最终代理输出上

    二、输入 guardrail

    输入guardrail 运行分为3个步骤:

    1. 首先,guardrail 接收传递给智能体的相同输入。
    2. 接下来,guardrail 函数运行以生成一个 [GuardrailFunctionOutput][agents.guardrail.GuardrailFunctionOutput],然后将其封装在 [InputGuardrailResult][agents.guardrail.InputGuardrailResult] 中
    3. 最后,我们检查 [.tripwire_triggered][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] 是否为 true。
      如果为 true,则抛出 [InputGuardrailTripwireTriggered][agents.exceptions.InputGuardrailTripwireTriggered] 异常,这样您就可以适当地响应用户或处理该异常。

    注意:输入保 guardrail 旨在运行在用户输入上,因此代理的保 guardrail 只有在代理是 第一个 代理时才会运行。

    你可能想知道,为什么代理上的 guardrails 属性而不是传递给 Runner.run

    这是因为 guardrail 通常与 实际的代理相关——你会为不同的代理 运行不同的保 guardrail ,因此将代码放在一起对于可读性是有用的。


    三、输出 guardrail

    输出防 guardrail 运行分为3个步骤:

    1. 首先,guardrail 接收与传递给代理相同的输入。
    2. 接着,guardrail 函数 运行以生成一个 [GuardrailFunctionOutput][agents.guardrail.GuardrailFunctionOutput],然后它被包装在一个 [OutputGuardrailResult][agents.guardrail.OutputGuardrailResult]
    3. 最后,我们检查 [.tripwire_triggered][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] 是否为真。
      如果为真,则抛出 [OutputGuardrailTripwireTriggered][agents.exceptions.OutputGuardrailTripwireTriggered] 异常,以便您可以适当地 响应用户或处理该异常。

    注意:输出 guardrail 旨在运行在最终代理输入上,因此只有当代理是 最后一个 代理时,代理的 guardrail 才会运行。

    与输入 guardrail 类似,我们这样做是因为 guardrail 通常与实际代理相关——您会为不同的代理运行不同的 guardrail ,所以将代码集中放置对可读性很有帮助。


    四、Tripwires

    如果输入或输出失败,守卫栏可以使用 Tripwires 来发出信号。
    一旦我们看到触发 Tripwires 的守卫栏,我们就立即抛出 {Input,Output}GuardrailTripwireTriggered 异常并停止代理执行。


    五、实现一个 guardrail

    您需要提供一个接收输入并返回一个[GuardrailFunctionOutput][agents.guardrail.GuardrailFunctionOutput]的函数。
    在这个例子中,我们将通过运行一个代理来实现这一点。

    from pydantic import BaseModel
    from agents import (
        Agent,
        GuardrailFunctionOutput,
        InputGuardrailTripwireTriggered,
        RunContextWrapper,
        Runner,
        TResponseInputItem,
        input_guardrail,
    )
    
    class MathHomeworkOutput(BaseModel):
        is_math_homework: bool
        reasoning: str
    
    guardrail_agent = Agent( # (1)!
        name="Guardrail check",
        instructions="Check if the user is asking you to do their math homework.",
        output_type=MathHomeworkOutput,
    )
    
    @input_guardrail
    async def math_guardrail( # (2)!
        ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem]
    ) -> GuardrailFunctionOutput:
        result = await Runner.run(guardrail_agent, input, context=ctx.context)
    
        return GuardrailFunctionOutput(
            output_info=result.final_output, # (3)!
            tripwire_triggered=result.final_output.is_math_homework,
        )
    
    agent = Agent(  # (4)!
        name="Customer support agent",
        instructions="You are a customer support agent. You help customers with their questions.",
        input_guardrails=[math_guardrail],
    )
    
    async def main():
        # This should trip the guardrail
        try:
            await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?")
            print("Guardrail didn't trip - this is unexpected")
    
        except InputGuardrailTripwireTriggered:
            print("Math homework guardrail tripped")
    

    1. 我们将在我们的 guardrail 功能中使用这个代理。
    2. 这是一个接收代理的输入/上下文并返回结果的 guardrail 功能。
    3. 我们可以在 guardrail 结果中包含额外信息。
    4. 这实际上是定义工作流程的代理。

    输出 guardrail 类似。


    from pydantic import BaseModel
    from agents import (
        Agent,
        GuardrailFunctionOutput,
        OutputGuardrailTripwireTriggered,
        RunContextWrapper,
        Runner,
        output_guardrail,
    )
    class MessageOutput(BaseModel): # (1)!
        response: str
    
    class MathOutput(BaseModel): # (2)!
        is_math: bool
        reasoning: str
    
    guardrail_agent = Agent(
        name="Guardrail check",
        instructions="Check if the output includes any math.",
        output_type=MathOutput,
    )
    
    @output_guardrail
    async def math_guardrail(  # (3)!
        ctx: RunContextWrapper, agent: Agent, output: MessageOutput
    ) -> GuardrailFunctionOutput:
        result = await Runner.run(guardrail_agent, output.response, context=ctx.context)
    
        return GuardrailFunctionOutput(
            output_info=result.final_output,
            tripwire_triggered=result.final_output.is_math,
        )
    
    agent = Agent( # (4)!
        name="Customer support agent",
        instructions="You are a customer support agent. You help customers with their questions.",
        output_guardrails=[math_guardrail],
        output_type=MessageOutput,
    )
    
    async def main():
        # This should trip the guardrail
        try:
            await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?")
            print("Guardrail didn't trip - this is unexpected")
    
        except OutputGuardrailTripwireTriggered:
            print("Math output guardrail tripped")
    

    模型

    https://github.com/openai/openai-agents-python/blob/main/docs/models.md

    一、关于 模型

    Agent SDK 以两种风格 提供了对OpenAI模型的 开箱即用支持:

  • 推荐: 使用新的 Responses API 的 [OpenAIResponsesModel][agents.models.openai_responses.OpenAIResponsesModel]。
  • [OpenAIChatCompletionsModel][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel],它使用 Chat Completions API 调用 OpenAI API。

  • 二、混合和匹配模型

    在单个工作流中,您可能希望为每个代理 使用不同的模型。
    例如,您可以使用较小的、较快的模型进行分诊,而使用较大的、功能更强大的模型来完成复杂任务。
    当配置一个Agent 时,您可以通过以下方式选择一个特定的模型:

    1. 传递一个 OpenAI 模型的名称。
    2. 传递任何模型名称 + 一个可以将该名称映射到模型实例的 ModelProvider
    3. 直接提供 Model 的实现。

    注意:虽然我们的SDK支持 [OpenAIResponsesModel][agents.models.openai_responses.OpenAIResponsesModel] 和 [OpenAIChatCompletionsModel][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel] 两种模型形状,但我们建议每个工作流程只使用一个模型形状,因为这两种形状 支持不同的功能集和工具。

    如果你的工作流程需要 混合和匹配模型形状,请确保你使用的所有功能 都在两种形状上都可用。


    from agents import Agent, Runner, AsyncOpenAI, OpenAIChatCompletionsModel
    import asyncio
    
    spanish_agent = Agent(
        name="Spanish agent",
        instructions="You only speak Spanish.",
        model="o3-mini", # (1)!
    )
    
    english_agent = Agent(
        name="English agent",
        instructions="You only speak English",
        model=OpenAIChatCompletionsModel( # (2)!
            model="gpt-4o",
            openai_client=AsyncOpenAI()
        ),
    )
    
    triage_agent = Agent(
        name="Triage agent",
        instructions="Handoff to the appropriate agent based on the language of the request.",
        handoffs=[spanish_agent, english_agent],
        model="gpt-3.5-turbo",
    )
    
    async def main():
        result = await Runner.run(triage_agent, input="Hola, ¿cómo estás?")
        print(result.final_output)
    

    1、直接设置 OpenAI 模型的名称。

    2、提供了 Model[agents.models.interface.Model] 的实现。


    使用其他 LLM 提供商

    许多提供商也支持 OpenAI API 格式,这意味着您可以传递一个 base_url 到现有的 OpenAI 模型实现中,并轻松使用它们。

    ModelSettings 用于配置 您选择的模型的调整参数(例如,温度、top_p)。

    external_client = AsyncOpenAI(
        api_key="EXTERNAL_API_KEY",
        base_url="https://api.external.com/v1/",
    )
    
    spanish_agent = Agent(
        name="Spanish agent",
        instructions="You only speak Spanish.",
        model=OpenAIChatCompletionsModel(
            model="EXTERNAL_MODEL_NAME",
            openai_client=external_client,
        ),
        model_settings=ModelSettings(temperature=0.5),
    )
    

    配置 SDK


    一、API key 和 客户端

    默认情况下,SDK 在导入后立即查找用于 LLM 请求和跟踪的 OPENAI_API_KEY 环境变量。

    如果您在应用启动之前无法设置该环境变量,可以使用 set_default_openai_key() 函数来设置密钥。

    from agents import set_default_openai_key
    
    set_default_openai_key("sk-...")
    

    或者,您也可以配置一个用于的 OpenAI 客户端。
    默认情况下,SDK 创建一个 AsyncOpenAI 实例,使用环境变量中的 API 密钥或上面设置的默认密钥。
    您可以通过使用 [set_default_openai_client()][agents.set_default_openai_client] 函数来更改此设置。

    from openai import AsyncOpenAI
    from agents import set_default_openai_client
    
    custom_client = AsyncOpenAI(base_url="...", api_key="...")
    set_default_openai_client(custom_client)
    

    最后,您还可以自定义使用的 OpenAI API。
    默认情况下,我们使用 OpenAI 响应 API。
    您可以通过使用 [set_default_openai_api()][agents.set_default_openai_api] 函数来覆盖此设置,使用 Chat Completions API。

    from agents import set_default_openai_api
    
    set_default_openai_api("chat_completions")
    

    二、跟踪

    默认情况下启用了追踪。它默认使用上面部分中提到的 OpenAI API 密钥(即环境变量或您设置的默认密钥)。
    您可以通过使用 set_tracing_export_api_key [agents.set_tracing_export_api_key] 函数来特别设置 用于追踪的 API 密钥。

    from agents import set_tracing_export_api_key
    
    set_tracing_export_api_key("sk-...")
    

    您也可以通过使用 set_tracing_disabled() 函数来完全禁用跟踪。

    from agents import set_tracing_disabled
    
    set_tracing_disabled(True)
    

    三、调试日志

    SDK 有两个没有设置处理器的 Python 日志记录器。
    默认情况下,这意味着警告和错误 会被发送到 stdout,但其他日志会被抑制。
    要启用详细日志记录,请使用 enable_verbose_stdout_logging()[agents.enable_verbose_stdout_logging] 函数。

    from agents import enable_verbose_stdout_logging
    
    enable_verbose_stdout_logging()
    

    另外,您可以通过添加处理器、过滤器、格式化程序等来自定义日志。
    您可以在Python日志指南中了解更多信息。

    import logging
    
    logger =  logging.getLogger("openai.agents") # or openai.agents.tracing for the Tracing logger
    
    # To make all logs show up
    logger.setLevel(logging.DEBUG)
    # To make info and above show up
    logger.setLevel(logging.INFO)
    # To make warning and above show up
    logger.setLevel(logging.WARNING)
    # etc
    
    # You can customize this as needed, but this will output to `stderr` by default
    logger.addHandler(logging.StreamHandler())
    

    日志中的敏感数据

    某些日志可能包含敏感数据(例如,用户数据)。如果您想 禁用这些数据被记录,请设置以下环境变量。
    要禁用记录LLM输入和输出:

    export OPENAI_AGENTS_DONT_LOG_MODEL_DATA=1
    

    要禁用日志工具的输入和输出:

    export OPENAI_AGENTS_DONT_LOG_TOOL_DATA=1
    

    上下文管理

    https://github.com/openai/openai-agents-python/blob/main/docs/context.md

    一、关于上下文

    上下文是一个多义词。你可能关心的上下文主要有两大类:

    1. 代码本地可用的上下文:这是在工具函数运行时、回调如 on_handoff、生命周期钩子等情况下,你可能需要的数据和依赖。
    2. LLM 可用的上下文:这是 LLM 在生成响应时看到的数据。

    二、本地上下文

    这是通过 [RunContextWrapper][agents.run_context.RunContextWrapper] 类和其中的 [context][agents.run_context.RunContextWrapper.context] 属性来表示的。这种工作的方式是:

    1. 您可以创建任何您想要的 Python 对象。一个常见的模式是使用数据类或 Pydantic 对象。
    2. 你将那个对象传递给各种运行方法(例如 Runner.run(..., **context= whatever**))。
    3. 所有您的工具调用、生命周期钩子等都将传递一个包装对象,RunContextWrapper[T],其中 T 代表您的上下文对象类型,您可以通过 wrapper.context 访问它。

    最重要的需要注意的事情:给定的一次代理运行中,每个代理、工具函数、生命周期等都必须使用相同类型的上下文。
    您可以使用上下文来进行诸如:

  • 您跑步的上下文数据(例如,用户名/uid或其他关于用户的信息)
  • 依赖项(例如:日志对象、数据获取器等)
  • 辅助函数

  • !!! danger “注意” : 上下文对象 发送到 LLM。它纯粹是一个本地对象,你可以从中读取、写入以及调用它的方法。


    import asyncio
    from dataclasses import dataclass
    
    from agents import Agent, RunContextWrapper, Runner, function_tool
    
    @dataclass
    class UserInfo:  # (1)!
        name: str
        uid: int
    
    async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str:  # (2)!
        return f"User {wrapper.context.name} is 47 years old"
    
    async def main():
        user_info = UserInfo(name="John", uid=123)  # (3)!
    
        agent = Agent[UserInfo](  # (4)!
            name="Assistant",
            tools=[function_tool(fetch_user_age)],
        )
    
        result = await Runner.run(
            starting_agent=agent,
            input="What is the age of the user?",
            context=user_info,
        )
    
        print(result.final_output)  # (5)!
        # The user John is 47 years old.
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    1. 这是一个上下文对象。这里我们使用了数据类,但你可以使用任何类型。
    2. 这是一个工具。你可以看到它接受一个 RunContextWrapper[UserInfo]。工具实现从上下文中读取。
    3. 我们用泛型 UserInfo 标记代理,这样类型检查器可以捕获错误(例如,如果我们尝试传递一个接受不同上下文类型的工具)。
    4. 上下文传递给 run 函数。
    5. 代理正确地调用了工具并获取了年龄。

    Agent/LLM 上下文

    当调用一个语言模型时,它所能看到的唯一数据来自对话历史。
    这意味着如果你想让一些新数据可用给LLM,你必须以使其可在该历史记录中可用的方式来实现。
    有几种方法可以做到这一点:

    1. 你可以将其添加到代理的 instructions 中。这也可以称为 system promptdeveloper message
      系统提示可以是静态字符串,也可以是接收上下文并输出字符串的动态函数。
      这是一种常用的策略,用于始终有用的信息(例如,用户的姓名或当前日期)。
    2. 在调用 Runner.run 函数时将其添加到 input 中。
      这与 instructions 策略类似,但允许您拥有处于 chain of command 更低层级的消息。
    3. 通过函数工具公开它。这对于 按需 上下文很有用 – LLM 决定何时需要某些数据,并可以调用工具来获取这些数据。
    4. 使用检索或网络搜索。这些是特殊工具,能够从文件或数据库(检索)或从网络(网络搜索)中获取相关数据。这对于将响应“定位”在相关上下文数据中非常有用。

    Handoffs

    https://github.com/openai/openai-agents-python/blob/main/docs/handoffs.md


    一、关于 Handoffs

    Handoffs 让一个代理可以将任务委派给另一个代理。这在不同代理专注于不同领域的情况下特别有用。

    例如,一个客户支持应用可能拥有 每个代理专门处理订单状态、退款、常见问题解答等任务的代理。

    Handoffs 被表示为LLM的工具。因此,如果有一个名为 Refund Agent 的代理的Handoffs ,该工具将被命名为 transfer_to_refund_agent


    二、创建Handoffs

    所有代理都有一个 handoffs 参数,它可以直接接受一个 Agent,或者一个自定义 Handoff 对象。

    您可以使用 Agents SDK 提供的 handoff() [agents.handoffs.handoff] 函数创建一个Handoffs 。
    此函数允许您指定要Handoffs 给代理,以及可选的重写和输入过滤器。


    1、基本用法

    以下是创建简单Handoffs 的方法:

    from agents import Agent, handoff
    
    billing_agent = Agent(name="Billing agent")
    refund_agent = Agent(name="Refund agent")
    
    # (1)!
    triage_agent = Agent(name="Triage agent", handoffs=[billing_agent, handoff(refund_agent)])
    
    1. 您可以直接使用代理(例如 billing_agent),或者您可以使用 handoff() 函数。

    2、通过 handoff() 函数自定义Handoffs

    The [handoff()][agents.handoffs.handoff] 函数让您可以自定义一些事情。

  • agent: 这是将要转交事务的代理。
  • tool_name_override: 默认情况下,使用 Handoff.default_tool_name() 函数,它解析为 transfer_to_<agent_name>。您可以覆盖它。
  • tool_description_override: 覆盖来自 Handoff.default_tool_description() 的默认工具描述
  • on_handoff: 当调用交班时执行的回调函数。这在知道交班被调用后立即启动一些数据获取时很有用。此函数接收代理上下文,并且可以选择接收 LLM 生成的输入。输入数据由 input_type 参数控制。
  • input_type: 手递手过程中期望的输入类型(可选)。
  • input_filter: 这允许您过滤下一个代理接收到的输入。更多信息请见下文。

  • from agents import Agent, handoff, RunContextWrapper
    
    def on_handoff(ctx: RunContextWrapper[None]):
        print("Handoff called")
    
    agent = Agent(name="My agent")
    
    handoff_obj = handoff(
        agent=agent,
        on_handoff=on_handoff,
        tool_name_override="custom_handoff_tool",
        tool_description_override="Custom description",
    )
    

    三、Handoffs 输入

    在某些情况下,您希望在LLM调用Handoffs 时提供一些数据。
    例如,想象一个转到“升级代理”的Handoffs 。
    您可能希望提供一个原因,以便您可以记录它。

    from pydantic import BaseModel
    
    from agents import Agent, handoff, RunContextWrapper
    
    class EscalationData(BaseModel):
        reason: str
    
    async def on_handoff(ctx: RunContextWrapper[None], input_data: EscalationData):
        print(f"Escalation agent called with reason: {input_data.reason}")
    
    agent = Agent(name="Escalation agent")
    
    handoff_obj = handoff(
        agent=agent,
        on_handoff=on_handoff,
        input_type=EscalationData,
    )
    

    四、输入过滤器

    当发生切换时,就像新的代理接管了对话,并可以看到整个之前的对话历史。

    如果你想改变这一点,你可以设置一个[输入过滤器][agents.handoffs.Handoff.input_filter]。
    输入过滤器是一个接收现有的输入通过一个[切换输入数据][agents.handoffs.HandoffInputData],并必须返回一个新的HandoffInputData的函数。

    以下是一些常见的模式(例如从历史记录中删除所有工具调用),这些模式已为您在 agents.extensions.handoff_filters 中实现。

    from agents import Agent, handoff
    from agents.extensions import handoff_filters
    
    agent = Agent(name="FAQ agent")
    
    handoff_obj = handoff(
        agent=agent,
        input_filter=handoff_filters.remove_all_tools, # (1)!
    )
    
    1. 这将在调用 FAQ agent 时自动从历史记录中移除所有工具。

    五、推荐提示

    为了确保 LLMs 正确理解Handoffs ,我们建议在您的代理中包含关于Handoffs 的信息。我

    们有一个建议的前缀在 agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX,或者您可以调用 agents.extensions.handoff_prompt.prompt_with_handoff_instructions 以自动将推荐数据添加到您的提示中。

    from agents import Agent
    from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
    
    billing_agent = Agent(
        name="Billing agent",
        instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
        <Fill in the rest of your prompt here>.""",
    )
    

    多代理编排

    一、关于 编排

    编排指的是您应用中代理的流程。哪些代理运行,以何种顺序运行,以及它们如何决定下一步该做什么?编排代理主要有两种方式:

    1. 允许 LLM 做出决策:这利用 LLM 的智能来规划、推理并决定采取哪些步骤。
    2. 通过代码进行编排:通过您的代码确定代理的流程。
      您可以根据需要混合搭配这些模式。每种模式都有其自身的权衡,具体描述如下。

    二、通过 LLM 进行编排

    一个代理是一个配备了指令、工具和Handoffs 的LLM。

    这意味着给定一个开放性任务,LLM可以自主地规划如何应对任务,使用工具采取行动和获取数据,并使用Handoffs 将任务委派给子代理。

    例如,一个研究代理可以配备如下工具:

  • 使用网络搜索以在线查找信息
  • 文件搜索和检索,用于搜索专有数据和连接
  • 计算机用于在计算机上执行操作
  • 执行代码进行数据分析
  • 将任务转交给擅长规划、报告撰写等工作的专业代理。

  • 这种模式在任务开放且希望依赖LLM的智能时非常出色。这里最重要的策略是:

    1. 投资于良好的提示。明确说明可用的工具、如何使用它们以及必须在其内操作的参数。
    2. 监控您的应用并对其进行迭代。查看哪里出了问题,并对您的提示进行迭代。
    3. 允许代理进行自我反思和改进。例如,将其在循环中运行,并让它自我批评;或者,提供错误信息并让它进行改进。
    4. 拥有专门从事一项任务的代理人,而不是拥有一个通用目的的代理人,期望其擅长任何事情。
    5. 投资于评估. 这让您可以训练您的智能体以改进并提高任务的能力。

    三、通过代码进行编排

    尽管通过 LLM 进行编排很强大,但通过代码进行编排可以使任务在速度、成本和性能方面更加确定性和可预测。这里的常见模式有:

  • 使用 结构化输出 生成可以供您用代码检查的良好格式数据。例如,您可能要求代理将任务分类到几个类别中,然后根据类别选择下一个代理。
  • 通过将一个代理的输出转换为下一个代理的输入来链式连接多个代理。您可以分解像撰写博客文章这样的任务,将其分解为一系列步骤 – 进行研究,编写提纲,撰写博客文章,评论它,然后改进它。
  • 使用 while 循环运行执行任务的代理,同时使用一个评估并提供反馈的代理,直到评估者表示输出满足某些标准。
  • 并行运行多个代理,例如通过 Python 原语如 asyncio.gather。当你有多个互不依赖的任务时,这有助于提高速度。
  • 我们有一些示例在examples/agent_patterns


    Results

    https://github.com/openai/openai-agents-python/blob/main/docs/results.md

    一、关于 Results

    当你调用 Runner.run 方法时,你会得到一个:

  • [RunResult][agents.result.RunResult],如果你调用 runrun_sync
  • [RunResultStreaming][agents.result.RunResultStreaming], 如果您调用 run_streamed
  • 这两者都继承自[RunResultBase][agents.result.RunResultBase],其中包含了大部分有用的信息。


    二、最终输出

    The [final_output][agents.result.RunResultBase.final_output] 属性包含最后一个运行的代理的最终输出。这可能是:

  • 如果最后一个代理 未定义 output_type ,则为 str
  • 一个类型为 last_agent.output_type 的对象,如果代理定义了输出类型的话。

  • 注意:final_output 类型为 Any。由于存在handoffs,我们无法对此进行静态类型化。

    如果发生handoffs,这意味着任何 Agent 都可能是最后一个 Agent,因此我们无法静态地知道可能输出类型集。


    三、下一次回合的输入

    您可以使用 [result.to_input_list()][agents.result.RunResultBase.to_input_list] 将结果转换为输入列表,该列表将您提供的原始输入 与代理运行期间生成的项目连接起来。

    这使得将一个代理运行的输出传递给另一个运行,或者在循环中运行并每次附加新的用户输入变得方便。


    四、最后的代理

    The [last_agent][agents.result.RunResultBase.last_agent] 属性包含最后运行的代理。

    根据您的应用程序,这通常在用户下次输入内容时非常有用。

    例如,如果您有一个前线分级代理,该代理将任务转交给特定语言的代理,您可以存储最后运行的代理,并在下次用户向代理发送消息时再次使用它。


    五、新项目

    The [new_items][agents.result.RunResultBase.new_items] 属性包含在运行过程中生成的新的项目。这些项目是 [RunItem][agents.items.RunItem]。一个运行项目包装了由 LLM 生成的原始项目。

  • [MessageOutputItem][agents.items.MessageOutputItem] 表示来自 LLM 的消息。原始项是生成的消息。
  • [HandoffCallItem][agents.items.HandoffCallItem] 表示 LLM 调用了Handoffs 工具。原始项目是来自 LLM 的工具调用项目。
  • [HandoffOutputItem][agents.items.HandoffOutputItem] 表示发生了交班。原始项目是工具对交班工具调用的响应。您还可以从项目访问源/目标代理。
  • [ToolCallItem][agents.items.ToolCallItem] 表示 LLM 调用了一个工具。
  • [ToolCallOutputItem][agents.items.ToolCallOutputItem] 表示调用了工具。原始项目是工具响应。您还可以从项目访问工具输出。
  • [ReasoningItem][agents.items.ReasoningItem] 表示来自LLM的推理项。原始项是生成的推理。

  • 六、其他信息

    1、Guardrail 结果

    The [input_guardrail_results][agents.result.RunResultBase.input_guardrail_results] 和 [output_guardrail_results][agents.result.RunResultBase.output_guardrail_results] 属性包含(如果有)围栏的结果。围栏结果有时可能包含您想要记录或存储的有用信息,因此我们使这些信息可供您使用。


    2、原始响应

    The [raw_responses][agents.result.RunResultBase.raw_responses] 属性包含 由 LLM 生成的 [ModelResponse][agents.items.ModelResponse] 。


    3、原始输入

    [input][agents.result.RunResultBase.input] 属性包含您提供给 run 方法的原始输入。在大多数情况下,您不需要这个属性,但它可供您使用。


    Streaming

    https://github.com/openai/openai-agents-python/blob/main/docs/streaming.md

    一、关于 Streaming

    流式传输让您能够订阅代理运行的更新。

    这可以用于向最终用户展示进度更新和部分响应。

    要进行流式传输,您可以调用 Runner.run_streamed(),这将返回一个 RunResultStreaming

    调用 result.stream_events() 将为您提供 StreamEvent 对象的异步流,以下是对这些对象的描述。[agents.run.Runner.run_streamed][agents.result.RunResultStreaming]。


    二、原始响应事件

    [RawResponsesStreamEvent][agents.stream_events.RawResponsesStreamEvent] 是直接从 LLM 传递的原始事件。

    它们遵循 OpenAI 响应 API 格式,这意味着每个事件都有一个类型(如 response.createdresponse.output_text.delta 等)和数据。

    如果您希望将响应消息实时流式传输给用户,这些事件非常有用。

    例如,这将输出 LLM 按字符生成的文本。

    import asyncio
    from openai.types.responses import ResponseTextDeltaEvent
    from agents import Agent, Runner
    
    async def main():
        agent = Agent(
            name="Joker",
            instructions="You are a helpful assistant.",
        )
    
        result = Runner.run_streamed(agent, input="Please tell me 5 jokes.")
        async for event in result.stream_events():
            if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
                print(event.data.delta, end="", flush=True)
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    三、运行项目事件和代理事件

    [RunItemStreamEvent][agents.stream_events.RunItemStreamEvent] 是更高级的事件。它们会通知你一个项目已经被完全生成。

    这允许你在“消息生成”、“工具运行” 等层面推送进度更新,而不是每个标记。

    同样, [AgentUpdatedStreamEvent][agents.stream_events.AgentUpdatedStreamEvent] 当当前代理发生变化时(例如,由于转接)会为你提供更新。

    例如,这将忽略原始事件和对用户的流更新。

    import asyncio
    import random
    from agents import Agent, ItemHelpers, Runner, function_tool
    
    @function_tool
    def how_many_jokes() -> int:
        return random.randint(1, 10)
    
    async def main():
        agent = Agent(
            name="Joker",
            instructions="First call the `how_many_jokes` tool, then tell that many jokes.",
            tools=[how_many_jokes],
        )
    
        result = Runner.run_streamed(
            agent,
            input="Hello",
        )
        print("=== Run starting ===")
    
        async for event in result.stream_events():
            # We'll ignore the raw responses event deltas
            if event.type == "raw_response_event":
                continue
            # When the agent updates, print that
            elif event.type == "agent_updated_stream_event":
                print(f"Agent updated: {event.new_agent.name}")
                continue
            # When items are generated, print them
            elif event.type == "run_item_stream_event":
                if event.item.type == "tool_call_item":
                    print("-- Tool was called")
                elif event.item.type == "tool_call_output_item":
                    print(f"-- Tool output: {event.item.output}")
                elif event.item.type == "message_output_item":
                    print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}")
                else:
                    pass  # Ignore other event types
    
        print("=== Run complete ===")
    
    if __name__ == "__main__":
        asyncio.run(main())
    

    Tracing

    一、关于 Tracing

    Agents SDK 包括内置跟踪,收集代理运行期间事件 的综合记录:LLM生成、工具调用、handoffs、guardrails,甚至发生的自定义事件。

    使用 Traces dashboard,您可以在开发和生产过程中调试、可视化和监视工作流。


    默认情况下,跟踪是启用的。有两种方法可以禁用跟踪:

    1. 您可以通过设置环境变量 OPENAI_AGENTS_DISABLE_TRACING=1 来全局禁用跟踪
    2. 您可以通过将[agents.run.RunConfig.tracing_disabled][]设置为True来禁用单个运行的跟踪

    二、Traces 和 spans

  • Traces代表“工作流”的单个端到端操作。它们由跨度组成。跟踪具有以下属性:
  • workflow_name:这是逻辑工作流或应用程序。例如“代码生成”或“客户服务”。
  • trace_id:跟踪的唯一标识符。如果没有传递,将自动生成。必须具有格式trace_<32_alphanumeric>
  • group_id:可选的组ID,用于链接来自同一对话的多个跟踪。例如,您可能使用聊天线程ID。
  • disabled:如果为True,则不会记录跟踪。
  • metadata:跟踪的可选元数据。
  • Spans代表具有开始和结束时间的操作。跨度具有:
  • started_atended_at时间戳。
  • trace_id,表示它们所属的跟踪。
  • parent_id,它指向此跨度的父跨度(如果有)。
  • span_data,它包含关于跨度的信息。例如,AgentSpanData包含有关代理的信息,GenerationSpanData包含有关LLM生成的信息等。

  • 三、默认跟踪

    默认情况下,SDK 跟踪以下内容:

  • 整个 Runner.{run, run_sync, run_streamed}() 都被 trace() 包裹。
  • 每次代理运行时,它都会被 agent_span() 包裹
  • LLM 生成内容被封装在 generation_span()
  • 函数工具调用都被包裹在 function_span()
  • 标准栏用 guardrail_span() 包装
  • 手续Handoffs 被包裹在 handoff_span()

  • 默认情况下,跟踪名称为 “Agent trace”。如果您使用 trace,您可以设置此名称,或者您可以使用 RunConfig 配置名称和其他属性。

    除了这些,您还可以设置自定义跟踪处理器以将跟踪推送到其他目的地(作为替代或次要目的地)。


    四、高级跟踪

    有时,您可能希望多个对 run() 的调用都属于同一个跟踪。

    您可以通过将整个代码包裹在 trace() 中来实现这一点。

    from agents import Agent, Runner, trace
    
    async def main():
        agent = Agent(name="Joke generator", instructions="Tell funny jokes.")
    
        with trace("Joke workflow"): # (1)!
            first_result = await Runner.run(agent, "Tell me a joke")
            second_result = await Runner.run(agent, f"Rate this joke: {first_output.final_output}")
            print(f"Joke: {first_result.final_output}")
            print(f"Rating: {second_result.final_output}")
    
    1. 因为两次对 Runner.run 的调用都被 with trace() 包裹,所以单个运行将作为整体跟踪的一部分,而不是创建两个跟踪。

    五、创建追踪

    您可以使用 [trace()`][agents.tracing.trace] 函数来创建一个跟踪。跟踪需要开始和结束。您有两种方法来做这件事:

    1. 推荐:使用 trace 作为上下文管理器,即 with trace(...) as my_trace。这将自动在正确的时间开始和结束跟踪。
    2. 您还可以手动调用 ['trace.start()']['trace.finish()']

    当前追踪是通过 Python 的 contextvar 上下文变量 来跟踪的。这意味着它能够自动处理并发。

    如果你手动开始/结束追踪,你需要将 mark_as_currentreset_current 传递给 start()/finish() 来更新当前追踪。


    六、创建span

    您可以使用各种[*_span()][agents.tracing.create]方法来创建span。通常,不需要手动创建 spans。[custom_span()][agents.tracing.custom\u span]函数可用于跟踪自定义span信息。

    Spans 自动成为当前跟踪的一部分,并嵌套在最近的当前范围下,该范围通过Python contextvar 进行跟踪。


    七、敏感数据

    一些 spans 跟踪可能敏感的数据。

    例如,generation_span() 存储了 LLM 生成输入/输出,而 function_span() 存储了函数调用输入/输出。

    这些可能包含敏感数据,因此您可以通过 [RunConfig.trace_include_sensitive_data][agents.run.RunConfig.trace_include_sensitive_data] 禁用捕获这些数据。


    八、自定义跟踪处理器

    追踪的高级架构如下:

  • 在初始化时,我们创建了一个全局的 [TraceProvider][agents.tracing.setup.TraceProvider],它负责创建追踪信息。
  • 我们使用一个 [BatchTraceProcessor][agents.tracing.processors.BatchTraceProcessor] 配置 TraceProvider,该处理器将跟踪/跨度批量发送到 [BackendSpanExporter][agents.tracing.processors.BackendSpanExporter],该导出器将跨度跟踪批量导出到 OpenAI 后端。

  • 要自定义此默认设置,将跟踪发送到替代或额外的后端或修改导出器行为,您有两个选项:

    1. [add_trace_processor()][agents.tracing.add_trace_processor] 允许您添加一个 附加 的跟踪处理器,该处理器将接收已准备好的跟踪和跨度。这使您能够在将跟踪发送到 OpenAI 的后端之外执行自己的处理。
    2. [set_trace_processors()][agents.tracing.set_trace_processors] 允许你 替换 默认的处理器为你自己的跟踪处理器。
      这意味着除非你包含一个将跟踪发送到 OpenAI 后端的 TracingProcessor,否则跟踪将不会被发送到 OpenAI 后端。

    外部跟踪处理器包括:

  • Braintrust
  • Pydantic Logfire
  • AgentOps

  • 2025-03-12(三)

    作者:知识搬运bot

    物联沃分享整理
    物联沃-IOTWORD物联网 » OpenAI Agents SDK in Python:深度解析openai-agents-python

    发表回复