YAOTU INSIGHTS

OpenAI Agents SDK 零基础指南:核心概念、工具注册与会话保持

OpenAI Agents SDK 零基础指南:核心概念、工具注册与会话保持
我在评估多 Agent 编排方案时正好赶上 OpenAI 官方发布 Agents SDK。用它重构了一个原本用原生 Chat Completions 硬写循环的 QA 机器人代码量少了近一半调试反而轻松了很多。这篇是“OpenAI Agents SDK 构建指南”系列的第一篇定位在零基础起步我会把 SDK 的核心抽象、安装方式、第一个 Agent、工具注册和会话保持讲清楚并列出已经踩过的坑。如果你有 Python 基础、了解基础 LLM API 调用但对 Agent 框架还比较陌生这篇非常适合你如果你已经在生产环境用过别的编排框架也可以把它理解成一套更贴近 OpenAI 官方行为、更轻量的方案。1. 为什么不用原生 API还要用 Agents SDK1.1 原生调用 agent 循环的真实痛点直接调用 LLM API 做一个“能调工具”的服务首先要自己维护一段循环。拿最简单的场景来说先调模型拿到 tool_calls接着执行对应函数再把函数结果回传给模型模型可能又发起下一个工具调用你又得继续循环。这套循环第一次写差不多三十行。可一旦加上多轮对话、超时重试、并发用户、历史截断、日志追踪就变成三百行了。我见过不少项目就栽在手写循环上业务逻辑没多少却被调度逻辑占满越改越不敢动。更麻烦的是手写循环时“什么时候把历史记录回传”“工具结果放在哪条消息里”“异常时是重试还是终止”这些细节不同开发者的做法完全不同代码风格没法统一。而 Agents SDK 把这段“LLM 驱动的 Agent 循环”封装成了 Runner你只需要告诉它“跑哪个 Agent、输入什么”它负责内部多步推理和工具调度。这不是省几行代码的问题而是把“流程控制”从业务代码里彻底剥离出来让整个项目的逻辑表达更干净。1.2 Agents SDK 的设计思路SDK 的思路概括起来就是开发者声明 Agent提示词加工具加行为参数然后用 Runner 跑起来。内部循环、工具分发、多步调用、历史管理全部由 SDK 处理。这种设计首先解决的是“流程归属”问题。原本你要在一堆业务代码里找 dispatch、turn 的逻辑现在声明规则完全配置化逻辑表达和驱动分离代码结构天然更清晰。需要辩证的是这种封装会掩盖细节吗会但那是可控的。SDK 提供了 RunConfig、Hooks、事件监听等接口可以在对应生命周期拿到循环内的每一步数据。也就是说它帮你处理繁琐部分却没有剥夺对关键断点的控制权。比如在 RunHooks 里可以监听 Agent 开始、结束、工具调用前、工具返回后这些时机插入自定义逻辑。对想做灰度过滤、权限校验、计数的场景这些钩子非常有用。1.3 跟手写循环和 LangChain 对比对比项原生 API 手写LangChain 早期版本OpenAI Agents SDK循环管理自己维护 while组件化但学习曲线陡Runner 内置工具注册手动拼 JSON SchemaTool 抽象function_tool 装饰器多 Agent 协同自己编排路由有但偏重Handoff 原生支持会话存储自己实现需要另外配置Session 与 Storage 接口追踪可观测性自己埋点需要插件内置 Tracing 与事件流我对轻量框架一直有保留因为“轻量”常常意味着功能缺但实际体验后发现它抓对了重点。官方对循环和交接的抽象比市场上大多数框架更精炼表达式也更接近业务语言。对我这种维护过多个 agent 项目的人来说最怕的是框架把简单问题复杂化Agent SDK 目前没有这个问题。2. 核心概念拆解几个绕不开的关键词2.1 Agent一个“带工具集的提示词执行单元”Agent 不是新的模型也不是新的进程它只是一个数据类配置包含 name、instructions、model、tools、handoffs、guardrails、model_settings。跑起来以后SDK 会按照“模型推理 → 如果是工具调用就执行 → 回传结果 → 继续推理”的循环方式执行。这里最关键的理解点是Agent 只是定义Runner 才是驱动。你可以在不修改 Agent 定义的情况下用不同的 Runner同步、异步、流式跑它也可以在运行时更换模型。另外instructions 字段直接决定 Agent 的稳定度。指令写得好不好工具描述写得好不好直接决定工具调用成功率和鲁棒性。框架能保证流程转起来但转得稳不稳仍然取决于提示词、工具描述和异常处理这些基本功。很多新人以为换了 Agent 框架就能自动解决工具误调用问题这是误解。2.2 Tool函数怎么注册进 AgentAgents SDK 里注册工具最直接的路径是function_tool装饰器。它对 Python 函数做反射把函数签名、docstring、类型注解转化成模型可读的 JSON Schema。函数式传参依赖类型所以类型注解必须写准。举个例子from agents import function_tool function_tool def get_weather(city: str, unit: str celsius) - str: 获取指定城市的天气信息。 Args: city: 城市名称例如“北京”。 unit: 温度单位取值为 celsius 或 fahrenheit。 return f{city} now, {unit}这里有个容易忽略的细节模型会根据函数签名与 docstring 来决定传参所以 docstring 描述得越细模型就越可能在正确场景下使用正确参数。pydantic 会在函数调用前做参数校验类型不对会直接报错这其实也是一种隐式护栏。有一点值得一提项目里老有人把工具函数写得特别厚比如直接在函数里查库、处理缓存、再调外部接口结果出问题时根本分不清是工具问题还是模型问题。我的习惯是工具函数做“薄封装”只做参数解析、调用底层服务、返回格式化文本业务逻辑全部放在工具外面。2.3 HandoffAgent 到 Agent 的移交Handoff 是目前开发者用 SDK 的核心理由之一。它的设计类似“工具调用”一个 Agent 可以将控制权移交给另一个 Agent整个切换行为由模型基于你的策略自主决定。代码写起来很直观from agents import Agent sales_agent Agent( name销售组, instructions你只负责回答产品价格与折扣相关问题。, modelgpt-4o-mini, ) support_agent Agent( name售后组, instructions你只负责处理退换货和物流问题。, modelgpt-4o-mini, ) triage_agent Agent( name总控台, instructions根据用户问题将对话转给销售组或售后组。, handoffs[sales_agent, support_agent], modelgpt-4o-mini, )注意 handoffs 里的 Agent 不能光给名字要带完整配置因为交接过程会把当前 Agent 的历史消息传递给下一个 Agent 的上下文。这种移交机制比在提示词里硬编码“你遇到售后问题就交给售后同事”更结构化也更可调试。你可以在事件流里看到明确的 handoff 事件知道是哪一轮、哪个 Agent 转给了谁。2.4 Guardrail最高优先级的硬拦截Guardrail 是安全护栏运行在正常 Agent 流程之前或之中用来终止不符合条件的行为。它分为 InputGuardrail对输入内容校验和 OutputGuardrail对输出内容校验。典型场景是金融类客服任何涉及“转账、贷款、账户安全”的输入先过校验命中高危就立刻切断不再进入 Agent 主流程。from agents import GuardrailFunctionOutput, InputGuardrail, Agent, Runner from pydantic import BaseModel class HomeOutput(BaseModel): is_home: bool response: str async def home_guardrail(ctx, agent, input_data): # 这里只做演示实际应调用模型或规则引擎做判断 return GuardrailFunctionOutput( output_infoHomeOutput(is_homeFalse, response输入未命中高危条件), tripwire_triggeredFalse, ) agent Agent( name客服助手, instructions你是客服。, input_guardrails[InputGuardrail(guardrail_functionhome_guardrail)], )多 Agent 场景下总控 Agent 和中层 Agent 都可能做无意的错误放行Guardrail 是最后一道硬防线。你要理解它不是替代业务校验而是确保 LLM 行为失控时有一个强制断点避免一连串错误动作继续往下走。3. 实操录搭出第一个能跑的 Agent3.1 环境与安装Agents SDK 要求 Python 3.9 以上我在 Python 3.12 环境下验证过没问题。安装命令就一行pip install openai-agents依赖会拉下 openai Python SDK 和 pydantic。我遇到过一个坑项目里旧版 pydantic 与 openai-agents 版本不完全匹配导致模型输出解析异常。所以建议顺序是先装 openai-agents再根据报错调整 pydantic 版本不要先手动装一个新版 pydantic 抢占依赖。运行之前需要准备 API Key。我的建议是通过环境变量设置不要写进代码export OPENAI_API_KEYsk-...如果你的运行环境走的是合规网关或代理通道可以通过标准的OPENAI_BASE_URL环境变量指向网关或在代码里用set_default_openai_client自定义客户端。网络链路这块必须由你所在环境保证合规可用这里不展开。3.2 最小可运行代码先来一个不需要工具的 Agent确认链路通from agents import Agent, Runner agent Agent( nameAssistant, instructions你是项目助理回答简洁、准确。, modelgpt-4o-mini, ) result Runner.run_sync(agent, 一句话解释什么是 Agent。) print(result.final_output)Runner.run_sync是同步阻塞方法适合脚本或普通后端服务如果代码跑在异步框架里要用await Runner.run(...)。result.final_output是整个 Agent 循环结束后的最终文本result.last_agent能拿到最终执行的 Agent 引用多 Agent 场景下很有用。跑通最小例子后我建议你做的第一件事不是直接上复杂工具而是打印result.raw_responses和result.new_items看看一次简单对话里到底走了几步模型生成了哪些中间对象。很多初学者不知道框架返还里藏着完整的“过程”只看 final_output导致出了问题完全无从排查。3.3 自定义工具接入加入工具后Agent 才真正具备行动能力。最常见场景是把内部服务封装成函数。以订单查询为例from agents import Agent, Runner, function_tool function_tool def get_order_status(order_id: str) - str: 查询订单状态。订单号是纯数字字符串。 # 实际项目中这里查缓存或调用订单服务 status_map {1001: 已发货, 1002: 已退款} return status_map.get(order_id, 订单不存在) agent Agent( name售前助手, instructions当用户询问订单时先用 get_order_status 获取状态再回复。, tools[get_order_status], modelgpt-4o-mini, ) result Runner.run_sync(agent, 帮我查一下订单 1001 到哪里了) print(result.final_output)跑这个例子你会发现输出不是一步生成而是触发一次工具调用、拿到结果后再生成了最终回复。如果你用run_sync想查看中间过程可以对result.new_items逐个打印里面会包含FunctionToolCallItem和FunctionCallOutputItem这对调试工具参数非常关键。工具函数设计上我总结了三条经验工厂函数做薄适配层不要往函数里塞复杂业务只负责参数解析和结果格式化。返回值尽量是可控字符串。不要给模型扔一个超大的 JSONtoken 会浪费还容易让模型抓不住重点。工具内部出错时不要直接抛异常最好兜底返回模型可读的错误文本比如“调用失败,原因: 服务超时”。这样模型能基于错误文本向用户解释或换策略。3.4 会话与共享状态Agent 本身不记住多轮对话但业务里你得让它记住“你是谁”。SDK 用 Session 对象承载上下文。示例from agents import Agent, Runner, Session session Session(u-10001) result1 Runner.run_sync(agent, 你好, sessionsession) result2 Runner.run_sync(agent, 记住我的会员等级是 VIP, sessionsession) result3 Runner.run_sync(agent, 我的会员等级是什么, sessionsession) print(result3.final_output) # 正确回答 VIP需要说明一点普通 Session 只保存在内存里进程重启就丢了。如果需要持久化SDK 的 storage 参数预留了 SessionStorage 接口生产环境建议自己实现 Redis 或数据库存储。入门阶段用内存 Session 做概念验证完全够。我踩过的坑是两次 run 传了不同的 Session模型就完全“失忆”了。另外Session 不只是为了记忆它也充当 Agent 事件入库的容器。所以即使不需要跨进程持久化也建议显式创建 Session后续排查时可对齐到同一对话链路。3.5 对流式输出和事件监听的基本认识SDK 同时也支持流式运行使用Runner.run_streamed。流式模式下stream.to_messages()能逐步拿到增量消息适合做打字机效果。日常做 Web 应用、聊天界面时这个接口值得掌握。如果你想精准控制每一步事件监听要关注AgentRunEvent。事件类型里比较常用的是RunItemStreamEvent、AgentUpdatedStreamEvent和RunCompletedStreamEvent。我建议第二篇围绕事件机制、Hooks 和 Tracing 展开这一篇先把同步流程跑通理解事件模型是什么就行。4. 常见问题与排查技巧实录4.1 API Key 与配置环境变量类问题常见报错是OpenAIError: The api_key client option must be set这类问题基本都是环境变量没生效。要注意openai和openai-agents是两个库前者是底层客户端后者是高层框架框架会通过底层客户端读环境。如果你在 Jupyter 里设置过环境变量需要重启内核才能生效如果在终端 export需要确认当前 shell 确实继承了变量。另外一个工程惯例不要把密钥硬编码进代码。我见过把密钥直接写在 git 仓库里导致泄露的真实案例。用环境变量或密钥管理服务是底线。4.2 Pydantic 与 openai-agents 版本冲突Agents SDK 对 Pydantic 有版本要求。如果业务项目为了兼容其他库锁定了 Pydantic v1运行时会发现框架内部做 schema 解析行为异常报错信息还可能指向与业务无关的类。先诊断pip show pydantic openai-agents通常建议在 Python 3.10 以上环境使用 Pydantic v2并保持 openai-agents 更新到较新稳定版。这个 SDK 发布节奏快旧版本可能带已知 bug保持更新是必要的事。如果线上项目不方便频繁升级至少要锁定一个经过验证的版本组合。4.3 异步运行时死锁有人喜欢在 FastAPI 的异步路由里直接调用Runner.run_sync并发量上来后请求阻塞。原因在于run_sync会占用底层事件循环在异步环境里阻塞主循环。正确做法# 在 async 上下文里使用 await Runner.run result await Runner.run(agent, 你好)如果工具函数本身也很耗时比如同步 HTTP 请求建议把工具函数改为异步版本或者用线程池隔离避免工具执行时阻塞整个 Agent 循环。4.4 工具调用循环卡住或反复触发现象是 Agent 不断调用同一个工具好像“卡住”了。常见原因有三个工具返回值包含模型无法理解的信息模型觉得信息不够还要再查一遍。docstring 与函数签名不一致比如函数要求数字docstring 却写了日期格式示例导致模型一直生成错误参数。instructions 里没有告诉模型“判断够用后必须停止”。排查时打印result.new_items看最近几轮拿到什么内容才触发新的函数调用基本能定位。多次重复调用同一个工具时还可以在工具执行层加“重复次数上限”熔断防止计费失控。生产环境里这种开关很有必要。4.5 排查小清单症状排查方向常用解决401 / invalid_api_key环境变量与密钥有效性重新配置并重启进程模型调用超时网络链路、平台负载统一走合规网关配置超时重试工具参数报 validation error类型注解与示例统一使用 pydantic 可解析的简单类型多轮回答失忆Session 使用是否一致同一会话传同一个 Session 对象运行后无日志事件监听未开启查看 RunConfig、实现 Hooks我特别想强调一点排错最大的误区是上来就改代码而不是先看事件流。我遇到过一个问题现象是“Agent 多轮不回复”我一股脑调提示词改了好几版都没用。后来打印事件流才发现是某个工具函数抛了非预期异常框架内部把异常吞掉后进入了等待状态跟提示词完全无关。Agents SDK 里事件模型是第一现场很多问题看事件流就能立刻看出是哪一环断了。4.6 关于“模型乱选工具”的实战心得模型乱选工具是 Agent 工程里最普遍的烦恼。不是框架 bug通常是工具描述写得不够清晰或者工具之间边界模糊。比如电商场景里查订单和查物流是两个工具如果 docstring 都泛泛写着“查询订单信息”模型很容易选错。我的解决方式是在工具描述里写清楚“适用范围”和“排除项”比如function_tool def get_logistics(order_id: str) - str: 查询物流轨迹仅用于已发货订单。 适用场景用户问快递、物流、配送进度。 不适合咨询订单金额、商品明细、退款状态。 看起来有点啰嗦但模型选错工具的情况会明显下降。工具描述是给模型看的“接口文档”值得多花几分钟写。结语第一篇先到这里按我的真实体验Agents SDK 目前最适合先用它做“单 Agent 加工具加简单会话”的概念验证手感和学习成本都不高。这一篇先把基础链路讲透了核心概念、安装、最小案例、工具注册、会话保持、常见问题。至于 Handoff 的编排策略、Guardrail 的边界细节、生产环境的持久存储和可观测性怎么接这些内容量不小留到系列第二篇再展开。如果你也在评估 Agent 框架我建议别急着对比功能清单先把第二部分的核心概念在头脑里过一遍再亲手跑通 3.2 的最小例子。这套流程走完比看十篇框架对比文章都更能建立直觉。最后按个人经验再提醒一下Agent 的成功率和效率大概率取决于 instructions 与工具描述的质量框架只保证“把流程转起来”转得稳不稳仍然取决于数据、提示词和异常处理这些基本功。