Compartir a través de


Guía de actualización: Opciones de chat como TypedDict con genéricos

Esta guía le ayuda a actualizar el código de Python al nuevo sistema basado en Options TypedDict introducido en la versión 1.0.0b260114 de Microsoft Agent Framework. Se trata de un cambio rompedor que proporciona una mejorada seguridad de tipos, autocompletar del IDE y extensibilidad en tiempo de ejecución.

Información general sobre los cambios

En esta versión se presenta una refactorización importante de cómo se pasan las opciones a los clientes de chat y a los agentes de chat.

Cómo funcionó antes

Anteriormente, las opciones se pasaron como argumentos de palabra clave directos en métodos como get_response(), get_streaming_response(), run(), y constructores de agentes:

# 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"},
)

Cómo funciona ahora

La mayoría de las opciones ahora se pasan a través de un único options parámetro tipado como un diccionario.

# 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
    },
)

Nota: Para Agentes, instructions y tools permanecen disponibles como argumentos por palabra clave en ChatAgent.__init__() y client.as_agent(). Para agent.run(), solo tools está disponible como argumento de palabra clave:

# Agent creation accepts both tools and instructions as keyword arguments
agent = ChatAgent(
    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
)

Cambios principales

  1. Parámetro de opciones consolidadas: la mayoría de los argumentos de palabra clave (model_id, temperature, etc.) ahora se pasan a través de un único options dict
  2. Excepción para la creación del agente: instructions y tools permanecen disponibles como argumentos de palabra clave directa en ChatAgent.__init__() y create_agent()
  3. Excepción para la ejecución del agente: tools permanece disponible como argumento de palabra clave directa en agent.run()
  4. Opciones basadas en TypedDict: las opciones se definen como TypedDict clases para la seguridad de tipos
  5. Compatibilidad con tipos genéricos: los clientes de chat y los agentes admiten genéricos para opciones específicas del proveedor, para permitir sobrecargas en tiempo de ejecución
  6. Opciones específicas del proveedor: cada proveedor tiene su propio TypedDict predeterminado (por ejemplo, OpenAIChatOptions, OllamaChatOptions)
  7. No más additional_properties: los parámetros específicos del proveedor ahora son campos de tipo de primera categoría

Ventajas

  • Seguridad de Tipos: Autocompletado de IDE y verificación de tipos para todas las opciones
  • Flexibilidad del proveedor: compatibilidad con parámetros específicos del proveedor el día uno
  • Código más limpio: paso coherente de parámetros basados en dict
  • Extensión más sencilla: cree opciones personalizadas para casos de uso especializados (por ejemplo, modelos de razonamiento u otros back-end de API).

Guía de migración

1. Convertir argumentos de palabra clave en Opciones Dict

El cambio más común consiste en convertir los argumentos individuales de palabra clave en el diccionario options.

Antes (argumentos de palabra clave):

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="")

Después (opciones del diccionario):

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="")

Si pasa opciones que no son adecuadas para el cliente, obtendrá un error de tipo en el IDE.

2. Usar opciones específicas del proveedor (no más propiedades_adicionales)

Anteriormente, para pasar parámetros específicos del proveedor que no formaban parte del conjunto base de argumentos de palabra clave, tenía que usar el additional_properties parámetro :

Antes (mediante 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
    },
)

Después (opciones directas con 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
    },
)

Después (subclasing personalizado para nuevos parámetros):

O bien, si es un parámetro que aún no forma parte de Agent Framework (porque es nuevo o porque es personalizado para un back-end compatible con OpenAI), ahora puede subclase las opciones y usar la compatibilidad genérica:

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!
    },
)

La ventaja principal es que la mayoría de los parámetros específicos del proveedor ahora forman parte del diccionario de opciones con tipo, lo que le ofrece:

  • La función de autocompletar del IDE para todas las opciones disponibles
  • Comprobación de tipos para detectar claves o valores no válidos
  • No se necesitan propiedades_adicionales para parámetros de proveedor conocidos
  • Extensión sencilla para parámetros personalizados o nuevos

3. Actualizar la configuración de ChatAgent

La inicialización y ejecución de ChatAgent siguen el mismo patrón:

Antes (argumentos de palabra clave en constructor y ejecución):

from agent_framework import ChatAgent
from agent_framework.openai import OpenAIChatClient

client = OpenAIChatClient()

# Default options as keyword arguments on constructor
agent = ChatAgent(
    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 ChatAgent
from agent_framework.openai import OpenAIChatClient, OpenAIChatOptions

client = OpenAIChatClient()
agent = ChatAgent(
    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. Opciones específicas del proveedor

Cada proveedor ahora tiene su propio TypedDict para las opciones, estos están habilitados de forma predeterminada. Esto le permite usar parámetros específicos del proveedor con seguridad de tipo completo:

Ejemplo de 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",
    },
)

Pero también puede hacer que sea explícito:

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. Crear opciones personalizadas para modelos especializados

Una característica eficaz del nuevo sistema es la capacidad de crear opciones de TypedDict personalizadas para modelos especializados. Esto es especialmente útil para los modelos que tienen parámetros únicos, como los modelos de razonamiento con 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
    },
)

6. Chat agents with Options (Agentes de chat con opciones)

La configuración genérica también se ha ampliado a los agentes de chat:

from agent_framework import ChatAgent
from agent_framework.openai import OpenAIChatClient

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

y puede especificar el genérico en el cliente y en el agente, por lo que esto también es válido:

from agent_framework import ChatAgent
from agent_framework.openai import OpenAIChatClient

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

6. Actualizar implementaciones de cliente de chat personalizadas

Si ha implementado un cliente de chat personalizado mediante la extensión de BaseChatClient, actualice los métodos internos:

Before:

from agent_framework import BaseChatClient, ChatMessage, ChatOptions, ChatResponse

class MyCustomClient(BaseChatClient):
    async def _inner_get_response(
        self,
        *,
        messages: MutableSequence[ChatMessage],
        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, ChatMessage, 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[ChatMessage],
        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")
        # ...

Patrones comunes de migración

Patrón 1: Actualización de parámetro simple

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

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

Patrón 2: Varios parámetros

# 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,
    },
)

Patrón 3: Cliente de chat con herramientas

En el caso de los clientes de chat, tools ahora se ubica en el diccionario de opciones.

# 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",
    },
)

Patrón 4: Agente con herramientas e instrucciones

Para la creación del agente, tools y instructions pueden permanecer como argumentos de palabra clave. Para run(), solo tools está disponible:

# Before
agent = ChatAgent(
    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 = ChatAgent(
    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",
    },
)

Patrón 5: Parámetros Específicos del Proveedor

# 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}

Resumen de cambios importantes

Aspecto Antes Después
Opciones de cliente de chat Argumentos de palabra clave individuales (temperature=0.7) Diccionario único options (options={"temperature": 0.7})
Herramientas de cliente de chat tools=[...] argumento de palabra clave options={"tools": [...]}
Creación tools de agente y instructions Argumentos de palabra clave Still keyword arguments (sin cambios)
Agente run()tools Argumento de palabra clave Argumento de palabra clave Still (sin cambios)
Agente run()instructions Argumento de palabra clave Movido a options={"instructions": ...}
Opciones específicas del proveedor additional_properties={...} Incluido directamente en options dict
Opciones predeterminadas del agente Argumentos de palabra clave en el constructor default_options={...}
Opciones de ejecución del agente Argumentos de palabra clave en run() Parámetro options={...}
Escritura de cliente OpenAIChatClient() OpenAIChatClient[CustomOptions]() (opcional)
Agente escribiendo ChatAgent(...) ChatAgent[CustomOptions](...) (opcional)

Prueba de tu migración

Actualizaciones de ChatClient

  1. Busque todas las llamadas a get_response() y get_streaming_response() que usen argumentos de palabra clave como model_id=, temperature=, tools=, , etc.
  2. Mover todos los argumentos de palabra clave a un options={...} diccionario
  3. Mover los additional_properties valores directamente al options dict

Actualizaciones de ChatAgent

  1. Búsqueda de todos los ChatAgent constructores y run() llamadas que usan argumentos de palabra clave
  2. Mover argumentos de palabra clave en constructores a default_options={...}
  3. Mover los argumentos de palabras clave de run() a options={...}
  4. Excepción: tools y instructions pueden permanecer como argumentos de palabra clave en ChatAgent.__init__() y create_agent()
  5. Excepción: tools puede permanecer como argumento de palabra clave en run()

Actualizaciones de cliente de chat personalizadas

  1. Actualice las firmas de método _inner_get_response() y _inner_get_streaming_response(): cambie el parámetro chat_options: ChatOptions a options: dict[str, Any].
  2. Actualice el acceso de atributos (por ejemplo, chat_options.model_id) a acceso de diccionario (por ejemplo, options.get("model_id"))
  3. (Opcional) Si usa parámetros no estándar: defina un TypedDict personalizado.
  4. Adición de parámetros de tipo genérico a la clase de cliente

Para todos

  1. Ejecutar comprobador de tipos: use mypy o pyright para detectar errores de tipo.
  2. Prueba de un extremo a otro: ejecute la aplicación para comprobar la funcionalidad.

Compatibilidad con IDE

El nuevo sistema basado en TypedDict proporciona una excelente compatibilidad con ide:

  • Autocompletar: obtener sugerencias para todas las opciones disponibles
  • Comprobación de tipos: detectar teclas de opción no válidas durante el desarrollo
  • Documentación: Mantenga el puntero sobre las claves para ver descripciones
  • Específico del proveedor: las opciones de cada proveedor muestran solo parámetros relevantes.

Pasos siguientes

Para ver los diccionarios tipados en acción en el caso de utilizar los modelos de razonamiento de OpenAI con la API de completado de chat, puede explorar este ejemplo.

Después de completar la migración:

  1. Exploración de opciones específicas del proveedor en la documentación de api
  2. Revisión de ejemplos actualizados
  3. Más información sobre la creación de clientes de chat personalizados

Para obtener ayuda adicional, consulte la documentación de Agent Framework o póngase en contacto con la comunidad.