使用 OpenAI Agents SDK 构建智能体——模型与上下文管理

359 阅读9分钟

到目前为止,我们在用 OpenAI Agents SDK 构建代理时大多依赖默认的 GPT-4o 模型。不过,OpenAI Agents SDK 的一大强项是“与模型解耦”。换句话说,你并不被某一家模型提供商或某个固定配置锁死。这种灵活性在复杂流程或多代理系统里尤其有价值——流程的不同阶段可能需要不同的能力、成本或时延特性。

想象一个多代理系统:起手的分诊代理只需向用户发些简单问题。为这一步动用 GPT-4 可能并不合适;更可取的是一个更轻量的模型,甚至是像 LLaMA 这样的开源模型,成本几乎为零。与此同时,更吃力的任务(比如深度检索或数学推理)也许需要更强的 Anthropic 模型。而头脑风暴型代理可能仍然使用标准的 GPT-4o,但把 temperature 调得更高。

OpenAI Agents SDK 天然支持这种架构模式。每个代理都可以使用不同的模型及其专属配置。

本章你将学到:

  • 模型管理:如何调整模型、模型参数,以及把第三方模型集成到代理中;
  • 上下文管理:如何利用本地上下文把信息传给工具,同时不让 LLM 看见这些信息。

读完本章,你就能在代理方案里有效管理模型与上下文。

技术要求

请先按照第 3 章的步骤完成环境配置。
本书各章的示例与完整代码见 GitHub 仓库:github.com/PacktPublis…
鼓励你克隆、复用并按需改造这些示例代码。

模型管理

在 OpenAI Agents SDK 中,每个代理都必须由某个 LLM 驱动。LLM 是代理的大脑:读取信息、调用工具、生成回复。此前我们一直用默认配置(即 GPT-4o 与默认参数)。本节讲如何以及何时修改这些设置。

调整底层模型

Agents SDK 允许你显式选择要用的模型。不同模型各有优劣,为不同类型的代理选对模型很重要。比如 GPT-4o 快且准,而 o3-pro 更擅长复杂推理但速度更慢。

通过在实例化代理时设置 model 参数即可:

agent = Agent(
    name="SampleAgent",
    instructions="You are an AI agent",
    model="gpt-4o"
)

该参数可取任意有效的 OpenAI 模型名。模型列表、特性与费用见:platform.openai.com/docs/models

来看一个为什么“选对模型”至关重要的例子。我们创建两个代理(GPT-4o 与 o3-pro),让它们回答同一道题,并比较用时:

from agents import Agent, Runner
import time

# 两个代理
gpt4o_agent = Agent(
    name="GPT4o Agent",
    instructions="You are an AI Agent",
    model="gpt-4o"
)
o3pro_agent = Agent(
    name="o3-pro Agent",
    instructions="You are an AI Agent",
    model="o3-pro"
)

prompt = "How many integers from 1 to 10000 (inclusive) are divisible by 3 or by 5 but not by both? Do reasoning but only return only the answer."

print("gpt4o agent")
start_fast = time.time()
response = Runner.run_sync(gpt4o_agent, prompt)
print(response.final_output)
end_fast = time.time()
print(f"Time taken: {end_fast - start_fast:.2f} seconds")
print("---")

print("o3pro agent")
start_fast = time.time()
response = Runner.run_sync(o3pro_agent, prompt)
print(response.final_output)
end_fast = time.time()
print(f"Time taken: {end_fast - start_fast:.2f} seconds")
print("---")

运行结果:

gpt4o agent
3334
Time taken: 1.18 seconds
---
o3pro agent
4001
Time taken: 8.41 seconds
---

实践中,GPT-4o 一般几秒就答复,但在这道较复杂的数论题上给错了;o3-pro 虽然更慢(且可能更贵),但答对了。因此,准确性 vs. 时延/成本 是天然权衡;Agents SDK 让你为每个代理自由选择。

调整模型参数

很多时候你想用同一个模型,但需微调生成风格。比如让回答更短、更有创意。OpenAI API(以及大多数 LLM API)提供“模型参数”。在 Agents SDK 中,通过 ModelSettings 传入 model_settings 参数。

常用参数包括:

  • temperature:控制随机性。越低(如 0.2)越稳定,越高(如 0.8)越有创造性。
  • max_tokens:限制单次生成的最大 token 数,控制输出长度。

完整参数见参考文档:
openai.github.io/openai-agen…

示例:两个代理,一个高温度+更长上限,一个低温度+更短上限:

from agents import Agent, Runner
from agents.model_settings import ModelSettings

creative_agent = Agent(
    name="CreativeAgent",
    instructions="You are an AI agent that answers questions.",
    model="gpt-4o",
    model_settings=ModelSettings(
        temperature=1.0,
        max_tokens=300
    )
)

precise_agent = Agent(
    name="PreciseAgent",
    instructions="You are an AI agent that answers questions.",
    model="gpt-4o",
    model_settings=ModelSettings(
        temperature=0.2,
        max_tokens=50
    )
)

prompt = "Describe the future of AI in customer service."

print("Creative agent:")
response = Runner.run_sync(creative_agent, prompt)
print(response.final_output)
print("---")

print("Precise agent:")
response = Runner.run_sync(precise_agent, prompt)
print(response.final_output)
print("---")

运行后你会看到两种截然不同的风格:creative_agent 更长更天马行空;precise_agent 更短更克制,而且因为 max_tokens 很小,甚至可能被截断。通过调参,你无需换模型就能获得不同语气与篇幅,便于把同一模型分别用于销售型客服与创意研究等不同代理。

第三方模型

正如开头所说,Agents SDK 是“模型无关”的:只要满足接口特征,就能使用任意厂商的模型。虽然 GPT-4o 等是很好的默认选择,但很多场景下用别家更合适。比如 Anthropic 的 Claude 在某些指令遵循上更好;又或者为了成本选择 Meta 的 LLaMA。

Agents SDK 借助 LiteLLM 来适配第三方模型。LiteLLM 是一个为多家主流 LLM 提供统一 API 的轻量库(Anthropic、Google 等)。用它接入第三方模型非常顺滑:它处理 API Key、路由与响应格式,你几乎不用改代理代码就能切换。

先安装:

pip install "openai-agents[litellm]"

然后获取非 OpenAI 提供商的 API Key。这里以 Anthropic 为例:在 www.anthropic.com/ 创建账号、生成 API Key。把它写入第 3 章中设置的 .env

ANTHROPIC_API_KEY=sk-ant-api03-[your-remaining-api-key]-995

接着在代理的 model 参数中填 LiteLLM 的模型字符串,例如:

litellm/gemini/gemini-pro
litellm/anthropic/claude-opus-4-20250514
litellm/meta_llama/Llama-3.3-70B-Instruct

示例:

from agents import Agent, Runner

agent = Agent(
    name="Claude Agent",
    instructions="You are an AI Agent",
    model="litellm/anthropic/claude-opus-4-20250514"
)

question = "How do I restart my computer? Answer in a few words."
response = Runner.run_sync(agent, question)
print(response.final_output)

输出示例:

Windows: Start menu → Power → Restart
Mac: Apple menu → Restart
Or: Hold power button, then turn back on

这样你就能在不重构逻辑的前提下,随意把代理背后的 LLM 从 GPT-4o 切到 Claude 或 LLaMA(只要 LiteLLM 支持即可)。这在基准评测、不同部署环境(隐私/时延/成本)切换时非常有用。比如开发期用 GPT-4o 原型,生产用 Claude 做摘要。

上下文管理

“上下文”指代理能够访问到的一切信息。一个恰当的类比是:代理的 LLM 是它的“大脑”,而上下文就是传递给大脑、用于生成回答的信息。前文我们已讨论了多种向代理暴露关键信息的方式:系统指令、过往对话历史、提示注入,甚至通过工具调用进行的知识检索。

本节我们把范围收窄到本地上下文(也称运行上下文,run context)。它指为实例化代理而需要的信息,并作为工具及其他钩子的依赖。

本地上下文

本地上下文让你的代理在不把数据显式放进 LLM 提示词的情况下,访问(在实例化时提供的)信息。这最适合存放用户相关数据(如用户 ID、姓名、偏好),好让工具据此抓取或计算答案。这解锁了一个强大的设计模式:让代理在不把敏感/私有数据写进模型提示的前提下,仍可用带权限或应用特定的数据(例如用户偏好、鉴权令牌或内部状态)做出决策或生成个性化输出。

在 OpenAI Agents SDK 中,你通过创建一个上下文对象,并在调用 Runner 时用 context 参数传入来实现本地上下文。只要工具函数也声明了接收上下文参数,任何工具都能使用该上下文。

下面用一个具体示例说明。设想客服代理可基于用户档案及其当前订单来查询物流状态。我们用一个 OrderContext 对象(含用户姓名与订单详情)来模拟,再写一个使用这些信息返回物流进度的工具函数。

首先,新建 local_context.py,写入如下代码:

from dataclasses import dataclass
from agents import Agent, Runner, RunContextWrapper, function_tool

@dataclass
class OrderContext:
    customer_name: str
    order_id: str
    shipping_status: str

order_context = OrderContext(
    customer_name="Henry Habib",
    order_id="123",
    shipping_status="Delayed"
)

@function_tool
def get_shipping_status(wrapper: RunContextWrapper[OrderContext]) -> str:
    """Provide the shipping status for the current order."""
    ctx = wrapper.context
    return (
        f"Hi {ctx.customer_name}, your order {ctx.order_id} is currently: "
        f"{ctx.shipping_status}."
    )

agent = Agent[OrderContext](
    name="Shipping Support Agent",
    instructions="You are a helpful support agent who can check the shipping status of a user's order.",
    tools=[get_shipping_status]
)

question = "Where is my order?"
result = Runner.run_sync(agent, input=question, context=order_context)
print(result.final_output)

我们先定义了一个数据类 OrderContext,包含三项:用户姓名、订单号、当前物流状态。它就是我们希望代理能访问的本地上下文(供工具函数消费的信息)。

接着,我们创建一个示例的 OrderContext 实例。在真实应用中,上下文通常会依据“代理被调用的位置/会话”按用户动态生成。

然后,我们定义工具函数 get_shipping_status,使其接收上下文对象RunContextWrapper[OrderContext])作为参数。这告诉代理:调用工具时把上下文对象一起传入。我们还在实例化代理时将其参数化为同一上下文类型Agent[OrderContext]),让代理知道会收到何种上下文——这一步非常关键。

有了这些,调用 Runner.run_sync() 时就必须传入该类型的上下文对象。SDK 会自动把查询路由到工具,由工具基于上下文数据生成响应。

示例输出如下,说明代理已把上下文信息传给了工具函数:

Hi Henry Habib, your order (123) is currently delayed. If you have any further questions or need assistance, please let me know!

这体现了本地上下文的一大益处:代理能用从未进入模型提示词的敏感信息,生成精准、个性化的回答。归纳起来,本地上下文让代理拥有一种不直接暴露给用户或模型提示的“内部记忆”,是一种将领域知识或用户特定数据注入工具层的方式。

小结

本章介绍了如何为每个代理选择与配置底层模型;如何调整模型与其参数;以及如何通过 LiteLLM 集成第三方模型(如 Claude、Gemini、LLaMA),从而在不同提供商间轻松切换。最后,我们引入了本地上下文:一种在不把数据写入模型提示的情况下,为代理提供敏感或会话特定数据的机制。

结合这些技术,你就能全面掌控代理“如何思考、如何作答”。下一章我们将把重心转向代理的管理、运维与安全:你将学习如何监控代理活动、设置防护栏,并在生产环境中采用最佳治理实践。