通过


升级指南:使用泛型作为 TypedDict 的聊天选项

本指南可帮助你将 Python 代码升级到 Microsoft Agent Framework 版本 Options 中引入的新 TypedDict 系统。 这是一项 破坏性变更,可提供更好的类型安全性、IDE 自动完成功能以及运行时可扩展性。

更改概述

此版本引入了将选项传递到聊天客户端和聊天代理的方式的重大重构。

之前的运作方式

以前,选项作为 直接关键字参数 传递给方法,例如 get_response()get_streaming_response()run()代理构造函数:

# Options were individual keyword arguments
response = await client.get_response(
    "Hello!",
    model_id="gpt-4",
    temperature=0.7,
    max_tokens=1000,
)

# For provider-specific options not in the base set, you used additional_properties
response = await client.get_response(
    "Hello!",
    model_id="gpt-4",
    additional_properties={"reasoning_effort": "medium"},
)

它现在的工作原理

现在,大多数选项通过单个 options 参数作为类型化字典传递:

# Most options go in a single typed dict
response = await client.get_response(
    "Hello!",
    options={
        "model_id": "gpt-4",
        "temperature": 0.7,
        "max_tokens": 1000,
        "reasoning_effort": "medium",  # Provider-specific options included directly
    },
)

注意:对于代理,参数instructionstools仍然可作为Agent.__init__()client.as_agent()上的直接关键字参数使用。 对于 agent.run(),仅 tools 可用作关键字参数:

# Agent creation accepts both tools and instructions as keyword arguments
agent = Agent(
    chat_client=client,
    tools=[my_function],
    instructions="You are a helpful assistant.",
    default_options={"model_id": "gpt-4", "temperature": 0.7},
)

# agent.run() only accepts tools as a keyword argument
response = await agent.run(
    "Hello!",
    tools=[another_function],  # Can override tools per-run
)

重要更改

  1. 合并选项参数:大多数关键字参数(model_idtemperature等)现在通过单个 options 字典传递
  2. 代理创建异常instructionstools作为直接关键字参数,在Agent.__init__()as_agent()上保持可用
  3. 代理运行的异常:在 tools 上,agent.run() 仍然可用作为直接关键字参数
  4. 基于 TypedDict 的选项:选项被定义为实现类型安全的类
  5. 泛型类型支持:聊天客户端和代理支持特定于提供程序的选项的泛型,以允许运行时重载
  6. 提供程序特定的选项:每个提供程序都有自己的默认 TypedDict(例如, OpenAIChatOptionsOllamaChatOptions
  7. 不再使用additional_properties:提供程序特定的参数现在为一等的类型字段

优点

  • 类型安全:IDE 的自动完成功能以及对所有选项进行类型检查
  • 提供程序灵活性:即刻支持提供程序特定的参数
  • 更简洁的代码:基于字典的一致参数传递
  • 简化扩展:为专用用例创建自定义选项(例如推理模型或其他 API 后端)

迁移指南

1. 将关键字参数转换为选项字典

最常见的更改是将单个关键字参数转换为 options 字典。

之前(关键字参数):

from agent_framework.openai import OpenAIChatClient

client = OpenAIChatClient()

# Options passed as individual keyword arguments
response = await client.get_response(
    "Hello!",
    model_id="gpt-4",
    temperature=0.7,
    max_tokens=1000,
)

# Streaming also used keyword arguments
async for chunk in client.get_streaming_response(
    "Tell me a story",
    model_id="gpt-4",
    temperature=0.9,
):
    print(chunk.text, end="")

之后(选项字典):

from agent_framework.openai import OpenAIChatClient

client = OpenAIChatClient()

# All options now go in a single 'options' parameter
response = await client.get_response(
    "Hello!",
    options={
        "model_id": "gpt-4",
        "temperature": 0.7,
        "max_tokens": 1000,
    },
)

# Same pattern for streaming
async for chunk in client.get_streaming_response(
    "Tell me a story",
    options={
        "model_id": "gpt-4",
        "temperature": 0.9,
    },
):
    print(chunk.text, end="")

如果传递的选项不适合该客户端,则 IDE 中会出现类型错误。

2. 使用供应商特定选项(不再使用additional_properties)

以前,若要传递不属于关键字参数基集的提供程序特定参数,必须使用参数 additional_properties

之前(使用 additional_properties):

from agent_framework.openai import OpenAIChatClient

client = OpenAIChatClient()
response = await client.get_response(
    "What is 2 + 2?",
    model_id="gpt-4",
    temperature=0.7,
    additional_properties={
        "reasoning_effort": "medium",  # No type checking or autocomplete
    },
)

之后(使用 TypedDict 的直接选项):

from agent_framework.openai import OpenAIChatClient

# Provider-specific options are now first-class citizens with full type support
client = OpenAIChatClient()
response = await client.get_response(
    "What is 2 + 2?",
    options={
        "model_id": "gpt-4",
        "temperature": 0.7,
        "reasoning_effort": "medium",  # Type checking or autocomplete
    },
)

之后(新参数的自定义子类):

或者,如果它是尚未成为 Agent Framework 的一部分的参数(因为它是新的,或者因为它是对 OpenAI 兼容的后端的自定义),则现在可以对选项进行子类化并使用泛型支持:

from typing import Literal
from agent_framework.openai import OpenAIChatOptions, OpenAIChatClient

class MyCustomOpenAIChatOptions(OpenAIChatOptions, total=False):
    """Custom OpenAI chat options with additional parameters."""

    # New or custom parameters
    custom_param: str

# Use with the client
client = OpenAIChatClient[MyCustomOpenAIChatOptions]()
response = await client.get_response(
    "Hello!",
    options={
        "model_id": "gpt-4",
        "temperature": 0.7,
        "custom_param": "my_value",  # IDE autocomplete works!
    },
)

关键好处是,大多数提供程序特定的参数现在是类型化选项字典的一部分,从而提供:

  • 所有可用选项的 IDE 自动完成
  • 类型检查 用于捕捉无效的键或值
  • 对于已知的提供程序参数不需要additional_properties
  • 简单扩展用于自定义或新参数

3. 更新代理配置

代理初始化和运行方法遵循相同的模式:

之前(构造函数和运行的关键字参数):

from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient

client = OpenAIChatClient()

# Default options as keyword arguments on constructor
agent = Agent(
    chat_client=client,
    name="assistant",
    model_id="gpt-4",
    temperature=0.7,
)

# Run also took keyword arguments
response = await agent.run(
    "Hello!",
    max_tokens=1000,
)

After:

from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient, OpenAIChatOptions

client = OpenAIChatClient()
agent = Agent(
    chat_client=client,
    name="assistant",
    default_options={ # <- type checkers will verify this dict
        "model_id": "gpt-4",
        "temperature": 0.7,
    },
)

response = await agent.run("Hello!", options={ # <- and this dict too
    "max_tokens": 1000,
})

4. 供应商特定选项

每个提供程序现在都有自己的 TypedDict 选项,这些选项默认处于启用状态。 这样,您可以使用提供程序特定的参数,并实现类型安全。

OpenAI 示例:

from agent_framework.openai import OpenAIChatClient

client = OpenAIChatClient()
response = await client.get_response(
    "Hello!",
    options={
        "model_id": "gpt-4",
        "temperature": 0.7,
        "reasoning_effort": "medium",
    },
)

但你也可以将其显式化:

from agent_framework_anthropic import AnthropicClient, AnthropicChatOptions

client = AnthropicClient[AnthropicChatOptions]()
response = await client.get_response(
    "Hello!",
    options={
        "model_id": "claude-3-opus-20240229",
        "max_tokens": 1000,
    },
)

5. 为专用模型创建自定义选项

新系统的一项强大功能是为专用模型创建自定义 TypedDict 选项。 这对于具有唯一参数的模型特别有用,例如使用 OpenAI 的推理模型:

from typing import Literal
from agent_framework.openai import OpenAIChatOptions, OpenAIChatClient

class OpenAIReasoningChatOptions(OpenAIChatOptions, total=False):
    """Chat options for OpenAI reasoning models (o1, o3, o4-mini, etc.)."""

    # Reasoning-specific parameters
    reasoning_effort: Literal["none", "minimal", "low", "medium", "high", "xhigh"]

    # Unsupported parameters for reasoning models (override with None)
    temperature: None
    top_p: None
    frequency_penalty: None
    presence_penalty: None
    logit_bias: None
    logprobs: None
    top_logprobs: None
    stop: None


# Use with the client
client = OpenAIChatClient[OpenAIReasoningChatOptions]()
response = await client.get_response(
    "What is 2 + 2?",
    options={
        "model_id": "o3",
        "max_tokens": 100,
        "allow_multiple_tool_calls": True,
        "reasoning_effort": "medium",  # IDE autocomplete works!
        # "temperature": 0.7,  # Would raise a type error, because the value is not None
    },
)

提供选项的聊天代理

通用设置也扩展到聊天代理:

from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient

agent = Agent(
    chat_client=OpenAIChatClient[OpenAIReasoningChatOptions](),
    default_options={
        "model_id": "o3",
        "max_tokens": 100,
        "allow_multiple_tool_calls": True,
        "reasoning_effort": "medium",
    },
)

并且可以在客户端和代理上指定泛型,因此这也有效:

from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient

agent = Agent[OpenAIReasoningChatOptions](
    chat_client=OpenAIChatClient(),
    default_options={
        "model_id": "o3",
        "max_tokens": 100,
        "allow_multiple_tool_calls": True,
        "reasoning_effort": "medium",
    },
)

6. 更新自定义聊天客户端实现

如果通过扩展 BaseChatClient实现了自定义聊天客户端,请更新内部方法:

Before:

from agent_framework import BaseChatClient, Message, ChatOptions, ChatResponse

class MyCustomClient(BaseChatClient):
    async def _inner_get_response(
        self,
        *,
        messages: MutableSequence[Message],
        chat_options: ChatOptions,
        **kwargs: Any,
    ) -> ChatResponse:
        # Access options via class attributes
        model = chat_options.model_id
        temp = chat_options.temperature
        # ...

After:

from typing import Generic
from agent_framework import BaseChatClient, Message, ChatOptions, ChatResponse

# Define your provider's options TypedDict
class MyCustomChatOptions(ChatOptions, total=False):
    my_custom_param: str

# This requires the TypeVar from Python 3.13+ or from typing_extensions, so for Python 3.13+:
from typing import TypeVar

TOptions = TypeVar("TOptions", bound=TypedDict, default=MyCustomChatOptions, covariant=True)

class MyCustomClient(BaseChatClient[TOptions], Generic[TOptions]):
    async def _inner_get_response(
        self,
        *,
        messages: MutableSequence[Message],
        options: dict[str, Any],  # Note: parameter renamed and just a dict
        **kwargs: Any,
    ) -> ChatResponse:
        # Access options via dict access
        model = options.get("model_id")
        temp = options.get("temperature")
        # ...

常见迁移模式

模式 1:简单参数更新

# Before - keyword arguments
await client.get_response("Hello", temperature=0.7)

# After - options dict
await client.get_response("Hello", options={"temperature": 0.7})

模式 2:多个参数

# Before - multiple keyword arguments
await client.get_response(
    "Hello",
    model_id="gpt-4",
    temperature=0.7,
    max_tokens=1000,
)

# After - all in options dict
await client.get_response(
    "Hello",
    options={
        "model_id": "gpt-4",
        "temperature": 0.7,
        "max_tokens": 1000,
    },
)

模式 3:使用工具聊天客户端

对于聊天客户端,tools 现在被放入配置字典中。

# Before - tools as keyword argument on chat client
await client.get_response(
    "What's the weather?",
    model_id="gpt-4",
    tools=[my_function],
    tool_choice="auto",
)

# After - tools in options dict for chat clients
await client.get_response(
    "What's the weather?",
    options={
        "model_id": "gpt-4",
        "tools": [my_function],
        "tool_choice": "auto",
    },
)

模式 4:具有工具和说明的代理

对于代理创建,toolsinstructions 可以保留为关键字参数。 对于 run(),仅 tools 可用:

# Before
agent = Agent(
    chat_client=client,
    name="assistant",
    tools=[my_function],
    instructions="You are helpful.",
    model_id="gpt-4",
)

# After - tools and instructions stay as keyword args on creation
agent = Agent(
    chat_client=client,
    name="assistant",
    tools=[my_function],  # Still a keyword argument!
    instructions="You are helpful.",  # Still a keyword argument!
    default_options={"model_id": "gpt-4"},
)

# For run(), only tools is available as keyword argument
response = await agent.run(
    "Hello!",
    tools=[another_function],  # Can override tools
    options={"max_tokens": 100},
)
# Before - using additional_properties
await client.get_response(
    "Solve this problem",
    model_id="o3",
    additional_properties={"reasoning_effort": "high"},
)

# After - directly in options
await client.get_response(
    "Solve this problem",
    options={
        "model_id": "o3",
        "reasoning_effort": "high",
    },
)

模式 5:供应商特定参数

# Define reusable options
my_options: OpenAIChatOptions = {
    "model_id": "gpt-4",
    "temperature": 0.7,
}

# Use with different messages
await client.get_response("Hello", options=my_options)
await client.get_response("Goodbye", options=my_options)

# Extend options using dict merge
extended_options = {**my_options, "max_tokens": 500}

重大更改摘要

方面 之前 之后
聊天客户端选项 单个关键字参数 (temperature=0.7 options 字典 (options={"temperature": 0.7}
聊天客户端工具 tools=[...] 关键字参数 options={"tools": [...]}
代理创建 toolsinstructions 关键字参数 仍然关键字参数 (未更改)
代理 run()tools 关键字参数 仍然关键字参数 (未更改)
代理 run()instructions 关键字参数 移动到 options={"instructions": ...}
提供程序特定的选项 additional_properties={...} 直接包含在字典options
代理默认选项 构造函数上的关键字参数 default_options={...}
代理运行选项 run() 上使用关键字参数 options={...} 参数
客户端正在输入 OpenAIChatClient() OpenAIChatClient[CustomOptions]()(可选)
代理正在输入 Agent(...) Agent[CustomOptions](...)(可选)

测试迁移

ChatClient 更新

  1. 查找所有对get_response()get_streaming_response()的调用,这些调用使用的关键字参数包括model_id=temperature=tools=等。
  2. 将所有关键字参数移动到字典中options={...}
  3. 将任何additional_properties值直接移动到options字典中

代理更新

  1. 查找使用关键字参数的所有 Agent 构造函数和 run() 调用
  2. 将构造函数上的关键字参数移动到 default_options={...}
  3. 将关键字参数 run() 移动到 options={...}
  4. 异常toolsinstructions可以保留为关键字参数Agent.__init__()as_agent()
  5. 异常tools 可以保留为关键字参数 run()

自定义聊天客户端更新

  1. 更新_inner_get_response()_inner_get_streaming_response()的方法签名,将chat_options: ChatOptions参数更改为options: dict[str, Any]
  2. 将属性访问(例如,chat_options.model_id)更新为字典访问(例如,options.get("model_id")
  3. (可选) 如果使用非标准参数:定义自定义 TypedDict
  4. 将泛型类型参数添加到客户端类

适用于所有人

  1. 运行类型检查器:使用 mypypyright 捕获类型错误
  2. 测试端到端:运行应用程序以验证功能

IDE 支持

基于 TypedDict 的新系统提供出色的 IDE 支持:

  • 自动完成:获取所有可用选项的建议
  • 类型检查:在开发阶段识别无效的选项键
  • 文档:将鼠标悬停在键上以查看说明
  • 提供程序特定:每个提供程序的选项仅显示相关参数

后续步骤

若要了解使用 OpenAI 推理模型和聊天完成 API 的类型化字典的运作方式,请浏览 此示例

完成迁移后:

  1. 浏览 API 文档中提供程序特有的选项
  2. 查看更新的 示例
  3. 了解如何 创建自定义聊天客户端

有关其他帮助,请参阅 Agent Framework 文档 或联系社区。