openai-agents-python-sdk 源码解析 | 第三篇:Agent 对象详解:instructions、output、tools 与 handoffs 本篇导读前两篇分别完成了项目地图和第一个文本 Agent。现在开始进入 SDK 的核心对象Agent。在 OpenAI Agents Python SDK 中Agent不是执行器。它更像一份运行声明这个智能体叫什么、系统指令是什么、能调用哪些工具、能交接给哪些下游 Agent、输出是否需要结构化、有哪些 guardrail、默认使用哪个模型。真正执行这份声明的是Runner。所以理解Agent的关键不是把它当成“会自己运行的对象”而是把它当成Runner每一轮执行时读取的配置入口。本篇重点回答五个问题Agent在源码中由哪些字段组成。instructions静态字符串和动态函数有什么区别。output_type如何把普通文本输出变成结构化输出。tools、handoffs、guardrails如何挂载到 Agent。clone()适合什么场景以及它的浅拷贝边界在哪里。Agent 是声明对象不是执行器先看官方文档对 Agent 的定义Agent 是配置了 instructions、tools以及 handoffs、guardrails、structured outputs 等可选运行时行为的大语言模型。这句话里最重要的是“configured”。Agent本身不直接调用模型也不直接执行工具。它保存的是配置后续由Runner.run(...)或Runner.run_sync(...)读取。最小 Agent 是这样fromagentsimportAgent agentAgent(nameSummaryAgent,instructions你是技术文章摘要助手。,)这段代码只做了一件事创建一个 Agent 定义。模型调用要等到fromagentsimportRunner resultawaitRunner.run(agent,请总结这段内容。)所以源码阅读时要先区分两个层次Agent声明当前智能体的能力和边界。Runner驱动一次 run决定什么时候调用模型、工具、handoff 和 guardrail。Agent 的源码位置Agent的主要源码在src/agents/agent.py。这个文件里有三个需要先认识的对象对象作用AgentBaseAgent和RealtimeAgent共享的基础字段Agent普通文本 Agent 的完整声明对象ToolsToFinalOutputResult自定义工具结果是否成为最终输出时使用AgentBase包含这些共享字段nameAgent 名称。handoff_descriptionAgent 作为 handoff 目标时的说明。tools函数工具、Hosted tools、Agent-as-tool 等工具列表。mcp_servers当前 Agent 可使用的 MCP server。mcp_configMCP 工具准备和错误处理配置。Agent在AgentBase之上继续增加instructionsprompthandoffsmodelmodel_settingsinput_guardrailsoutput_guardrailsoutput_typehookstool_use_behaviorreset_tool_choice这个字段列表基本就是 SDK 文档中 Agent 配置表的源码版本。字段总览可以先把Agent字段按职责分组分组字段说明身份与指令name、instructions、prompt、handoff_description定义 Agent 是谁、该怎么回答、作为交接目标时如何描述模型配置model、model_settings定义使用哪个模型以及模型参数能力挂载tools、mcp_servers、mcp_config定义 Agent 可调用的外部能力编排关系handoffs定义可委派的下游 Agent安全边界input_guardrails、output_guardrails定义输入和输出校验输出控制output_type、tool_use_behavior、reset_tool_choice定义最终输出形态和工具结果处理方式生命周期hooks定义 Agent 级别的生命周期回调这张表可以作为后续阅读源码的索引。遇到一个字段时先判断它属于哪一组再看它在哪个运行阶段被使用。name可读名称也是运行时身份name是唯一必填字段。源码中的__post_init__会检查它必须是字符串ifnotisinstance(self.name,str):raiseTypeError(...)它不是只给人看的显示名称。name会出现在多个运行时场景Trace 和 span 中标识当前 Agent。Handoff 默认工具名和说明中使用。Agent-as-tool 默认工具名中使用。调试输出和错误上下文中使用。因此建议给 Agent 起明确、稳定、可读的名字。不要用agent1、test这类在业务中难以追踪的名字。instructions静态系统指令instructions是最常用的字段。它会作为 Agent 被调用时的系统指令用来描述 Agent 应该做什么、如何回答。静态写法如下agentAgent(nameSummaryAgent,instructions用三句话总结输入内容避免添加原文没有的信息。,)这类 instructions 适合稳定职责例如客服 Agent只处理订单、退款、账户问题。摘要 Agent输出短摘要不做主观评价。提取 Agent从文本中抽取结构化信息。源码中get_system_prompt会直接返回字符串ifisinstance(self.instructions,str):returnself.instructions所以静态 instructions 的行为很直接创建 Agent 时确定运行时读取。instructions动态系统指令instructions也可以是函数。函数会在运行时接收RunContextWrapper和当前Agent然后返回字符串。示例fromdataclassesimportdataclassfromagentsimportAgent,RunContextWrapperdataclassclassUserContext:tone:strdefbuild_instructions(ctx:RunContextWrapper[UserContext],agent:Agent)-str:returnf使用{ctx.context.tone}风格回答保持准确。挂载到 AgentagentAgent[UserContext](nameAdaptiveAgent,instructionsbuild_instructions,)运行时传入 contextresultawaitRunner.run(agent,解释什么是 structured output。,contextUserContext(tone简洁),)源码中的关键行为有三点instructions可以是字符串、callable 或None。callable 必须接收两个参数context和agent。callable 可以是同步函数也可以是异步函数。get_system_prompt会检查函数签名参数数量。如果不是两个参数会抛出TypeError。注意源码只强制参数数量不依赖参数名但从可读性和类型提示角度建议使用(context, agent)的顺序和命名。prompt平台 Prompt 模板除了instructionsAgent还有一个prompt字段。它面向 OpenAI Responses API 的平台 Prompt 模板。静态 prompt 可以这样写agentAgent(namePromptedAgent,prompt{id:pmpt_123,version:1,variables:{style:haiku},},)动态 prompt 则通过GenerateDynamicPromptData接收上下文和 Agent然后返回 prompt 配置。src/agents/prompts.py中的PromptUtil.to_model_input会把prompt转换成模型请求需要的ResponsePromptParam。如果动态函数返回的不是 dict会抛出UserError。使用建议本地代码中稳定维护系统指令时用instructions。希望在 OpenAI 平台维护 Prompt 模板并通过变量注入时用prompt。不要在同一个 Agent 里堆叠过多配置来源否则排查最终系统指令会变复杂。model 与 model_settingsmodel决定当前 Agent 使用哪个模型。它可以是字符串、Model实例或None。fromagentsimportAgent,ModelSettings agentAgent(namePreciseAgent,instructions回答要准确避免发散。,modelgpt-5.4-mini,model_settingsModelSettings(temperature0.2),)如果model不设置运行时会通过默认 model provider 解析默认模型。model_settings用于模型参数例如 temperature、top_p、tool_choice 等。源码中还有一个细节如果你通过clone()改了model但没有显式传新的model_settings并且旧 settings 仍等于旧模型的隐式默认值SDK 会为新模型重新计算默认 settings。这能避免 clone 后模型变了但默认模型配置仍残留旧模型语义的问题。tools给 Agent 挂载能力tools来自AgentBase表示当前 Agent 能调用的工具列表。最常见的是函数工具fromagentsimportAgent,function_toolfunction_tooldefget_order_status(order_id:str)-str:returnf订单{order_id}当前为已发货。agentAgent(nameOrderAgent,instructions你负责回答订单状态问题。,tools[get_order_status],)运行时并不是直接读取agent.tools就结束。AgentBase.get_all_tools会合并两类工具当前 Agent 显式配置的tools。当前 Agent 的mcp_servers动态提供的 MCP tools。同时它还会处理FunctionTool.is_enabled。如果工具被动态禁用它不会暴露给模型。这说明工具列表是运行时可变的同一个 Agent在不同 context 下可见工具可能不同。mcp_servers 与 mcp_configMCP 相关字段也放在AgentBase中因为普通Agent和RealtimeAgent都可能需要 MCP 工具。mcp_servers表示当前 Agent 可使用的 MCP server 列表。源码注释特别说明调用方需要自己管理 server 生命周期例如连接和清理。mcp_config控制 MCP 工具准备行为当前包含convert_schemas_to_strict是否尽量转换为 strict schema。failure_error_functionMCP 工具失败时如何转换成模型可见错误。include_server_in_tool_names是否把 server 前缀纳入工具名避免多 server 工具名冲突。MCP 会在后续单独成篇。第三篇只需要知道MCP tools 最终也会被整理成 Agent 可见工具参与同一套 tool execution 流程。handoffs声明可交接的下游 Agenthandoffs表示当前 Agent 可以把任务委派给哪些下游 Agent。最简单写法billing_agentAgent(nameBillingAgent,instructions处理账单问题。)refund_agentAgent(nameRefundAgent,instructions处理退款问题。)triage_agentAgent(nameTriageAgent,instructions根据用户问题选择合适的专家。,handoffs[billing_agent,refund_agent],)这里triage_agent不需要自己处理所有问题。模型可以根据 instructions 和 handoff 描述选择交接目标。handoffs列表中可以直接放Agent也可以放通过handoff(...)创建的Handoff对象。后者适合自定义工具名、说明、输入过滤和启用条件。源码测试中覆盖了三种情况handoffs[agent_1, agent_2]handoffs[handoff(agent_1), handoff(agent_2)]handoffs[handoff(agent_1), agent_2]这说明 SDK 允许混合使用简单配置和精细配置。handoff_description让路由更清晰handoff_description是AgentBase字段。它的作用不是给当前 Agent 自己看而是当该 Agent 作为 handoff 目标时帮助上游 Agent 判断什么时候交给它。示例refund_agentAgent(nameRefundAgent,handoff_description处理退款资格、退款进度和退款政策问题。,instructions你是退款专家。,)如果没有清晰的handoff_description上游 Agent 只能从 name 或默认描述中推断用途。多 Agent 系统里这会增加错误路由概率。实践建议name写角色名。handoff_description写“什么时候应该交给它”。instructions写“它接手后应该怎么处理”。这三个字段不要写成同一句话它们面向不同运行时阶段。input_guardrails 与 output_guardrailsinput_guardrails和output_guardrails是 Agent 的安全边界。agentAgent(nameSupportAgent,instructions只处理订单和账户问题。,input_guardrails[...],output_guardrails[...],)两者区别input_guardrails在 Agent 链路开始时检查输入。output_guardrails在 Agent 产生最终输出后检查结果。源码注释里有一个重要细节输入 guardrails 只在链路的第一个 Agent 上运行。也就是说如果发生 handoff下游 Agent 不会自动重新执行上游的 input guardrails。这对安全设计很重要。需要下游独立校验时应给下游 Agent 自己配置 guardrail或者在工具层增加 tool guardrail。output_type从文本输出到结构化输出默认情况下Agent 输出是普通文本也就是str。如果设置output_typeSDK 会让模型使用结构化输出并把模型返回的 JSON 校验成目标 Python 类型。常见写法frompydanticimportBaseModelfromagentsimportAgentclassTaskAnalysis(BaseModel):summary:strrisk_level:strnext_actions:list[str]agentAgent(nameTaskAnalyzer,instructions分析任务并输出结构化结果。,output_typeTaskAnalysis,)运行后可以读取resultawaitRunner.run(agent,评估上线前风险。)analysisresult.final_output_as(TaskAnalysis)print(analysis.risk_level)源码中的解析入口是get_output_schema(agent)。逻辑很短如果output_type是None或str不创建 schema。如果已经是AgentOutputSchemaBase直接使用。否则包装成AgentOutputSchema(agent.output_type)。这就是为什么传 Pydantic model、dataclass、TypedDict 或泛型类型都可以工作底层会通过 PydanticTypeAdapter生成 schema 并做 JSON 校验。AgentOutputSchema 的严格模式AgentOutputSchema默认启用 strict JSON schema。源码注释也明确建议开启因为它能提高模型输出合法 JSON 的概率。但不是所有 Python 类型都能转换成 strict schema。examples/basic/non_strict_output_type.py展示了这种情况当 schema 不兼容 strict mode 时可以显式关闭fromagentsimportAgentOutputSchema agent.output_typeAgentOutputSchema(TaskAnalysis,strict_json_schemaFalse,)关闭 strict mode 后模型可能产生不符合 schema 的 JSON运行时仍可能抛出ModelBehaviorError。所以它不是“更宽松就更稳定”而是在 schema 本身无法 strict 化时的退路。实践建议优先使用 PydanticBaseModel。字段类型保持明确避免复杂嵌套和模糊 union。默认使用 strict schema。只有确认 schema 不兼容时再考虑strict_json_schemaFalse。tool_use_behavior工具结果如何变成最终输出当 Agent 使用工具后默认行为是run_llm_again工具执行完后把工具结果交回模型让模型生成最终回答。agentAgent(nameWeatherAgent,instructions查询天气并简洁回答。,tools[get_weather],tool_use_behaviorrun_llm_again,)除此之外还有几种方式配置行为run_llm_again默认行为工具结果回填给模型继续生成stop_on_first_tool第一个工具输出直接作为最终结果StopAtTools指定某些工具一旦调用就停止自定义函数根据工具结果决定停止还是继续如果你的工具输出已经是面向用户的最终内容可以考虑stop_on_first_tool。如果工具输出只是中间数据应该保留默认值让模型整合后再回答。源码还提供reset_tool_choice默认是True。它用于避免强制工具调用后模型反复继续调用工具形成工具循环。hooksAgent 级生命周期回调hooks用于观察 Agent 生命周期。它和Runner.run(..., hooks...)的 run-level hooks 不是同一个作用域。区别可以这样理解类型作用域RunHooks观察整次 run包括多个 Agent 和 handoffAgentHooks只挂在某个 Agent 上观察该 Agent 自己的生命周期如果你要做全局审计、全链路日志优先使用RunHooks。如果某个 Agent 需要独立记录、预加载或特殊副作用再使用agent.hooks。生命周期和 tracing 会在后续文章中展开。第三篇只需要知道hooks是 Agent 声明的一部分但它不改变 Agent 的核心推理能力。clone配置复用而不是深拷贝Agent.clone(**kwargs)用来复制一个 Agent并覆盖部分字段。base_agentAgent(nameBaseWriter,instructions输出简洁中文。,modelgpt-5.4-mini,)review_agentbase_agent.clone(nameReviewWriter,instructions输出代码审查意见。,)源码里clone()使用dataclasses.replace。这意味着它是浅拷贝不是深拷贝。这点非常关键如果不覆盖tools新 Agent 和旧 Agent 会引用同一个 tools 列表。如果只复制列表列表对象不同但列表里的 tool 对象仍然共享。handoffs也是同样语义。如果要独立修改列表请显式传入新列表。安全写法new_agentold_agent.clone(toolsold_agent.tools.copy(),handoffsold_agent.handoffs.copy(),)如果你需要连工具对象内部状态也隔离仅复制列表还不够需要创建新的工具对象或重新定义工具。post_init尽早暴露错误配置Agent是 dataclass但它不是完全无校验的数据容器。__post_init__会检查常见配置错误。目前源码覆盖的关键校验包括name必须是字符串。handoff_description必须是字符串或None。tools、mcp_servers、handoffs、guardrails 必须是列表。mcp_config必须是 dict。instructions必须是字符串、callable 或None。prompt必须是 prompt dict、动态函数或None。model必须是字符串、Model或None。model_settings必须是ModelSettings。output_type必须是类型、AgentOutputSchemaBase或None。tool_use_behavior必须是受支持的字符串、dict 或 callable。reset_tool_choice必须是 bool。这些校验可以把很多错误提前到 Agent 创建阶段而不是等到模型调用中途才暴露。例如Agent(nameBadAgent,toolsnot-a-list)这会直接抛出类型错误因为tools必须是列表。一个结构化任务分析 Agent现在把本篇内容组合成一个小例子。先定义输出类型frompydanticimportBaseModelclassTaskAnalysis(BaseModel):summary:strrisk_level:strnext_actions:list[str]再定义动态上下文fromdataclassesimportdataclassdataclassclassReviewContext:audience:strstrict:bool动态 instructionsfromagentsimportAgent,RunContextWrapperdefbuild_instructions(ctx:RunContextWrapper[ReviewContext],agent:Agent)-str:strict_text严格指出风险ifctx.context.strictelse只指出主要风险returnf面向{ctx.context.audience}输出任务分析{strict_text}。Agent 定义task_agentAgent[ReviewContext](nameTaskAnalysisAgent,instructionsbuild_instructions,output_typeTaskAnalysis,)运行resultawaitRunner.run(task_agent,评估上线前缺少回滚方案和核心链路压测。,contextReviewContext(audience研发负责人,strictTrue),)print(result.final_output_as(TaskAnalysis))这个例子同时使用了泛型 context。动态 instructions。Pydantic 结构化输出。final_output_as(...)类型转换。常见误区1. 把运行状态塞进 Agent不要把一次 run 的临时状态写入 Agent 字段。Agent 应该是可复用声明对象。运行状态应通过context、session、工具输入或外部存储传递。错误倾向agent.current_user_idu_123更合适的方式是定义 contextdataclassclassUserContext:user_id:str然后通过Runner.run(..., contextUserContext(...))传入。2. instructions 写得过宽如果一个 Agent 的 instructions 同时要求它做路由、查数据、审批、写报告和处理异常后续会很难调试。更好的方式当前 Agent 只负责入口路由。专家能力通过 handoffs 或 tools 拆出去。安全规则放进 guardrails。外部动作放进 tools。3. output_type 过度复杂结构化输出不是越复杂越好。过深嵌套、宽泛 union、动态 key 都会增加 schema 和模型输出难度。建议先从简单 Pydantic model 开始稳定后再扩展字段。4. clone 后直接修改共享列表如果直接这样写new_agentold_agent.clone(nameNewAgent)new_agent.tools.append(extra_tool)在未覆盖tools的情况下可能会影响原 Agent 的工具列表。更稳妥的方式是 clone 时显式传入新列表。源码阅读路线第三篇建议按这个顺序读源码src/agents/agent.py先读AgentBase和Agent字段定义。src/agents/agent.py继续读__post_init__理解配置校验。src/agents/agent.py读get_system_prompt理解动态 instructions。src/agents/agent.py读clone理解浅拷贝语义。src/agents/agent_output.py读AgentOutputSchema理解结构化输出 schema。src/agents/prompts.py读PromptUtil.to_model_input理解平台 prompt 模板。src/agents/run_internal/turn_preparation.py读get_output_schema确认运行时如何解析output_type。tests/test_agent_config.py看配置行为的回归测试。tests/test_agent_clone_shallow_copy.py看 clone 浅拷贝边界。这里仍然不建议从__init__.py开始读。__init__.py主要负责公共 API re-export适合查符号暴露不适合建立 Agent 字段模型。本篇小结Agent是 SDK 最核心的声明对象。理解它时要抓住三条主线身份和行为name、instructions、prompt、model、model_settings。能力和编排tools、mcp_servers、handoffs、guardrails。输出和控制output_type、tool_use_behavior、reset_tool_choice、hooks。它本身不执行任务而是把任务执行所需的配置交给Runner。后续工具调用、多 Agent 编排、结构化输出和 guardrail 都会回到这个对象。下一篇会进入Runner运行机制重点拆解一次Runner.run如何从输入开始经过模型调用、工具调用、handoff 判断最终生成RunResult。实践任务建议完成以下练习定义一个TaskAnalysisPydantic model包含summary、risk_level、next_actions。定义一个ReviewContext包含audience和strict。编写动态instructions函数根据ReviewContext.strict切换输出要求。创建Agent[ReviewContext]设置output_typeTaskAnalysis。使用Runner.run(...)执行一次任务分析。打印result.final_output_as(TaskAnalysis)。调用agent.clone(...)创建另一个风格不同的 Agent。显式传入toolsagent.tools.copy()观察 clone 前后列表是否共享。

相关新闻

最新新闻

GEO优化工具怎么选?合规性与语义适配评估标准

GEO优化工具怎么选?合规性与语义适配评估标准

很多品牌在尝试AI搜索优化时,常会遇到一种困惑:为什么在传统搜索引擎里排名还不错,但在主流AI对话界面里却总是“查无此人”,或者被描述得驴唇不对马嘴?这种现象并非偶然,而是因为AI搜索逻辑与传统SEO存在根…

2026/8/3 12:13:57
2026外贸企业邮箱怎么选?全球收发必备条件

2026外贸企业邮箱怎么选?全球收发必备条件

外贸企业的核心沟通链路依托跨境邮件往来,邮件送达率、传输稳定性直接决定客户对接与订单推进效率。区别于普通内贸办公邮箱,外贸邮箱对海外投递、安全防护、文件传输的要求更高。2026年跨境通讯监管与网络环境持续更新,不少企业因邮箱链路不…

2026/8/3 12:13:57
SAP ABAP生产订单工序数据共享技术解析

SAP ABAP生产订单工序数据共享技术解析

1. 项目背景与需求解析 在SAP ERP系统的ABAP开发中,生产订单管理模块的开发需求非常普遍。最近我在处理一个生产执行系统的增强开发时,遇到了一个典型场景:需要在自定义报表程序中直接获取标准生产订单的工序信息,但发现标准程序中…

2026/8/3 12:13:57
AI写SEO文章不是替代人,而是筛选人:顶级内容团队正在用的“人机协同漏斗模型(仅限内部培训版)”

AI写SEO文章不是替代人,而是筛选人:顶级内容团队正在用的“人机协同漏斗模型(仅限内部培训版)”

更多请点击: https://kaifayun.com 第一章:AI写SEO文章不是替代人,而是筛选人:顶级内容团队正在用的“人机协同漏斗模型(仅限内部培训版)” AI写作工具在SEO内容生产中的真实角色,不是取代编辑…

2026/8/3 12:13:57
基于PNPM+Turborepo的Monorepo架构实践与AI辅助规划

基于PNPM+Turborepo的Monorepo架构实践与AI辅助规划

在大型前端项目中,随着业务模块和团队规模的增长,传统的多仓库(Multi-Repo)模式常常会带来依赖管理混乱、代码复用困难、构建流程复杂等一系列工程化难题。近期,我们团队在重构一个包含多个子应用和共享组件的复杂系统…

2026/8/3 12:13:57
如何免费获取网盘直链下载地址:八大平台高速下载完整指南

如何免费获取网盘直链下载地址:八大平台高速下载完整指南

如何免费获取网盘直链下载地址:八大平台高速下载完整指南 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天…

2026/8/3 12:08:57