云计算百科
云计算领域专业知识百科平台

openai-agents-python-sdk 源码解析 | 第十二篇:模型适配层:Responses、Chat Completions 与第三方 Provider

本篇导读

前十一篇已经把 Agents SDK 的运行主线拆开了:

  • Agent 定义角色、工具、handoffs 和输出类型。
  • Runner 推进一次 run。
  • Function Tool 把 Python 函数暴露给模型。
  • Handoff 和 Agent-as-tool 组织多 Agent 工作流。
  • Streaming 把运行过程变成事件流。
  • Sessions 保存多轮历史。
  • Guardrails 控制输入、输出和工具边界。
  • Tracing 记录一次 run 的可观测链路。
  • 这些机制最终都要落到一个核心动作上:

    调用模型,拿到模型响应,再把模型响应归一化成 SDK 能处理的 items。

    这就是模型适配层的职责。

    本篇重点回答五个问题:

  • SDK 如何抽象 Model 和 ModelProvider。
  • Runner 如何决定一次 run 用哪个模型。
  • OpenAI Responses 和 Chat Completions 两条路径有什么差异。
  • ModelSettings 如何合并、转换和下发。
  • LiteLLM、Any-LLM、OpenAI-compatible endpoint 如何接入。
  • 先给结论:Runner 不关心具体 Provider

    Agents SDK 的核心 run loop 不直接依赖 OpenAI Responses、Chat Completions、LiteLLM 或 Any-LLM 的请求结构。

    它依赖的是统一接口:

    class Model(abc.ABC):
    async def get_response(...) > ModelResponse:
    ...

    def stream_response(...) > AsyncIterator[TResponseStreamEvent]:
    ...

    也就是说,run loop 只需要:

  • 把本轮输入、工具、handoffs、输出 schema、model settings 交给 Model。
  • 拿回统一的 ModelResponse 或统一的 Responses 风格 stream events。
  • 继续执行工具、handoff、输出校验和下一轮。
  • 真正知道“怎么调用某个后端”的,是 model adapter:

    Adapter负责什么
    OpenAIResponsesModel 调用 OpenAI Responses API
    OpenAIResponsesWSModel 通过 WebSocket 调用 OpenAI Responses API
    OpenAIChatCompletionsModel 调用 Chat Completions API,并做格式转换
    LitellmModel 通过 LiteLLM 调用第三方 provider
    AnyLLMModel 通过 any-llm 调用 Responses 或 Chat Completions 风格 provider

    因此,模型适配层的核心设计是:

    Runner 只看 Model 抽象;
    Provider 负责把名字解析成 Model;
    Adapter 负责 provider-specific 请求、能力校验、错误和 usage 归一化。

    第十二篇关注的源码入口

    本篇主要涉及这些文件:

    文件作用
    src/agents/models/interface.py Model、ModelProvider、ModelTracing 抽象
    src/agents/models/openai_provider.py OpenAI 默认 provider
    src/agents/models/multi_provider.py 前缀路由 provider
    src/agents/models/openai_responses.py OpenAI Responses adapter
    src/agents/models/openai_chatcompletions.py Chat Completions adapter
    src/agents/models/chatcmpl_converter.py Responses items 与 Chat messages 转换
    src/agents/model_settings.py ModelSettings 定义和合并逻辑
    src/agents/models/default_models.py 默认模型和默认 settings
    src/agents/run_internal/turn_preparation.py run loop 解析模型和 settings
    src/agents/run_internal/run_loop.py 调用 model.get_response() / stream_response()
    src/agents/run_internal/model_retry.py runner-managed retry
    src/agents/extensions/models/litellm_model.py LiteLLM adapter
    src/agents/extensions/models/any_llm_model.py Any-LLM adapter
    docs/models/index.md 模型配置文档
    examples/model_providers 第三方 provider 示例

    建议阅读顺序:

  • 先读 interface.py,理解 SDK 对模型的最低要求。
  • 再读 turn_preparation.py,看 Runner 如何解析模型。
  • 然后读 openai_provider.py,看默认 provider 如何选择 Responses 或 Chat Completions。
  • 最后对比 openai_responses.py 与 openai_chatcompletions.py。
  • ModelTracing:模型调用的 trace 开关

    ModelTracing 是模型 adapter 接收的 tracing 配置:

    class ModelTracing(enum.Enum):
    DISABLED = 0
    ENABLED = 1
    ENABLED_WITHOUT_DATA = 2

    def is_disabled(self) > bool:
    return self == ModelTracing.DISABLED

    def include_data(self) > bool:
    return self == ModelTracing.ENABLED

    它来自 RunConfig:

    def get_model_tracing_impl(
    tracing_disabled: bool,
    trace_include_sensitive_data: bool,
    ) > ModelTracing:
    if tracing_disabled:
    return ModelTracing.DISABLED
    if trace_include_sensitive_data:
    return ModelTracing.ENABLED
    return ModelTracing.ENABLED_WITHOUT_DATA

    含义很明确:

    配置行为
    tracing_disabled=True 模型 span 禁用
    trace_include_sensitive_data=True span 记录输入输出
    trace_include_sensitive_data=False span 存在,但不记录输入输出

    这就是第十一篇提到的敏感数据边界在模型层的落点。

    Model 接口

    Model 是所有模型 adapter 的公共接口:

    class Model(abc.ABC):
    async def get_response(
    self,
    system_instructions,
    input,
    model_settings,
    tools,
    output_schema,
    handoffs,
    tracing,
    *,
    previous_response_id,
    conversation_id,
    prompt,
    ) > ModelResponse:
    ...

    stream_response(…) 参数几乎相同,只是返回异步事件流:

    def stream_response(...) > AsyncIterator[TResponseStreamEvent]:
    ...

    注意几个设计点:

  • input 使用 Responses 风格的 SDK canonical items。
  • tools 是 SDK 统一的 Tool 对象。
  • handoffs 也交给 model adapter 转换成模型可见工具。
  • output_schema 由 adapter 转换为 provider 支持的结构化输出配置。
  • previous_response_id、conversation_id、prompt 是 Responses 特性,但接口统一暴露。
  • 这意味着 Chat Completions adapter 也会收到这些参数,但它不一定支持。

    它的处理策略是:

  • 默认兼容模式下记录 warning 并忽略不支持的特性。
  • strict_feature_validation=True 时直接抛 UserError。
  • ModelProvider 接口

    ModelProvider 只做一件事:把模型名解析成 Model 实例。

    class ModelProvider(abc.ABC):
    @abc.abstractmethod
    def get_model(self, model_name: str | None) > Model:
    ...

    async def aclose(self) > None:
    return None

    这个接口的边界很重要:

  • Model 负责一次具体模型调用。
  • ModelProvider 负责模型名解析、缓存、连接复用和资源关闭。
  • 例如 OpenAI WebSocket Responses 模型可能持有持久连接,所以 OpenAIProvider.aclose() 要关闭缓存的 websocket model。

    普通 HTTP Responses model 不需要在 provider 里缓存实例。

    Runner 如何选择模型

    turn_preparation.py 中的 get_model(…) 是模型解析入口:

    def get_model(agent: Agent[Any], run_config: RunConfig) > Model:
    if isinstance(run_config.model, Model):
    return run_config.model
    elif isinstance(run_config.model, str):
    return run_config.model_provider.get_model(run_config.model)
    elif isinstance(agent.model, Model):
    return agent.model

    return run_config.model_provider.get_model(agent.model)

    优先级是:

    优先级来源
    1 RunConfig.model 是具体 Model 实例
    2 RunConfig.model 是字符串,通过 RunConfig.model_provider 解析
    3 Agent.model 是具体 Model 实例
    4 Agent.model 是字符串或 None,通过 RunConfig.model_provider 解析

    这说明 RunConfig.model 可以覆盖 Agent 自己的模型。

    例如:

    agent = Agent(name="Assistant", model="gpt-5.4-mini")

    result = await Runner.run(
    agent,
    "你好",
    run_config=RunConfig(model="gpt-5.6-sol"),
    )

    本次 run 实际使用 gpt-5.6-sol。

    ModelSettings 如何解析

    模型 settings 不是简单拿 Agent 上的配置。

    源码中有一层默认 settings 对齐逻辑:

    def get_model_settings(agent: Agent[Any], run_config: RunConfig) > ModelSettings:
    model_settings = agent.model_settings
    if model_settings == _implicit_model_settings_for_agent(agent):
    model_settings = _model_settings_for_resolved_name(agent, run_config)
    return model_settings.resolve(run_config.model_settings)

    含义是:

  • 如果 Agent 使用的是隐式默认 settings,且 run-level model 覆盖了模型名,默认 settings 要跟着新的模型名重新计算。
  • 如果 Agent 明确设置过 ModelSettings,则保持用户显式配置。
  • 最后用 run_config.model_settings 覆盖 Agent settings。
  • ModelSettings.resolve(…) 的覆盖规则是:

    changes = {
    field.name: getattr(override, field.name)
    for field in fields(self)
    if getattr(override, field.name) is not None
    }

    即非 None 字段覆盖。

    两个特殊合并:

  • extra_args 会合并 dict,而不是整体替换。
  • retry 会深度合并 backoff 等字段。
  • 默认模型和默认 Settings

    当前源码中默认模型来自:

    OPENAI_DEFAULT_MODEL_ENV_VARIABLE_NAME = "OPENAI_DEFAULT_MODEL"

    def get_default_model() > str:
    return os.getenv(OPENAI_DEFAULT_MODEL_ENV_VARIABLE_NAME, "gpt-5.4-mini").lower()

    默认是:

    gpt-5.4-mini

    GPT-5 系列会有默认 ModelSettings:

    _GPT_5_NONE_DEFAULT_MODEL_SETTINGS = ModelSettings(
    reasoning=Reasoning(effort="none"),
    verbosity="low",
    )

    例如 gpt-5.4-mini 默认是:

    ModelSettings(
    reasoning=Reasoning(effort="none"),
    verbosity="low",
    )

    非 GPT-5 模型默认是空 ModelSettings()。

    这解释了一个常见现象:

    Agent(name="A")

    不传模型时,并不是完全没有模型配置。默认模型如果是 GPT-5 系列,SDK 会给它补一组低延迟默认 settings。

    OpenAIProvider:默认模型 Provider

    OpenAIProvider 是默认 provider。

    它的构造参数包括:

    OpenAIProvider(
    api_key=None,
    base_url=None,
    websocket_base_url=None,
    openai_client=None,
    organization=None,
    project=None,
    use_responses=None,
    use_responses_websocket=None,
    strict_feature_validation=False,
    responses_websocket_options=None,
    buffer_streamed_tool_calls=False,
    )

    几个关键点:

  • 如果传入 openai_client,不能再传 api_key、base_url、websocket_base_url。
  • 如果没有传 client,会懒加载 AsyncOpenAI。
  • HTTP client 是共享的,避免每次请求新建连接池。
  • 默认使用 Responses API。
  • 可以切换到 Chat Completions。
  • Responses 还可以切换 HTTP 或 websocket transport。
  • 默认 client 解析:

    self._client = _openai_shared.get_default_openai_client() or AsyncOpenAI(
    api_key=self._stored_api_key or _openai_shared.get_default_openai_key(),
    base_url=self._stored_base_url or os.getenv("OPENAI_BASE_URL"),
    websocket_base_url=(
    self._stored_websocket_base_url or os.getenv("OPENAI_WEBSOCKET_BASE_URL")
    ),
    http_client=shared_http_client(),
    )

    这说明你可以用环境变量或代码全局配置 OpenAI client。

    切换默认 OpenAI API

    默认是 Responses:

    set_default_openai_api("responses")

    也可以切换为 Chat Completions:

    from agents import set_default_openai_api

    set_default_openai_api("chat_completions")

    切换后,OpenAIProvider().get_model("…") 会返回不同 adapter:

    配置返回
    Responses HTTP OpenAIResponsesModel
    Responses WebSocket OpenAIResponsesWSModel
    Chat Completions OpenAIChatCompletionsModel

    如果只想本次 run 使用 Chat Completions,更推荐直接传 provider:

    provider = OpenAIProvider(use_responses=False)

    result = await Runner.run(
    agent,
    "你好",
    run_config=RunConfig(model_provider=provider),
    )

    这样不会影响进程内其他 run。

    OpenAI Responses Model

    OpenAIResponsesModel 的非流式调用是:

    with response_span(disabled=tracing.is_disabled()) as span_response:
    response = await self._fetch_response(...)

    它最终调用:

    response = await client.responses.create(**create_kwargs)

    _build_response_create_kwargs(…) 负责构造 Responses 请求。

    核心字段包括:

    create_kwargs = {
    "previous_response_id": ...,
    "conversation": ...,
    "instructions": ...,
    "model": ...,
    "input": list_input,
    "include": include,
    "tools": tools_param,
    "prompt": ...,
    "temperature": ...,
    "top_p": ...,
    "truncation": ...,
    "max_output_tokens": ...,
    "tool_choice": tool_choice_param,
    "parallel_tool_calls": parallel_tool_calls,
    "text": response_format,
    "store": ...,
    "prompt_cache_options": ...,
    "reasoning": ...,
    "metadata": ...,
    "context_management": ...,
    }

    这条路径天然支持 Responses 的能力:

  • previous_response_id
  • conversation
  • prompt
  • Responses tool surfaces
  • hosted tools
  • response include
  • context management
  • Responses structured output
  • Responses streaming terminal events
  • 所以 OpenAI-only 应用默认推荐走 Responses。

    Responses 工具转换

    Responses adapter 会把 SDK tools 和 handoffs 转成 Responses 工具参数。

    converted_tools = Converter.convert_tools(
    tools,
    handoffs,
    model=effective_computer_tool_model,
    tool_choice=model_settings.tool_choice,
    )

    Converter.convert_tool_choice(…) 支持的选择包括:

  • auto
  • required
  • none
  • function tool name
  • file_search
  • web_search
  • computer
  • image_generation
  • code_interpreter
  • mcp
  • MCPToolChoice
  • 同时它会校验不合法组合。例如 deferred-loading function tools 没有 ToolSearchTool() 时,tool_choice="required" 可能会被拒绝。

    这类校验放在 adapter 层是合理的:只有 adapter 知道目标 API 支持什么。

    Responses 输出格式

    结构化输出通过 text 字段传给 Responses API:

    {
    "format": {
    "type": "json_schema",
    "name": "final_output",
    "schema": output_schema.json_schema(),
    "strict": output_schema.is_strict_json_schema(),
    }
    }

    如果没有结构化输出,返回 omit。

    如果设置了 ModelSettings.verbosity,adapter 会把它放到 response format 相关结构中:

    if model_settings.verbosity is not None:
    if response_format is not omit:
    response_format["verbosity"] = model_settings.verbosity
    else:
    response_format = {"verbosity": model_settings.verbosity}

    这也是为什么 ModelSettings 不是简单照搬到 API 参数,而是需要 adapter 逐项解释。

    OpenAI Chat Completions Model

    Chat Completions adapter 的核心工作是转换。

    SDK 内部输入是 Responses 风格 items,但 Chat Completions API 要的是 messages:

    converted_messages = Converter.items_to_messages(
    input,
    model=self.model,
    base_url=str(self._client.base_url),
    strict_feature_validation=self._strict_feature_validation,
    )

    如果有 system instructions,会插到 messages 开头:

    if system_instructions:
    converted_messages.insert(
    0,
    {"content": system_instructions, "role": "system"},
    )

    最终调用:

    ret = await self._get_client().chat.completions.create(**create_kwargs)

    核心字段包括:

    create_kwargs = {
    "model": self.model,
    "messages": converted_messages,
    "tools": tools_param,
    "temperature": ...,
    "top_p": ...,
    "frequency_penalty": ...,
    "presence_penalty": ...,
    "max_tokens": ...,
    "tool_choice": tool_choice,
    "response_format": response_format,
    "parallel_tool_calls": parallel_tool_calls,
    "stream": ...,
    "stream_options": ...,
    "store": ...,
    "reasoning_effort": ...,
    "verbosity": ...,
    "top_logprobs": ...,
    }

    Chat Completions 路径会把返回的 ChatCompletionMessage 再转换回 Responses 风格 output items:

    items = Converter.message_to_output_items(message)

    这保证 run loop 后续仍然处理统一的 SDK item。

    Chat Completions 不支持的能力

    Chat Completions adapter 会收到接口中的 Responses-only 参数,例如:

  • previous_response_id
  • conversation_id
  • prompt
  • reasoning.mode
  • reasoning.context
  • 一些 Responses-only tool surface
  • 默认兼容模式下,它会 warning 并忽略。

    如果启用 strict validation:

    provider = OpenAIProvider(
    use_responses=False,
    strict_feature_validation=True,
    )

    这些不匹配会变成 UserError。

    开发期建议打开 strict validation,避免某些字段被静默降级。

    生产期是否打开,要看你是否依赖兼容老 provider 的容忍行为。

    Responses 与 Chat Completions 对比

    维度ResponsesChat Completions
    SDK 默认推荐
    OpenAI hosted tools 支持更多 支持有限
    previous_response_id 支持 不支持
    conversation_id 支持 不支持
    prompt-managed request 支持 不支持
    Responses tool search 支持 不支持
    输入格式 Responses items chat messages
    SDK 转换成本
    第三方 provider 兼容性 取决于 provider 通常更好
    stream terminal 语义 原生 Responses events 需要转换成 Responses 风格 events

    实用建议:

  • OpenAI-only 新项目优先 Responses。
  • OpenAI-compatible 第三方 provider 通常先试 Chat Completions。
  • 如果依赖 server-managed conversation、Responses hosted tools、tool search,用 Responses。
  • 如果必须混用两种 adapter,逐项验证工具、结构化输出、streaming 和 retry 行为。
  • Streaming 终止语义

    Responses stream 的终止事件不是简单的“流结束就成功”。

    Responses adapter 会检查:

  • response.completed
  • response.failed
  • response.incomplete
  • error
  • response.error
  • 如果收到 failed 或 incomplete terminal,会转换成 ModelBehaviorError。

    Chat Completions stream 则需要 ChatCmplStreamHandler 把 chat chunks 转换成 Responses 风格 events。

    这也是 adapter 层的职责:不同 provider 的 stream 协议不同,但 run loop 只消费统一的 TResponseStreamEvent。

    ModelResponse:统一返回值

    无论底层是 Responses 还是 Chat Completions,最终都要返回:

    ModelResponse(
    output=response.output,
    usage=usage,
    response_id=response.id,
    request_id=getattr(response, "_request_id", None),
    )

    或由 Chat Completions 转换后的:

    ModelResponse(
    output=items,
    usage=usage,
    response_id=None,
    )

    output 是 SDK 后续处理的关键:

  • message output 进入最终输出判断。
  • function call 进入工具执行。
  • handoff tool call 进入 Agent 切换。
  • reasoning item 进入历史和追踪。
  • 只要 adapter 归一化正确,run loop 不需要知道底层 provider。

    ModelSettings 到请求参数的映射

    ModelSettings 是 SDK 统一模型配置:

    字段常见用途
    temperature 采样温度
    top_p nucleus sampling
    frequency_penalty 频率惩罚
    presence_penalty 存在惩罚
    tool_choice 工具选择
    parallel_tool_calls 是否允许并行工具调用
    truncation Responses 截断策略
    max_tokens 最大输出 token
    reasoning 推理模型配置
    verbosity 输出详略
    metadata provider metadata
    store 是否服务端存储响应
    response_include Responses include
    top_logprobs top token logprobs
    retry runner-managed retry
    context_management Responses context management
    prompt_cache_options prompt cache 配置
    extra_args 直接传给 provider 的额外参数

    但不是每个 adapter 都支持所有字段。

    例如:

  • Responses 使用 max_output_tokens。
  • Chat Completions 使用 max_tokens。
  • Responses 使用 text 表示结构化输出。
  • Chat Completions 使用 response_format。
  • Chat Completions 只使用 reasoning.effort,不支持 reasoning.mode 和 reasoning.context。
  • 所以 ModelSettings 是统一入口,不是统一 wire format。

    extra_args 的重复键保护

    两个 OpenAI adapter 都会检查 extra_args 与显式参数是否重复。

    例如 Responses:

    duplicate_extra_arg_keys = sorted(
    k
    for k in extra_args
    if k in create_kwargs and not _is_openai_omitted_value(create_kwargs[k])
    )

    如果重复,会抛:

    responses.create() got multiple values for keyword argument '…'

    这可以避免同一个字段既通过 ModelSettings.temperature 传,又通过 extra_args={"temperature": …} 传,造成行为不确定。

    建议:

  • SDK 已有字段,优先用 ModelSettings 直接字段。
  • SDK 暂未暴露的新 provider 参数,再用 extra_args。
  • 不要同时使用两条路径传同一个参数。
  • Runner-managed Retry

    模型重试不是默认开启的。

    需要显式设置:

    from agents import ModelRetrySettings, ModelSettings, retry_policies

    model_settings = ModelSettings(
    retry=ModelRetrySettings(
    max_retries=2,
    policy=retry_policies.network_error(),
    )
    )

    run loop 调用模型时会包一层:

    new_response = await get_response_with_retry(
    get_response=lambda: model.get_response(...),
    rewind=rewind_model_request,
    retry_settings=model_settings.retry,
    get_retry_advice=model.get_retry_advice,
    previous_response_id=previous_response_id,
    conversation_id=conversation_id,
    )

    重试需要考虑 replay safety。

    特别是:

  • 使用 previous_response_id。
  • 使用 conversation_id。
  • 流式响应已经开始输出。
  • 可能已经发生工具副作用。
  • 这些场景不能简单按 HTTP 500 或网络错误重放。

    Adapter 可以通过 get_retry_advice(…) 提供 provider-specific 建议。OpenAI、LiteLLM、Any-LLM adapter 都会复用 OpenAI 风格 retry advice 提取逻辑。

    Provider-managed retry 与 Runner retry

    OpenAI SDK、LiteLLM、any-llm 自身也可能有重试机制。

    Runner-managed retry 在重放时会通过 contextvar 禁用 provider-managed retries:

    provider_managed_retries_disabled(disabled=True)

    adapter 里会检查:

    if should_disable_provider_managed_retries():
    return client.with_options(max_retries=0)

    这样避免两层 retry 叠加,导致请求次数难以控制。

    文章里的实践建议是:如果你需要可解释、可审计的重试策略,优先使用 runner-managed retry,并明确 policy。

    MultiProvider:基于前缀的路由

    MultiProvider 是默认 RunConfig.model_provider。

    它支持按模型名前缀路由:

    模型名默认路由
    gpt-5.4-mini OpenAI provider
    openai/gpt-4.1 OpenAI provider,默认去掉 openai/
    litellm/openrouter/openai/gpt-5.4-mini LitellmProvider
    any-llm/openrouter/openai/gpt-5.4-mini AnyLLMProvider

    源码中默认说明:

    class MultiProvider(ModelProvider):
    """Maps model based on prefix."""

    如果遇到未知前缀,默认抛 UserError。

    如果你使用 OpenAI-compatible endpoint,并且后端期望完整 namespaced model id,可以配置:

    provider = MultiProvider(
    openai_base_url="https://openrouter.ai/api/v1",
    openai_api_key="…",
    openai_prefix_mode="model_id",
    unknown_prefix_mode="model_id",
    )

    这样 openai/gpt-4.1 不会被当成 OpenAI alias 去掉前缀。

    OpenAI-compatible endpoint

    最小接入方式是自定义 AsyncOpenAI:

    from openai import AsyncOpenAI
    from agents import OpenAIChatCompletionsModel

    client = AsyncOpenAI(
    api_key="provider_api_key",
    base_url="https://provider.example.com/v1",
    )

    model = OpenAIChatCompletionsModel(
    model="provider-model-name",
    openai_client=client,
    )

    然后放到 Agent:

    agent = Agent(
    name="Assistant",
    instructions="你是一个简洁助手。",
    model=model,
    )

    如果要按 run 生效,可以写 ModelProvider:

    class CustomModelProvider(ModelProvider):
    def get_model(self, model_name: str | None) > Model:
    return OpenAIChatCompletionsModel(
    model=model_name or "provider-model-name",
    openai_client=client,
    )

    再传入:

    result = await Runner.run(
    agent,
    "你好",
    run_config=RunConfig(model_provider=CustomModelProvider()),
    )

    这适合一个 run 内所有 Agent 都走同一个自定义 provider 的场景。

    LiteLLM adapter

    LitellmModel 通过 LiteLLM 调用第三方 provider。

    直接使用:

    from agents.extensions.models.litellm_model import LitellmModel

    agent = Agent(
    name="Assistant",
    model=LitellmModel(
    model="openrouter/openai/gpt-5.4-mini",
    api_key="…",
    ),
    )

    或使用 MultiProvider 前缀:

    agent = Agent(
    name="Assistant",
    model="litellm/openrouter/openai/gpt-5.4-mini",
    )

    LiteLLM adapter 基本走 Chat Completions 风格:

  • 把 SDK items 转成 chat messages。
  • 把 SDK tools 转成 chat completion tools。
  • 调用 litellm.acompletion(…)。
  • 把返回 message 转成 SDK output items。
  • 使用 generation_span 做 tracing。
  • 源码中调用形态:

    ret = await litellm.acompletion(
    model=self.model,
    messages=converted_messages,
    tools=converted_tools or None,
    temperature=model_settings.temperature,
    tool_choice=self._remove_not_given(tool_choice),
    response_format=self._remove_not_given(response_format),
    parallel_tool_calls=parallel_tool_calls,
    api_key=self.api_key,
    base_url=self.base_url,
    **extra_kwargs,
    )

    适合:

  • 需要接入多个第三方 provider。
  • provider 已被 LiteLLM 支持。
  • 能接受 Chat Completions 风格的能力边界。
  • Any-LLM adapter

    AnyLLMModel 的特点是可以按 provider 能力选择 Responses 或 Chat Completions:

    def _selected_api(self) > Literal["responses", "chat_completions"]:
    if self.api is not None:
    ...
    return "responses" if self._supports_responses() else "chat_completions"

    直接使用:

    from agents.extensions.models.any_llm_model import AnyLLMModel

    agent = Agent(
    name="Assistant",
    model=AnyLLMModel(
    model="openrouter/openai/gpt-5.4-mini",
    api_key="…",
    ),
    )

    或使用 MultiProvider 前缀:

    agent = Agent(
    name="Assistant",
    model="any-llm/openrouter/openai/gpt-5.4-mini",
    )

    如果 provider 支持 Responses,它会走 _get_response_via_responses(…);否则走 _get_response_via_chat(…)。

    这比 LiteLLM 多了一个选择层:

    AdapterAPI 风格
    LiteLLM Chat Completions 风格
    Any-LLM Responses 或 Chat Completions,取决于 provider 能力

    但选择更多也意味着更要测试 provider 能力差异。

    非 OpenAI Provider 与 Tracing

    第三方 provider 示例里通常会调用:

    set_tracing_disabled(disabled=True)

    原因是默认 tracing 会上传到 OpenAI traces backend,需要 OpenAI API key。

    如果你的模型 provider 不是 OpenAI,但仍想使用 OpenAI tracing,可以单独设置 tracing export key:

    from agents import set_tracing_export_api_key

    set_tracing_export_api_key("sk-…")

    或者接入自定义 tracing processor。

    生产建议:

  • 非 OpenAI provider 默认先关闭 OpenAI tracing。
  • 如需 trace,接入企业内部 tracing processor。
  • 不要把第三方 provider 的 API key 写进 trace metadata。
  • 混用模型时的边界

    Agents SDK 允许一个 workflow 中不同 Agent 使用不同模型。

    例如:

    triage_agent = Agent(
    name="Triage agent",
    model="gpt-5.4-mini",
    )

    expert_agent = Agent(
    name="Expert agent",
    model=OpenAIChatCompletionsModel(
    model="provider-model",
    openai_client=custom_client,
    ),
    )

    但混用模型要注意:

  • 工具能力是否一致。
  • handoff 是否能被目标 adapter 转成工具。
  • output schema 是否被目标 provider 支持。
  • reasoning items 是否能正确 replay。
  • stream event 是否能归一化。
  • usage 是否完整。
  • retry 是否 replay-safe。
  • 文档也建议:同一个 workflow 尽量使用同一种 model shape。只有确实需要时再混用。

    实践一:显式使用 OpenAI Responses

    默认情况下 OpenAIProvider 已经走 Responses。

    你仍然可以显式写清楚:

    from agents import Agent, OpenAIProvider, RunConfig, Runner

    agent = Agent(
    name="Assistant",
    instructions="你是一个简洁助手。",
    model="gpt-5.4-mini",
    )

    provider = OpenAIProvider(use_responses=True)

    result = await Runner.run(
    agent,
    "用一句话解释 Responses API。",
    run_config=RunConfig(model_provider=provider),
    )

    如果要启用 websocket transport:

    provider = OpenAIProvider(
    use_responses=True,
    use_responses_websocket=True,
    )

    WebSocket transport 适合需要复用连接或特定低延迟场景,但要注意连接生命周期和 keepalive 配置。

    实践二:切换到 Chat Completions

    如果 provider 不支持 Responses,可以用 Chat Completions:

    from openai import AsyncOpenAI
    from agents import Agent, OpenAIChatCompletionsModel

    client = AsyncOpenAI(
    api_key="provider_api_key",
    base_url="https://provider.example.com/v1",
    )

    agent = Agent(
    name="Assistant",
    instructions="你是一个简洁助手。",
    model=OpenAIChatCompletionsModel(
    model="provider-model",
    openai_client=client,
    strict_feature_validation=True,
    ),
    )

    开发期建议打开 strict_feature_validation=True。

    如果发现代码依赖 conversation_id、prompt、Responses tool search 等能力,就应该切回 Responses 或调整功能设计。

    实践三:使用 LiteLLM

    from agents import Agent, Runner, function_tool, set_tracing_disabled

    set_tracing_disabled(True)

    @function_tool
    def get_weather(city: str) > str:
    return f"{city} 天气晴。"

    agent = Agent(
    name="Assistant",
    instructions="你只用俳句回答。",
    model="litellm/openrouter/openai/gpt-5.4-mini",
    tools=[get_weather],
    )

    result = await Runner.run(agent, "东京天气怎么样?")

    这里 litellm/ 前缀会被 MultiProvider 路由到 LitellmProvider。

    如果要传显式 key,使用 LitellmModel(…) 直接构造更清楚。

    实践四:使用 Any-LLM

    from agents import Agent, Runner, set_tracing_disabled

    set_tracing_disabled(True)

    agent = Agent(
    name="Assistant",
    instructions="你是一个简洁助手。",
    model="any-llm/openrouter/openai/gpt-5.4-mini",
    )

    result = await Runner.run(agent, "你好")

    Any-LLM 会解析 provider:

    openrouter/openai/gpt-5.4-mini

    provider 是 openrouter,provider model 是 openai/gpt-5.4-mini。

    如果你要强制 API 类型:

    AnyLLMModel(
    model="openrouter/openai/gpt-5.4-mini",
    api="chat_completions",
    )

    这适合 provider 同时支持多种 API,但你希望固定行为的场景。

    测试建议

    模型适配层测试不能只测 final output。

    至少覆盖:

  • RunConfig.model 覆盖 Agent.model。
  • ModelSettings run-level 覆盖 agent-level。
  • extra_args 与显式字段重复时报错。
  • Responses 路径能传 previous_response_id 和 conversation_id。
  • Chat Completions strict validation 能拒绝 Responses-only 字段。
  • Function tool call 能从 provider response 转成 SDK item。
  • streaming 终止事件能正确处理 failed/incomplete。
  • usage 能归一化。
  • retry policy 只在 replay-safe 场景重试。
  • provider aclose() 能释放缓存资源。
  • 本仓库已有相关测试:

  • tests/models/test_openai_responses.py
  • tests/models/test_openai_chatcompletions.py
  • tests/models/test_openai_chatcompletions_converter.py
  • tests/models/test_model_retry.py
  • tests/models/test_default_models.py
  • tests/models/test_litellm_chatcompletions_stream.py
  • tests/models/test_any_llm_model.py
  • tests/test_config.py
  • 新增 provider 或 adapter 时,应优先参考这些测试风格。

    常见错误

    第一,把 ModelSettings 当成所有 provider 都支持的参数集合。

    它是 SDK 统一配置入口,不代表所有 adapter 都能原样支持每个字段。

    第二,用 Chat Completions 但依赖 Responses-only 功能。

    例如 conversation_id、prompt、tool search、deferred-loading Responses tools。

    第三,混用 Responses 与 Chat Completions 后没有测 streaming。

    非流式能跑不代表 streaming event 归一化正确。

    第四,忽略 strict validation。

    开发期不开 strict validation,容易把字段静默降级成 warning。

    第五,把 extra_args 当成万能出口。

    重复传参会报错;未知 provider 参数也可能只在运行时失败。

    第六,第三方 provider 不关闭 tracing。

    没有 OpenAI tracing key 时,默认 exporter 可能出现 401 或跳过上报。

    第七,重试策略过于粗暴。

    带 conversation_id 或已开始 streaming 的请求不能简单重放。

    第八,没有关闭持久 provider 资源。

    WebSocket Responses model 和某些第三方 client 可能持有连接,应在生命周期结束时调用 provider/model 的 close。

    本篇小结

    本篇拆解了 Agents SDK 的模型适配层。

    核心结论:

  • Runner 只依赖 Model 抽象,不依赖具体 provider 请求结构。
  • ModelProvider 负责把模型名解析成具体 Model。
  • RunConfig.model 优先级高于 Agent.model。
  • ModelSettings 使用非 None 覆盖,并特殊合并 extra_args 和 retry。
  • 当前源码默认模型是 gpt-5.4-mini,并带 GPT-5 默认 settings。
  • OpenAIProvider 默认走 Responses,可切换 Chat Completions 或 Responses WebSocket。
  • Responses adapter 原生支持 server-managed state、prompt、Responses tools、context management 等能力。
  • Chat Completions adapter 需要在 SDK Responses items 与 chat messages 之间转换。
  • LiteLLM 更偏 Chat Completions 风格,Any-LLM 可按 provider 能力选择 Responses 或 Chat Completions。
  • 新增或切换 provider 时,必须验证工具、handoff、结构化输出、streaming、usage、retry 和 tracing。
  • 下一篇会进入 MCP 集成:让 Agent 使用外部工具生态,理解 MCP server 如何映射为 SDK tools,以及连接、缓存、过滤和审批如何工作。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » openai-agents-python-sdk 源码解析 | 第十二篇:模型适配层:Responses、Chat Completions 与第三方 Provider
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!