开始之前
在 Python 3.10 或更高版本上用 pip install openai-agents 安装 SDK,TypeScript 则用 npm install @openai/agents zod。TypeScript SDK 可以运行在 Node.js 22 或更高版本、Deno、Bun,以及开启了 nodejs_compat 的 Cloudflare Workers 上。两者都和第 1 课一样,从 OPENAI_API_KEY 读取密钥。Python 里的导入名是 agents,不是 openai_agents。
这里说的智能体,是一个带指令和工具、会一直工作到任务完成的模型。这个 SDK 是 OpenAI 用来搭建智能体的库,文档称它是早先实验项目 Swarm 的生产级升级版。它的组成部分很少:智能体,负责委派的交接和“智能体作为工具”,负责检查的护栏,以及内置的追踪。其余部分都是普通的 Python 或 TypeScript。
OpenAI 提供了三种搭建智能体的方式,智能体概览对它们做了比较。用 Responses API,循环要你自己写,就像第 2 课和第 3 课那样。用 Agents SDK,循环在你的应用内部运行,工具、存储和审批都由你的服务器掌控。用 Agents API,则由 OpenAI 运行一个托管的 Codex 运行框架来处理长时间任务,并替你保存进度。
如果你用过 Anthropic 的工具,Claude Agent SDK 一课讲的是一个目标相近、设计不同的库,Claude API 课程的最后一课则是手写循环。OpenAI 的 SDK 介于两者之间:智能体在代码里定义,循环交给运行器。
Agents SDK 发布
一个用来设计、构建和部署智能体的编排框架。
TypeScript 版本
Agents SDK 推出 TypeScript 版。
沙箱和记忆控制
智能体可以在受控沙箱里运行,记忆何时创建、存在哪里也由你决定。
更新来到 TypeScript
带上沙箱智能体和开源的智能体运行框架。
Agents API 公开 beta
OpenAI 用托管的 Codex 环境运行智能体,并负责会话、压缩和恢复。
来源:OpenAI Docs: Changelog (2026年11月5日 查阅)
创建并运行你的第一个智能体
给一个名叫“History tutor”的智能体一条指令,再问它“When did the Roman Empire fall?”,快速入门说你会在终端里看到一个简洁的回答。这就是一次运行。下面的代码是快速入门里的 Python 版本,原样照搬。
撑起它的是三个名字。Agent 保存配置:一个会出现在追踪和交接里的 name,instructions,以及 model。Runner.run 接收一个起始智能体和一段输入,运行循环并返回结果。result.final_output 就是回答。在 TypeScript 里,同一个程序写作 new Agent({ ... }),接着 await run(agent, "..."),再读 result.finalOutput。
运行器有三个入口。Runner.run 是异步的,Runner.run_sync 把它包装成普通脚本可用的同步版本,Runner.run_streamed 则在运行进行中就把事件交给你。流式运行用的是同一个循环,所以要等流结束,才能把这次运行当作已经完成。没写 model 的智能体,会在设置了 OPENAI_DEFAULT_MODEL 时退回到它,这样整个进程可以共用一个默认模型。如果指令要随当前用户或租户而变,定义指南建议用一个动态指令回调,而不是在调用智能体的地方拼接字符串。
import asyncio
from agents import Agent, Runner
agent = Agent(
name="History tutor",
instructions="You answer history questions clearly and concisely.",
model="gpt-6-astra",
)
async def main() -> None:
result = await Runner.run(agent, "When did the Roman Empire fall?")
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())- 用
pip install openai-agents安装 SDK,并导出OPENAI_API_KEY。 - 把代码存成
agent.py。 - 运行
python agent.py,读一读智能体打印出来的回答。 - 把指令改成只要求一句话,再运行一次。
- 在 OpenAI 平台的追踪记录里,找到你刚才那次运行。
给智能体加上工具,弄懂这个循环
工具通常是智能体最先需要的东西。在 Python 里,@function_tool 装饰器能把一个普通函数变成工具。SDK 读取函数签名来生成 JSON Schema,并用文档字符串作为工具描述和每个参数的描述。在快速入门的示例里,“Return a short history fact.”就成了模型看到的描述。
托管工具也能用,比如第 2 课的网页搜索和文件搜索。在 TypeScript 里,函数工具写作 tool({ name, description, parameters, execute }),参数用一个 Zod 对象来写。工具很多时,可以先藏起来:@function_tool(defer_loading=True) 让一个工具先不露面,等 ToolSearchTool() 需要时再加载。
MCP 服务器有两种接法。托管的 MCP 工具适合公开的远程服务器,模型通过 OpenAI 访问它们。本地或私有的服务器则从你自己的运行环境经 stdio 或可流式 HTTP 连接,连接、过滤和审批都由你的代码掌控。
接下来,运行器反复执行同一个循环。它用准备好的输入调用当前智能体的模型,再看返回了什么。如果是工具调用,就运行它们,把结果追加进去,再转一圈。如果是交接,就切换当前智能体,再转一圈。如果是最终回答,运行就结束。
最终回答的意思是:输出了预期类型的文本,并且没有工具调用。如果把 output_type 设为一个 Pydantic 模型或 Zod schema,预期类型就是这个结构,背后用的正是第 3 课的结构化输出。没有设置时,预期类型就是纯文本。
每转一圈算一个回合,Python SDK 默认把一次运行限制在 10 个回合。超过上限,运行器会抛出 MaxTurnsExceeded。传入 max_turns 可以调高或调低这个上限,传 max_turns=None 则取消上限。想停得温和一些,可以用 error_handlers,它接受 "max_turns"、"model_refusal" 和 "invalid_final_output",让你返回一个可控的最终输出,而不是抛出异常。
有些工具不该在没人点头的情况下运行。用 @function_tool(needs_approval=True) 标记它,运行就会暂停,而不是直接调用。这时结果里会有 interruptions,以及一个可以用 result.to_state() 恢复的状态。你的代码调用 state.approve(interruption) 批准或拒绝,再用 Runner.run(agent, state) 继续。指南把这看作同一次运行,而不是新的用户回合,审核期间还可以把状态存起来。
import asyncio
from agents import Agent, Runner, function_tool
@function_tool
def history_fun_fact() -> str:
"""Return a short history fact."""
return "Sharks are older than trees."
agent = Agent(
name="History tutor",
instructions="Answer history questions clearly. Use history_fun_fact when it helps.",
tools=[history_fun_fact],
)
async def main() -> None:
result = await Runner.run(
agent,
"Tell me something surprising about ancient life on Earth.",
)
print(result.final_output)
if __name__ == "__main__":
asyncio.run(main())交接给专职智能体,并给运行加上护栏
给一个“Homework triage”智能体列上历史辅导和数学辅导两个交接对象,再问它“Who was the first president of the United States?”。运行最后给出的是历史辅导的回答,result.last_agent.name 会告诉你是哪个专职智能体给的。这个示例就是快速入门里的原样代码。
在背后,每个交接都是一个工具。一个叫“Refund Agent”的智能体,会变成一个叫 transfer_to_refund_agent 的工具,分诊智能体像调用别的工具一样调用它。每个专职智能体上的 handoff_description 告诉路由智能体什么时候该选它,所以指南要求写得简短具体。交接之后,如果希望这个专职智能体继续主导,就把 last_agent 传进下一个回合。交接还可以携带结构化的元数据或过滤过的历史,具体接法见各语言的 SDK 文档。
编排指南把这个选择看成归属问题。交接是把对话交给专职智能体。“智能体作为工具”则不同,Python 里用 agent.as_tool(),TypeScript 里用 agent.asTool(),由一个管理者智能体调用专职智能体完成摘要这类边界清楚的任务,最终回复由它自己来写。指南的建议是从一个智能体开始,只有当某个分支需要不同的指令、工具、策略或模型时才拆分。
护栏是自动检查。输入护栏验证用户发来的内容,输出护栏检查最终回答,工具护栏包住函数工具的参数或结果。检查不通过时会触发绊线,运行器随即抛出 InputGuardrailTripwireTriggered 或对应的输出异常,并停止运行。
输入护栏默认和智能体并行运行,也就是 run_in_parallel=True,这样能省时间。可一旦它触发,智能体可能已经用掉了 token、运行了工具。如果启动智能体的代价或风险太高,就设置 run_in_parallel=False,让检查先跑完。指南的示例正是这样做的,它把一个作业检查放在客服智能体前面。问到“Can you solve 2x + 3 = 11 for me?”时,检查触发,运行器抛出异常,示例打印出“Guardrail blocked the request.”。异常里的 guardrail_result 会写明是哪个护栏触发的。
两种护栏都有作用范围。输入护栏只对链条上的第一个智能体运行,输出护栏只对产出最终输出的那个智能体运行。工具护栏则在它所挂的函数工具上运行。在管理者模式的流程里,要把检查放在真正产生副作用的那个工具旁边。
import asyncio
from agents import Agent, Runner
history_tutor = Agent(
name="History tutor",
handoff_description="Specialist for history questions.",
instructions="Answer history questions clearly and concisely.",
)
math_tutor = Agent(
name="Math tutor",
handoff_description="Specialist for math questions.",
instructions="Explain math step by step and include worked examples.",
)
triage_agent = Agent(
name="Homework triage",
instructions="Route each homework question to the right specialist.",
handoffs=[history_tutor, math_tutor],
)
async def main() -> None:
result = await Runner.run(
triage_agent,
"Who was the first president of the United States?",
)
print(result.final_output)
print(result.last_agent.name)
if __name__ == "__main__":
asyncio.run(main())| 模式 | 什么时候用 | 谁来写回复 |
|---|---|---|
| 交接 | 某个专职智能体应该接管对话的这一分支 | 专职智能体 |
| 智能体作为工具 | 管理者应该一直主导,调用专职智能体完成边界清楚的任务 | 管理者 |
记住对话,再读懂追踪记录
先问“What city is the Golden Gate Bridge in?”,再问“What state is it in?”,第二个问题只有在记得第一个时才说得通。在下面会话页面的示例里,注释写着文档预期的回答,先是“San Francisco”,然后是“California”。每次调用 Runner.run 都传入同一个 SQLiteSession 对象,SDK 会在每次运行前后读取和保存历史。
会话是 SDK 的记忆层。它把一段对话的输出项按一个 id 存起来,你就不用再手动回放 result.to_input_list()。SQLiteSession("conversation_123") 把它存在内存里,再加第二个参数,也就是 "conversations.db" 这样的文件路径,就会存到磁盘上。对话很长时,OpenAIResponsesCompactionSession 可以包在另一种会话外面,每个回合后压缩保存的历史。它不能包住 OpenAIConversationsSession,因为两者管理历史的方式不同。
会话是保存状态的四种方式之一,运行指南要求每段对话只选一种。另外三种是自己回放历史、Conversations API 的 conversation_id,以及 previous_response_id,都在第 1 课讲过。同一次运行里,会话不能和那些服务端选项一起用,因为混用会让上下文重复。
追踪默认是打开的。每次运行都会记录模型调用、工具调用、交接、护栏,以及你自己加的任何自定义跨度,你可以在 OpenAI 平台的 Traces 控制台里查看。可观测性指南建议,第一次运行跑通后就马上打开追踪记录,然后再去调提示词。
关闭追踪有三种办法。设置环境变量 OPENAI_AGENTS_DISABLE_TRACING=1,在代码里调用 set_tracing_disabled(True),或者在某一次运行的 RunConfig 上设置 tracing_disabled。采用零数据保留政策的组织完全无法使用追踪。如果只是想少记录一些,可观测性指南更推荐用这些 SDK 级或单次运行的开关,而不是把可见性全部去掉。等你开始给行为打分时,追踪记录也会成为智能体评估的原始材料。
from agents import Agent, Runner, SQLiteSession
# Create agent
agent = Agent(
name="Assistant",
instructions="Reply very concisely.",
)
# Create a session instance with a session ID
session = SQLiteSession("conversation_123")
# First turn
result = await Runner.run(agent, "What city is the Golden Gate Bridge in?", session=session)
print(result.final_output) # "San Francisco"
# Second turn - agent automatically remembers previous context
result = await Runner.run(agent, "What state is it in?", session=session)
print(result.final_output) # "California"
# Also works with synchronous runner
result = Runner.run_sync(agent, "What's the population?", session=session)
print(result.final_output) # "Approximately 39 million"| 会话类型 | 适合 |
|---|---|
SQLiteSession | 本地开发和简单应用,存在内存或文件里 |
RedisSession | 在多个工作进程和服务之间共享记忆 |
SQLAlchemySession | 已有数据库的生产应用 |
OpenAIConversationsSession | 通过 Conversations API 由 OpenAI 保存历史 |
EncryptedSession | 在另一种会话外面加上加密和过期 |
OpenAI Developers 和 Steve Coffey 谈 Agents SDK
OpenAI Developers 在 2025-03-11 在 X 上介绍了 Agents SDK:一个用来编排多智能体工作流的开源 SDK,是对 Swarm 的改进。帖子点名的几样东西,正是本课用到的:内置工具、交接、安全护栏,以及用来调试的执行追踪。这四样至今仍是 SDK 的核心,所以先把它们学会,再去碰更新的功能。
Steve Coffey 在 OpenAI 做 API,他在 2026-04-15 发帖介绍了一次很大的更新。照他的说法,有了这次更新,你不用自己搭整套运行框架,也能在生产环境里扩展 Codex 式的智能体。计算机操作、技能、记忆和压缩也都一起加了进来。如果你的智能体要长时间运行或需要沙箱,自己写运行框架之前,先看看这次更新带来了什么。
给智能体配置内置工具、交接任务、加上安全护栏,并把执行追踪可视化,用来调试和优化性能。

X 官方嵌入卡片的截图,拍摄于 2026-11-05,时间为 UTC。点击截图可到 X 上打开原帖。X 用 Show more 截断了 Steve Coffey 的帖子。
常见错误
- 一个智能体还没答好一个问题,就先设计了五个智能体。
- 拆出来的专职智能体,用的工具和指令却完全一样。
- 本该由管理者继续写回复的地方,用了交接。
- 代价高的输入护栏还留在并行模式,为它后来拦下的工作白白付费。
- 指望第一个智能体上的输入护栏,去检查交接之后发生的调用。
- 把审批暂停当成新的用户回合,而不是从状态恢复。
- 在同一次运行里既传了会话,又传了
previous_response_id。 - 调高
max_turns,来掩盖一个永远得不到最终回答的循环。
自己试一试
- 运行历史辅导智能体,然后打开它的追踪记录,找到那次模型调用。
- 加上
history_fun_fact工具,问一个应该用到它的问题。追踪记录里有这次调用吗? - 给分诊智能体一个数学问题,打印
result.last_agent.name。 - 用
as_tool()把数学辅导改写成工具,比较一下最终回答是谁写的。 - 在一个需要工具的运行上设置
max_turns=1,捕获MaxTurnsExceeded。 - 给
SQLiteSession加上文件路径,停掉脚本,重新启动,再问一个追问。 - 用
needs_approval=True标记一个工具,在代码里批准这次中断,确认运行从它的状态继续。
接下来
这是 OpenAI API 课程的最后一课。RAG 教程是下一门课的开头,它会更深入地讲怎样让模型检索你自己的文档,比第 2 课的托管文件搜索走得更远。
智能体能在你自己的数据里查找信息之后,会更有用。这一课的工具、会话和追踪,都能直接用到那部分工作里。
自测一下
一次 Python 运行超过了回合上限,默认会怎样?
查看答案
D. 运行器抛出 MaxTurnsExceeded 默认上限是 10 个回合,超过就会抛出 MaxTurnsExceeded,除非你设置了错误处理函数。
哪种模式能让管理者智能体一直负责最终回复?
查看答案
B. 智能体作为工具,用 as_tool() 智能体作为工具时,管理者调用专职智能体完成边界清楚的任务,回复由它自己来写。
一个输入护栏保持默认的 run_in_parallel 设置并触发了,之前可能已经发生了什么?
查看答案
C. 智能体用掉了 token 并运行了工具 并行护栏和智能体同时启动,所以在绊线叫停之前,工作可能已经做了。
不用手动回放,什么能让对话历史在多次 Runner.run 调用之间保留下来?
查看答案
A. 把同一个 SQLiteSession 作为 session 传入 会话会在每次运行前后读取和保存对话的输出项。
常见问题
Agents SDK 能用其他厂商的模型吗?
能。Python SDK 的 README 说它与厂商无关:它支持 OpenAI 的 Responses 和 Chat Completions 接口,以及 100 多种其他模型,并提供 any-llm 和 LiteLLM 等集成。厂商的配置方式因语言而异,模型指南让你去看各语言 SDK 自己的文档。
Agents SDK 有 TypeScript 版本吗?
有,就是 @openai/agents,放在 openai-agents-js 仓库里。名字遵循 TypeScript 风格:用 run() 代替 Runner.run,还有 finalOutput、lastAgent、handoffDescription、outputType 和 asTool()。函数工具用 tool() 加 Zod 参数来写,快速入门用 Agent.create 搭建路由智能体。
追踪会记录我的用户数据吗?
可能会。生成跨度会保存模型的输入和输出,函数跨度会保存工具的输入和输出。如果这些数据敏感,就在 RunConfig 上把 trace_include_sensitive_data 设为 false,这样跨度还在,但不带内容。
怎样把数据库客户端交给工具,又不让模型看到它?
把它作为运行上下文传进去。Runner.run 接受一个 context 参数,工具和护栏通过 RunContextWrapper 拿到它,这样数据库客户端、日志器或已登录的用户能到达你的代码,却从不进入对话。定义指南把界线划得很清楚:模型需要的事实,放进指令、输入或工具里,只有运行环境需要的,就留在本地上下文里。
资料来源
- OpenAI Docs: Agents https://developers.openai.com/api/docs/guides/agents
- OpenAI Docs: Agents SDK https://developers.openai.com/api/docs/guides/agents/sdk
- OpenAI Docs: Agents SDK quickstart https://developers.openai.com/api/docs/guides/agents/quickstart
- OpenAI Docs: Agent definitions https://developers.openai.com/api/docs/guides/agents/define-agents
- OpenAI Docs: Agents SDK models and providers https://developers.openai.com/api/docs/guides/agents/models
- OpenAI Docs: Running agents https://developers.openai.com/api/docs/guides/agents/running-agents
- OpenAI Docs: Orchestration and handoffs https://developers.openai.com/api/docs/guides/agents/orchestration
- OpenAI Docs: Guardrails and human review https://developers.openai.com/api/docs/guides/agents/guardrails-approvals
- OpenAI Docs: Integrations and observability https://developers.openai.com/api/docs/guides/agents/integrations-observability
- OpenAI Agents SDK (Python): Running agents https://openai.github.io/openai-agents-python/running_agents/
- OpenAI Agents SDK (Python): Sessions https://openai.github.io/openai-agents-python/sessions/
- OpenAI Agents SDK (Python): Tracing https://openai.github.io/openai-agents-python/tracing/
- OpenAI Agents SDK (Python): Guardrails https://openai.github.io/openai-agents-python/guardrails/
- OpenAI Agents SDK (Python): Handoffs https://openai.github.io/openai-agents-python/handoffs/
- OpenAI Agents SDK (Python): Tools https://openai.github.io/openai-agents-python/tools/
- openai/openai-agents-python on GitHub https://github.com/openai/openai-agents-python
- openai/openai-agents-js on GitHub https://github.com/openai/openai-agents-js
- OpenAI Docs: Changelog https://developers.openai.com/api/docs/changelog
- X: OpenAI Developers introduce the Agents SDK https://x.com/OpenAIDevs/status/1899531857143972051
- X: Steve Coffey on the April 2026 Agents SDK update https://x.com/stevendcoffey/status/2044465818239701041
- YouTube (OpenAI): Build Hour: Agents SDK https://www.youtube.com/watch?v=tK32trvj_b4


