Condividi tramite


Guida all'aggiornamento: Opzioni di chat come TypedDict con generics

Questa guida consente di aggiornare il codice Python al nuovo sistema basato su Options TypedDict introdotto nella versione 1.0.0b260114 di Microsoft Agent Framework. Si tratta di una modifica di rilievo che offre una maggiore sicurezza dei tipi, completamento automatico dell'IDE ed estendibilità del runtime.

Panoramica delle modifiche

Questa versione introduce un importante refactoring del modo in cui le opzioni vengono passate ai client di chat e agli agenti di chat.

Come funzionava prima

In precedenza, le opzioni venivano passate come argomenti passati direttamente per parola chiave sui metodi come get_response(), get_streaming_response(), run() e i costruttori di agenti.

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

Come funziona ora

La maggior parte delle opzioni viene ora passata tramite un parametro singolo options che funge da dizionario tipizzato.

# 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: Per Agents, i instructions parametri e tools rimangono disponibili come argomenti di parola chiave diretta in ChatAgent.__init__() e client.as_agent(). Per agent.run(), è disponibile solo tools come argomento parola chiave:

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

Modifiche principali:

  1. Parametro opzioni consolidate: la maggior parte degli argomenti delle parole chiave (model_id, temperaturee così via) viene ora passata tramite un singolo options dict
  2. Eccezione per la creazione dell'agente: instructions e tools rimangono disponibili come argomenti di parola chiave diretta in ChatAgent.__init__() e create_agent()
  3. Eccezione per l'esecuzione dell'agente: tools rimane disponibile come argomento parola chiave diretta in agent.run()
  4. Opzioni basate su TypedDict: le opzioni sono definite come TypedDict classi per la sicurezza dei tipi
  5. Supporto dei tipi generici: i client chat e gli agenti supportano i generici per opzioni specifiche del provider, per consentire sovraccarichi durante l'esecuzione.
  6. Opzioni specifiche del provider: ogni provider ha il proprio typedDict predefinito (ad esempio, OpenAIChatOptions, OllamaChatOptions)
  7. Nessun altro additional_properties: i parametri specifici del provider sono ora campi tipizzati di prima classe

Vantaggi

  • Sicurezza dei tipi: completamento automatico dell'IDE e controllo del tipo per tutte le opzioni
  • Flessibilità del provider: supporto per parametri specifici del provider al giorno 1
  • Codice più pulito: passaggio di parametri coerenti basati su dict
  • Estensione più semplice: creare opzioni personalizzate per casi d'uso specializzati (ad esempio, modelli di ragionamento o altri back-end API)

Guida alla migrazione

1. Convertire argomenti parola chiave in Opzioni Dict

La modifica più comune consiste nella conversione di singoli argomenti di parole chiave nel options dizionario.

Prima (argomenti parola chiave):

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

Dopo (opzioni dict):

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

Se si passano opzioni non appropriate per tale client, verrà visualizzato un errore di tipo nell'IDE.

2. Uso delle opzioni specifiche del provider (non più additional_properties)

In precedenza, per passare parametri specifici del provider che non facevano parte del set di argomenti di base di parole chiave, era necessario usare il additional_properties parametro :

Prima dell'utilizzo di 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
    },
)

After (opzioni dirette 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
    },
)

Dopo (sottoclasse personalizzata per i nuovi parametri):

In alternativa, se si tratta di un parametro che non fa ancora parte di Agent Framework (perché è nuovo o perché è personalizzato per un back-end compatibile con OpenAI), è ora possibile sottoclassere le opzioni e usare il supporto generico:

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

Il vantaggio principale è che la maggior parte dei parametri specifici del provider fa ora parte del dizionario delle opzioni tipizzato, offrendo:

  • Completamento automatico dell'IDE per tutte le opzioni disponibili
  • Controllo dei tipi per rilevare chiavi o valori non validi
  • Non è necessario additional_properties per i parametri del provider noti
  • Estensione semplice per parametri personalizzati o nuovi

3. Aggiornare la configurazione di ChatAgent

L'inizializzazione e l'esecuzione di ChatAgent seguono lo stesso modello:

Prima (argomenti con parole chiave nel costruttore ed esecuzione della funzione):

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

Opzioni specifiche del Provider

Ogni provider dispone ora del proprio TypedDict per le opzioni, che sono abilitate per impostazione predefinita. In questo modo è possibile usare parametri specifici del provider con sicurezza dei tipi completa:

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

Ma è anche possibile renderlo esplicito:

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. Creazione di opzioni personalizzate per modelli specializzati

Una potente funzionalità del nuovo sistema è la possibilità di creare opzioni TypedDict personalizzate per modelli specializzati. Ciò è particolarmente utile per i modelli con parametri univoci, ad esempio i modelli di ragionamento 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
    },
)

Agenti Chat con Opzioni

La configurazione generica è stata estesa anche agli agenti di 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",
    },
)

ed è possibile specificare il generico sia nel client che nell'agente, quindi questo è valido anche:

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. Aggiornare le implementazioni client di chat personalizzate

Se è stato implementato un client di chat personalizzato estendendo BaseChatClient, aggiornare i metodi interni:

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")
        # ...

Modelli di migrazione comuni

Modello 1: aggiornamento semplice dei parametri

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

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

Modello 2: più parametri

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

Modello 3: Client di chat con strumenti

Per i client di chat, tools ora va nel dizionario delle opzioni:

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

Modello 4: Agente con strumenti e istruzioni

Per la creazione dell'agente tools e instructions può rimanere come argomenti di parola chiave. Solo run() è disponibile per tools.

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

Modello 5: Parametri Specifici del Fornitore

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

Riepilogo delle modifiche di rilievo

Aspetto Prima di Dopo
Opzioni client chat Singoli argomenti di parole chiave (temperature=0.7) Dizionario singolo options (options={"temperature": 0.7})
Strumenti client di chat tools=[...] argomento parola chiave options={"tools": [...]}
Creazione dell'agente tools e instructions Argomenti di parola chiave Ancora argomenti di parole chiave (invariati )
Agente run()tools Argomento per parola chiave Still keyword argument (invariato)
Agente run()instructions Argomento per parola chiave Spostato in options={"instructions": ...}
Opzioni specifiche del provider additional_properties={...} Incluso direttamente in options dict
Opzioni predefinite dell'agente Argomenti di parola chiave nel costruttore default_options={...}
Opzioni di esecuzione dell'agente Argomenti di parola chiave in run() parametro options={...}
Digitazione del client OpenAIChatClient() OpenAIChatClient[CustomOptions]() (facoltativo)
Digitazione dell'agente ChatAgent(...) ChatAgent[CustomOptions](...) (facoltativo)

Test della migrazione

Aggiornamenti di ChatClient

  1. Trovare tutte le chiamate a get_response() e get_streaming_response() che usano argomenti di parole chiave come model_id=, temperature=, tools=e così via.
  2. Spostare tutti gli argomenti di parole chiave in un options={...} dizionario
  3. Spostare tutti i additional_properties valori direttamente nel options dict

Aggiornamenti di ChatAgent

  1. Trovare tutti i costruttori ChatAgent e chiamate run() che utilizzano argomenti con parole chiave
  2. Spostare argomenti con parola chiave nei costruttori a default_options={...}
  3. Spostare gli argomenti a parola chiave da run() a options={...}
  4. Eccezione: tools e instructions può rimanere come argomenti di parola chiave in ChatAgent.__init__() e create_agent()
  5. Eccezione: tools può rimanere come argomento di parola chiave in run()

Aggiornamenti di chat client personalizzati

  1. Aggiornare le firme del metodo _inner_get_response() e _inner_get_streaming_response(): modificare il parametro chat_options: ChatOptions a options: dict[str, Any]
  2. Aggiornare l'accesso agli attributi (ad esempio, chat_options.model_id) a utilizzare l'accesso tramite dizionario (ad esempio, options.get("model_id"))
  3. (Facoltativo) Se si usano parametri non standard: definire un typedDict personalizzato
  4. Aggiungere parametri di tipo generico alla classe client

Per tutti

  1. Esegui controllo tipi: usare mypy o pyright per intercettare gli errori di tipo
  2. Test end-to-end: eseguire l'applicazione per verificare la funzionalità

Supporto dell'IDE

Il nuovo sistema basato su TypedDict offre un eccellente supporto IDE:

  • Completamento automatico: ottenere suggerimenti per tutte le opzioni disponibili
  • Controllo dei tipi: intercettare chiavi di opzione non valide in fase di sviluppo
  • Documentazione: Passare il puntatore del mouse sulle chiavi per visualizzare le descrizioni
  • Specifica del provider: le opzioni di ogni provider mostrano solo i parametri pertinenti

Passaggi successivi

Per vedere i dizionari tipizzati in azione utilizzando i modelli di ragionamento di OpenAI con l'API di completamento chat, esplora questo esempio

Al termine della migrazione:

  1. Esplorare le opzioni specifiche del provider nella documentazione dell'API
  2. Esaminare gli esempi aggiornati
  3. Informazioni sulla creazione di client di chat personalizzati

Per altre informazioni, vedere la documentazione di Agent Framework o contattare la community.