
从「为什么不直接调 API」这个开发者最常见困惑出发,系统拆解 OpenAI Agents Python SDK 中 Agent 的核心字段、Runner 驱动的 Agent Loop 执行机制,并通过天气 Agent 完整代码逐行解析。结尾给出三条可立即落地的实践建议。
Agent 是整个框架最核心的构建单元。写了这么久 openai.chat.completions.create(),你真的清楚它和 Agent 的区别吗?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 就好。)2Agent 不是一次 LLM 调用,而是一个配置好行为边界的执行单元;Runner 是驱动它反复运转的引擎。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,
)
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:max_turns 上限 → 抛出 MaxTurnsExceeded 异常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。result = await Runner.run(agent, "What's the weather like in Tokyo?")
print(result.final_output)
# → 可能是一首关于东京晴天的俳句get_weather("Tokyo") → 拿到 "The weather in Tokyo is sunny" → 再次调用 LLM → 用俳句形式回答。整个过程 Runner 自动管理,你不用写一行循环代码。tool_use_behavior 这个参数决定工具调用完成后,SDK 怎么处理结果2:run_llm_again(默认):工具结果交给 LLM 再处理stop_on_first_tool:第一个工具调用的输出直接作为最终响应,不再过 LLMStopAtTools(["tool_a", "tool_b"]):指定某些工具触发时立刻终止ToolsToFinalOutputFunction:自定义函数决定是停止还是继续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],
)dynamic_instructions。其实先把 instructions 写成具体的静态文本,把 Agent 调通,再考虑要不要动态化。静态文本更容易调试,出问题一眼看出是指令问题还是工具问题。Runner 的完整执行机制——多轮对话的状态怎么管、RunConfig 里有哪些值得关注的参数、流式输出怎么用。mp.weixin.qq.com
openai.github.io
openai.github.io
openai.github.io
openai.github.io
mp.weixin.qq.com
openai.github.io
This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.