OpenAI Agents SDK in Python:深度解析openai-agents-python
文章目录
openai-agents-python
一、关于 OpenAI Agents SDK
The OpenAI Agents SDK 是一个轻量级但功能强大的框架,用于构建多智能体工作流程。
核心概念:
- Agents: 配置有指令、工具、约束和Handoffs 的 LLMs
- Handoffs : 允许代理将控制权转移到其他代理以执行特定任务
- Guardrails: 可配置的输入和输出验证安全检查
- 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()
时,我们运行一个循环,直到我们得到最终输出。
- 我们调用 LLM,使用代理上的模型和设置以及消息历史。
- LLM 返回一个响应,其中可能包括工具调用。
- 如果响应有一个最终输出(下面将对此进行更多说明),我们返回它并结束循环。
- 如果响应包含Handoffs ,我们将代理设置为新的代理并返回步骤 1。
- 我们处理工具调用(如果有)并追加工具响应消息。然后我们回到步骤 1。
有一个 max_turns
参数可用于 限制循环执行的次数。
最终输出
最终输出是代理在循环中产生的最后一件事。
- 如果你为代理设置了
output_type
,最终的输出就是当 LLM 返回该类型的内容时。我们使用 结构化输出 来实现这一点。 - 如果没有
output_type
(即纯文本响应),那么第一个 没有任何工具调用 或 移交的 LLM 响应 将被视为最终输出。
因此,代理循环的心理模型是:
- 如果当前代理有
output_type
,循环将一直运行,直到代理产生 与该类型匹配的 结构化输出。 - 如果当前代理没有
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
致谢
我们想感谢开源社区的优秀工作,特别是:
我们致力于继续构建 Agents SDK 作为开源框架,以便社区中的其他人 可以在此基础上 扩展我们的方法。
Index
一、关于 OpenAI Agents SDK
OpenAI 代理 SDK 允许您在一个轻量级、易于使用的包中构建代理人工智能应用,具有非常少的抽象。它是我们之前针对代理的实验 Swarm 的生产就绪升级。代理 SDK 有一个非常小的原语集:
结合 Python,这些原语强大到 足以表达工具和代理之间复杂的关联,并允许你在没有陡峭的学习曲线的情况下 构建现实世界的应用程序。
此外,该 SDK 随带内置的 跟踪 功能,让你可以可视化并调试你的代理流程,以及评估它们甚至微调模型以适应你的应用程序。
为什么使用代理 SDK
SDK 有两个驱动设计原则:
- 充足的功能使其值得一用,但基础元素又足够少,使其易于学习。
- 开箱即用效果极佳,但您可以自定义具体会发生什么。
以下是 SDK 的主要功能:
二、安装使用
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
来配置模型调整参数,如 temperature
、top_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 中的项目。
跑步者随后运行一个循环:
- 我们为当前代理调用 LLM,并使用当前输入。
- LLM生成其输出。
- 如果 LLM 返回一个
final_output
,循环结束并返回结果。 - 如果LLM进行Handoffs ,我们更新当前代理和输入,并重新运行循环。
- 如果LLM产生工具调用,我们运行这些工具调用,添加结果,并重新运行循环。
- 如果 LLM 返回一个
- 如果我们超过传递的
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]: 覆盖特定代理的设置。例如,您可以设置全局的 temperature
或 top_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 调用),但这在聊天对话中代表一个单一的逻辑回合。例如:
- 用户回合:用户输入文本
- 运行器运行:第一个代理调用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 工具
OpenAI 在使用 OpenAIResponsesModel
时提供了一些内置工具:
WebSearchTool
][agents.tool.WebSearchTool] 让Agent 搜索网络。FileSearchTool
][agents.tool.FileSearchTool] 允许从您的 OpenAI 向量存储中检索信息。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 的 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()
- 你可以将任何 Python 类型作为函数的参数,函数可以是同步的或异步的。
- 如果存在,文档字符串用于捕获描述和参数描述。
- 函数可以可选地接受
context
(必须是第一个参数)。你还可以设置覆盖项,如工具名称、描述、要使用的文档字符串样式等。 - 你可以将装饰过的函数传递给工具列表。
??? 注意 “展开以查看输出”
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
来解析文档字符串。
支持的文档字符串格式有 google
、sphinx
和 numpy
。
我们尝试自动检测文档字符串格式,但这是一种尽力而为的做法,您在调用 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发生了错误。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 :
- 输入guardrails 运行在初始用户输入上
- 输出 guardrails 运行在最终代理输出上
二、输入 guardrail
输入guardrail 运行分为3个步骤:
- 首先,guardrail 接收传递给智能体的相同输入。
- 接下来,guardrail 函数运行以生成一个 [GuardrailFunctionOutput][agents.guardrail.GuardrailFunctionOutput],然后将其封装在 [InputGuardrailResult][agents.guardrail.InputGuardrailResult] 中
- 最后,我们检查 [
.tripwire_triggered
][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] 是否为 true。
如果为 true,则抛出 [InputGuardrailTripwireTriggered
][agents.exceptions.InputGuardrailTripwireTriggered] 异常,这样您就可以适当地响应用户或处理该异常。
注意:输入保 guardrail 旨在运行在用户输入上,因此代理的保 guardrail 只有在代理是 第一个 代理时才会运行。
你可能想知道,为什么代理上的 guardrails
属性而不是传递给 Runner.run
?
这是因为 guardrail 通常与 实际的代理相关——你会为不同的代理 运行不同的保 guardrail ,因此将代码放在一起对于可读性是有用的。
三、输出 guardrail
输出防 guardrail 运行分为3个步骤:
- 首先,guardrail 接收与传递给代理相同的输入。
- 接着,guardrail 函数 运行以生成一个
[GuardrailFunctionOutput][agents.guardrail.GuardrailFunctionOutput]
,然后它被包装在一个[OutputGuardrailResult][agents.guardrail.OutputGuardrailResult]
中 - 最后,我们检查 [
.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")
- 我们将在我们的 guardrail 功能中使用这个代理。
- 这是一个接收代理的输入/上下文并返回结果的 guardrail 功能。
- 我们可以在 guardrail 结果中包含额外信息。
- 这实际上是定义工作流程的代理。
输出 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模型的 开箱即用支持:
OpenAIResponsesModel
][agents.models.openai_responses.OpenAIResponsesModel]。OpenAIChatCompletionsModel
][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel],它使用 Chat Completions API 调用 OpenAI API。二、混合和匹配模型
在单个工作流中,您可能希望为每个代理 使用不同的模型。
例如,您可以使用较小的、较快的模型进行分诊,而使用较大的、功能更强大的模型来完成复杂任务。
当配置一个Agent
时,您可以通过以下方式选择一个特定的模型:
- 传递一个 OpenAI 模型的名称。
- 传递任何模型名称 + 一个可以将该名称映射到模型实例的
ModelProvider
。 - 直接提供
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
一、关于上下文
上下文是一个多义词。你可能关心的上下文主要有两大类:
- 代码本地可用的上下文:这是在工具函数运行时、回调如
on_handoff
、生命周期钩子等情况下,你可能需要的数据和依赖。 - LLM 可用的上下文:这是 LLM 在生成响应时看到的数据。
二、本地上下文
这是通过 [RunContextWrapper
][agents.run_context.RunContextWrapper] 类和其中的 [context
][agents.run_context.RunContextWrapper.context] 属性来表示的。这种工作的方式是:
- 您可以创建任何您想要的 Python 对象。一个常见的模式是使用数据类或 Pydantic 对象。
- 你将那个对象传递给各种运行方法(例如
Runner.run(..., **context= whatever**)
)。 - 所有您的工具调用、生命周期钩子等都将传递一个包装对象,
RunContextWrapper[T]
,其中T
代表您的上下文对象类型,您可以通过wrapper.context
访问它。
最重要的需要注意的事情:给定的一次代理运行中,每个代理、工具函数、生命周期等都必须使用相同类型的上下文。
您可以使用上下文来进行诸如:
!!! 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())
- 这是一个上下文对象。这里我们使用了数据类,但你可以使用任何类型。
- 这是一个工具。你可以看到它接受一个
RunContextWrapper[UserInfo]
。工具实现从上下文中读取。 - 我们用泛型
UserInfo
标记代理,这样类型检查器可以捕获错误(例如,如果我们尝试传递一个接受不同上下文类型的工具)。 - 上下文传递给
run
函数。 - 代理正确地调用了工具并获取了年龄。
Agent/LLM 上下文
当调用一个语言模型时,它所能看到的唯一数据来自对话历史。
这意味着如果你想让一些新数据可用给LLM,你必须以使其可在该历史记录中可用的方式来实现。
有几种方法可以做到这一点:
- 你可以将其添加到代理的
instructions
中。这也可以称为system prompt
或developer message
。
系统提示可以是静态字符串,也可以是接收上下文并输出字符串的动态函数。
这是一种常用的策略,用于始终有用的信息(例如,用户的姓名或当前日期)。 - 在调用
Runner.run
函数时将其添加到input
中。
这与instructions
策略类似,但允许您拥有处于 chain of command 更低层级的消息。 - 通过函数工具公开它。这对于 按需 上下文很有用 – LLM 决定何时需要某些数据,并可以调用工具来获取这些数据。
- 使用检索或网络搜索。这些是特殊工具,能够从文件或数据库(检索)或从网络(网络搜索)中获取相关数据。这对于将响应“定位”在相关上下文数据中非常有用。
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)])
- 您可以直接使用代理(例如
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)!
)
- 这将在调用
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>.""",
)
多代理编排
一、关于 编排
编排指的是您应用中代理的流程。哪些代理运行,以何种顺序运行,以及它们如何决定下一步该做什么?编排代理主要有两种方式:
- 允许 LLM 做出决策:这利用 LLM 的智能来规划、推理并决定采取哪些步骤。
- 通过代码进行编排:通过您的代码确定代理的流程。
您可以根据需要混合搭配这些模式。每种模式都有其自身的权衡,具体描述如下。
二、通过 LLM 进行编排
一个代理是一个配备了指令、工具和Handoffs 的LLM。
这意味着给定一个开放性任务,LLM可以自主地规划如何应对任务,使用工具采取行动和获取数据,并使用Handoffs 将任务委派给子代理。
例如,一个研究代理可以配备如下工具:
这种模式在任务开放且希望依赖LLM的智能时非常出色。这里最重要的策略是:
- 投资于良好的提示。明确说明可用的工具、如何使用它们以及必须在其内操作的参数。
- 监控您的应用并对其进行迭代。查看哪里出了问题,并对您的提示进行迭代。
- 允许代理进行自我反思和改进。例如,将其在循环中运行,并让它自我批评;或者,提供错误信息并让它进行改进。
- 拥有专门从事一项任务的代理人,而不是拥有一个通用目的的代理人,期望其擅长任何事情。
- 投资于评估. 这让您可以训练您的智能体以改进并提高任务的能力。
三、通过代码进行编排
尽管通过 LLM 进行编排很强大,但通过代码进行编排可以使任务在速度、成本和性能方面更加确定性和可预测。这里的常见模式有:
while
循环运行执行任务的代理,同时使用一个评估并提供反馈的代理,直到评估者表示输出满足某些标准。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],如果你调用 run
或 run_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.created
、response.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,您可以在开发和生产过程中调试、可视化和监视工作流。
默认情况下,跟踪是启用的。有两种方法可以禁用跟踪:
- 您可以通过设置环境变量
OPENAI_AGENTS_DISABLE_TRACING=1
来全局禁用跟踪 - 您可以通过将[
agents.run.RunConfig.tracing_disabled
][]设置为True
来禁用单个运行的跟踪
二、Traces 和 spans
workflow_name
:这是逻辑工作流或应用程序。例如“代码生成”或“客户服务”。trace_id
:跟踪的唯一标识符。如果没有传递,将自动生成。必须具有格式trace_<32_alphanumeric>
。group_id
:可选的组ID,用于链接来自同一对话的多个跟踪。例如,您可能使用聊天线程ID。disabled
:如果为True,则不会记录跟踪。metadata
:跟踪的可选元数据。started_at
和ended_at
时间戳。trace_id
,表示它们所属的跟踪。parent_id
,它指向此跨度的父跨度(如果有)。span_data
,它包含关于跨度的信息。例如,AgentSpanData
包含有关代理的信息,GenerationSpanData
包含有关LLM生成的信息等。三、默认跟踪
默认情况下,SDK 跟踪以下内容:
Runner.{run, run_sync, run_streamed}()
都被 trace()
包裹。agent_span()
包裹generation_span()
中function_span()
中guardrail_span()
包装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}")
- 因为两次对
Runner.run
的调用都被with trace()
包裹,所以单个运行将作为整体跟踪的一部分,而不是创建两个跟踪。
五、创建追踪
您可以使用 [
trace()`][agents.tracing.trace] 函数来创建一个跟踪。跟踪需要开始和结束。您有两种方法来做这件事:
- 推荐:使用
trace
作为上下文管理器,即with trace(...) as my_trace
。这将自动在正确的时间开始和结束跟踪。 - 您还可以手动调用
['trace.start()']
和['trace.finish()']
。
当前追踪是通过 Python 的 contextvar
上下文变量 来跟踪的。这意味着它能够自动处理并发。
如果你手动开始/结束追踪,你需要将 mark_as_current
和 reset_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 后端。要自定义此默认设置,将跟踪发送到替代或额外的后端或修改导出器行为,您有两个选项:
- [
add_trace_processor()
][agents.tracing.add_trace_processor] 允许您添加一个 附加 的跟踪处理器,该处理器将接收已准备好的跟踪和跨度。这使您能够在将跟踪发送到 OpenAI 的后端之外执行自己的处理。 - [
set_trace_processors()
][agents.tracing.set_trace_processors] 允许你 替换 默认的处理器为你自己的跟踪处理器。
这意味着除非你包含一个将跟踪发送到 OpenAI 后端的TracingProcessor
,否则跟踪将不会被发送到 OpenAI 后端。
外部跟踪处理器包括:
2025-03-12(三)
作者:知识搬运bot