
从「给 Agent 设置了 model 参数却被静默覆盖」这个高频陷阱切入,系统拆解 OpenAI Agents SDK 的三层模型优先级覆盖链(OPENAI_DEFAULT_MODEL → Agent.model → RunConfig.model);完整讲解 LitellmModel 接入 Anthropic/Gemini/Ollama 的三种写法及版本注意事项;对比 OpenAIChatCompletionsModel 与 LitellmModel 的适用边界;深入 ModelSettings.resolve() 的字段覆盖与字典合并机制;结尾给出 A/B 测试模型、Tracing 配置、国内模型选型等四个实战场景,以及 3 条可落地建议。
model="gpt-4o",它就会用 gpt-4o。model 值,你 Agent 上写的那行配置会被完全覆盖。静默覆盖,没有报错,没有警告,Agent 就这么换了个脑子跑完。环境变量 OPENAI_DEFAULT_MODEL ←── 全局默认(最低优先级)
↑ 被覆盖
RunConfig.model ←── 单次运行级别
↑ 被覆盖
Agent.model ←── 单个 Agent 级别(最高优先级)model,RunConfig 不设 model
→ Agent.model 生效,每个 Agent 各走各的model,统一覆盖
→ 所有 Agent 都换,一行搞定from agents import Agent, Runner, RunConfig
# 情境 A:不同 Agent 各自的模型
analyst = Agent(
name="数据分析师",
model="gpt-4o", # 复杂推理用强模型
instructions="分析数据,给出判断",
)
summarizer = Agent(
name="摘要助手",
model="gpt-4o-mini", # 简单总结用轻量模型,省钱
instructions="把分析结果整理成一段话",
)
# 两个 Agent 会分别使用各自配置的模型
result = await Runner.run(analyst, "分析Q1销售数据")# 情境 B:RunConfig 强制覆盖,整个 pipeline 用同一个模型
# 无论 analyst 和 summarizer 各自配了什么,都会被替换
result = await Runner.run(
analyst,
"分析Q1销售数据",
run_config=RunConfig(model="gpt-4o-mini"), # 临时降级测试
)2
set_default_openai_client(全局替换 OpenAI 客户端)import asyncio
from openai import AsyncOpenAI
from agents import Agent, Runner, set_default_openai_client, set_default_openai_api
# 替换全局 OpenAI 客户端为 DeepSeek 兼容端点
custom_client = AsyncOpenAI(
base_url="https://api.deepseek.com/v1",
api_key="your-deepseek-api-key",
)
# 告诉 SDK 用这个客户端,不要再去找 OPENAI_API_KEY
set_default_openai_client(custom_client)
set_default_openai_api("chat_completions") # 非 OpenAI 提供商通常只支持 Chat Completions
agent = Agent(
name="DeepSeek Agent",
model="deepseek-chat", # 直接写提供商的模型名
instructions="你是一个有帮助的助手",
)
async def main():
result = await Runner.run(agent, "你好,介绍一下自己")
print(result.final_output)
asyncio.run(main())ModelProvider(单次运行级别)from openai import AsyncOpenAI
from agents import Agent, Runner, RunConfig, OpenAIChatCompletionsModel, ModelProvider
class MyCustomProvider(ModelProvider):
"""自定义 ModelProvider,按模型名路由到不同提供商"""
def get_model(self, model_name: str | None):
if model_name and model_name.startswith("claude"):
# Anthropic 走 OpenAI 兼容端点(需要 Anthropic 支持)
client = AsyncOpenAI(
base_url="https://api.anthropic.com/v1",
api_key="your-anthropic-key",
)
return OpenAIChatCompletionsModel(model=model_name, openai_client=client)
# 其他模型走默认 OpenAI
return None # 返回 None 时 SDK 使用默认 MultiProvider
agent = Agent(
name="Claude Agent",
model="claude-opus-4-5",
instructions="你是一个有帮助的助手",
)
result = await Runner.run(
agent,
"解释量子纠缠",
run_config=RunConfig(model_provider=MyCustomProvider()),
)LitellmModel(单个 Agent 级别,覆盖 100+ 供应商)pip install "openai-agents[litellm]"from agents import Agent, Runner
from agents.extensions.models.litellm_model import LitellmModel
# 写法一:直接用 litellm/ 前缀格式的字符串
agent_anthropic = Agent(
name="Anthropic Agent",
model="litellm/anthropic/claude-opus-4-5", # litellm 格式:provider/model
instructions="用中文回答问题",
)
# 写法二:实例化 LitellmModel(更清晰,可传额外参数)
agent_gemini = Agent(
name="Gemini Agent",
model=LitellmModel(
model="gemini/gemini-2.0-flash",
api_key="your-google-api-key", # 也可以靠环境变量
),
instructions="用中文回答问题",
)
# 写法三:接入本地 Ollama(无需 API Key)
agent_local = Agent(
name="本地 Llama Agent",
model=LitellmModel(
model="ollama/llama3.2",
api_base="http://localhost:11434", # Ollama 默认端口
),
instructions="你是本地运行的助手",
)
async def main():
# 三个 Agent 各跑各的
r1 = await Runner.run(agent_anthropic, "什么是 Transformer?")
r2 = await Runner.run(agent_gemini, "什么是 Transformer?")
r3 = await Runner.run(agent_local, "什么是 Transformer?")
print("Claude 说:", r1.final_output)
print("Gemini 说:", r2.final_output)
print("Llama 说:", r3.final_output)1一个版本提示:v0.14.2 修复了 LiteLLM 的extra_body转发问题,如果你在 Agents SDK 中使用 LiteLLM 且发现自定义请求体参数没生效,请先升级到 v0.14.2+4。
OpenAIChatCompletionsModel vs LitellmModel:两种路线,不是竞争OpenAIChatCompletionsModel:SDK 内置实现,直接调用 OpenAI Chat Completions API 协议。任何支持 OpenAI API 格式的提供商(Azure、DeepSeek、Qwen、国内大部分厂商)都可以用,只需要传一个自定义 AsyncOpenAI 客户端5。LitellmModel:借助 LiteLLM 这个第三方适配层,统一封装了 100+ 提供商的 API 差异。好处是不用自己处理各家 API 格式的差异;代价是多一层依赖、处于测试阶段、偶尔有兼容性 bug1。OpenAIChatCompletionsModel,因为国内和海外大部分主流提供商都支持 OpenAI 兼容格式——用标准路径更稳定。只有内置方式真的搞不定时,再考虑 LiteLLM。# 推荐路径:OpenAI 兼容格式 → OpenAIChatCompletionsModel
from openai import AsyncOpenAI
from agents import Agent
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
qwen_client = AsyncOpenAI(
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key="your-dashscope-key",
)
agent = Agent(
name="通义千问 Agent",
model=OpenAIChatCompletionsModel(
model="qwen-max",
openai_client=qwen_client,
),
instructions="你是通义千问助手",
)5ModelSettings:参数覆盖也有层级ModelSettings.resolve() 方法负责合并:把 override 里所有非 None 的字段覆盖到当前实例,返回新实例。这意味着——你在 RunConfig 里设的 model_settings 只会覆盖你明确传了值的字段,没传的字段保留 Agent 自己的配置2。from agents import Agent, Runner, RunConfig
from agents.model_settings import ModelSettings
# Agent 自己的模型参数
creative_agent = Agent(
name="创意写作",
model="gpt-4o",
model_settings=ModelSettings(
temperature=0.9, # 高温,更有创意
max_tokens=2000,
top_p=0.95,
),
instructions="你是一位富有创意的作家",
)
# RunConfig 的 model_settings 只覆盖 temperature
# max_tokens 和 top_p 仍然用 Agent 自己的值
result = await Runner.run(
creative_agent,
"写一首关于代码的诗",
run_config=RunConfig(
model_settings=ModelSettings(
temperature=0.3, # 覆盖为低温,更确定
# max_tokens 没传 → 保留 Agent 的 2000
# top_p 没传 → 保留 Agent 的 0.95
)
),
)extra_args:它的合并策略和其他字段不同,是字典合并,而不是直接覆盖6。Agent 的 extra_args 和 RunConfig 的 extra_args 会被合并,键冲突时 RunConfig 的值优先。# Agent 设了 extra_args
agent = Agent(
model_settings=ModelSettings(
extra_args={"response_format": "json_object", "seed": 42}
)
)
# RunConfig 覆盖其中一个键,另一个保留
run_config = RunConfig(
model_settings=ModelSettings(
extra_args={"seed": 99} # 只改 seed,response_format 保留
)
)
# 最终生效:{"response_format": "json_object", "seed": 99}ModelRefusalError 是 v0.15.0 新加的:当模型拒绝请求时,不再返回空文本,而是直接抛出异常4。这对调试模型参数很有用:以前 temperature 设得过低模型可能悄悄不回应,现在至少会有明确报错。gpt-4o-mini;深度分析任务复杂,用 gpt-4o。只需要在 Agent 定义时各自指定 model,RunConfig 不设 model,就能自动路由。import random
from agents import Agent, Runner, RunConfig
agent = Agent(name="客服机器人", instructions="解答用户问题")
async def run_with_ab_test(user_input: str):
# 50% 流量用 gpt-4o,50% 用 gpt-4o-mini
model = "gpt-4o" if random.random() > 0.5 else "gpt-4o-mini"
result = await Runner.run(
agent,
user_input,
run_config=RunConfig(model=model),
)
# 记录 model 到日志,用于后续对比
print(f"[{model}] {result.final_output}")
return resultfrom agents import set_tracing_export_api_key
# 全局配置:即使用非 OpenAI 模型,Trace 数据仍发到 OpenAI Tracing
set_tracing_export_api_key("sk-your-openai-key-for-tracing")
# 或者单次运行配置
run_config = RunConfig(
model=LitellmModel(model="claude-opus-4-5"),
tracing={"api_key": "sk-your-openai-key-for-tracing"},
)OpenAIChatCompletionsModel 的优质选择。print(run_config.model) 就能排查这个问题。LitellmModel 前,先 pip install "openai-agents[litellm]",不然会静默 import 失败。用完了别忘了确认 SDK 版本 ≥ 0.14.2,这个版本修了 extra_body 转发的 bug4。ModelSettings 做精细参数分层,不要全靠全局配置model_settings 里,RunConfig 只做全局兜底。这样不同任务的参数各司其职,改一个不影响另一个。OpenAIChatCompletionsModel 和 LitellmModel 的完整参考代码,是本篇所有示例最好的延伸阅读5。openai.github.io
openai.github.io
openai.github.io
github.com
github.com
openai.github.io
github.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.