diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index 0b90d295c..624966a96 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -19,6 +19,7 @@ on: - mkdocs.yml - '!docs/ja/**' - '!docs/ko/**' + - '!docs/zh/**' permissions: contents: write diff --git a/docs/scripts/translate_docs.py b/docs/scripts/translate_docs.py index c480ac3cd..b2b619ec9 100644 --- a/docs/scripts/translate_docs.py +++ b/docs/scripts/translate_docs.py @@ -27,6 +27,7 @@ languages = { "ja": "Japanese", "ko": "Korean", + "zh": "Chinese", # Add more languages here, e.g., "fr": "French" } @@ -114,6 +115,30 @@ "Build your first agent in minutes.": "단 몇 분 만에 첫 에이전트를 만들 수 있습니다", "Let's build": "시작하기", }, + "zh": { + "agents": "智能体", + "computer use": "计算机操作", + "OAI hosted tools": "由OpenAI托管的工具", + "well formed data": "格式良好的数据", + "guardrail": "安全防护措施", + "handoffs": "任务转移", + "function tools": "工具调用", + "tracing": "追踪", + "code examples": "代码示例", + "vector store": "向量存储", + "deep research": "深度研究", + "category": "目录", + "user": "用户", + "parameter": "参数", + "processor": "进程", + "server": "服务", + "web search": "网络检索", + "file search": "文件检索", + "streaming": "流式传输", + "system prompt": "系统提示词", + "Python first": "Python 优先", + # Add more mappings here + }, # Add more languages here } eng_to_non_eng_instructions = { @@ -136,6 +161,13 @@ "* 'instructions', 'tools' 같은 API 매개변수와 temperature, top_p, max_tokens, presence_penalty, frequency_penalty 등은 영문 그대로 유지하세요.", "* 문장이 아닌 불릿 항목 끝에는 마침표를 찍지 마세요.", ], + "zh": [ + "* The term 'examples' must be code examples when the page mentions the code examples in the repo, it can be translated as either 'code examples' or 'sample code'.", + "* The term 'primitives' can be translated as basic components.", + "* When the terms 'instructions' and 'tools' are mentioned as API parameter names, they must be kept as is.", + "* The terms 'temperature', 'top_p', 'max_tokens', 'presence_penalty', 'frequency_penalty' as parameter names must be kept as is.", + "* Keep the original structure like `* **The thing**: foo`; this needs to be translated as `* **(translation)**: (translation)`", + ], # Add more languages here } diff --git a/docs/zh/agents.md b/docs/zh/agents.md new file mode 100644 index 000000000..5ad426052 --- /dev/null +++ b/docs/zh/agents.md @@ -0,0 +1,285 @@ +# 智能体 + +智能体是应用程序中的核心构建块。一个智能体是一个大型语言模型(LLM),配置了指令和工具。 + +## 基本配置 + +智能体最常用到的配置属性包括: + +- `name`: 一个必需的字符串,用于标识你的智能体。 +- `instructions`: 也称为开发者消息或系统提示。 +- `model`: 使用哪个LLM,以及可选的 `model_settings` 来配置模型调优参数如temperature、top_p等。 +- `tools`: 智能体可以用来完成任务的工具。 + +```python +from agents import Agent, ModelSettings, function_tool + +@function_tool +def get_weather(city: str) -> str: + """returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + +agent = Agent( + name="Haiku agent", + instructions="Always respond in haiku form", + model="gpt-5-nano", + tools=[get_weather], +) +``` + +## 上下文 + +智能体对于其 `context` 类型是通用的。上下文是一种依赖注入工具:它是你创建并传递给 `Runner.run()` 的对象,会传递给每个智能体、工具、交接等,作为智能体运行的依赖项和状态的容器。你可以提供任何Python对象作为上下文。 + +```python +@dataclass +class UserContext: + name: str + uid: str + is_pro_user: bool + + async def fetch_purchases() -> list[Purchase]: + return ... + +agent = Agent[UserContext]( + ..., +) +``` + +## 输出类型 + +默认情况下,智能体会产生纯文本(即 `str`)输出。如果你想让智能体产生特定类型的输出,可以使用 `output_type` 参数。一个常见的选择是使用 [Pydantic](https://docs.pydantic.dev/) 对象,但我们支持任何可以包装在 Pydantic [TypeAdapter](https://docs.pydantic.dev/latest/api/type_adapter/) 中的类型 - 数据类、列表、TypedDict等。 + +```python +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` 时,这告诉模型使用 [结构化输出](https://platform.openai.com/docs/guides/structured-outputs) 而不是常规的纯文本响应。 + +## 多智能体系统设计模式 + +设计多智能体系统有很多方法,但我们通常看到两种广泛适用的模式: + +1. 管理器(智能体作为工具):一个中央管理器/编排器调用作为工具公开的专门子智能体,并保持对对话的控制。 +2. 交接:对等智能体将控制权委托给一个专门的智能体,该智能体接管对话。这是分散式的。 + +更多详细信息请参见[我们的智能体构建实用指南](https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf)。 + +### 管理器(智能体作为工具) + +`customer_facing_agent` 处理所有用户交互,并调用作为工具公开的专门子智能体。更多详细信息请参见 [tools](tools.md#agents-as-tools) 文档。 + +```python +from agents import Agent + +booking_agent = Agent(...) +refund_agent = Agent(...) + +customer_facing_agent = Agent( + name="Customer-facing agent", + instructions=( + "Handle all direct user communication. " + "Call the relevant tools when specialized expertise is needed." + ), + tools=[ + booking_agent.as_tool( + tool_name="booking_expert", + tool_description="Handles booking questions and requests.", + ), + refund_agent.as_tool( + tool_name="refund_expert", + tool_description="Handles refund questions and requests.", + ) + ], +) +``` + +### 交接 + +交接是智能体可以委托的子智能体。当发生交接时,被委托的智能体接收对话历史并接管对话。这种模式使得在单一任务上表现出色的模块化专门智能体成为可能。更多详细信息请参见 [handoffs](handoffs.md) 文档。 + +```python +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, hand off to the booking agent. " + "If they ask about refunds, hand off to the refund agent." + ), + handoffs=[booking_agent, refund_agent], +) +``` + +## 动态指令 + +在大多数情况下,你可以在创建智能体时提供指令。然而,你也可以通过函数提供动态指令。该函数将接收智能体和上下文,并必须返回提示。既可以使用普通函数,也可以使用 `async` 函数。 + +```python +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` 属性在智能体的生命周期中设置钩子。子类化 [`AgentHooks`][agents.lifecycle.AgentHooks] 类,并覆盖你感兴趣的方法。 + +## 护栏 + +护栏允许你在智能体运行的同时并行运行对用户输入的检查/验证,并在智能体输出产生后对其进行检查/验证。例如,你可以根据相关性筛选用户输入和智能体输出。更多详细信息请参见 [guardrails](guardrails.md) 文档。 + +## 克隆/复制智能体 + +通过使用智能体上的 `clone()` 方法,你可以复制智能体,并选择性地更改任何属性。 + +```python +pirate_agent = Agent( + name="Pirate", + instructions="Write like a pirate", + model="gpt-4.1", +) + +robot_agent = pirate_agent.clone( + name="Robot", + instructions="Write like a robot", +) +``` + +## 强制工具使用 + +提供工具列表并不总是意味着LLM会使用工具。你可以通过设置 [`ModelSettings.tool_choice`][agents.model_settings.ModelSettings.tool_choice] 来强制工具使用。有效值包括: + +1. `auto`,允许LLM决定是否使用工具。 +2. `required`,要求LLM使用工具(但它可以智能地决定使用哪个工具)。 +3. `none`,要求LLM _不_ 使用工具。 +4. 设置特定字符串,例如 `my_tool`,要求LLM使用该特定工具。 + +```python +from agents import Agent, Runner, function_tool, ModelSettings + +@function_tool +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + +agent = Agent( + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], + model_settings=ModelSettings(tool_choice="get_weather") +) +``` + +## 工具使用行为 + +`Agent` 配置中的 `tool_use_behavior` 参数控制如何处理工具输出: + +- `"run_llm_again"`: 默认。工具运行后,LLM处理结果以产生最终响应。 +- `"stop_on_first_tool"`: 第一个工具调用的输出用作最终响应,无需进一步的LLM处理。 + +```python +from agents import Agent, Runner, function_tool, ModelSettings + +@function_tool +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + +agent = Agent( + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], + tool_use_behavior="stop_on_first_tool" +) +``` + +- `StopAtTools(stop_at_tool_names=[...])`: 当指定的工具中的任何一个被调用时停止,并将其输出用作最终响应。 + +```python +from agents import Agent, Runner, function_tool +from agents.agent import StopAtTools + +@function_tool +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + +@function_tool +def sum_numbers(a: int, b: int) -> int: + """Adds two numbers.""" + return a + b + +agent = Agent( + name="Stop At Stock Agent", + instructions="Get weather or sum numbers.", + tools=[get_weather, sum_numbers], + tool_use_behavior=StopAtTools(stop_at_tool_names=["get_weather"]) +) +``` + +- `ToolsToFinalOutputFunction`: 处理工具结果并决定是停止还是继续LLM的自定义函数。 + +```python +from agents import Agent, Runner, function_tool, FunctionToolResult, RunContextWrapper +from agents.agent import ToolsToFinalOutputResult +from typing import List, Any + +@function_tool +def get_weather(city: str) -> str: + """Returns weather info for the specified city.""" + return f"The weather in {city} is sunny" + +def custom_tool_handler( + context: RunContextWrapper[Any], + tool_results: List[FunctionToolResult] +) -> ToolsToFinalOutputResult: + """Processes tool results to decide final output.""" + for result in tool_results: + if result.output and "sunny" in result.output: + return ToolsToFinalOutputResult( + is_final_output=True, + final_output=f"Final weather: {result.output}" + ) + return ToolsToFinalOutputResult( + is_final_output=False, + final_output=None + ) + +agent = Agent( + name="Weather Agent", + instructions="Retrieve weather details.", + tools=[get_weather], + tool_use_behavior=custom_tool_handler +) +``` + +!!! note + + 为了防止无限循环,框架会在工具调用后自动将 `tool_choice` 重置为 "auto"。此行为可通过 [`agent.reset_tool_choice`][agents.agent.Agent.reset_tool_choice] 进行配置。无限循环的发生是因为工具结果被发送给LLM,然后由于 `tool_choice` 导致LLM再次生成工具调用,如此循环往复。 \ No newline at end of file diff --git a/docs/zh/config.md b/docs/zh/config.md new file mode 100644 index 000000000..798aaef1b --- /dev/null +++ b/docs/zh/config.md @@ -0,0 +1,98 @@ +--- +search: + exclude: true +--- +# 配置 SDK + +## API 密钥和客户端 + +默认情况下,SDK 会在导入后立即查找 `OPENAI_API_KEY` 环境变量以进行 LLM 请求和追踪。如果你无法在应用启动前设置该环境变量,可以使用 [set_default_openai_key()][agents.set_default_openai_key] 函数来设置密钥。 + +```python +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] 函数来更改此设置。 + +```python +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 Responses API。你可以通过使用 [set_default_openai_api()][agents.set_default_openai_api] 函数来覆盖此设置以使用聊天完成 API。 + +```python +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 密钥。 + +```python +from agents import set_tracing_export_api_key + +set_tracing_export_api_key("sk-...") +``` + +你还可以通过使用 [`set_tracing_disabled()`][agents.set_tracing_disabled] 函数来完全禁用追踪。 + +```python +from agents import set_tracing_disabled + +set_tracing_disabled(True) +``` + +## 调试日志记录 + +SDK 有两个 Python 日志记录器,没有设置任何处理程序。默认情况下,这意味着警告和错误会发送到 `stdout`,但其他日志会被抑制。 + +要启用详细日志记录,请使用 [`enable_verbose_stdout_logging()`][agents.enable_verbose_stdout_logging] 函数。 + +```python +from agents import enable_verbose_stdout_logging + +enable_verbose_stdout_logging() +``` + +或者,你可以通过添加处理程序、过滤器、格式化程序等来定制日志。你可以在 [Python 日志记录指南](https://docs.python.org/3/howto/logging.html) 中阅读更多内容。 + +```python +import logging + +logger = logging.getLogger("openai.agents") # 或 openai.agents.tracing 用于追踪日志记录器 + +# 要使所有日志显示 +logger.setLevel(logging.DEBUG) +# 要使信息及以上级别显示 +logger.setLevel(logging.INFO) +# 要使警告及以上级别显示 +logger.setLevel(logging.WARNING) +# 等等 + +# 你可以根据需要自定义此设置,但默认情况下这会输出到 `stderr` +logger.addHandler(logging.StreamHandler()) +``` + +### 日志中的敏感数据 + +某些日志可能包含敏感数据(例如,用户数据)。如果你想禁用记录这些数据,请设置以下环境变量。 + +要禁用记录 LLM 输入和输出: + +```bash +export OPENAI_AGENTS_DONT_LOG_MODEL_DATA=1 +``` + +要禁用记录工具输入和输出: + +```bash +export OPENAI_AGENTS_DONT_LOG_TOOL_DATA=1 +``` \ No newline at end of file diff --git a/docs/zh/context.md b/docs/zh/context.md new file mode 100644 index 000000000..1176d54f6 --- /dev/null +++ b/docs/zh/context.md @@ -0,0 +1,82 @@ +--- +search: + exclude: true +--- +# 上下文管理 + +"上下文"是一个含义丰富的术语。你可能关心的主要有两类上下文: + +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。它纯粹是一个本地对象,你可以读取、写入和调用其方法。 + +```python +import asyncio +from dataclasses import dataclass + +from agents import Agent, RunContextWrapper, Runner, function_tool + +@dataclass +class UserInfo: # (1)! + name: str + uid: int + +@function_tool +async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)! + """Fetch the age of the user. Call this function to get user's age information.""" + return f"The user {wrapper.context.name} is 47 years old" + +async def main(): + user_info = UserInfo(name="John", uid=123) + + agent = Agent[UserInfo]( # (3)! + name="Assistant", + tools=[fetch_user_age], + ) + + result = await Runner.run( # (4)! + 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. 智能体正确调用工具并获取年龄。 + +## 智能体/LLM上下文 + +当调用LLM时,它**只能**看到来自对话历史的数据。这意味着如果你想让LLM看到一些新数据,你必须以使其在该历史中可用的方式来实现。有几种方法可以做到这一点: + +1. 你可以将其添加到智能体的`instructions`中。这也被称为"系统提示"或"开发者消息"。系统提示可以是静态字符串,也可以是接收上下文并输出字符串的动态函数。这对于始终有用的信息(例如,用户名或当前日期)是常见策略。 +2. 在调用`Runner.run`函数时将其添加到`input`中。这类似于`instructions`策略,但允许你在[命令链](https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command)中拥有较低位置的消息。 +3. 通过函数工具公开它。这对于_按需_上下文很有用 - LLM决定何时需要某些数据,并可以调用工具来获取该数据。 +4. 使用检索或网络搜索。这些是特殊工具,能够从文件或数据库(检索),或从网络(网络搜索)获取相关数据。这对于在相关上下文中"基于"响应很有用。 \ No newline at end of file diff --git a/docs/zh/examples.md b/docs/zh/examples.md new file mode 100644 index 000000000..701524f5c --- /dev/null +++ b/docs/zh/examples.md @@ -0,0 +1,93 @@ +--- +search: + exclude: true +--- +# 使用示例 + +在[仓库](https://github.com/openai/openai-agents-python/tree/main/examples)的示例部分查看SDK的各种示例实现。这些示例被组织成几个类别,展示了不同的模式和功能。 + +## 示例分类 + +- **[agent_patterns](https://github.com/openai/openai-agents-python/tree/main/examples/agent_patterns):** + 此分类中的示例展示了常见的智能体设计模式,例如: + + - 确定性工作流 + - 智能体作为工具 + - 智能体并行执行 + - 条件工具使用 + - 输入/输出护栏 + - LLM作为评判者 + - 路由 + - 流式护栏 + +- **[basic](https://github.com/openai/openai-agents-python/tree/main/examples/basic):** + 这些示例展示了SDK的基础功能,例如: + + - Hello World示例(默认模型、GPT-5、开源权重模型) + - 智能体生命周期管理 + - 动态系统提示 + - 流式输出(文本、项目、函数调用参数) + - 提示模板 + - 文件处理(本地和远程、图像和PDF) + - 使用情况跟踪 + - 非严格输出类型 + - 先前响应ID使用 + +- **[customer_service](https://github.com/openai/openai-agents-python/tree/main/examples/customer_service):** + 航空公司客户服务系统示例。 + +- **[financial_research_agent](https://github.com/openai/openai-agents-python/tree/main/examples/financial_research_agent):** + 金融研究智能体,展示了使用智能体和工具进行金融数据分析的结构化研究工作流。 + +- **[handoffs](https://github.com/openai/openai-agents-python/tree/main/examples/handoffs):** + 查看带有消息过滤的智能体交接的实际示例。 + +- **[hosted_mcp](https://github.com/openai/openai-agents-python/tree/main/examples/hosted_mcp):** + 展示如何使用托管MCP(模型上下文协议)连接器和批准的示例。 + +- **[mcp](https://github.com/openai/openai-agents-python/tree/main/examples/mcp):** + 学习如何使用MCP(模型上下文协议)构建智能体,包括: + + - 文件系统示例 + - Git示例 + - MCP提示服务器示例 + - SSE(服务器发送事件)示例 + - 可流式HTTP示例 + +- **[memory](https://github.com/openai/openai-agents-python/tree/main/examples/memory):** + 智能体的不同内存实现示例,包括: + + - SQLite会话存储 + - 高级SQLite会话存储 + - Redis会话存储 + - SQLAlchemy会话存储 + - 加密会话存储 + - OpenAI会话存储 + +- **[model_providers](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers):** + 探索如何在SDK中使用非OpenAI模型,包括自定义提供程序和LiteLLM集成。 + +- **[realtime](https://github.com/openai/openai-agents-python/tree/main/examples/realtime):** + 展示如何使用SDK构建实时体验的示例,包括: + + - Web应用程序 + - 命令行界面 + - Twilio集成 + +- **[reasoning_content](https://github.com/openai/openai-agents-python/tree/main/examples/reasoning_content):** + 展示如何处理推理内容和结构化输出的示例。 + +- **[research_bot](https://github.com/openai/openai-agents-python/tree/main/examples/research_bot):** + 简单的深度研究克隆,展示了复杂的多智能体研究工作流。 + +- **[tools](https://github.com/openai/openai-agents-python/tree/main/examples/tools):** + 学习如何实现OpenAI托管工具: + + - 网络搜索和带过滤的网络搜索 + - 文件搜索 + - 代码解释器 + - 计算机使用 + - 图像生成 + +- **[voice](https://github.com/openai/openai-agents-python/tree/main/examples/voice):** + 查看使用我们的TTS和STT模型的语音智能体示例,包括流式语音示例。 \ No newline at end of file diff --git a/docs/zh/guardrails.md b/docs/zh/guardrails.md new file mode 100644 index 000000000..baa6628a3 --- /dev/null +++ b/docs/zh/guardrails.md @@ -0,0 +1,158 @@ +--- +search: + exclude: true +--- +# 护栏 + +护栏与你的智能体 _并行_ 运行,使你能够对用户输入进行检查和验证。例如,假设你有一个使用非常智能(因此速度慢/成本高)模型的智能体来帮助处理客户请求。你不会希望恶意用户要求模型帮助他们做数学作业。因此,你可以使用一个快速/低成本的模型运行护栏。如果护栏检测到恶意使用,它可以立即引发错误,这会阻止昂贵的模型运行并为你节省时间/金钱。 + +有两种护栏: + +1. 输入护栏在初始用户输入上运行 +2. 输出护栏在最终的智能体输出上运行 + +## 输入护栏 + +输入护栏分3步运行: + +1. 首先,护栏接收传递给智能体的相同输入。 +2. 接下来,护栏函数运行以产生一个 [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput],然后将其包装在 [`InputGuardrailResult`][agents.guardrail.InputGuardrailResult] 中 +3. 最后,我们检查 [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] 是否为真。如果为真,会引发一个 [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered] 异常,这样你就可以适当地响应用户或处理异常。 + +!!! 注意 + + 输入护栏旨在在用户输入上运行,因此智能体的护栏仅在智能体是 *第一个* 智能体时才运行。你可能想知道,为什么 `guardrails` 属性在智能体上而不是传递给 `Runner.run`?这是因为护栏往往与实际的智能体相关 - 你会为不同的智能体运行不同的护栏,因此将代码并置有助于可读性。 + +## 输出护栏 + +输出护栏分3步运行: + +1. 首先,护栏接收智能体产生的输出。 +2. 接下来,护栏函数运行以产生一个 [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput],然后将其包装在 [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult] 中 +3. 最后,我们检查 [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] 是否为真。如果为真,会引发一个 [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] 异常,这样你就可以适当地响应用户或处理异常。 + +!!! 注意 + + 输出护栏旨在在最终的智能体输出上运行,因此智能体的护栏仅在智能体是 *最后一个* 智能体时才运行。与输入护栏类似,我们这样做是因为护栏往往与实际的智能体相关 - 你会为不同的智能体运行不同的护栏,因此将代码并置有助于可读性。 + +## 触发器 + +如果输入或输出未通过护栏,护栏可以通过触发器发出信号。一旦我们看到已触发触发器的护栏,我们会立即引发一个 `{Input,Output}GuardrailTripwireTriggered` 异常并停止智能体执行。 + +## 实现护栏 + +你需要提供一个接收输入并返回 [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput] 的函数。在这个例子中,我们将通过在底层运行一个智能体来实现这一点。 + +```python +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. 我们将在护栏函数中使用这个智能体。 +2. 这是接收智能体输入/上下文并返回结果的护栏函数。 +3. 我们可以在护栏结果中包含额外信息。 +4. 这是定义工作流的实际智能体。 + +输出护栏类似。 + +```python +from pydantic import BaseModel +from agents import ( + Agent, + GuardrailFunctionOutput, + OutputGuardrailTripwireTriggered, + RunContextWrapper, + Runner, + output_guardrail, +) +class MessageOutput(BaseModel): # (1)! + response: str + +class MathOutput(BaseModel): # (2)! + reasoning: str + is_math: bool + +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") +``` + +1. 这是实际智能体的输出类型。 +2. 这是护栏的输出类型。 +3. 这是接收智能体输出并返回结果的护栏函数。 +4. 这是定义工作流的实际智能体。 \ No newline at end of file diff --git a/docs/zh/handoffs.md b/docs/zh/handoffs.md new file mode 100644 index 000000000..8423d7a4b --- /dev/null +++ b/docs/zh/handoffs.md @@ -0,0 +1,118 @@ +--- +search: + exclude: true +--- +# 交接 + +交接允许智能体将任务委托给另一个智能体。这在不同智能体专门从事不同领域的场景中特别有用。例如,客户支持应用可能让每个智能体专门处理订单状态、退款、常见问题等任务。 + +交接对LLM来说表示为工具。因此,如果有一个名为 `Refund Agent` 的智能体交接,该工具将被调用为 `transfer_to_refund_agent`。 + +## 创建交接 + +所有智能体都有一个 [`handoffs`][agents.agent.Agent.handoffs] 参数,它可以直接接受一个 `Agent`,或者一个自定义交接的 `Handoff` 对象。 + +你可以使用Agents SDK提供的 [`handoff()`][agents.handoffs.handoff] 函数创建交接。这个函数允许你指定要交接到的智能体,以及可选的覆盖和输入过滤器。 + +### 基本用法 + +以下是你如何创建一个简单交接的方法: + +```python +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()` 函数。 + +### 通过 `handoff()` 函数自定义交接 + +[`handoff()`][agents.handoffs.handoff] 函数允许你自定义内容。 + +- `agent`: 这是将要交接到的智能体。 +- `tool_name_override`: 默认情况下,使用 `Handoff.default_tool_name()` 函数,该函数解析为 `transfer_to_`。你可以覆盖这个名称。 +- `tool_description_override`: 覆盖来自 `Handoff.default_tool_description()` 的默认工具描述 +- `on_handoff`: 调用交接时执行的回调函数。这对于诸如一旦知道正在调用交接就启动一些数据获取之类的事情很有用。这个函数接收智能体上下文,还可以选择接收LLM生成的输入。输入数据由 `input_type` 参数控制。 +- `input_type`: 交接期望的输入类型(可选)。 +- `input_filter`: 这允许你过滤下一个智能体接收的输入。详见下文。 +- `is_enabled`: 是否启用交接。这可以是布尔值或返回布尔值的函数,允许你在运行时动态启用或禁用交接。 + +```python +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", +) +``` + +## 交接输入 + +在某些情况下,你希望LLM在调用交接时提供一些数据。例如,想象一个交接给"升级智能体"。你可能想要提供一个原因,以便你可以记录它。 + +```python +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, +) +``` + +## 输入过滤器 + +当发生交接时,就好像新智能体接管了对话,并且可以看到整个之前的对话历史。如果你想改变这一点,你可以设置一个 [`input_filter`][agents.handoffs.Handoff.input_filter]。输入过滤器是一个接收通过 [`HandoffInputData`][agents.handoffs.HandoffInputData] 的现有输入的函数,并且必须返回一个新的 `HandoffInputData`。 + +有一些常见的模式(例如从历史记录中删除所有工具调用),这些模式在 [`agents.extensions.handoff_filters`][] 中为你实现。 + +```python +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` 时,这将自动从历史记录中删除所有工具。 + +## 推荐的提示 + +为了确保LLM正确理解交接,我们建议在你的智能体中包含关于交接的信息。我们在 [`agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX`][] 中有一个建议的前缀,或者你可以调用 [`agents.extensions.handoff_prompt.prompt_with_handoff_instructions`][] 来自动将推荐数据添加到你的提示中。 + +```python +from agents import Agent +from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX + +billing_agent = Agent( + name="Billing agent", + instructions=f"""{RECOMMENDED_PROMPT_PREFIX} + .""", +) +``` \ No newline at end of file diff --git a/docs/zh/index.md b/docs/zh/index.md new file mode 100644 index 000000000..adfe32ae4 --- /dev/null +++ b/docs/zh/index.md @@ -0,0 +1,58 @@ +--- +search: + exclude: true +--- +# OpenAI Agents SDK + +[OpenAI Agents SDK](https://github.com/openai/openai-agents-python) 让您能够在轻量级、易于使用的软件包中构建智能体 AI 应用,抽象化程度极低。它是我们之前用于智能体的实验项目 [Swarm](https://github.com/openai/swarm/tree/main) 的生产级升级版本。Agents SDK 只包含极少量原语: + +- **Agents**,即配备指令和工具的 LLM +- **Handoffs**,允许智能体将特定任务委托给其他智能体 +- **Guardrails**,支持对智能体输入和输出进行验证 +- **Sessions**,在智能体运行之间自动维护对话历史 + +结合 Python,这些原语足以表达工具和智能体之间的复杂关系,让您无需陡峭的学习曲线即可构建实际应用。此外,SDK 还内置了**追踪**功能,让您能够可视化和调试智能体流程,以及评估它们,甚至为您的应用微调模型。 + +## 为何使用 Agents SDK + +SDK 有两个核心设计原则: + +1. 功能足够丰富以值得使用,但原语足够少以便快速学习。 +2. 开箱即用表现出色,但您可以精确自定义发生的情况。 + +以下是 SDK 的主要功能: + +- Agent loop:内置的智能体循环,处理调用工具、将结果发送给 LLM,以及循环直到 LLM 完成。 +- Python-first:使用内置语言功能来编排和链接智能体,而非需要学习新的抽象概念。 +- Handoffs:在多个智能体之间协调和委托的强大功能。 +- Guardrails:与智能体并行运行输入验证和检查,如果检查失败则提前中断。 +- Sessions:跨智能体运行自动管理对话历史,消除手动状态处理。 +- Function tools:将任何 Python 函数转换为工具,具有自动模式生成和 Pydantic 驱动的验证。 +- Tracing:内置追踪功能让您能够可视化、调试和监控工作流程,以及使用 OpenAI 的评估、微调和蒸馏工具套件。 + +## 安装 + +```bash +pip install openai-agents +``` + +## Hello world 示例 + +```python +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` 环境变量_) + +```bash +export OPENAI_API_KEY=sk-... +``` \ No newline at end of file diff --git a/docs/zh/mcp.md b/docs/zh/mcp.md new file mode 100644 index 000000000..2613c70ab --- /dev/null +++ b/docs/zh/mcp.md @@ -0,0 +1,322 @@ +--- +search: + exclude: true +--- +# 模型上下文协议 (MCP) + +[模型上下文协议](https://modelcontextprotocol.io/introduction) (MCP) 标准化了应用程序如何向语言模型公开工具和上下文的方法。来自官方文档: + +> MCP是一个开放协议,它标准化了应用程序如何向LLM提供上下文的方法。将MCP视为AI应用的USB-C端口。正如USB-C提供了一种将设备连接到各种外围设备和配件的标准化方式,MCP提供了一种将AI模型连接到不同数据源和工具的标准化方式。 + +Agents Python SDK支持多种MCP传输方式。这使你可以重用现有的MCP服务器或构建自己的服务器,将文件系统、HTTP或由连接器支持的工具暴露给智能体。 + +## 选择MCP集成 + +在将MCP服务器连接到智能体之前,决定工具调用应该在哪里执行以及你可以访问哪些传输方式。下表总结了Python SDK支持的选项。 + +| 你需要什么 | 推荐选项 | +| ------------------------------------------------------------------------------------ | --------------------------------------------------------- | +| 让OpenAI的Responses API代表模型调用公开可访问的MCP服务器 | **托管MCP服务器工具**(通过[`HostedMCPTool`][agents.tool.HostedMCPTool]) | +| 连接到你在本地或远程运行的可流式HTTP服务器 | **可流式HTTP MCP服务器**(通过[`MCPServerStreamableHttp`][agents.mcp.server.MCPServerStreamableHttp]) | +| 与实现了带服务器发送事件(SSE)的HTTP服务器进行交互 | **带SSE的HTTP MCP服务器**(通过[`MCPServerSse`][agents.mcp.server.MCPServerSse]) | +| 启动本地进程并通过stdin/stdout进行通信 | **stdio MCP服务器**(通过[`MCPServerStdio`][agents.mcp.server.MCPServerStdio]) | + +以下各节将介绍每个选项、如何配置它们,以及何时优先选择某个传输方式。 + +## 1. 托管MCP服务器工具 + +托管工具将整个工具往返过程推送到OpenAI的基础设施中。你的代码不再列出和调用工具,而是[`HostedMCPTool`][agents.tool.HostedMCPTool]将服务器标签(和可选的连接器元数据)转发到Responses API。模型列出远程服务器的工具并在没有额外回调到你的Python进程的情况下调用它们。托管工具目前适用于支持Responses API的托管MCP集成的OpenAI模型。 + +### 基本托管MCP工具 + +通过向智能体的`tools`列表添加[`HostedMCPTool`][agents.tool.HostedMCPTool]来创建托管工具。`tool_config`字典反映了你要发送到REST API的JSON: + +```python +import asyncio + +from agents import Agent, HostedMCPTool, Runner + +async def main() -> None: + agent = Agent( + name="Assistant", + tools=[ + HostedMCPTool( + tool_config={ + "type": "mcp", + "server_label": "gitmcp", + "server_url": "https://gitmcp.io/openai/codex", + "require_approval": "never", + } + ) + ], + ) + + result = await Runner.run(agent, "Which language is this repository written in?") + print(result.final_output) + +asyncio.run(main()) +``` + +托管服务器会自动公开其工具,无需将其添加到 `mcp_servers`。 + +### 支持流式传输的托管MCP执行结果 + +托管工具以与函数工具完全相同的方式支持流式传输。向 `Runner.run_streamed` 传递 `stream=True`,即可在模型仍在运行时增量获取MCP输出: + +```python +result = Runner.run_streamed(agent, "Summarise this repository's top languages") +async for event in result.stream_events(): + if event.type == "run_item_stream_event": + print(f"Received: {event.item}") +print(result.final_output) +``` + +### 可选的审批流程 + +如果服务器能够执行敏感操作,可以在每次工具执行前要求人工或程序审批。将 `tool_config` 中的 `require_approval` 设置为单一策略(`"always"`、`"never"`)或从工具名到策略的字典。要在Python中进行判断,请指定 `on_approval_request` 回调函数。 + +```python +from agents import MCPToolApprovalFunctionResult, MCPToolApprovalRequest + +SAFE_TOOLS = {"read_project_metadata"} + +def approve_tool(request: MCPToolApprovalRequest) -> MCPToolApprovalFunctionResult: + if request.data.name in SAFE_TOOLS: + return {"approve": True} + return {"approve": False, "reason": "Escalate to a human reviewer"} + +agent = Agent( + name="Assistant", + tools=[ + HostedMCPTool( + tool_config={ + "type": "mcp", + "server_label": "gitmcp", + "server_url": "https://gitmcp.io/openai/codex", + "require_approval": "always", + }, + on_approval_request=approve_tool, + ) + ], +) +``` + +回调可以是同步或异步的,每当模型需要审批数据才能继续执行时就会被调用。 + +### 支持连接器的托管服务器 + +托管MCP也支持OpenAI连接器。不指定 `server_url`,而是指定 `connector_id` 和访问令牌。Responses API会处理认证,托管服务器会公开该连接器的工具。 + +```python +import os + +HostedMCPTool( + tool_config={ + "type": "mcp", + "server_label": "google_calendar", + "connector_id": "connector_googlecalendar", + "authorization": os.environ["GOOGLE_CALENDAR_AUTHORIZATION"], + "require_approval": "never", + } +) +``` + +包含流式传输、审批和连接器的完整可运行托管工具示例可在 +[`examples/hosted_mcp`](https://github.com/openai/openai-agents-python/tree/main/examples/hosted_mcp) 中找到。 + +## 2. 可流式HTTP MCP服务器 + +如果你想自己管理网络连接,可以使用 [`MCPServerStreamableHttp`][agents.mcp.server.MCPServerStreamableHttp]。可流式HTTP服务器最适合需要自己控制传输方式的场景,或者在自己的基础设施中运行服务器同时保持低延迟的情况。 + +```python +import asyncio +import os + +from agents import Agent, Runner +from agents.mcp import MCPServerStreamableHttp +from agents.model_settings import ModelSettings + +async def main() -> None: + token = os.environ["MCP_SERVER_TOKEN"] + async with MCPServerStreamableHttp( + name="Streamable HTTP Python Server", + params={ + "url": "http://localhost:8000/mcp", + "headers": {"Authorization": f"Bearer {token}"}, + "timeout": 10, + }, + cache_tools_list=True, + max_retry_attempts=3, + ) as server: + agent = Agent( + name="Assistant", + instructions="Use the MCP tools to answer the questions.", + mcp_servers=[server], + model_settings=ModelSettings(tool_choice="required"), + ) + + result = await Runner.run(agent, "Add 7 and 22.") + print(result.final_output) + +asyncio.run(main()) +``` + +构造函数接受额外的选项: + +- `client_session_timeout_seconds` 控制HTTP读取超时时间。 +- `use_structured_content` 切换是否优先使用 `tool_result.structured_content` 而非文本输出。 +- `max_retry_attempts` 和 `retry_backoff_seconds_base` 为 `list_tools()` 和 `call_tool()` 添加自动重试。 +- `tool_filter` 可以将公开的工具限制为子集(参见[工具过滤](#工具过滤))。 + +## 3. 带SSE的HTTP MCP服务器 + +如果MCP服务器实现了带SSE(服务器发送事件)的HTTP传输,可以实例化 [`MCPServerSse`][agents.mcp.server.MCPServerSse]。除了传输方式外,API与可流式HTTP服务器完全相同。 + +```python + +from agents import Agent, Runner +from agents.model_settings import ModelSettings +from agents.mcp import MCPServerSse + +workspace_id = "demo-workspace" + +async with MCPServerSse( + name="SSE Python Server", + params={ + "url": "http://localhost:8000/sse", + "headers": {"X-Workspace": workspace_id}, + }, + cache_tools_list=True, +) as server: + agent = Agent( + name="Assistant", + mcp_servers=[server], + model_settings=ModelSettings(tool_choice="required"), + ) + result = await Runner.run(agent, "What's the weather in Tokyo?") + print(result.final_output) +``` + +## 4. stdio MCP服务器 + +对于作为本地子进程运行的MCP服务器,使用 [`MCPServerStdio`][agents.mcp.server.MCPServerStdio]。SDK会启动进程,保持管道打开,并在上下文管理器退出时自动关闭。此选项适用于快速原型开发,或者当服务器仅公开命令行入口点时。 + +```python +from pathlib import Path +from agents import Agent, Runner +from agents.mcp import MCPServerStdio + +current_dir = Path(__file__).parent +samples_dir = current_dir / "sample_files" + +async with MCPServerStdio( + name="Filesystem Server via npx", + params={ + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", str(samples_dir)], + }, +) as server: + agent = Agent( + name="Assistant", + instructions="Use the files in the sample directory to answer questions.", + mcp_servers=[server], + ) + result = await Runner.run(agent, "List the files available to you.") + print(result.final_output) +``` + +## 工具过滤 + +每个MCP服务器都支持工具过滤器,可以只公开智能体需要的函数。过滤可以在构建时进行,也可以在每次运行时动态进行。 + +### 静态工具过滤 + +使用 [`create_static_tool_filter`][agents.mcp.create_static_tool_filter] 设置简单的允许/阻止列表: + +```python +from pathlib import Path + +from agents.mcp import MCPServerStdio, create_static_tool_filter + +samples_dir = Path("/path/to/files") + +filesystem_server = MCPServerStdio( + params={ + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", str(samples_dir)], + }, + tool_filter=create_static_tool_filter(allowed_tool_names=["read_file", "write_file"]), +) +``` + +如果同时指定了 `allowed_tool_names` 和 `blocked_tool_names`,SDK会首先应用允许列表,然后从剩余集合中删除被阻止的工具。 + +### 动态工具过滤 + +对于更高级的逻辑,可以传递一个接受 [`ToolFilterContext`][agents.mcp.ToolFilterContext] 的可调用对象。可调用对象可以是同步或异步的,当应该公开工具时返回 `True`。 + +```python +from pathlib import Path + +from agents.mcp import MCPServerStdio, ToolFilterContext + +samples_dir = Path("/path/to/files") + +async def context_aware_filter(context: ToolFilterContext, tool) -> bool: + if context.agent.name == "Code Reviewer" and tool.name.startswith("danger_"): + return False + return True + +async with MCPServerStdio( + params={ + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", str(samples_dir)], + }, + tool_filter=context_aware_filter, +) as server: + ... +``` + +过滤器上下文公开了活动的 `run_context`、请求工具的 `agent` 以及 `server_name`。 + +## 提示词 + +MCP服务器还可以提供提示词来动态生成智能体的指令。支持提示词的服务器会公开以下两个方法: + +- `list_prompts()` 枚举可用的提示词模板。 +- `get_prompt(name, arguments)` 获取具体的提示词,必要时可带参数。 + +```python +from agents import Agent + +prompt_result = await server.get_prompt( + "generate_code_review_instructions", + {"focus": "security vulnerabilities", "language": "python"}, +) +instructions = prompt_result.messages[0].content.text + +agent = Agent( + name="Code Reviewer", + instructions=instructions, + mcp_servers=[server], +) +``` + +## 缓存 + +所有智能体执行都会对每个MCP服务器调用 `list_tools()`。由于远程服务器可能导致显著的延迟,所有MCP服务器类都公开了 `cache_tools_list` 选项。只有当你确信工具定义不会频繁更改时才设置为 `True`。如需在之后强制获取新列表,可以在服务器实例上调用 `invalidate_tools_cache()`。 + +## 追踪 + +[追踪](./tracing.md)会自动捕获MCP活动,包括: + +1. 为枚举工具而对MCP服务器的调用。 +2. 与工具调用相关的MCP信息。 + +![MCP追踪截图](../assets/images/mcp-tracing.jpg) + +## 参考资料 + +- [模型上下文协议](https://modelcontextprotocol.io/) – 规范与设计指南。 +- [examples/mcp](https://github.com/openai/openai-agents-python/tree/main/examples/mcp) – 可运行的stdio、SSE、可流式HTTP示例。 +- [examples/hosted_mcp](https://github.com/openai/openai-agents-python/tree/main/examples/hosted_mcp) – 包含审批和连接器的完整托管MCP演示。 diff --git a/docs/zh/models/index.md b/docs/zh/models/index.md new file mode 100644 index 000000000..7235d8800 --- /dev/null +++ b/docs/zh/models/index.md @@ -0,0 +1,192 @@ +--- +search: + exclude: true +--- +# 模型 + +Agents SDK 内置支持两种 OpenAI 模型: + +- **推荐**:[`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel],使用新的 [Responses API](https://platform.openai.com/docs/api-reference/responses) 调用 OpenAI API。 +- [`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel],使用 [Chat Completions API](https://platform.openai.com/docs/api-reference/chat) 调用 OpenAI API。 + +## OpenAI 模型 + +初始化 `Agent` 时如果没有指定模型,将使用默认模型。当前默认模型是 [`gpt-4.1`](https://platform.openai.com/docs/models/gpt-4.1),它在智能体工作流的可预测性和低延迟之间提供了良好的平衡。 + +如果你想切换到其他模型如 [`gpt-5`](https://platform.openai.com/docs/models/gpt-5),请按照下一节的步骤操作。 + +### 默认 OpenAI 模型 + +如果你想为所有未设置自定义模型的智能体一致地使用特定模型,请在运行智能体前设置 `OPENAI_DEFAULT_MODEL` 环境变量。 + +```bash +export OPENAI_DEFAULT_MODEL=gpt-5 +python3 my_awesome_agent.py +``` + +#### GPT-5 模型 + +当你以这种方式使用 GPT-5 的推理模型([`gpt-5`](https://platform.openai.com/docs/models/gpt-5)、[`gpt-5-mini`](https://platform.openai.com/docs/models/gpt-5-mini) 或 [`gpt-5-nano`](https://platform.openai.com/docs/models/gpt-5-nano))时,SDK 默认应用合理的 `ModelSettings`。具体来说,它将 `reasoning.effort` 和 `verbosity` 都设置为 `"low"`。如果你想自己构建这些设置,请调用 `agents.models.get_default_model_settings("gpt-")`。 + +为了更低延迟或特定需求,你可以选择不同的模型和设置。要调整默认模型的推理强度,请传递你自己的 `ModelSettings`: + +```python +from openai.types.shared import Reasoning +from agents import Agent, ModelSettings + +my_agent = Agent( + name="My Agent", + instructions="You're a helpful agent.", + model_settings=ModelSettings(reasoning=Reasoning(effort="minimal"), verbosity="low") + # 如果设置了 OPENAI_DEFAULT_MODEL=gpt-5,只传递 model_settings 即可。 + # 显式传递 GPT-5 模型名也是可以的: + # model="gpt-5", +) +``` + +特别是对于更低延迟,使用 [`gpt-5-mini`](https://platform.openai.com/docs/models/gpt-5-mini) 或 [`gpt-5-nano`](https://platform.openai.com/docs/models/gpt-5-nano) 模型配合 `reasoning.effort="minimal"` 通常会比默认设置更快地返回响应。然而,Responses API 中的一些内置工具(如文件搜索和图像生成)不支持 `"minimal"` 推理强度,这就是为什么 Agents SDK 默认使用 `"low"`。 + +#### 非 GPT-5 模型 + +如果你没有传递自定义 `model_settings` 而使用了非 GPT-5 模型名称,SDK 将回退到与任何模型兼容的通用 `ModelSettings`。 + +## 非 OpenAI 模型 + +你可以通过 [LiteLLM 集成](./litellm.md) 使用大多数其他非 OpenAI 模型。首先,安装 litellm 依赖组: + +```bash +pip install "openai-agents[litellm]" +``` + +然后,使用 `litellm/` 前缀的任何[支持的模型](https://docs.litellm.ai/docs/providers): + +```python +claude_agent = Agent(model="litellm/anthropic/claude-3-5-sonnet-20240620", ...) +gemini_agent = Agent(model="litellm/gemini/gemini-2.5-flash-preview-04-17", ...) +``` + +### 使用非 OpenAI 模型的其他方法 + +你有另外 3 种方式集成其他 LLM 提供商(示例[在此](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/)): + +1. [`set_default_openai_client`][agents.set_default_openai_client] 在你想全局使用 `AsyncOpenAI` 实例作为 LLM 客户端的情况下很有用。这适用于 LLM 提供商有 OpenAI 兼容 API 端点的情况,你可以设置 `base_url` 和 `api_key`。查看可配置示例 [examples/model_providers/custom_example_global.py](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/custom_example_global.py)。 +2. [`ModelProvider`][agents.models.interface.ModelProvider] 在 `Runner.run` 级别。这让你可以指定"此运行中的所有智能体都使用自定义模型提供商"。查看可配置示例 [examples/model_providers/custom_example_provider.py](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/custom_example_provider.py)。 +3. [`Agent.model`][agents.agent.Agent.model] 让你在特定智能体实例上指定模型。这让你可以为不同智能体混合匹配不同提供商。查看可配置示例 [examples/model_providers/custom_example_agent.py](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/custom_example_agent.py)。使用大多数可用模型的简单方法是通过 [LiteLLM 集成](./litellm.md)。 + +如果你没有来自 `platform.openai.com` 的 API 密钥,我们建议通过 `set_tracing_disabled()` 禁用追踪,或设置[不同的追踪处理器](../tracing.md)。 + +!!! note + + 在这些示例中,我们使用 Chat Completions API/模型,因为大多数 LLM 提供商还不支持 Responses API。如果你的 LLM 提供商支持它,我们推荐使用 Responses。 + +## 混合和匹配模型 + +在单个工作流中,你可能想为每个智能体使用不同的模型。例如,你可以使用更小、更快的模型进行分类,而对复杂任务使用更大、更有能力的模型。配置 [`Agent`][agents.Agent] 时,你可以通过以下方式选择特定模型: + +1. 传递模型名称。 +2. 传递任何模型名称 + 可以将该名称映射到模型实例的 [`ModelProvider`][agents.models.interface.ModelProvider]。 +3. 直接提供 [`Model`][agents.models.interface.Model] 实现。 + +!!!note + + 虽然我们的 SDK 支持 [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel] 和 [`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel] 两种形状,但我们建议为每个工作流使用单一模型形状,因为这两种形状支持不同的功能和工具集。如果你的工作流需要混合匹配模型形状,请确保你使用的所有功能在两者上都可用。 + +```python +from agents import Agent, Runner, AsyncOpenAI, OpenAIChatCompletionsModel +import asyncio + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", + model="gpt-5-mini", # (1)! +) + +english_agent = Agent( + name="English agent", + instructions="You only speak English", + model=OpenAIChatCompletionsModel( # (2)! + model="gpt-5-nano", + 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-5", +) + +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] 实现。 + +当你想进一步配置智能体使用的模型时,你可以传递 [`ModelSettings`][agents.models.interface.ModelSettings],它提供可选的模型配置参数如 temperature。 + +```python +from agents import Agent, ModelSettings + +english_agent = Agent( + name="English agent", + instructions="You only speak English", + model="gpt-4.1", + model_settings=ModelSettings(temperature=0.1), +) +``` + +另外,当你使用 OpenAI 的 Responses API 时,[还有其他一些可选参数](https://platform.openai.com/docs/api-reference/responses/create)(例如 `user`、`service_tier` 等)。如果它们在顶层不可用,你也可以使用 `extra_args` 传递它们。 + +```python +from agents import Agent, ModelSettings + +english_agent = Agent( + name="English agent", + instructions="You only speak English", + model="gpt-4.1", + model_settings=ModelSettings( + temperature=0.1, + extra_args={"service_tier": "flex", "user": "user_12345"}, + ), +) +``` + +## 使用其他 LLM 提供商时的常见问题 + +### 追踪客户端错误 401 + +如果你遇到与追踪相关的错误,这是因为追踪被上传到 OpenAI 服务器,而你没有 OpenAI API 密钥。你有三个选项来解决这个问题: + +1. 完全禁用追踪:[`set_tracing_disabled(True)`][agents.set_tracing_disabled]。 +2. 为追踪设置 OpenAI 密钥:[`set_tracing_export_api_key(...)`][agents.set_tracing_export_api_key]。此 API 密钥将仅用于上传追踪,且必须来自 [platform.openai.com](https://platform.openai.com/)。 +3. 使用非 OpenAI 追踪处理器。参见 [追踪文档](../tracing.md#custom-tracing-processors)。 + +### Responses API 支持 + +SDK 默认使用 Responses API,但大多数其他 LLM 提供商还不支持它。你可能会因此看到 404 或类似问题。要解决此问题,你有两个选项: + +1. 调用 [`set_default_openai_api("chat_completions")`][agents.set_default_openai_api]。如果你通过环境变量设置 `OPENAI_API_KEY` 和 `OPENAI_BASE_URL`,这将会生效。 +2. 使用 [`OpenAIChatCompletionsModel`][agents.models.openai_chatcompletions.OpenAIChatCompletionsModel]。示例在[这里](https://github.com/openai/openai-agents-python/tree/main/examples/model_providers/)。 + +### 结构化输出支持 + +一些模型提供商不支持[结构化输出](https://platform.openai.com/docs/guides/structured-outputs)。这有时会导致如下错误: + +``` + +BadRequestError: Error code: 400 - {'error': {'message': "'response_format.type' : value is not one of the allowed values ['text','json_object']", 'type': 'invalid_request_error'}} + +``` + +这是某些模型提供商的缺陷 - 它们支持 JSON 输出,但不允许你指定输出使用的 `json_schema`。我们正在研究解决方案,但建议你依赖支持 JSON 模式输出的提供商,否则你的应用经常会因为格式错误的 JSON 而崩溃。 + +## 跨提供商混合模型 + +你需要了解模型提供商之间的功能差异,否则可能会遇到错误。例如,OpenAI 支持结构化输出、多模态输入以及托管文件搜索和网络搜索,但许多其他提供商不支持这些功能。请注意这些限制: + +- 不要向不理解它们的提供商发送不受支持的 `tools` +- 在调用仅支持文本的模型前过滤掉多模态输入 +- 注意不支持结构化 JSON 输出的提供商偶尔会生成无效的 JSON \ No newline at end of file diff --git a/docs/zh/models/litellm.md b/docs/zh/models/litellm.md new file mode 100644 index 000000000..4b1db4783 --- /dev/null +++ b/docs/zh/models/litellm.md @@ -0,0 +1,94 @@ +--- +search: + exclude: true +--- +# 通过 LiteLLM 使用任意模型 + +!!! note + + LiteLLM 集成处于测试阶段。特别是小型模型提供商可能会遇到问题。如果有问题,请通过 [GitHub issues](https://github.com/openai/openai-agents-python/issues) 报告,我们会快速修复。 + +[LiteLLM](https://docs.litellm.ai/docs/) 是一个允许你通过单一接口使用 100+ 模型的库。我们在 Agents SDK 中添加了 LiteLLM 集成,让你可以使用任何 AI 模型。 + +## 设置 + +你需要确保 `litellm` 可用。你可以通过安装可选的 `litellm` 依赖组来实现这一点: + +```bash +pip install "openai-agents[litellm]" +``` + +完成后,你可以在任何智能体中使用 [`LitellmModel`][agents.extensions.models.litellm_model.LitellmModel]。 + +## 例 + +这是一个完全工作的示例。当你运行它时,系统会要求你输入模型名称和 API 密钥。例如,你可以输入: + +- 模型输入 `openai/gpt-4.1`,以及你的 OpenAI API 密钥 +- 模型输入 `anthropic/claude-3-5-sonnet-20240620`,以及你的 Anthropic API 密钥 +- 等等 + +有关 LiteLLM 支持的完整模型列表,请参见 [litellm 提供商文档](https://docs.litellm.ai/docs/providers)。 + +```python +from __future__ import annotations + +import asyncio + +from agents import Agent, Runner, function_tool, set_tracing_disabled +from agents.extensions.models.litellm_model import LitellmModel + +@function_tool +def get_weather(city: str): + print(f"[debug] getting weather for {city}") + return f"The weather in {city} is sunny." + + +async def main(model: str, api_key: str): + agent = Agent( + name="Assistant", + instructions="You only respond in haikus.", + model=LitellmModel(model=model, api_key=api_key), + tools=[get_weather], + ) + + result = await Runner.run(agent, "What's the weather in Tokyo?") + print(result.final_output) + + +if __name__ == "__main__": + # First try to get model/api key from args + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--model", type=str, required=False) + parser.add_argument("--api-key", type=str, required=False) + args = parser.parse_args() + + model = args.model + if not model: + model = input("Enter a model name for Litellm: ") + + api_key = args.api_key + if not api_key: + api_key = input("Enter an API key for Litellm: ") + + asyncio.run(main(model, api_key)) +``` + +## 跟踪使用数据 + +如果你想让 LiteLLM 响应填充 Agents SDK 的使用指标,请在创建智能体时传递 `ModelSettings(include_usage=True)`。 + +```python +from agents import Agent, ModelSettings +from agents.extensions.models.litellm_model import LitellmModel + +agent = Agent( + name="Assistant", + model=LitellmModel(model="your/model", api_key="..."), + model_settings=ModelSettings(include_usage=True), +) +``` + +使用 `include_usage=True` 时,LiteLLM 请求会通过 `result.context_wrapper.usage` 报告令牌和请求计数,就像内置的 OpenAI 模型一样。 \ No newline at end of file diff --git a/docs/zh/multi_agent.md b/docs/zh/multi_agent.md new file mode 100644 index 000000000..fec68f45a --- /dev/null +++ b/docs/zh/multi_agent.md @@ -0,0 +1,41 @@ +--- +search: + exclude: true +--- +# 多智能体编排 + +编排指的是应用中智能体的流程。哪些智能体运行,以什么顺序运行,它们如何决定接下来发生什么?编排智能体主要有两种方式: + +1. 允许LLM做出决策:这使用LLM的智能来计划、推理,并基于此决定采取哪些步骤。 +2. 通过代码编排:通过你的代码确定智能体的流程。 + +你可以混合搭配这些模式。每种方式都有自己的权衡,如下所述。 + +## 通过LLM编排 + +智能体是一个配备了指令、工具和交接的LLM。这意味着给定一个开放式任务时,LLM可以自主计划如何解决这个问题,使用工具采取行动和获取数据,并使用交接将任务委托给子智能体。例如,研究智能体可以配备如下工具: + +- 网络搜索以在线查找信息 +- 文件搜索和检索以搜索专有数据和连接 +- 计算机使用以在计算机上执行操作 +- 代码执行以进行数据分析 +- 交接给专门擅长规划、报告撰写等的专业智能体。 + +这种模式在任务是开放式且你希望依赖LLM的智能时非常出色。这里最重要的策略是: + +1. 投资好的提示。明确说明可用的工具、如何使用它们以及它必须在哪些参数内操作。 +2. 监控你的应用并进行迭代。看看哪里出了问题,并对你的提示进行迭代。 +3. 允许智能体进行内省和改进。例如,在循环中运行它,让它自我批评;或者提供错误消息并让它改进。 +4. 拥有专门擅长一项任务的专业智能体,而不是期望通用智能体擅长任何事情。 +5. 投资[评估](https://platform.openai.com/docs/guides/evals)。这让你可以训练你的智能体改进并更好地完成任务。 + +## 通过代码编排 + +虽然通过LLM编排很强大,但通过代码编排使任务在速度、成本和性能方面更加确定和可预测。这里的常见模式是: + +- 使用[结构化输出](https://platform.openai.com/docs/guides/structured-outputs)生成格式良好的数据,你可以用代码检查这些数据。例如,你可以要求智能体将任务分类为几个类别,然后根据该类别选择下一个智能体。 +- 通过将前一个的输出转换为下一个的输入来链接多个智能体。你可以将撰写博客文章之类的任务分解为一系列步骤——进行研究、撰写大纲、撰写博客文章、批评它,然后改进它。 +- 在`while`循环中运行执行任务的智能体和提供评估与反馈的智能体,直到评估者说输出通过某些标准。 +- 并行运行多个智能体,例如通过Python原语如`asyncio.gather`。当你有多个不相互依赖的任务时,这对于速度很有用。 + +我们在[`examples/agent_patterns`](https://github.com/openai/openai-agents-python/tree/main/examples/agent_patterns)中有许多示例。 \ No newline at end of file diff --git a/docs/zh/quickstart.md b/docs/zh/quickstart.md new file mode 100644 index 000000000..6f64776be --- /dev/null +++ b/docs/zh/quickstart.md @@ -0,0 +1,203 @@ +--- +search: + exclude: true +--- +# 快速开始 + +## 创建项目和虚拟环境 + +您只需要执行一次此操作。 + +```bash +mkdir my_project +cd my_project +python -m venv .venv +``` + +### 激活虚拟环境 + +每次开始新的终端会话时都要执行此操作。 + +```bash +source .venv/bin/activate +``` + +### 安装 Agents SDK + +```bash +pip install openai-agents # 或者 `uv add openai-agents` 等 +``` + +### 设置 OpenAI API 密钥 + +如果您还没有 OpenAI API 密钥,请按照[这些说明](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key)创建。 + +```bash +export OPENAI_API_KEY=sk-... +``` + +## 创建您的第一个智能体 + +智能体通过指令、名称和可选配置(如 `model_config`)来定义 + +```python +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` 为确定交接路由提供额外的上下文 + +```python +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", +) +``` + +## 定义您的交接 + +在每个智能体上,您可以定义一个出站交接选项清单,智能体可以从中选择来决定如何在任务上取得进展。 + +```python +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] +) +``` + +## 运行智能体编排 + +让我们检查工作流是否运行正常,以及分诊智能体是否正确地在这两个专业智能体之间进行路由。 + +```python +from agents import Runner + +async def main(): + result = await Runner.run(triage_agent, "What is the capital of France?") + print(result.final_output) +``` + +## 添加护栏 + +您可以定义自定义护栏在输入或输出上运行。 + +```python +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, + ) +``` + +## 整合在一起 + +让我们把所有内容整合在一起,运行整个工作流,使用交接和输入护栏。 + +```python +from agents import Agent, InputGuardrail, GuardrailFunctionOutput, Runner +from agents.exceptions import InputGuardrailTripwireTriggered +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(): + # Example 1: History question + try: + result = await Runner.run(triage_agent, "who was the first president of the united states?") + print(result.final_output) + except InputGuardrailTripwireTriggered as e: + print("Guardrail blocked this input:", e) + + # Example 2: General/philosophical question + try: + result = await Runner.run(triage_agent, "What is the meaning of life?") + print(result.final_output) + except InputGuardrailTripwireTriggered as e: + print("Guardrail blocked this input:", e) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## 查看您的追踪 + +要查看智能体运行期间发生的情况,请导航到 [OpenAI 仪表板中的追踪查看器](https://platform.openai.com/traces) 查看智能体运行的追踪。 + +## 后续步骤 + +了解如何构建更复杂的智能体流程: + +- 了解如何配置[智能体](agents.md)。 +- 了解[运行智能体](running_agents.md)。 +- 了解[工具](tools.md)、[护栏](guardrails.md)和[模型](models/index.md)。 \ No newline at end of file diff --git a/docs/zh/realtime/guide.md b/docs/zh/realtime/guide.md new file mode 100644 index 000000000..969776939 --- /dev/null +++ b/docs/zh/realtime/guide.md @@ -0,0 +1,176 @@ +--- +search: + exclude: true +--- +# 指南 + +本指南深入介绍如何使用 OpenAI Agents SDK 的实时功能构建支持语音的 AI 智能体。 + +!!! warning "测试版功能" +实时智能体处于测试版。随着我们改进实现,可能会出现重大更改。 + +## 概述 + +实时智能体支持对话流程,实时处理音频和文本输入,并以实时音频进行响应。它们与 OpenAI 的实时 API 保持持久连接,实现低延迟的自然语音对话,并能优雅地处理打断情况。 + +## 架构 + +### 核心组件 + +实时系统由以下关键组件组成: + +- **RealtimeAgent**: 由指令、工具、交接组成的智能体。 +- **RealtimeRunner**: 管理配置。你可以调用 `runner.run()` 来获取会话。 +- **RealtimeSession**: 单个交互会话。通常,每次用户开始对话时创建一个,并在对话结束时保持活动状态。 +- **RealtimeModel**: 底层模型接口(通常是 OpenAI 的 WebSocket 实现) + +### 会话流程 + +典型的实时会话流程如下: + +1. 使用指令、工具、交接**创建 RealtimeAgent**。 +2. 使用智能体和配置选项**设置 RealtimeRunner**。 +3. 使用 `await runner.run()`**开始会话**,返回 RealtimeSession。 +4. 使用 `send_audio()` 或 `send_message()`**发送音频或文本消息**。 +5. 通过迭代会话**监听事件**。事件包括音频输出、转录、工具调用、交接、错误。 +6. **处理打断**,当用户在智能体说话时覆盖说话,这将自动停止当前音频生成。 + +会话维护对话历史并管理与实时模型的持久连接。 + +## 智能体配置 + +RealtimeAgent 的工作方式与常规的 Agent 类相似,但有一些关键区别。有关完整的 API 详情,请参阅 [`RealtimeAgent`][agents.realtime.agent.RealtimeAgent] API 参考。 + +与常规智能体的主要区别: + +- 模型选择在会话级别配置,而不是智能体级别。 +- 不支持结构化输出(不支持 `outputType`)。 +- 可以为每个智能体配置语音,但在第一个智能体发言后无法更改。 +- 所有其他功能如工具、交接和指令的工作方式相同。 + +## 会话配置 + +### 模型设置 + +会话配置允许您控制底层实时模型的行为。您可以配置模型名称(如 `gpt-realtime`)、语音选择(alloy、echo、fable、onyx、nova、shimmer)以及支持的模态(文本和/或音频)。输入和输出的音频格式都可以设置,PCM16 是默认格式。 + +### 音频配置 + +音频设置控制会话如何处理语音输入和输出。您可以配置使用 Whisper 等模型的输入音频转录,设置语言偏好,并提供转录提示以提高特定领域术语的准确性。轮流检测设置控制智能体何时开始和停止响应,包括语音活动检测阈值、静音持续时间和检测到的语音周围的填充选项。 + +## 工具和函数 + +### 添加工具 + +就像常规智能体一样,实时智能体支持在对话期间执行的函数工具: + +```python +from agents import function_tool + +@function_tool +def get_weather(city: str) -> str: + """Get current weather for a city.""" + # Your weather API logic here + return f"The weather in {city} is sunny, 72°F" + +@function_tool +def book_appointment(date: str, time: str, service: str) -> str: + """Book an appointment.""" + # Your booking logic here + return f"Appointment booked for {service} on {date} at {time}" + +agent = RealtimeAgent( + name="Assistant", + instructions="You can help with weather and appointments.", + tools=[get_weather, book_appointment], +) +``` + +## 交接 + +### 创建交接 + +交接允许在专门智能体之间转移对话。 + +```python +from agents.realtime import realtime_handoff + +# Specialized agents +billing_agent = RealtimeAgent( + name="Billing Support", + instructions="You specialize in billing and payment issues.", +) + +technical_agent = RealtimeAgent( + name="Technical Support", + instructions="You handle technical troubleshooting.", +) + +# Main agent with handoffs +main_agent = RealtimeAgent( + name="Customer Service", + instructions="You are the main customer service agent. Hand off to specialists when needed.", + handoffs=[ + realtime_handoff(billing_agent, tool_description="Transfer to billing support"), + realtime_handoff(technical_agent, tool_description="Transfer to technical support"), + ] +) +``` + +## 事件处理 + +会话流式传输事件,您可以通过迭代会话对象来监听这些事件。事件包括音频输出块、转录结果、工具执行开始和结束、智能体交接和错误。需要处理的关键事件包括: + +- **audio**:来自智能体响应的原始音频数据 +- **audio_end**:智能体结束发言 +- **audio_interrupted**:用户打断智能体 +- **tool_start/tool_end**:工具执行生命周期 +- **handoff**:发生智能体交接 +- **error**:处理过程中发生错误 + +有关完整的事件详情,请参阅 [`RealtimeSessionEvent`][agents.realtime.events.RealtimeSessionEvent]。 + +## 护栏功能 + +实时智能体仅支持输出护栏。这些护栏会进行防抖处理并定期运行(不是每个词都运行),以避免实时生成期间的性能问题。默认的防抖长度是 100 个字符,但这是可配置的。 + +护栏可以直接附加到 `RealtimeAgent`,也可以通过会话的 `run_config` 提供。来自两个来源的护栏会一起运行。 + +```python +from agents.guardrail import GuardrailFunctionOutput, OutputGuardrail + +def sensitive_data_check(context, agent, output): + return GuardrailFunctionOutput( + tripwire_triggered="password" in output, + output_info=None, + ) + +agent = RealtimeAgent( + name="Assistant", + instructions="...", + output_guardrails=[OutputGuardrail(guardrail_function=sensitive_data_check)], +) +``` + +当护栏被触发时,它会生成一个 `guardrail_tripped` 事件,并可以中断智能体的当前响应。防抖行为有助于在安全性和实时性能需求之间取得平衡。与文本智能体不同,实时智能体在护栏被触发时**不会**引发异常。 + +## 音频处理 + +使用 [`session.send_audio(audio_bytes)`][agents.realtime.session.RealtimeSession.send_audio] 向会话发送音频,或使用 [`session.send_message()`][agents.realtime.session.RealtimeSession.send_message] 发送文本。 + +对于音频输出,监听 `audio` 事件并通过您首选的音频库播放音频数据。确保监听 `audio_interrupted` 事件,以便在用户打断智能体时立即停止播放并清除任何排队的音频。 + +## 直接模型访问 + +您可以访问底层模型以添加自定义监听器或执行高级操作: + +```python +# Add a custom listener to the model +session.model.add_listener(my_custom_listener) +``` + +这使您可以直接访问 [`RealtimeModel`][agents.realtime.model.RealtimeModel] 接口,用于需要更底层连接控制的高级用例。 + +## 示例 + +有关完整的工作示例,请查看 [examples/realtime 目录](https://github.com/openai/openai-agents-python/tree/main/examples/realtime),其中包含带 UI 组件和不带 UI 组件的演示。 \ No newline at end of file diff --git a/docs/zh/realtime/quickstart.md b/docs/zh/realtime/quickstart.md new file mode 100644 index 000000000..c14d2a461 --- /dev/null +++ b/docs/zh/realtime/quickstart.md @@ -0,0 +1,232 @@ +--- +search: + exclude: true +--- +# 快速入门 + +实时智能体通过 OpenAI 的 Realtime API 实现与 AI 智能体的语音对话。本指南将逐步介绍如何创建您的第一个实时语音智能体。 + +!!! warning "测试版功能" +实时智能体处于测试阶段。随着我们改进实现,可能会出现重大更改。 + +## 前提条件 + +- Python 3.9 或更高版本 +- OpenAI API 密钥 +- 对 OpenAI Agents SDK 的基本了解 + +## 安装 + +如果尚未安装,请安装 OpenAI Agents SDK: + +```bash +pip install openai-agents +``` + +## 创建您的第一个实时智能体 + +### 1. 导入所需组件 + +```python +import asyncio +from agents.realtime import RealtimeAgent, RealtimeRunner +``` + +### 2. 创建实时智能体 + +```python +agent = RealtimeAgent( + name="Assistant", + instructions="You are a helpful voice assistant. Keep your responses conversational and friendly.", +) +``` + +### 3. 设置运行器 + +```python +runner = RealtimeRunner( + starting_agent=agent, + config={ + "model_settings": { + "model_name": "gpt-realtime", + "voice": "ash", + "modalities": ["audio"], + "input_audio_format": "pcm16", + "output_audio_format": "pcm16", + "input_audio_transcription": {"model": "gpt-4o-mini-transcribe"}, + "turn_detection": {"type": "semantic_vad", "interrupt_response": True}, + } + } +) +``` + +### 4. 启动会话 + +```python +# Start the session +session = await runner.run() + +async with session: + print("Session started! The agent will stream audio responses in real-time.") + # Process events + async for event in session: + try: + if event.type == "agent_start": + print(f"Agent started: {event.agent.name}") + elif event.type == "agent_end": + print(f"Agent ended: {event.agent.name}") + elif event.type == "handoff": + print(f"Handoff from {event.from_agent.name} to {event.to_agent.name}") + elif event.type == "tool_start": + print(f"Tool started: {event.tool.name}") + elif event.type == "tool_end": + print(f"Tool ended: {event.tool.name}; output: {event.output}") + elif event.type == "audio_end": + print("Audio ended") + elif event.type == "audio": + # Enqueue audio for callback-based playback with metadata + # Non-blocking put; queue is unbounded, so drops won’t occur. + pass + elif event.type == "audio_interrupted": + print("Audio interrupted") + # Begin graceful fade + flush in the audio callback and rebuild jitter buffer. + elif event.type == "error": + print(f"Error: {event.error}") + elif event.type == "history_updated": + pass # Skip these frequent events + elif event.type == "history_added": + pass # Skip these frequent events + elif event.type == "raw_model_event": + print(f"Raw model event: {_truncate_str(str(event.data), 200)}") + else: + print(f"Unknown event type: {event.type}") + except Exception as e: + print(f"Error processing event: {_truncate_str(str(e), 200)}") + +def _truncate_str(s: str, max_length: int) -> str: + if len(s) > max_length: + return s[:max_length] + "..." + return s +``` + +## 完整示例 + +这是一个完整的工作示例: + +```python +import asyncio +from agents.realtime import RealtimeAgent, RealtimeRunner + +async def main(): + # Create the agent + agent = RealtimeAgent( + name="Assistant", + instructions="You are a helpful voice assistant. Keep responses brief and conversational.", + ) + # Set up the runner with configuration + runner = RealtimeRunner( + starting_agent=agent, + config={ + "model_settings": { + "model_name": "gpt-realtime", + "voice": "ash", + "modalities": ["audio"], + "input_audio_format": "pcm16", + "output_audio_format": "pcm16", + "input_audio_transcription": {"model": "gpt-4o-mini-transcribe"}, + "turn_detection": {"type": "semantic_vad", "interrupt_response": True}, + } + }, + ) + # Start the session + session = await runner.run() + + async with session: + print("Session started! The agent will stream audio responses in real-time.") + # Process events + async for event in session: + try: + if event.type == "agent_start": + print(f"Agent started: {event.agent.name}") + elif event.type == "agent_end": + print(f"Agent ended: {event.agent.name}") + elif event.type == "handoff": + print(f"Handoff from {event.from_agent.name} to {event.to_agent.name}") + elif event.type == "tool_start": + print(f"Tool started: {event.tool.name}") + elif event.type == "tool_end": + print(f"Tool ended: {event.tool.name}; output: {event.output}") + elif event.type == "audio_end": + print("Audio ended") + elif event.type == "audio": + # Enqueue audio for callback-based playback with metadata + # Non-blocking put; queue is unbounded, so drops won’t occur. + pass + elif event.type == "audio_interrupted": + print("Audio interrupted") + # Begin graceful fade + flush in the audio callback and rebuild jitter buffer. + elif event.type == "error": + print(f"Error: {event.error}") + elif event.type == "history_updated": + pass # Skip these frequent events + elif event.type == "history_added": + pass # Skip these frequent events + elif event.type == "raw_model_event": + print(f"Raw model event: {_truncate_str(str(event.data), 200)}") + else: + print(f"Unknown event type: {event.type}") + except Exception as e: + print(f"Error processing event: {_truncate_str(str(e), 200)}") + +def _truncate_str(s: str, max_length: int) -> str: + if len(s) > max_length: + return s[:max_length] + "..." + return s + +if __name__ == "__main__": + # Run the session + asyncio.run(main()) +``` + +## 配置选项 + +### 模型设置 + +- `model_name`: 从可用的实时模型中选择 (例如: `gpt-realtime`) +- `voice`: 选择语音 (`alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer`) +- `modalities`: 启用文本或音频 (`["text"]` 或 `["audio"]`) + +### 音频设置 + +- `input_audio_format`: 输入音频格式 (`pcm16`, `g711_ulaw`, `g711_alaw`) +- `output_audio_format`: 输出音频格式 +- `input_audio_transcription`: 转录配置 + +### 轮次检测 + +- `type`: 检测方法 (`server_vad`, `semantic_vad`) +- `threshold`: 语音活动阈值 (0.0-1.0) +- `silence_duration_ms`: 检测轮次结束的静音时长 +- `prefix_padding_ms`: 语音前的音频填充 + +## 后续步骤 + +- [详细了解实时智能体](guide.md) +- 查看 [examples/realtime](https://github.com/openai/openai-agents-python/tree/main/examples/realtime) 文件夹中的工作示例 +- 向智能体添加工具 +- 实现智能体间的交接 +- 设置安全护栏 + +## 身份验证 + +请确保您的 OpenAI API 密钥已设置在环境中: + +```bash +export OPENAI_API_KEY="your-api-key-here" +``` + +或者在创建会话时直接传递: + +```python +session = await runner.run(model_config={"api_key": "your-api-key"}) +``` \ No newline at end of file diff --git a/docs/zh/release.md b/docs/zh/release.md new file mode 100644 index 000000000..9228910b0 --- /dev/null +++ b/docs/zh/release.md @@ -0,0 +1,32 @@ +--- +search: + exclude: true +--- +# 发布流程/变更日志 + +本项目采用稍作修改的语义化版本控制,使用 `0.Y.Z` 形式。开头的 `0` 表示 SDK 仍在快速发展。各组件的增量如下: + +## 次要(`Y`)版本 + +对于未标记为beta的公共接口的**重大更改**,我们将增加次要版本 `Y`。例如,从 `0.0.x` 到 `0.1.x` 的更新可能包含重大更改。 + +如果你想避免重大更改,我们建议在你的项目中固定到 `0.0.x` 版本。 + +## 补丁(`Z`)版本 + +对于非重大更改,我们将递增 `Z`: + +- 错误修复 +- 新功能 +- 私有接口的更改 +- beta功能的更新 + +## 重大更改变更日志 + +### 0.2.0 + +在此版本中,一些以前接受 `Agent` 作为参数的地方现在改为接受 `AgentBase` 作为参数。例如,MCP 服务器中的 `list_tools()` 调用。这只是一个类型更改,你仍然可以接收 `Agent` 对象。要更新,只需通过将 `Agent` 替换为 `AgentBase` 来修复类型错误。 + +### 0.1.0 + +在此版本中,[`MCPServer.list_tools()`][agents.mcp.server.MCPServer] 添加了两个新参数:`run_context` 和 `agent`。你需要将这些参数添加到任何继承自 `MCPServer` 的类中。 \ No newline at end of file diff --git a/docs/zh/repl.md b/docs/zh/repl.md new file mode 100644 index 000000000..83482beb1 --- /dev/null +++ b/docs/zh/repl.md @@ -0,0 +1,24 @@ +--- +search: + exclude: true +--- +# REPL 实用工具 + +SDK 提供了 `run_demo_loop`,用于直接在终端中快速、交互式地测试智能体的行为。 + + +```python +import asyncio +from agents import Agent, run_demo_loop + +async def main() -> None: + agent = Agent(name="Assistant", instructions="You are a helpful assistant.") + await run_demo_loop(agent) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +`run_demo_loop` 会在循环中提示用户输入,并在轮次之间保持对话历史。默认情况下,它会随着生成而流式传输模型输出。当你运行上面的示例时,run_demo_loop 会启动一个交互式聊天会话。它会持续要求你的输入,在轮次之间记住整个对话历史(这样你的智能体就知道讨论了什么),并自动实时地将智能体的响应流式传输给你,就像它们生成时一样。 + +要结束此聊天会话,只需输入 `quit` 或 `exit`(然后按 Enter)或使用 `Ctrl-D` 键盘快捷键。 \ No newline at end of file diff --git a/docs/zh/results.md b/docs/zh/results.md new file mode 100644 index 000000000..a9002fd2b --- /dev/null +++ b/docs/zh/results.md @@ -0,0 +1,56 @@ +--- +search: + exclude: true +--- +# 结果 + +当你调用`Runner.run`方法时,你会得到: + +- 如果调用`run`或`run_sync`,则为 [`RunResult`][agents.result.RunResult] +- 如果调用`run_streamed`,则为 [`RunResultStreaming`][agents.result.RunResultStreaming] + +这两者都继承自 [`RunResultBase`][agents.result.RunResultBase],其中包含大部分有用信息。 + +## 最终输出 + +[`final_output`][agents.result.RunResultBase.final_output] 属性包含最后运行的智能体的最终输出。这是以下之一: + +- 如果最后一个智能体没有定义`output_type`,则为 `str` +- 如果智能体定义了输出类型,则为 `last_agent.output_type` 类型的对象 + +!!! note + + `final_output` 的类型是 `Any`。由于交接的存在,我们无法对其进行静态类型化。如果发生交接,意味着任何智能体都可能成为最后一个智能体,因此我们无法静态地知道可能的输出类型集合。 + +## 下一轮输入 + +你可以使用 [`result.to_input_list()`][agents.result.RunResultBase.to_input_list] 将结果转换为输入列表,将你提供的原始输入与智能体运行期间生成的项目连接起来。这使得将一个智能体运行的输出传递到另一个运行中,或者在循环中运行并每次添加新的用户输入变得很方便。 + +## 最后一个智能体 + +[`last_agent`][agents.result.RunResultBase.last_agent] 属性包含最后运行的智能体。根据你的应用,这在用户下次输入某些内容时通常很有用。例如,如果你有一个前线分类智能体交接给特定语言的智能体,你可以存储最后一个智能体,并在用户下次向智能体发送消息时重用它。 + +## 新项目 + +[`new_items`][agents.result.RunResultBase.new_items] 属性包含运行期间生成的新项目。这些项目是 [`RunItem`][agents.items.RunItem]。运行项目包装了LLM生成的原始项目。 + +- [`MessageOutputItem`][agents.items.MessageOutputItem] 表示来自LLM的消息。原始项目是生成的消息。 +- [`HandoffCallItem`][agents.items.HandoffCallItem] 表示LLM调用了交接工具。原始项目是LLM的工具调用项目。 +- [`HandoffOutputItem`][agents.items.HandoffOutputItem] 表示发生了交接。原始项目是对交接工具调用的工具响应。你也可以从项目中访问源/目标智能体。 +- [`ToolCallItem`][agents.items.ToolCallItem] 表示LLM调用了工具。 +- [`ToolCallOutputItem`][agents.items.ToolCallOutputItem] 表示调用了工具。原始项目是工具响应。你也可以从项目中访问工具输出。 +- [`ReasoningItem`][agents.items.ReasoningItem] 表示来自LLM的推理项目。原始项目是生成的推理。 + +## 其他信息 + +### 护栏结果 + +[`input_guardrail_results`][agents.result.RunResultBase.input_guardrail_results] 和 [`output_guardrail_results`][agents.result.RunResultBase.output_guardrail_results] 属性包含护栏的结果(如果有)。护栏结果有时包含有用的信息,你可能想要记录或存储,因此我们让你可以访问这些信息。 + +### 原始响应 + +[`raw_responses`][agents.result.RunResultBase.raw_responses] 属性包含LLM生成的 [`ModelResponse`][agents.items.ModelResponse]。 + +### 原始输入 + +[`input`][agents.result.RunResultBase.input] 属性包含你提供给`run`方法的原始输入。在大多数情况下你不需要这个,但如果你需要,它可供你使用。 \ No newline at end of file diff --git a/docs/zh/running_agents.md b/docs/zh/running_agents.md new file mode 100644 index 000000000..72df09cfd --- /dev/null +++ b/docs/zh/running_agents.md @@ -0,0 +1,207 @@ +--- +search: + exclude: true +--- +# 运行智能体 + +你可以通过 [`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,并在接收到事件时将这些事件流式传输给你。 + +```python +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 +``` + +阅读更多内容请参考[结果指南](results.md)。 + +## 智能体循环 + +当你在`Runner`中使用运行方法时,你传入一个起始智能体和输入。输入可以是字符串(被视为用户消息),或者输入项列表,这些是OpenAI响应API中的项。 + +然后运行器运行一个循环: + +1. 我们使用当前输入为当前智能体调用LLM。 +2. LLM产生其输出。 + 1. 如果LLM返回`final_output`,循环结束,我们返回结果。 + 2. 如果LLM进行交接,我们更新当前智能体和输入,并重新运行循环。 + 3. 如果LLM产生工具调用,我们运行这些工具调用,追加结果,并重新运行循环。 +3. 如果我们超过了传入的`max_turns`,我们会引发一个 [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] 异常。 + +!!! 注意 + + LLM输出是否被视为"最终输出"的规则是它产生所需类型的文本输出,并且没有工具调用。 + +## 流式传输 + +流式传输允许你在LLM运行时额外接收流式传输事件。流式传输完成后,[`RunResultStreaming`][agents.result.RunResultStreaming] 将包含有关运行的完整信息,包括产生的所有新输出。你可以调用 `.stream_events()` 来获取流式传输事件。阅读更多内容请参考[流式传输指南](streaming.md)。 + +## 运行配置 + +`run_config` 参数允许你为智能体运行配置一些全局设置: + +- [`model`][agents.run.RunConfig.model]: 允许设置要使用的全局LLM模型,不管每个智能体有什么`model`。 +- [`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]: 要包含在所有运行上的输入或输出护栏列表。 +- [`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.md)。 +- [`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,运行工具,交接给第二个智能体,第二个智能体运行更多工具,然后产生输出。 + +在智能体运行结束时,你可以选择向用户显示什么。例如,你可能会向用户显示智能体生成的每个新项目,或者只显示最终输出。无论哪种方式,用户都可能会问后续问题,在这种情况下,你可以再次调用运行方法。 + +### 手动对话管理 + +你可以使用 [`RunResultBase.to_input_list()`][agents.result.RunResultBase.to_input_list] 方法手动管理对话历史记录,以获取下一轮次的输入: + +```python +async def main(): + agent = Agent(name="Assistant", instructions="Reply very concisely.") + + thread_id = "thread_123" # 示例线程ID + with trace(workflow_name="Conversation", group_id=thread_id): + # 第一轮次 + result = await Runner.run(agent, "What city is the Golden Gate Bridge in?") + print(result.final_output) + # San Francisco + + # 第二轮次 + new_input = result.to_input_list() + [{"role": "user", "content": "What state is it in?"}] + result = await Runner.run(agent, new_input) + print(result.final_output) + # California +``` + +### 使用Sessions自动对话管理 + +对于更简单的方法,你可以使用[Sessions](sessions.md)来自动处理对话历史记录,而无需手动调用`.to_input_list()`: + +```python +from agents import Agent, Runner, SQLiteSession + +async def main(): + agent = Agent(name="Assistant", instructions="Reply very concisely.") + + # 创建会话实例 + session = SQLiteSession("conversation_123") + + thread_id = "thread_123" # 示例线程ID + with trace(workflow_name="Conversation", group_id=thread_id): + # 第一轮次 + result = await Runner.run(agent, "What city is the Golden Gate Bridge in?", session=session) + print(result.final_output) + # San Francisco + + # 第二轮次 - 智能体自动记住之前的上下文 + result = await Runner.run(agent, "What state is it in?", session=session) + print(result.final_output) + # California +``` + +Sessions自动: + +- 在每次运行前检索对话历史记录 +- 在每次运行后存储新消息 +- 为不同的会话ID维护单独的对话 + +有关详细信息,请参见[Sessions文档](sessions.md)。 + + +### 服务器管理的对话 + +你还可以让OpenAI对话状态功能在服务器端管理对话状态,而不是使用`to_input_list()`或`Sessions`在本地处理它。这允许你保留对话历史记录,而无需手动重新发送所有过去的消息。有关详细信息,请参见[OpenAI对话状态指南](https://platform.openai.com/docs/guides/conversation-state?api-mode=responses)。 + +OpenAI提供了两种跨轮次跟踪状态的方法: + +#### 1. 使用`conversation_id` + +你首先使用OpenAI对话API创建一个对话,然后为每个后续调用重用其ID: + +```python +from agents import Agent, Runner +from openai import AsyncOpenAI + +client = AsyncOpenAI() + +async def main(): + # 创建一个服务器管理的对话 + conversation = await client.conversations.create() + conv_id = conversation.id + + agent = Agent(name="Assistant", instructions="Reply very concisely.") + + # 第一轮次 + result1 = await Runner.run(agent, "What city is the Golden Gate Bridge in?", conversation_id=conv_id) + print(result1.final_output) + # San Francisco + + # 第二轮次重用相同的conversation_id + result2 = await Runner.run( + agent, + "What state is it in?", + conversation_id=conv_id, + ) + print(result2.final_output) + # California +``` + +#### 2. 使用`previous_response_id` + +另一个选项是**响应链接**,其中每个轮次显式链接到前一轮次的响应ID。 + +```python +from agents import Agent, Runner + +async def main(): + agent = Agent(name="Assistant", instructions="Reply very concisely.") + + # 第一轮次 + result1 = await Runner.run(agent, "What city is the Golden Gate Bridge in?") + print(result1.final_output) + # San Francisco + + # 第二轮次,链接到之前的响应 + result2 = await Runner.run( + agent, + "What state is it in?", + previous_response_id=result1.last_response_id, + ) + print(result2.final_output) + # California +``` + + +## 长时间运行的智能体和人机交互 + +你可以使用Agents SDK [Temporal](https://temporal.io/) 集成来运行持久的、长时间运行的工作流,包括人机交互任务。查看[此视频](https://www.youtube.com/watch?v=fFBZqzT4DD8)中Temporal和Agents SDK协同工作完成长时间运行任务的演示,以及[查看此处的文档](https://github.com/temporalio/sdk-python/tree/main/temporalio/contrib/openai_agents)。 + +## 异常 + +SDK在某些情况下会引发异常。完整列表在 [`agents.exceptions`][] 中。概述如下: + +- [`AgentsException`][agents.exceptions.AgentsException]: 这是SDK内引发的所有异常的基类。它充当所有其他特定异常的通用类型。 +- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded]: 当智能体的运行超过传递给`Runner.run`、`Runner.run_sync`或`Runner.run_streamed`方法的`max_turns`限制时,会引发此异常。它表明智能体无法在指定的交互轮次内完成其任务。 +- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError]: 当底层模型(LLM)产生意外或无效的输出时,会发生此异常。这可能包括: + - 格式错误的JSON:当模型为工具调用或其直接输出提供格式错误的JSON结构时,特别是如果定义了特定的`output_type`。 + - 意外的工具相关失败:当模型未能以预期方式使用工具时 +- [`UserError`][agents.exceptions.UserError]: 当你(使用SDK编写代码的人)在使用SDK时出错,会引发此异常。这通常是由于代码实现不正确、配置无效或误用SDK的API导致的。 +- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered]: 当满足输入护栏或输出护栏的条件时,分别会引发此异常。输入护栏在处理前检查传入消息,而输出护栏在传递前检查智能体的最终响应。 \ No newline at end of file diff --git a/docs/zh/sessions.md b/docs/zh/sessions.md new file mode 100644 index 000000000..d60964748 --- /dev/null +++ b/docs/zh/sessions.md @@ -0,0 +1,407 @@ +--- +search: + exclude: true +--- +# 会话 + +Agents SDK 提供内置的会话记忆功能,可自动在多次智能体运行之间维护对话历史,无需在轮次之间手动处理 `.to_input_list()`。 + +会话存储特定会话的对话历史,使智能体能够在无需显式手动内存管理的情况下保持上下文。这对于构建聊天应用程序或智能体需要记住先前交互的多轮对话特别有用。 + +## 快速开始 + +```python +from agents import Agent, Runner, SQLiteSession + +# 创建智能体 +agent = Agent( + name="Assistant", + instructions="Reply very concisely.", +) + +# 使用会话ID创建会话实例 +session = SQLiteSession("conversation_123") + +# 第一轮 +result = await Runner.run( + agent, + "What city is the Golden Gate Bridge in?", + session=session +) +print(result.final_output) # "San Francisco" + +# 第二轮 - 智能体自动记住之前的上下文 +result = await Runner.run( + agent, + "What state is it in?", + session=session +) +print(result.final_output) # "California" + +# 同步运行器也同样适用 +result = Runner.run_sync( + agent, + "What's the population?", + session=session +) +print(result.final_output) # "Approximately 39 million" +``` + +## 工作原理 + +启用会话记忆时: + +1. **每次运行前**:运行器自动检索会话的对话历史,并将其前置到输入项中。 +2. **每次运行后**:运行期间生成的所有新项(用户输入、助手响应、工具调用等)都会自动存储在会话中。 +3. **上下文保持**:同一会话的每次后续运行都包含完整的对话历史,使智能体能够保持上下文。 + +这消除了手动调用 `.to_input_list()` 和管理运行之间对话状态的需要。 + +## 内存操作 + +### 基本操作 + +会话支持用于管理对话历史的多种操作: + +```python +from agents import SQLiteSession + +session = SQLiteSession("user_123", "conversations.db") + +# 获取会话中的所有项 +items = await session.get_items() + +# 向会话添加新项 +new_items = [ + {"role": "user", "content": "Hello"}, + {"role": "assistant", "content": "Hi there!"} +] +await session.add_items(new_items) + +# 移除并返回最近的一项 +last_item = await session.pop_item() +print(last_item) # {"role": "assistant", "content": "Hi there!"} + +# 清除会话中的所有项 +await session.clear_session() +``` + +### 使用 pop_item 进行更正 + +`pop_item` 方法在你想要撤消或修改对话中的最后一项时特别有用: + +```python +from agents import Agent, Runner, SQLiteSession + +agent = Agent(name="Assistant") +session = SQLiteSession("correction_example") + +# 初始对话 +result = await Runner.run( + agent, + "What's 2 + 2?", + session=session +) +print(f"Agent: {result.final_output}") + +# 用户想要更正他们的问题 +assistant_item = await session.pop_item() # 移除智能体的响应 +user_item = await session.pop_item() # 移除用户的问题 + +# 询问更正后的问题 +result = await Runner.run( + agent, + "What's 2 + 3?", + session=session +) +print(f"Agent: {result.final_output}") +``` + +## 内存选项 + +### 无内存(默认) + +```python +# 默认行为 - 无会话内存 +result = await Runner.run(agent, "Hello") +``` + +### OpenAI 对话 API 内存 + +使用 [OpenAI 对话 API](https://platform.openai.com/docs/api-reference/conversations/create) 来持久化 +[对话状态](https://platform.openai.com/docs/guides/conversation-state?api-mode=responses#using-the-conversations-api),无需管理自己的数据库。当你已经依赖 OpenAI 托管的基础设施来存储对话历史时,这很有帮助。 + +```python +from agents import OpenAIConversationsSession + +session = OpenAIConversationsSession() + +# 可选地通过传递对话 ID 来恢复之前的对话 +# session = OpenAIConversationsSession(conversation_id="conv_123") + +result = await Runner.run( + agent, + "Hello", + session=session, +) +``` + +### SQLite 内存 + +```python +from agents import SQLiteSession + +# 内存数据库(进程结束时丢失) +session = SQLiteSession("user_123") + +# 基于文件的持久数据库 +session = SQLiteSession("user_123", "conversations.db") + +# 使用会话 +result = await Runner.run( + agent, + "Hello", + session=session +) +``` + +### 多个会话 + +```python +from agents import Agent, Runner, SQLiteSession + +agent = Agent(name="Assistant") + +# 不同的会话维护单独的对话历史 +session_1 = SQLiteSession("user_123", "conversations.db") +session_2 = SQLiteSession("user_456", "conversations.db") + +result1 = await Runner.run( + agent, + "Hello", + session=session_1 +) +result2 = await Runner.run( + agent, + "Hello", + session=session_2 +) +``` + +### SQLAlchemy 驱动的会话 + +对于更高级的用例,你可以使用 SQLAlchemy 驱动的会话后端。这允许你使用 SQLAlchemy 支持的任何数据库(PostgreSQL、MySQL、SQLite 等)进行会话存储。 + +**示例 1:使用 `from_url` 和内存 SQLite** + +这是最简单的入门方式,非常适合开发和测试。 + +```python +import asyncio +from agents import Agent, Runner +from agents.extensions.memory.sqlalchemy_session import SQLAlchemySession + +async def main(): + agent = Agent("Assistant") + session = SQLAlchemySession.from_url( + "user-123", + url="sqlite+aiosqlite:///:memory:", + create_tables=True, # 为演示自动创建表 + ) + + result = await Runner.run(agent, "Hello", session=session) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +**示例 2:使用现有的 SQLAlchemy 引擎** + +在生产应用中,你可能已经有一个 SQLAlchemy `AsyncEngine` 实例。你可以直接将其传递给会话。 + +```python +import asyncio +from agents import Agent, Runner +from agents.extensions.memory.sqlalchemy_session import SQLAlchemySession +from sqlalchemy.ext.asyncio import create_async_engine + +async def main(): + # 在你的应用中,你会使用现有的引擎 + engine = create_async_engine("sqlite+aiosqlite:///conversations.db") + + agent = Agent("Assistant") + session = SQLAlchemySession( + "user-456", + engine=engine, + create_tables=True, # 为演示自动创建表 + ) + + result = await Runner.run(agent, "Hello", session=session) + print(result.final_output) + + await engine.dispose() + +if __name__ == "__main__": + asyncio.run(main()) +``` + + +## 自定义内存实现 + +你可以通过创建遵循 [`Session`][agents.memory.session.Session] 协议的类来实现自己的会话内存: + +```python +from agents.memory.session import SessionABC +from agents.items import TResponseInputItem +from typing import List + +class MyCustomSession(SessionABC): + """遵循会话协议的自定义会话实现。""" + + def __init__(self, session_id: str): + self.session_id = session_id + # 你的初始化代码在这里 + + async def get_items(self, limit: int | None = None) -> List[TResponseInputItem]: + """检索此会话的对话历史。""" + # 你的实现代码在这里 + pass + + async def add_items(self, items: List[TResponseInputItem]) -> None: + """为此会话存储新项。""" + # 你的实现代码在这里 + pass + + async def pop_item(self) -> TResponseInputItem | None: + """从此会话中移除并返回最近的一项。""" + # 你的实现代码在这里 + pass + + async def clear_session(self) -> None: + """清除此会话的所有项。""" + # 你的实现代码在这里 + pass + +# 使用你的自定义会话 +agent = Agent(name="Assistant") +result = await Runner.run( + agent, + "Hello", + session=MyCustomSession("my_session") +) +``` + +## 会话管理 + +### 会话ID命名 + +使用有意义的会话ID来帮助你组织对话: + +- 基于用户:`"user_12345"` +- 基于线程:`"thread_abc123"` +- 基于上下文:`"support_ticket_456"` + +### 内存持久性 + +- 使用内存 SQLite (`SQLiteSession("session_id")`) 进行临时对话 +- 使用基于文件的 SQLite (`SQLiteSession("session_id", "path/to/db.sqlite")`) 进行持久对话 +- 使用 SQLAlchemy 驱动的会话 (`SQLAlchemySession("session_id", engine=engine, create_tables=True)`) 用于具有 SQLAlchemy 支持的现有数据库的生产系统 +- 使用 OpenAI 托管存储 (`OpenAIConversationsSession()`) 当你希望将历史记录存储在 OpenAI 对话 API 中时 +- 考虑为其他生产系统(Redis、Django 等)实现自定义会话后端,以应对更高级的用例 + +### 会话管理 + +```python +# 当对话应该重新开始时清除会话 +await session.clear_session() + +# 不同的智能体可以共享相同的会话 +support_agent = Agent(name="Support") +billing_agent = Agent(name="Billing") +session = SQLiteSession("user_123") + +# 两个智能体都将看到相同的对话历史 +result1 = await Runner.run( + support_agent, + "Help me with my account", + session=session +) +result2 = await Runner.run( + billing_agent, + "What are my charges?", + session=session +) +``` + +## 完整示例 + +以下是展示会话内存实际应用的完整示例: + +```python +import asyncio +from agents import Agent, Runner, SQLiteSession + + +async def main(): + # 创建智能体 + agent = Agent( + name="Assistant", + instructions="Reply very concisely.", + ) + + # 创建一个将在运行之间持久的会话实例 + session = SQLiteSession("conversation_123", "conversation_history.db") + + print("=== 会话示例 ===") + print("智能体将自动记住之前的消息。\n") + + # 第一轮 + print("第一轮:") + print("User: What city is the Golden Gate Bridge in?") + result = await Runner.run( + agent, + "What city is the Golden Gate Bridge in?", + session=session + ) + print(f"Assistant: {result.final_output}") + print() + + # 第二轮 - 智能体将记住之前的对话 + print("第二轮:") + print("User: What state is it in?") + result = await Runner.run( + agent, + "What state is it in?", + session=session + ) + print(f"Assistant: {result.final_output}") + print() + + # 第三轮 - 继续对话 + print("第三轮:") + print("User: What's the population of that state?") + result = await Runner.run( + agent, + "What's the population of that state?", + session=session + ) + print(f"Assistant: {result.final_output}") + print() + + print("=== 对话完成 ===") + print("注意智能体如何从前几轮记住上下文!") + print("会话自动处理对话历史。") + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## API 参考 + +有关详细的 API 文档,请参见: + +- [`Session`][agents.memory.Session] - 协议接口 +- [`SQLiteSession`][agents.memory.SQLiteSession] - SQLite 实现 +- [`OpenAIConversationsSession`](ref/memory/openai_conversations_session.md) - OpenAI 对话 API 实现 +- [`SQLAlchemySession`][agents.extensions.memory.sqlalchemy_session.SQLAlchemySession] - SQLAlchemy 驱动的实现 \ No newline at end of file diff --git a/docs/zh/streaming.md b/docs/zh/streaming.md new file mode 100644 index 000000000..13c2f0884 --- /dev/null +++ b/docs/zh/streaming.md @@ -0,0 +1,91 @@ +--- +search: + exclude: true +--- +# 流式处理 + +流式处理让你可以订阅智能体运行过程中的更新。这对于向最终用户显示进度更新和部分响应很有用。 + +要进行流式处理,你可以调用 [`Runner.run_streamed()`][agents.run.Runner.run_streamed],它会返回一个 [`RunResultStreaming`][agents.result.RunResultStreaming]。调用 `result.stream_events()` 会给你一个在下面描述的 [`StreamEvent`][agents.stream_events.StreamEvent] 对象的异步流。 + +## 原始响应事件 + +[`RawResponsesStreamEvent`][agents.stream_events.RawResponsesStreamEvent] 是从LLM直接传递的原始事件。它们采用OpenAI响应API格式,这意味着每个事件都有一个类型(如`response.created`、`response.output_text.delta`等)和数据。当你想在生成响应消息时立即将它们流式传输给用户,这些事件很有用。 + +例如,这将逐token输出LLM生成的文本。 + +```python +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] 是更高级别的事件。它们通知你某个项目已完全生成的时间。这使你可以在"消息已生成"、"工具已运行"等层面上推送进度更新,而不是每个token。同样,[`AgentUpdatedStreamEvent`][agents.stream_events.AgentUpdatedStreamEvent] 在当前智能体发生更改时(例如由于交接的结果)为你提供更新。 + +例如,这将忽略原始事件并向用户流式传输更新。 + +```python +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("=== 运行开始 ===") + + async for event in result.stream_events(): + # 我们将忽略原始响应事件增量 + if event.type == "raw_response_event": + continue + # 当智能体更新时,打印该信息 + elif event.type == "agent_updated_stream_event": + print(f"智能体已更新: {event.new_agent.name}") + continue + # 当项目生成时,打印它们 + elif event.type == "run_item_stream_event": + if event.item.type == "tool_call_item": + print("-- 工具被调用") + elif event.item.type == "tool_call_output_item": + print(f"-- 工具输出: {event.item.output}") + elif event.item.type == "message_output_item": + print(f"-- 消息输出:\n {ItemHelpers.text_message_output(event.item)}") + else: + pass # 忽略其他事件类型 + + print("=== 运行完成 ===") + + +if __name__ == "__main__": + asyncio.run(main()) +``` \ No newline at end of file diff --git a/docs/zh/tools.md b/docs/zh/tools.md new file mode 100644 index 000000000..b72b53a71 --- /dev/null +++ b/docs/zh/tools.md @@ -0,0 +1,417 @@ +--- +search: + exclude: true +--- +# 工具 + +工具让智能体能够执行操作:比如获取数据、运行代码、调用外部API,甚至使用计算机。Agent SDK中有三类工具: + +- 托管工具:这些工具在AI模型旁边的LLM服务器上运行。OpenAI提供检索、网络搜索和计算机使用作为托管工具。 +- 函数调用:这些允许你将任何Python函数作为工具使用。 +- 智能体作为工具:这允许你将智能体作为工具使用,让智能体可以调用其他智能体而不需要交接控制权。 + +## 托管工具 + +使用 [`OpenAIResponsesModel`][agents.models.openai_responses.OpenAIResponsesModel] 时,OpenAI提供了一些内置工具: + +- [`WebSearchTool`][agents.tool.WebSearchTool] 让智能体搜索网络。 +- [`FileSearchTool`][agents.tool.FileSearchTool] 允许从你的OpenAI向量存储中检索信息。 +- [`ComputerTool`][agents.tool.ComputerTool] 允许自动化计算机使用任务。 +- [`CodeInterpreterTool`][agents.tool.CodeInterpreterTool] 让LLM在沙盒环境中执行代码。 +- [`HostedMCPTool`][agents.tool.HostedMCPTool] 将远程MCP服务器的工具暴露给模型。 +- [`ImageGenerationTool`][agents.tool.ImageGenerationTool] 根据提示生成图像。 +- [`LocalShellTool`][agents.tool.LocalShellTool] 在你的机器上运行shell命令。 + +```python +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函数作为工具。Agents SDK会自动设置工具: + +- 工具的名称将是Python函数的名称(或者你可以提供一个名称) +- 工具描述将从函数的文档字符串中获取(或者你可以提供一个描述) +- 函数输入的模式会自动从函数的参数创建 +- 每个输入的描述从函数的文档字符串中获取,除非被禁用 + +我们使用Python的 `inspect` 模块来提取函数签名,同时使用 [`griffe`](https://mkdocstrings.github.io/griffe/) 来解析文档字符串,使用 `pydantic` 来创建模式。 + +```python +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 "" + + +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. 你可以将装饰后的函数传递给工具列表。 + +??? note "展开查看输出" + + ``` + 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`,这是一个异步函数,接收 [`ToolContext`][agents.tool_context.ToolContext] 和作为JSON字符串的参数,并且必须返回作为字符串的工具输出。 + +```python +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模型、TypedDict等。 +2. 我们使用 `griffe` 来解析文档字符串。支持的文档字符串格式有 `google`、`sphinx` 和 `numpy`。我们尝试自动检测文档字符串格式,但这是尽力而为的,你可以在调用 `function_tool` 时显式设置它。你也可以通过设置 `use_docstring_info` 为 `False` 来禁用文档字符串解析。 + +模式提取的代码位于 [`agents.function_schema`][] 中。 + +## 智能体作为工具 + +在某些工作流中,你可能希望一个中央智能体来协调一组专门的智能体网络,而不是交接控制权。你可以通过将智能体建模为工具来实现这一点。 + +```python +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) +``` + +### 自定义工具智能体 + +`agent.as_tool` 函数是一个方便的方法,使得将智能体转换为工具变得容易。然而,它不支持所有配置;例如,你不能设置 `max_turns`。对于高级用例,直接在工具实现中使用 `Runner.run`: + +```python +@function_tool +async def run_my_agent() -> str: + """A tool that runs the agent with custom configs""" + + agent = Agent(name="My agent", instructions="...") + + result = await Runner.run( + agent, + input="...", + max_turns=5, + run_config=... + ) + + return str(result.final_output) +``` + +### 自定义输出提取 + +在某些情况下,你可能希望在将工具智能体的输出返回给中央智能体之前修改它。这可能有用,如果你想: + +- 从子智能体的聊天记录中提取特定信息(例如,JSON有效载荷)。 +- 转换或重新格式化智能体的最终答案(例如,将Markdown转换为纯文本或CSV)。 +- 验证输出或在智能体的响应缺失或格式错误时提供回退值。 + +你可以通过向 `as_tool` 方法提供 `custom_output_extractor` 参数来做到这一点: + +```python +async def extract_json_payload(run_result: RunResult) -> str: + # 以相反的顺序扫描智能体的输出,直到我们找到来自工具调用的类似JSON的消息。 + for item in reversed(run_result.new_items): + if isinstance(item, ToolCallOutputItem) and item.output.strip().startswith("{"): + return item.output.strip() + # 如果什么都没找到,回退到空JSON对象 + return "{}" + + +json_tool = data_agent.as_tool( + tool_name="get_data_json", + tool_description="Run the data agent and return only its JSON payload", + custom_output_extractor=extract_json_payload, +) +``` + +### 条件工具启用 + +你可以使用 `is_enabled` 参数在运行时条件性地启用或禁用智能体工具。这允许你根据上下文、用户偏好或运行时条件动态过滤哪些工具可供LLM使用。 + +```python +import asyncio +from agents import Agent, AgentBase, Runner, RunContextWrapper +from pydantic import BaseModel + +class LanguageContext(BaseModel): + language_preference: str = "french_spanish" + +def french_enabled(ctx: RunContextWrapper[LanguageContext], agent: AgentBase) -> bool: + """Enable French for French+Spanish preference.""" + return ctx.context.language_preference == "french_spanish" + +# Create specialized agents +spanish_agent = Agent( + name="spanish_agent", + instructions="You respond in Spanish. Always reply to the user's question in Spanish.", +) + +french_agent = Agent( + name="french_agent", + instructions="You respond in French. Always reply to the user's question in French.", +) + +# Create orchestrator with conditional tools +orchestrator = Agent( + name="orchestrator", + instructions=( + "You are a multilingual assistant. You use the tools given to you to respond to users. " + "You must call ALL available tools to provide responses in different languages. " + "You never respond in languages yourself, you always use the provided tools." + ), + tools=[ + spanish_agent.as_tool( + tool_name="respond_spanish", + tool_description="Respond to the user's question in Spanish", + is_enabled=True, # Always enabled + ), + french_agent.as_tool( + tool_name="respond_french", + tool_description="Respond to the user's question in French", + is_enabled=french_enabled, + ), + ], +) + +async def main(): + context = RunContextWrapper(LanguageContext(language_preference="french_spanish")) + result = await Runner.run(orchestrator, "How are you?", context=context.context) + print(result.final_output) + +asyncio.run(main()) +``` + +`is_enabled` 参数接受: + +- **布尔值**:`True`(始终启用)或 `False`(始终禁用) +- **可调用函数**:接收 `(context, agent)` 并返回布尔值的函数 +- **异步函数**:用于复杂条件逻辑的异步函数 + +禁用的工具在运行时对LLM完全隐藏,这使得它适用于: + +- 基于用户权限的功能开关 +- 环境特定的工具可用性(开发环境 vs 生产环境) +- A/B测试不同的工具配置 +- 基于运行时状态的动态工具过滤 + +## 函数工具中的错误处理 + +当你通过 `@function_tool` 创建函数工具时,你可以传递一个 `failure_error_function`。这是一个函数,在工具调用崩溃的情况下向LLM提供错误响应。 + +- 默认情况下(即如果你没有传递任何东西),它会运行一个 `default_tool_error_function`,告诉LLM发生了错误。 +- 如果你传递自己的错误函数,它会运行那个函数,并将响应发送给LLM。 +- 如果你显式地传递 `None`,那么任何工具调用错误都会被重新抛出,供你处理。这可能是 `ModelBehaviorError`(如果模型产生了无效的JSON),或者 `UserError`(如果你的代码崩溃了)等。 + +```python +from agents import function_tool, RunContextWrapper +from typing import Any + +def my_custom_error_function(context: RunContextWrapper[Any], error: Exception) -> str: + """A custom function to provide a user-friendly error message.""" + print(f"A tool call failed with the following error: {error}") + return "An internal server error occurred. Please try again later." + +@function_tool(failure_error_function=my_custom_error_function) +def get_user_profile(user_id: str) -> str: + """Fetches a user profile from a mock API. + This function demonstrates a 'flaky' or failing API call. + """ + if user_id == "user_123": + return "User profile for user_123 successfully retrieved." + else: + raise ValueError(f"Could not retrieve profile for user_id: {user_id}. API returned an error.") + +``` + +如果你手动创建一个 `FunctionTool` 对象,那么你必须在 `on_invoke_tool` 函数内部处理错误。 \ No newline at end of file diff --git a/docs/zh/tracing.md b/docs/zh/tracing.md new file mode 100644 index 000000000..b0ba3bea5 --- /dev/null +++ b/docs/zh/tracing.md @@ -0,0 +1,151 @@ +--- +search: + exclude: true +--- +# 追踪功能 + +Agents SDK包含内置的追踪功能,可全面记录智能体运行期间的事件:LLM生成、工具调用、交接、护栏,甚至发生的自定义事件。使用[追踪仪表板](https://platform.openai.com/traces),你可以在开发和生产环境中调试、可视化和监控你的工作流程。 + +!!!note + + 追踪功能默认启用。有两种方法可以禁用追踪: + + 1. 你可以通过设置环境变量 `OPENAI_AGENTS_DISABLE_TRACING=1` 来全局禁用追踪 + 2. 你可以通过将 [`agents.run.RunConfig.tracing_disabled`][] 设置为 `True` 来针对单次运行禁用追踪 + +***对于使用OpenAI API并在零数据保留(ZDR)策略下运营的组织,追踪功能不可用。*** + +## 追踪和跨度 + +- **追踪** 代表"工作流"的单个端到端操作。它们由跨度组成。追踪具有以下属性: + - `workflow_name`: 这是逻辑工作流或应用。例如"代码生成"或"客户服务"。 + - `trace_id`: 追踪的唯一ID。如果你没有传递一个,它会自动生成。格式必须是 `trace_<32_字母数字>`。 + - `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()` 包装 +- LLM生成被 `generation_span()` 包装 +- 函数工具的调用分别被 `function_span()` 包装 +- 护栏被 `guardrail_span()` 包装 +- 交接被 `handoff_span()` 包装 +- 语音输入(语音识别)被 `transcription_span()` 包装 +- 语音输出(语音合成)被 `speech_span()` 包装 +- 相关的语音跨度可能成为 `speech_group_span()` 的子跨度 + +默认情况下,追踪名称为"Agent workflow"。你可以在使用 `trace` 时设置此名称,也可以在 [`RunConfig`][agents.run.RunConfig] 中设置名称和其他属性。 + +此外,你可以配置[自定义追踪处理器](#自定义追踪处理器)来将追踪输出到其他目标(作为替代或附加目标)。 + +## 高层级追踪 + +有时你可能想将多次 `run()` 调用合并到一个追踪中。要做到这一点,可以用 `trace()` 包装整个代码块。 + +```python +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_result.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. 推荐:将追踪作为上下文管理器使用(例如:`with trace(...) as my_trace`)。这会在适当的时候自动开始和结束。 +2. 也可以手动调用 [`trace.start()`][agents.tracing.Trace.start] 和 [`trace.finish()`][agents.tracing.Trace.finish]。 + +当前追踪由Python的 [`contextvar`](https://docs.python.org/3/library/contextvars.html) 跟踪。这意味着它在并发处理中自动工作。如果手动开始/结束追踪,需要向 `start()`/`finish()` 传递 `mark_as_current` 和 `reset_current` 来更新当前追踪。 + +## 创建跨度 + +你可以使用各种 [`*_span()`][agents.tracing.create] 方法创建跨度。通常不需要手动创建跨度。要追踪自定义跨度信息,可以使用 [`custom_span()`][agents.tracing.custom_span] 函数。 + +跨度会自动成为当前追踪的一部分,并嵌套在由Python的 [`contextvar`](https://docs.python.org/3/library/contextvars.html) 跟踪的最近的当前跨度之下。 + +## 敏感数据 + +某些跨度可能会捕获敏感数据。 + +`generation_span()` 保存LLM生成的输入/输出,`function_span()` 保存函数调用的输入/输出。这些可能包含敏感数据,因此可以通过 [`RunConfig.trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data] 禁用其捕获。 + +类似地,语音跨度默认包含输入/输出音频的base64编码PCM数据。可以设置 [`VoicePipelineConfig.trace_include_sensitive_audio_data`][agents.voice.pipeline_config.VoicePipelineConfig.trace_include_sensitive_audio_data] 来禁用此音频数据的捕获。 + +## 自定义追踪处理器 + +追踪的高层级架构如下: + +- 初始化时,创建一个全局的 [`TraceProvider`][agents.tracing.setup.TraceProvider],负责创建追踪。 +- 为 `TraceProvider` 设置一个 [`BatchTraceProcessor`][agents.tracing.processors.BatchTraceProcessor],它将跨度/追踪批量发送到 [`BackendSpanExporter`][agents.tracing.processors.BackendSpanExporter]。`BackendSpanExporter` 将其批量导出到OpenAI后端。 + +要自定义默认设置,发送到其他后端/复制到额外后端/更改导出器行为,有两种方法: + +1. [`add_trace_processor()`][agents.tracing.add_trace_processor] 可以添加**额外的**追踪处理器,一旦追踪和跨度准备就绪就会接收它们。这允许你在发送到OpenAI后端的同时执行自己的处理。 +2. [`set_trace_processors()`][agents.tracing.set_trace_processors] 可以用自己的追踪处理器**替换**默认处理器。要将追踪发送到OpenAI后端,需要包含执行该操作的 `TracingProcessor`。 + +## 非OpenAI模型的追踪 + +使用OpenAI的API密钥配合非OpenAI模型时,可以在不禁用追踪的情况下启用OpenAI追踪仪表板中的免费追踪。 + +```python +import os +from agents import set_tracing_export_api_key, Agent, Runner +from agents.extensions.models.litellm_model import LitellmModel + +tracing_api_key = os.environ["OPENAI_API_KEY"] +set_tracing_export_api_key(tracing_api_key) + +model = LitellmModel( + model="your-model-name", + api_key="your-api-key", +) + +agent = Agent( + name="Assistant", + model=model, +) +``` + +## 注意事项 +- 免费追踪可以在OpenAI追踪仪表板中查看。 + +## 外部追踪处理器列表 + +- [Weights & Biases](https://weave-docs.wandb.ai/guides/integrations/openai_agents) +- [Arize-Phoenix](https://docs.arize.com/phoenix/tracing/integrations-tracing/openai-agents-sdk) +- [Future AGI](https://docs.futureagi.com/future-agi/products/observability/auto-instrumentation/openai_agents) +- [MLflow (self-hosted/OSS)](https://mlflow.org/docs/latest/tracing/integrations/openai-agent) +- [MLflow (Databricks hosted)](https://docs.databricks.com/aws/en/mlflow/mlflow-tracing#-automatic-tracing) +- [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#openai-agents-sdk) +- [Pydantic Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#openai-agents) +- [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk) +- [Scorecard](https://docs.scorecard.io/docs/documentation/features/tracing#openai-agents-sdk-integration) +- [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent) +- [LangSmith](https://docs.smith.langchain.com/observability/how_to_guides/trace_with_openai_agents_sdk) +- [Maxim AI](https://www.getmaxim.ai/docs/observe/integrations/openai-agents-sdk) +- [Comet Opik](https://www.comet.com/docs/opik/tracing/integrations/openai_agents) +- [Langfuse](https://langfuse.com/docs/integrations/openaiagentssdk/openai-agents) +- [Langtrace](https://docs.langtrace.ai/supported-integrations/llm-frameworks/openai-agents-sdk) +- [Okahu-Monocle](https://github.com/monocle2ai/monocle) +- [Galileo](https://v2docs.galileo.ai/integrations/openai-agent-integration#openai-agent-integration) +- [Portkey AI](https://portkey.ai/docs/integrations/agents/openai-agents) +- [LangDB AI](https://docs.langdb.ai/getting-started/working-with-agent-frameworks/working-with-openai-agents-sdk) +- [Agenta](https://docs.agenta.ai/observability/integrations/openai-agents) diff --git a/docs/zh/usage.md b/docs/zh/usage.md new file mode 100644 index 000000000..67ee8b705 --- /dev/null +++ b/docs/zh/usage.md @@ -0,0 +1,86 @@ +--- +search: + exclude: true +--- +# 使用统计 + +Agents SDK会自动跟踪每次运行的token使用情况。你可以从运行上下文中访问它,用于监控成本、强制执行限制或记录分析。 + +## 跟踪内容 + +- **requests**: 发出的LLM API调用数量 +- **input_tokens**: 发送的总输入token数 +- **output_tokens**: 接收的总输出token数 +- **total_tokens**: 输入 + 输出 +- **details**: + - `input_tokens_details.cached_tokens` + - `output_tokens_details.reasoning_tokens` + +## 从运行中访问使用情况 + +在 `Runner.run(...)` 之后,通过 `result.context_wrapper.usage` 访问使用情况。 + +```python +result = await Runner.run(agent, "What's the weather in Tokyo?") +usage = result.context_wrapper.usage + +print("Requests:", usage.requests) +print("Input tokens:", usage.input_tokens) +print("Output tokens:", usage.output_tokens) +print("Total tokens:", usage.total_tokens) +``` + +使用情况在运行期间的所有模型调用(包括工具调用和交接)中进行汇总。 + +### 启用LiteLLM模型的使用情况 + +LiteLLM提供程序默认不报告使用指标。当你使用 [`LitellmModel`](models/litellm.md) 时,向你的智能体传递 `ModelSettings(include_usage=True)`,这样LiteLLM响应就会填充到 `result.context_wrapper.usage` 中。 + +```python +from agents import Agent, ModelSettings, Runner +from agents.extensions.models.litellm_model import LitellmModel + +agent = Agent( + name="Assistant", + model=LitellmModel(model="your/model", api_key="..."), + model_settings=ModelSettings(include_usage=True), +) + +result = await Runner.run(agent, "What's the weather in Tokyo?") +print(result.context_wrapper.usage.total_tokens) +``` + +## 在会话中获取使用情况 + +当你使用 `Session`(例如 `SQLiteSession`)时,每次调用 `Runner.run(...)` 都会返回该特定运行的使用情况。会话为上下文维护对话历史,但每次运行的使用情况是独立的。 + +```python +session = SQLiteSession("my_conversation") + +first = await Runner.run(agent, "Hi!", session=session) +print(first.context_wrapper.usage.total_tokens) # 第一次运行的使用情况 + +second = await Runner.run(agent, "Can you elaborate?", session=session) +print(second.context_wrapper.usage.total_tokens) # 第二次运行的使用情况 +``` + +请注意,虽然会话在运行之间保留对话上下文,但每次 `Runner.run()` 调用返回的使用情况指标只代表该特定执行。在会话中,先前的消息可能会作为输入重新提供给每次运行,这会影响后续轮次的输入token计数。 + +## 在钩子中使用使用情况 + +如果你正在使用 `RunHooks`,传递给每个钩子的 `context` 对象包含 `usage`。这让你在关键生命周期时刻记录使用情况。 + +```python +class MyHooks(RunHooks): + async def on_agent_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None: + u = context.usage + print(f"{agent.name} → {u.requests} 次请求, {u.total_tokens} 总token数") +``` + +## API 参考 + +有关详细的 API 文档,请参见: + +- [`Usage`][agents.usage.Usage] - 使用情况跟踪数据结构 +- [`RunContextWrapper`][agents.run.RunContextWrapper] - 从运行上下文访问使用情况 +- [`RunHooks`][agents.run.RunHooks] - 挂钩到使用情况跟踪生命周期 \ No newline at end of file diff --git a/docs/zh/visualization.md b/docs/zh/visualization.md new file mode 100644 index 000000000..9af8a1a76 --- /dev/null +++ b/docs/zh/visualization.md @@ -0,0 +1,107 @@ +--- +search: + exclude: true +--- +# 智能体可视化 + +智能体可视化允许你使用 **Graphviz** 生成智能体及其关系的结构化图形表示。这有助于理解应用程序中智能体、工具和交接如何相互作用。 + +## 安装 + +安装可选的 `viz` 依赖组: + +```bash +pip install "openai-agents[viz]" +``` + +## 生成图表 + +你可以使用 `draw_graph` 函数生成智能体可视化。此函数创建一个有向图,其中: + +- **智能体** 表示为黄色框。 +- **MCP 服务器** 表示为灰色框。 +- **工具** 表示为绿色椭圆。 +- **交接** 表示为智能体之间的有向边。 + +### 使用示例 + +```python +import os + +from agents import Agent, function_tool +from agents.mcp.server import MCPServerStdio +from agents.extensions.visualization import draw_graph + +@function_tool +def get_weather(city: str) -> str: + return f"The weather in {city} is sunny." + +spanish_agent = Agent( + name="Spanish agent", + instructions="You only speak Spanish.", +) + +english_agent = Agent( + name="English agent", + instructions="You only speak English", +) + +current_dir = os.path.dirname(os.path.abspath(__file__)) +samples_dir = os.path.join(current_dir, "sample_files") +mcp_server = MCPServerStdio( + name="Filesystem Server, via npx", + params={ + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir], + }, +) + +triage_agent = Agent( + name="Triage agent", + instructions="Handoff to the appropriate agent based on the language of the request.", + handoffs=[spanish_agent, english_agent], + tools=[get_weather], + mcp_servers=[mcp_server], +) + +draw_graph(triage_agent) +``` + +![智能体图表](../assets/images/graph.png) + +这会生成一个可视化图表,表示 **分类智能体** 的结构及其与子智能体和工具的连接。 + + +## 理解可视化 + +生成的图表包括: + +- 一个 **开始节点** (`__start__`) 表示入口点。 +- 智能体表示为带有黄色填充的 **矩形**。 +- 工具表示为带有绿色填充的 **椭圆**。 +- MCP 服务器表示为带有灰色填充的 **矩形**。 +- 指示交互的有向边: + - **实线箭头** 用于智能体到智能体的交接。 + - **虚线箭头** 用于工具调用。 + - **点线箭头** 用于 MCP 服务器调用。 +- 一个 **结束节点** (`__end__`) 表示执行终止的位置。 + +**注意:** MCP 服务器在 `agents` 包的最新版本中渲染(已在 **v0.2.8** 中验证)。如果你的可视化中没有看到 MCP 框,请升级到最新版本。 + +## 自定义图表 + +### 图表显示 +默认情况下,`draw_graph` 会内联显示图表。要在单独的窗口中显示图表,请编写以下内容: + +```python +draw_graph(triage_agent).view() +``` + +### 保存图表 +默认情况下,`draw_graph` 会内联显示图表。要将其保存为文件,请指定文件名: + +```python +draw_graph(triage_agent, filename="agent_graph") +``` + +这将在工作目录中生成 `agent_graph.png`。 \ No newline at end of file diff --git a/docs/zh/voice/pipeline.md b/docs/zh/voice/pipeline.md new file mode 100644 index 000000000..b0e5577da --- /dev/null +++ b/docs/zh/voice/pipeline.md @@ -0,0 +1,79 @@ +--- +search: + exclude: true +--- +# 管道和工作流 + +[`VoicePipeline`][agents.voice.pipeline.VoicePipeline] 是一个可以轻松将智能体工作流转换为语音应用的类。您传入要运行的工作流,管道会负责转录输入音频、检测音频何时结束、在适当时间调用您的工作流,并将工作流输出转换回音频。 + +```mermaid +graph LR + %% Input + A["🎤 Audio Input"] + + %% Voice Pipeline + subgraph Voice_Pipeline [Voice Pipeline] + direction TB + B["Transcribe (speech-to-text)"] + C["Your Code"]:::highlight + D["Text-to-speech"] + B --> C --> D + end + + %% Output + E["🎧 Audio Output"] + + %% Flow + A --> Voice_Pipeline + Voice_Pipeline --> E + + %% Custom styling + classDef highlight fill:#ffcc66,stroke:#333,stroke-width:1px,font-weight:700; + +``` + +## 配置管道 + +创建管道时,您可以设置以下内容: + +1. [`workflow`][agents.voice.workflow.VoiceWorkflowBase],即每次转录新音频时运行的代码 +2. 使用的 [`speech-to-text`][agents.voice.model.STTModel] 和 [`text-to-speech`][agents.voice.model.TTSModel] 模型 +3. [`config`][agents.voice.pipeline_config.VoicePipelineConfig],可让您配置以下内容: + - 模型提供程序,可将模型名称映射到模型 + - 追踪,包括是否禁用追踪、是否上传音频文件、工作流名称、追踪ID等 + - TTS 和 STT 模型的设置,如提示、语言和使用的数据类型 + +## 运行管道 + +您可以通过 [`run()`][agents.voice.pipeline.VoicePipeline.run] 方法运行管道,该方法允许您以两种形式传入音频输入: + +1. [`AudioInput`][agents.voice.input.AudioInput] 用于当您有完整的音频转录,并且只想为其生成结果时。这在您不需要检测说话者何时完成发言的情况下很有用;例如,当您有预录制音频或在按下说话应用中用户完成发言很明确时。 +2. [`StreamedAudioInput`][agents.voice.input.StreamedAudioInput] 用于当您可能需要检测用户何时完成发言时。它允许您在检测到音频块时推送它们,语音管道将通过称为"活动检测"的过程在适当时间自动运行智能体工作流。 + +## 结果 + +语音管道运行的结果是 [`StreamedAudioResult`][agents.voice.result.StreamedAudioResult]。这是一个允许您流式接收发生事件的对象。有几种 [`VoiceStreamEvent`][agents.voice.events.VoiceStreamEvent],包括: + +1. [`VoiceStreamEventAudio`][agents.voice.events.VoiceStreamEventAudio],包含音频块 +2. [`VoiceStreamEventLifecycle`][agents.voice.events.VoiceStreamEventLifecycle],通知生命周期事件,如轮次开始或结束 +3. [`VoiceStreamEventError`][agents.voice.events.VoiceStreamEventError],是错误事件 + +```python + +result = await pipeline.run(input) + +async for event in result.stream(): + if event.type == "voice_stream_event_audio": + # play audio + elif event.type == "voice_stream_event_lifecycle": + # lifecycle + elif event.type == "voice_stream_event_error" + # error + ... +``` + +## 最佳实践 + +### 中断 + +Agents SDK 目前不支持 [`StreamedAudioInput`][agents.voice.input.StreamedAudioInput] 的任何内置中断支持。相反,对于每个检测到的轮次,它将触发您工作流的单独运行。如果您想在应用程序内处理中断,可以监听 [`VoiceStreamEventLifecycle`][agents.voice.events.VoiceStreamEventLifecycle] 事件。`turn_started` 将表示新轮次已被转录并且处理正在开始。`turn_ended` 将在相应轮次的所有音频被调度后触发。您可以使用这些事件在模型开始轮次时静音说话者的麦克风,并在刷新轮次的所有相关音频后取消静音。 \ No newline at end of file diff --git a/docs/zh/voice/quickstart.md b/docs/zh/voice/quickstart.md new file mode 100644 index 000000000..7f5185097 --- /dev/null +++ b/docs/zh/voice/quickstart.md @@ -0,0 +1,198 @@ +--- +search: + exclude: true +--- +# 快速开始 + +## 前提条件 + +请遵循 OpenAI Agents SDK 的基本[快速开始说明](../quickstart.md),并设置虚拟环境。然后,从 SDK 安装可选的语音依赖项: + +```bash +pip install 'openai-agents[voice]' +``` + +## 概念 + +需要了解的主要概念是 [`VoicePipeline`][agents.voice.pipeline.VoicePipeline],它是一个3步骤的过程: + +1. 运行语音转文本模型将音频转换为文本。 +2. 运行您的代码(通常是智能体驱动的工作流)以产生结果。 +3. 运行文本转语音模型将结果文本转换回音频。 + +```mermaid +graph LR + %% Input + A["🎤 Audio Input"] + + %% Voice Pipeline + subgraph Voice_Pipeline [Voice Pipeline] + direction TB + B["Transcribe (speech-to-text)"] + C["Your Code"]:::highlight + D["Text-to-speech"] + B --> C --> D + end + + %% Output + E["🎧 Audio Output"] + + %% Flow + A --> Voice_Pipeline + Voice_Pipeline --> E + + %% Custom styling + classDef highlight fill:#ffcc66,stroke:#333,stroke-width:1px,font-weight:700; + +``` + +## 智能体 + +首先,让我们设置一些智能体。如果您使用此 SDK 创建过智能体,这应该看起来很熟悉。我们将设置两个智能体、一个交接和一个工具。 + +```python +import asyncio +import random + +from agents import ( + Agent, + function_tool, +) +from agents.extensions.handoff_prompt import prompt_with_handoff_instructions + + + +@function_tool +def get_weather(city: str) -> str: + """Get the weather for a given city.""" + print(f"[debug] get_weather called with city: {city}") + choices = ["sunny", "cloudy", "rainy", "snowy"] + return f"The weather in {city} is {random.choice(choices)}." + + +spanish_agent = Agent( + name="Spanish", + handoff_description="A spanish speaking agent.", + instructions=prompt_with_handoff_instructions( + "You're speaking to a human, so be polite and concise. Speak in Spanish.", + ), + model="gpt-4.1", +) + +agent = Agent( + name="Assistant", + instructions=prompt_with_handoff_instructions( + "You're speaking to a human, so be polite and concise. If the user speaks in Spanish, handoff to the spanish agent.", + ), + model="gpt-4.1", + handoffs=[spanish_agent], + tools=[get_weather], +) +``` + +## 语音管道 + +我们将使用 [`SingleAgentVoiceWorkflow`][agents.voice.workflow.SingleAgentVoiceWorkflow] 作为工作流来设置一个简单的语音管道。 + +```python +from agents.voice import SingleAgentVoiceWorkflow, VoicePipeline +pipeline = VoicePipeline(workflow=SingleAgentVoiceWorkflow(agent)) +``` + +## 运行管道 + +```python +import numpy as np +import sounddevice as sd +from agents.voice import AudioInput + +# For simplicity, we'll just create 3 seconds of silence +# In reality, you'd get microphone data +buffer = np.zeros(24000 * 3, dtype=np.int16) +audio_input = AudioInput(buffer=buffer) + +result = await pipeline.run(audio_input) + +# Create an audio player using `sounddevice` +player = sd.OutputStream(samplerate=24000, channels=1, dtype=np.int16) +player.start() + +# Play the audio stream as it comes in +async for event in result.stream(): + if event.type == "voice_stream_event_audio": + player.write(event.data) + +``` + +## 統合 + +```python +import asyncio +import random + +import numpy as np +import sounddevice as sd + +from agents import ( + Agent, + function_tool, + set_tracing_disabled, +) +from agents.voice import ( + AudioInput, + SingleAgentVoiceWorkflow, + VoicePipeline, +) +from agents.extensions.handoff_prompt import prompt_with_handoff_instructions + + +@function_tool +def get_weather(city: str) -> str: + """Get the weather for a given city.""" + print(f"[debug] get_weather called with city: {city}") + choices = ["sunny", "cloudy", "rainy", "snowy"] + return f"The weather in {city} is {random.choice(choices)}." + + +spanish_agent = Agent( + name="Spanish", + handoff_description="A spanish speaking agent.", + instructions=prompt_with_handoff_instructions( + "You're speaking to a human, so be polite and concise. Speak in Spanish.", + ), + model="gpt-4.1", +) + +agent = Agent( + name="Assistant", + instructions=prompt_with_handoff_instructions( + "You're speaking to a human, so be polite and concise. If the user speaks in Spanish, handoff to the spanish agent.", + ), + model="gpt-4.1", + handoffs=[spanish_agent], + tools=[get_weather], +) + + +async def main(): + pipeline = VoicePipeline(workflow=SingleAgentVoiceWorkflow(agent)) + buffer = np.zeros(24000 * 3, dtype=np.int16) + audio_input = AudioInput(buffer=buffer) + + result = await pipeline.run(audio_input) + + # Create an audio player using `sounddevice` + player = sd.OutputStream(samplerate=24000, channels=1, dtype=np.int16) + player.start() + + # Play the audio stream as it comes in + async for event in result.stream(): + if event.type == "voice_stream_event_audio": + player.write(event.data) + + +if __name__ == "__main__": + asyncio.run(main()) +``` + +如果您运行此示例,智能体将与您对话!请查看 [examples/voice/static](https://github.com/openai/openai-agents-python/tree/main/examples/voice/static) 中的示例,了解您可以自己与智能体对话的演示。 \ No newline at end of file diff --git a/docs/zh/voice/tracing.md b/docs/zh/voice/tracing.md new file mode 100644 index 000000000..4de718ec7 --- /dev/null +++ b/docs/zh/voice/tracing.md @@ -0,0 +1,18 @@ +--- +search: + exclude: true +--- +# 追踪 + +与[智能体追踪](../tracing.md)类似,语音管道也会自动被追踪。 + +您可以阅读上述追踪文档了解基本的追踪信息,但您还可以通过 [`VoicePipelineConfig`][agents.voice.pipeline_config.VoicePipelineConfig] 额外配置管道的追踪。 + +主要的追踪相关字段包括: + +- [`tracing_disabled`][agents.voice.pipeline_config.VoicePipelineConfig.tracing_disabled]: 控制是否禁用追踪。默认情况下,追踪是启用的。 +- [`trace_include_sensitive_data`][agents.voice.pipeline_config.VoicePipelineConfig.trace_include_sensitive_data]: 控制追踪是否包含潜在敏感数据,如音频转录。这专门针对语音管道,而不适用于您工作流内部进行的任何处理。 +- [`trace_include_sensitive_audio_data`][agents.voice.pipeline_config.VoicePipelineConfig.trace_include_sensitive_audio_data]: 控制追踪是否包含音频数据。 +- [`workflow_name`][agents.voice.pipeline_config.VoicePipelineConfig.workflow_name]: 追踪工作流的名称。 +- [`group_id`][agents.voice.pipeline_config.VoicePipelineConfig.group_id]: 追踪的 `group_id`,让您可以链接多个追踪。 +- [`trace_metadata`][agents.voice.pipeline_config.VoicePipelineConfig.tracing_disabled]: 要包含在追踪中的额外元数据。 \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index d2213e2b9..6dae53417 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -217,6 +217,40 @@ plugins: - 실시간 에이전트: - realtime/quickstart.md - realtime/guide.md + - locale: zh + name: 简体中文 + build: true + nav: + - 介绍: index.md + - 快速开始: quickstart.md + - 示例: examples.md + - 文档: + - agents.md + - running_agents.md + - sessions.md + - results.md + - streaming.md + - repl.md + - tools.md + - mcp.md + - handoffs.md + - tracing.md + - context.md + - guardrails.md + - multi_agent.md + - usage.md + - 模型: + - models/index.md + - models/litellm.md + - config.md + - visualization.md + - 语音智能体: + - voice/quickstart.md + - voice/pipeline.md + - voice/tracing.md + - 实时智能体: + - realtime/quickstart.md + - realtime/guide.md extra: # Remove material generation message in footer @@ -232,6 +266,9 @@ extra: - name: 한국어 link: /openai-agents-python/ko/ lang: ko + - name: 简体中文 + link: /openai-agents-python/zh/ + lang: zh markdown_extensions: - pymdownx.superfences: