openai-agents-python Agent 类完整指南:构造参数、工具行为与多智能体编排
【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python
导读
Agent是 openai-agents-python 框架的核心构建单元——它是一个由 LLM 驱动的实体,通过instructions(系统提示)、tools(工具)、handoffs(交接)、guardrails(护栏)与结构化输出(output_type)等配置组合而成。本文以 Agent API 参考(即agents.agent模块)为主体,结合 用户指南 与 模块源码 的 docstring 实现,逐项讲解Agent的全部构造参数、底层校验逻辑、clone()/as_tool()/get_system_prompt()等关键方法,以及tool_use_behavior、MCPConfig、StopAtTools等配套类型。读完本文,你将能够独立完成一个生产级 Agent 的配置、工具接入、多智能体编排与生命周期观测。
Agent 在框架中的定位
在 openai-agents-python 中,Agent与Runner是一对核心组合:Agent负责"配置"(指令、工具、护栏、交接、输出结构),Runner负责"执行"(管理轮次、调用模型、执行工具、处理交接与会话)。文档明确指出,两者区分的关键在于编排层——如果你希望框架替你管理 turns、tools、guardrails、handoffs 与 sessions,就使用Agent+Runner;如果你要完全自己掌控这个循环,则应直接使用 Responses API。
从源码结构看,agent.py 中先定义了基类AgentBase(泛型于上下文类型TContext),它承载Agent与RealtimeAgent共用的参数(name、handoff_description、tools、mcp_servers、mcp_config),随后 Agent 在基类之上扩展出指令、交接、模型、护栏、输出类型、生命周期钩子与工具行为等完整配置面。
from agents import Agent from agents.decorators import tool @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], )需要特别说明的是:docs/ref/agent.md是 mkdocstrings 的渲染占位文件(由 generate_ref_files.py 生成,内容为::: agents.agent指令),其真正的文档内容来自 agent.py 中每个类与方法的 docstring,本文所有参数说明均以这些 docstring 与__post_init__校验逻辑为准。
Agent 构造参数全景
Agent是一个泛型 dataclass,其全部构造参数、类型与默认行为如下表所示(对应 agent.py 的字段定义与 docs/agents.md 的配置说明):
| 参数 | 必填 | 类型 | 说明 |
|---|---|---|---|
name | 是 | str | 人类可读的 Agent 名称。 |
instructions | 否 | str \| Callable[[RunContextWrapper, Agent], MaybeAwaitable[str]] \| None | 系统提示词,强烈建议提供;也支持返回字符串的动态函数。 |
prompt | 否 | Prompt \| DynamicPromptFunction \| None | OpenAI Responses API 的 prompt 配置(平台提示词模板),仅对 OpenAI 模型 + Responses API 可用。 |
handoff_description | 否 | str \| None | 当该 Agent 作为交接目标暴露给 LLM 时的短描述,LLM 据此判断何时调用它。 |
handoffs | 否 | list[Agent \| Handoff] | 子 Agent 列表,Agent 可在相关时委派(delegate)给它,实现关注点分离与模块化。 |
model | 否 | str \| Model \| None | 调用 LLM 时使用的模型实现。未设置时使用agents.models.get_default_model()的默认值。 |
model_settings | 否 | ModelSettings | 模型调参(如temperature、top_p、tool_choice),接受ModelSettings实例或其字段字典。 |
tools | 否 | list[Tool] | Agent 可调用的工具列表。 |
mcp_servers | 否 | list[MCPServer] | 提供 MCP 工具的服务器列表,每次运行都会并入可用工具。 |
mcp_config | 否 | MCPConfig | 微调 MCP 工具的准备方式(严格模式转换、失败格式化等)。 |
input_guardrails | 否 | list[InputGuardrail] | 在 Agent 链中首个 Agent 生成响应前并行运行的检查。 |
output_guardrails | 否 | list[OutputGuardrail] | 在 Agent 产生最终输出后对输出运行的检查。 |
output_type | 否 | type \| AgentOutputSchemaBase \| None | 结构化输出类型,未提供时输出为str。 |
hooks | 否 | AgentHooks \| None | 该 Agent 实例的生命周期回调。 |
tool_use_behavior | 否 | "run_llm_again" \| "stop_on_first_tool" \| StopAtTools \| ToolsToFinalOutputFunction | 控制工具结果是否回环给模型或直接结束运行。 |
reset_tool_choice | 否 | bool(默认True) | 工具调用后是否将tool_choice重置为默认值,避免工具使用死循环。 |
构造时的类型校验(post_init)
agent.py 的__post_init__对每个参数做了严格类型检查,任何不合法输入都会在构造阶段抛出TypeError,而不是等到运行期才暴露。值得注意的校验点:
name必须是字符串;handoff_description必须是字符串或None;instructions必须是字符串、可调用对象或None;prompt必须是Prompt、动态函数或None;model必须是字符串、Model实例或None;model_settings会经_coerce_model_settings统一处理(支持直接传字典);tool_use_behavior必须是两个字符串字面量之一、StopAtTools字典或可调用对象;reset_tool_choice必须是布尔值。
此外有一个隐式行为:当显式设置了model且model_settings仍是全局默认值时,会自动将该 Agent 的model_settings切换为该模型对应的默认设置(见 agent.py)。
默认模型与默认模型设置
若model未指定,Agent 使用 default_models.py 中get_default_model()的返回值,其逻辑为读取环境变量OPENAI_DEFAULT_MODEL,未设置时回退到"gpt-5.6-luna"。而model_settings的默认值由get_default_model_settings()决定:对于 GPT-5 系列模型,会根据模型名匹配对应的默认推理强度(reasoning.effort)与verbosity="low"(见 default_models.py)。这意味着你通常无需手动为每个 Agent 配置model_settings,框架已按模型族给出合理默认。
instructions:静态字符串与动态函数
instructions是 Agent 的"系统提示词",描述 Agent 应该做什么、如何回应。它有两种形态:
- 静态字符串:直接传入。
- 动态函数:函数签名必须恰好为
(context: RunContextWrapper[TContext], agent: Agent[TContext]),返回str;同步与async函数均可。
底层实现见 get_system_prompt():框架会通过inspect.signature强制校验函数参数恰好为 2 个,然后调用函数;若返回值是 awaitable 则自动await。注意注释中的细节——可调用实例(实现了__call__的类实例)的__call__是异步的时,iscoroutinefunction判断会跳过,因此框架统一用inspect.isawaitable判断,保证两类写法都能工作。
from agents import Agent, RunContextWrapper 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 = AgentUserContextprompt:OpenAI 平台提示词模板
prompt参数允许你在代码之外动态配置指令、工具等,仅对 OpenAI 模型 + Responses API 可用。用法:在 OpenAI 平台创建提示词模板(如系统提示为Write a poem in {{poem_style}}),然后通过prompt引用模板 ID 与版本:
from agents import Agent agent = Agent( name="Prompted assistant", prompt={ "id": "pmpt_123", "version": "1", "variables": {"poem_style": "haiku"}, }, )也可以在运行时动态生成 prompt——传入一个接收GenerateDynamicPromptData、返回 prompt 字典的函数(完整示例见 docs/agents.md)。底层转换由 get_prompt() 调用PromptUtil.to_model_input完成,最终产出 Responses API 的ResponsePromptParam。
output_type:结构化输出
默认情况下 Agent 输出纯文本str。传入output_type后,模型将使用结构化输出(structured outputs)而非普通文本。支持任何可被 PydanticTypeAdapter包装的类型:dataclass、Pydantic 模型、TypedDict、list 等。
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, )如需更精细的控制,output_type还支持两种自定义方式(见 agent.py 的 docstring):想用非严格 JSON schema 时传AgentOutputSchema(MyClass, strict_json_schema=False);想完全自定义 JSON schema 时继承AgentOutputSchemaBase子类传入。相关概念可参考 结构化输出与严格模式 与 output_type 参考。
工具与 MCP 服务器
tools参数是 Agent 可调用的本地工具列表(如@tool装饰器定义的FunctionTool)。除本地工具外,AgentBase还支持通过mcp_servers接入 MCP(Model Context Protocol)服务器,每次 Agent 运行都会将这些服务器提供的工具并入可用工具集。
MCPConfig 配置项
mcp_config是一个TypedDict(定义见 agent.py),用于微调 MCP 工具的准备方式:
| 字段 | 默认值 | 说明 |
|---|---|---|
convert_schemas_to_strict | False | 是否尝试将 MCP schema 转换为严格模式 schema。这是尽力而为的转换,部分 schema 可能无法转换。 |
failure_error_function | default_tool_error_function | 将 MCP 工具失败转换为模型可见消息的可选函数。显式设为None时,工具错误将被直接抛出。 |
include_server_in_tool_names | False | 为True时,本地 MCP 工具以"服务器名前缀"的公开名称暴露,避免多个 MCP 服务器之间的工具名冲突。 |
这些字段在 get_mcp_tools() 中被实际消费:读取mcp_config取值后调用MCPUtil.get_all_function_tools拉取全部工具。当include_server_in_tool_names开启时,还会先计算保留名称(包括本地FunctionTool名称与已启用的 handoff 工具名),防止命名冲突。详见 MCP 指南。
工具聚合与启用控制
get_all_tools() 汇总了最终暴露给 LLM 的工具集合:先取 MCP 工具,再对每个FunctionTool检查is_enabled(布尔值或返回布尔值的可调用函数),随后裁剪"孤儿"工具搜索工具(prune_orphaned_tool_search_tools),最后校验 Codex 工具名冲突(重复的 Codex 工具名会抛出UserError)。也就是说,禁用工具会在运行时对 LLM 隐藏,而非简单从列表中移除。
MCP 服务器的生命周期需要你自己管理:必须在传入 Agent 前调用server.connect(),不再需要时调用server.cleanup()。文档建议使用agents.mcp中的MCPServerManager将 connect/cleanup 放在同一任务中。
多智能体设计的两种模式
框架文档归纳了两种常见的多智能体系统设计模式(详见 docs/agents.md):
模式一:Manager(agents as tools)
中央编排 Agent 将专业子 Agent 暴露为工具调用,并始终保留对话控制权。这是通过as_tool()方法实现的:
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(交接)
配置的交接目标(handoff targets)是 Agent 可委派的子 Agent。发生交接时,被委派的 Agent接收完整对话历史并接管对话,这是一种去中心化的协作方式:
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], )完整细节见 Handoffs 指南 与 多智能体编排。另外,handoff_description在此处至关重要——它作为交接目标被 LLM 看到时的描述,决定 LLM 是否会、何时调用该 Agent。
tool_use_behavior:控制工具结果的去向
tool_use_behavior(定义见 agent.py)决定工具调用结果如何被处理,共有四种取值:
| 取值 | 行为 |
|---|---|
"run_llm_again"(默认) | 执行工具后,把结果发回 LLM,由 LLM 继续生成最终响应。 |
"stop_on_first_tool" | 第一个工具调用的输出直接作为最终结果,不再送回 LLM 处理。 |
StopAtTools(stop_at_tool_names=[...]) | 若调用列表中任一工具则停止运行,最终输出为第一个匹配工具调用的输出;LLM 不再处理该结果。 |
可调用函数(ToolsToFinalOutputFunction) | 接收运行上下文与工具结果列表,返回ToolsToFinalOutputResult,自行决定是否将工具结果作为最终输出。 |
重要限制:tool_use_behavior只对FunctionTool生效——托管工具(hosted tools,如 file search、web search)始终由 LLM 处理,不受此配置控制。
StopAtTools
StopAtTools是一个TypedDict(agent.py),包含stop_at_tool_names: list[str],即任一工具名命中即停止:
from agents import Agent from agents.agent import StopAtTools from agents.decorators import tool @tool def get_weather(city: str) -> str: """Returns weather info for the specified city.""" return f"The weather in {city} is sunny" @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
自定义函数的类型为Callable[[RunContextWrapper[TContext], list[FunctionToolResult]], MaybeAwaitable[ToolsToFinalOutputResult]]。ToolsToFinalOutputResult(agent.py)包含两个字段:is_final_output: bool(是否最终输出;为False时 LLM 会再次运行并接收工具输出)与final_output: Any | None(最终输出,is_final_output为True时必须匹配 Agent 的output_type):
from agents import Agent, FunctionToolResult, RunContextWrapper from agents.agent import ToolsToFinalOutputResult from agents.decorators import tool from typing import List, Any @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: 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 )reset_tool_choice 与强制工具使用
框架为防止死循环,会在一次工具调用后自动把tool_choice重置为"auto"(可通过agent.reset_tool_choice配置,默认True)。死循环的成因是:工具结果被送回 LLM,LLM 因固定的tool_choice又生成一次工具调用,如此往复。相关测试见 test_tool_choice_reset.py。
如果你想强制LLM 使用某个工具,可设置ModelSettings.tool_choice,合法值为:auto(LLM 自行决定)、required(必须用工具,但可智能选择)、none(禁止使用工具)、或具体工具名字符串(强制使用该工具):
from agents import Agent, ModelSettings from agents.decorators import tool @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") )注意:使用 OpenAI Responses 工具搜索(tool search)时,命名工具选择受限——无法用tool_choice定位裸命名空间名称或仅延迟(deferred)的工具,tool_choice="tool_search"也不能直接指定ToolSearchTool,此时应优先auto或required(详见 docs/tools.md)。
clone():克隆 Agent 及其浅拷贝语义
clone() 基于dataclasses.replace实现,返回参数被替换后的新 Agent:
pirate_agent = Agent( name="Pirate", instructions="Write like a pirate", model="gpt-5.6-sol", ) robot_agent = pirate_agent.clone( name="Robot", instructions="Write like a robot", )其语义有四个关键点(docstring 中明确说明):
- 执行的是浅拷贝,列表属性(
tools、handoffs、mcp_servers、input_guardrails、output_guardrails)永远不会被复制; - 未覆盖的属性会沿用原 Agent 自己的列表——两个 Agent 持有同一个列表对象,通过任一 Agent 修改(如
cloned.tools.append(extra_tool))都会影响另一个; - 覆盖传入的属性按传入值原样使用,只有当你复用了同一个列表/条目时才与原 Agent 共享;
- 想要一个完全独立的列表容器,请传入新列表:
pirate_agent.clone(tools=[*pirate_agent.tools, extra_tool])——新列表中的条目仍是原对象,除非你也替换了这些条目。
另外clone()还有两个隐含的智能行为:若只改model而未改model_settings,且原设置匹配隐式模型默认值,则会自动为新模型套用对应默认设置;若改动了model_settings,会基于原类型做_coerce_model_settings统一处理。相关测试见 test_agent_clone_shallow_copy.py 与 test_agent_config.py。
as_tool():把 Agent 变成工具
as_tool() 将一个 Agent 转换为FunctionTool,供其他 Agent 调用。它与 handoffs 有两点本质区别(docstring 原文):
- 输入来源不同:handoffs 中,新 Agent 接收完整对话历史;而 as_tool 中,新 Agent 接收的是生成的结构化输入。
- 控制权归属不同:handoffs 中新 Agent 接管对话;而 as_tool 中新 Agent 作为工具被调用,对话仍由原 Agent 继续。
方法参数如下:
| 参数 | 说明 |
|---|---|
tool_name | 工具名,未提供时使用 Agent 名(转换为函数风格,如MyAgent→my_agent)。 |
tool_description | 工具描述,应说明它做什么、何时使用。 |
custom_output_extractor | 从运行结果中提取输出的函数;未提供时使用 Agent 的最后一条消息。嵌套运行结果会通过agent_tool_invocation元数据暴露。 |
is_enabled | 布尔值或接收 (运行上下文, Agent) 的可调用函数,禁用工具在运行时对 LLM 隐藏。 |
on_stream | 同步/异步回调,接收嵌套 Agent 运行的流式事件(AgentToolStreamEvent,含嵌套 agent、原始工具调用与每个流事件);提供后嵌套 Agent 以流式模式执行,且回调在后台分发,慢 handler 不会阻塞事件消费。 |
run_config | 嵌套运行的RunConfig(或字典)。 |
max_turns | 嵌套运行最大轮次,未提供时使用Runner的DEFAULT_MAX_TURNS。 |
hooks | 嵌套运行的RunHooks。 |
previous_response_id/conversation_id/session | 嵌套运行的会话相关参数(恢复运行时这些会被忽略)。 |
failure_error_function | 嵌套运行失败时生成发给 LLM 的错误消息;为None时直接抛出异常。 |
needs_approval | 布尔值或可调用函数,决定该 Agent 工具是否暂停等待审批。 |
parameters | 工具参数的结构化输入类型(dataclass 或 Pydantic 模型);未提供时使用默认的AgentAsToolInput。 |
input_builder | 从结构化数据构建嵌套 Agent 输入的可选函数。 |
include_input_schema | 结构化输入中是否包含完整 JSON schema。 |
源码层面的实现要点:_run_agent_impl会先解析并校验 JSON 输入(用TypeAdapter验证,失败抛ModelBehaviorError),构造嵌套的ToolContext(避免与父运行共享审批状态),然后调用Runner.run或Runner.run_streamed;运行结果按tool_call身份缓存(record_agent_tool_run_result),支持嵌套中断(interruption)的恢复与审批状态流转;最终返回逻辑依次为:自定义提取器 →final_output(非空字符串时)→ 反向扫描new_items中的文本输出 → 工具调用输出。返回的工具会打上_is_agent_tool = True标记,便于框架识别其"Agent 即工具"来源。相关测试见 test_agent_as_tool.py。
生命周期钩子(hooks)
hooks参数允许你观察 Agent 的生命周期,例如记录日志、预取数据或记录用量。框架提供两种作用域(详见 生命周期参考):
RunHooks:观察整个Runner.run(...)调用,包括交接给其他 Agent 的过程;AgentHooks:通过agent.hooks挂载到特定 Agent 实例。
回调上下文也因事件而异:Agent 开始/结束钩子接收AgentHookContext(包装你的原始上下文并携带共享运行用量状态);LLM、工具、交接钩子接收RunContextWrapper。典型时序:on_agent_start/on_agent_end围绕单个 Agent 的起止;on_llm_start/on_llm_end紧贴每次模型调用;on_tool_start/on_tool_end围绕每次本地工具调用(函数工具的钩子上下文通常是ToolContext,可查看tool_call_id等元数据);on_handoff在控制权转移时触发。
from agents import Agent, RunHooks, Runner class LoggingHooks(RunHooks): async def on_agent_start(self, context, agent): print(f"Starting {agent.name}") async def on_llm_end(self, context, agent, response): print(f"{agent.name} produced {len(response.output)} output items") async def on_agent_end(self, context, agent, output): print(f"{agent.name} finished with usage: {context.usage}") agent = Agent(name="Assistant", instructions="Be concise.") result = await Runner.run(agent, "Explain quines", hooks=LoggingHooks()) print(result.final_output)护栏(Guardrails)
input_guardrails与output_guardrails分别对"首个用户输入"和"最终输出"运行检查(如相关性筛查),且都并行于 Agent 执行。需要注意运行时机上的两个限定:输入护栏仅当该 Agent 是链中首个 Agent 时运行;输出护栏仅当该 Agent 产生最终输出时运行。具体实现与示例见 Guardrails 指南。
与上下文(Context)的关系
Agent是泛型于上下文类型的:Agent[TContext]。上下文是你创建的可变对象,通过Runner.run(..., context=...)传入,并传递给工具函数、交接、护栏等所有环节,充当依赖注入的"杂货袋"。例如:
from dataclasses import dataclass @dataclass class UserContext: name: str uid: str is_pro_user: bool agent = AgentUserContext完整的能力面(RunContextWrapper、共享用量跟踪、嵌套tool_input、序列化注意事项)见 Context 指南。
延伸阅读
- Agent 用户指南:本文配套的实操级使用文档(含更多示例);
- 运行 Agent(Runner):轮次、流式事件与会话管理;
- 运行结果(Results):最终输出、run items 与可恢复状态;
- 模型与提供商:
model参数的取值与自定义Model; - 模型设置参考:
model_settings全部字段; - 工具指南 与 MCP 指南:
tools、mcp_servers、mcp_config的深入用法; - Handoffs 指南 与 多智能体编排:两种多智能体模式的完整实践;
- 源码与测试:Agent 实现、默认模型设置、浅拷贝语义测试、as_tool 测试、工具选择重置测试。
【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考