OpenAI Agents SDK #1:Agent 到底是什么?一文搞懂核心概念

从「为什么不直接调 API」这个开发者最常见困惑出发,系统拆解 OpenAI Agents Python SDK 中 Agent 的核心字段、Runner 驱动的 Agent Loop 执行机制,并通过天气 Agent 完整代码逐行解析。结尾给出三条可立即落地的实践建议。

リサーチノート

🎯 今天要学的东西:OpenAI Agents Python SDK 的 Agent 是整个框架最核心的构建单元。写了这么久 openai.chat.completions.create(),你真的清楚它和 Agent 的区别吗?
这是系列第一讲,把 Agent 的本质、组成、运行机制和最典型的代码示例全部拆开讲清楚。

一、从一个常见困惑说起

很多开发者第一次接触 Agents SDK 时,第一个问题往往是:我直接调 API 不也能调工具、多轮对话吗,为什么要多一层 Agent
这个问题问得很准。差别在于谁来管「循环」。
直接调 chat.completions,你要自己写循环:收到工具调用请求 → 执行工具 → 把结果拼回 messages → 再次调用 API。每一步手工管,状态管理、错误处理、上下文截断,全压在你身上。这在原型阶段跑得动,一旦涉及多步骤长流程或多个 Agent 协作,代码马上失控1
Agent + Runner 把这套逻辑内化掉了。官方文档说得直接:「Agent plus Runner lets the SDK manage turns, tools, guardrails, handoffs, and sessions for you. If you want to own that loop yourself, use the Responses API directly instead.」(Agent 加上 Runner,让 SDK 替你管理对话轮次、工具调用、护栏、交接和会话。如果你想自己掌控那个循环,直接用 Responses API 就好。)2
所以,Agent 不是一次 LLM 调用,而是一个配置好行为边界的执行单元;Runner 是驱动它反复运转的引擎。

二、Agent 的核心字段

Agent 类只有两个必填参数,其余都是可选的2
参数必填说明
name可读的 Agent 名称,用于日志和 Tracing
instructions系统提示词,也可以是动态回调函数
model可选覆盖全局模型,不填则继承 RunConfig
tools可选工具列表(函数工具 / 托管工具 / MCP 工具等)
handoffs可选可转交控制权的目标 Agent 列表
output_type可选结构化输出类型,支持 Pydantic / TypedDict
hooks可选生命周期回调,用于日志埋点、预取数据
tool_use_behavior可选工具调用后是继续 LLM 还是直接终止
其中 instructions 有个常被忽视的特性:它支持动态回调。你可以传一个函数,运行时拿到当前上下文动态生成提示词2
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,
)
这在多用户场景下很有用,比每次手动拼 system prompt 干净多了。

Python 代码示例
Python 代码示例

三、Runner 如何驱动 Agent 运转

Agent 本身是「配置」,跑起来靠 Runner。运行一个 Agent 最简单的方式3
from agents import Agent, Runner

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant.",
)

result = await Runner.run(agent, "你好,帮我写一首五言绝句。")
print(result.final_output)
Runner.run() 内部跑的是一个 Agent Loop,逻辑分三步3
  1. 调用 LLM
  2. 解析返回结果:
    • 如果是最终文本输出 → 循环结束,返回结果
    • 如果是工具调用 → 执行工具,把结果追加进上下文,重新回到第 1 步
    • 如果是 handoff → 切换当前 Agent,重新回到第 1 步
  3. 如果循环次数超过 max_turns 上限 → 抛出 MaxTurnsExceeded 异常
这个「循环」是 Agents SDK 区别于普通 API 调用的核心机制。工具调用、多 Agent 协作、对话历史管理,全都建立在这个 Loop 上。

四、完整代码示例解读

下面是官方文档中经典的「天气 Agent」示例2
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],
)
逐行解析:
@function_tool 装饰器:这是注册工具最快的方式。SDK 会自动解析函数签名和 docstring,生成 JSON Schema 传给 LLM。函数名就是工具名,docstring 就是工具描述,直接影响 LLM 是否会调用它4
model="gpt-5-nano":Agent 级别的模型设置会覆盖 RunConfig 里的全局设置。不同 Agent 可以用不同模型,比如主 Agent 用 GPT-5、子 Agent 用 GPT-5 nano,节省成本2
tools=[get_weather]:工具列表。给了工具并不意味着 LLM 一定会调用,具体行为取决于 ModelSettings.tool_choice(默认 auto,让 LLM 自己决定)2
运行这个 Agent:
result = await Runner.run(agent, "What's the weather like in Tokyo?")
print(result.final_output)
# → 可能是一首关于东京晴天的俳句
LLM 看到问题 → 决定调 get_weather("Tokyo") → 拿到 "The weather in Tokyo is sunny" → 再次调用 LLM → 用俳句形式回答。整个过程 Runner 自动管理,你不用写一行循环代码。

五、Agent 的运行边界

两个容易踩坑的地方:
tool_use_behavior 这个参数决定工具调用完成后,SDK 怎么处理结果2
  • run_llm_again(默认):工具结果交给 LLM 再处理
  • stop_on_first_tool:第一个工具调用的输出直接作为最终响应,不再过 LLM
  • StopAtTools(["tool_a", "tool_b"]):指定某些工具触发时立刻终止
  • ToolsToFinalOutputFunction:自定义函数决定是停止还是继续
如果你在做「执行型 Agent」,比如调一个工具拿到数据直接返回给用户,stop_on_first_tool 可以省掉一次不必要的 LLM 调用。
handoffs 是 Agents SDK 的另一个核心原语。把一个 Agent 放进另一个 Agent 的 handoffs 列表,LLM 就可以把控制权移交给它,后续对话由目标 Agent 接管5
from agents import Agent

booking_agent = Agent(name="Booking agent", instructions="Handle booking requests.")
refund_agent = Agent(name="Refund agent", instructions="Handle refund requests.")

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],
)
这是「去中心化」的多 Agent 模式。与「Manager 模式」(把子 Agent 注册为工具)的区别在于:Handoff 会直接移交对话历史,Manager 模式则由主 Agent 保持控制权2。中文社区的一篇分析指出,Handoff 设计是 OpenAI 官方 SDK 对比 CrewAI / AutoGen 的差异化竞争力之一6

六、三条可以今天就用上的实践建议

1. instructions 先写得具体,再考虑动态化
很多人一上来就想搞 dynamic_instructions。其实先把 instructions 写成具体的静态文本,把 Agent 调通,再考虑要不要动态化。静态文本更容易调试,出问题一眼看出是指令问题还是工具问题。
2. 从单 Agent + 1-2 个工具开始
中文社区的实践建议很一致:急于使用高级特性(Handoffs、MCP 集成、Guardrails)往往是踩坑的主要原因。先把最简单的 Agent Loop 跑通,搞清楚 Runner 如何驱动工具调用,再往上叠6
3. 从项目第一天就开启 Tracing
Agents SDK 的 Tracing 默认就是开启的7,直接去 platform.openai.com 的 Traces 面板就能看到每次 Agent 运行的完整链路,包括哪一步调了 LLM、哪个工具被调用、用了多少 token。多 Agent 系统一旦行为异常,Tracing 是最快的定位手段,别等出了问题再想起来加。

下一讲预告Runner 的完整执行机制——多轮对话的状态怎么管、RunConfig 里有哪些值得关注的参数、流式输出怎么用。

本系列持续更新,聚焦 OpenAI Agents Python SDK 每一个技术细节。

このコンテンツについて、さらに観点や背景を補足しましょう。

  • ログインするとコメントできます。