Condividi tramite


Esplorazione del kernel semantico ChatCompletionAgent

Suggerimento

La documentazione dettagliata dell'API relativa a questa discussione è disponibile all'indirizzo:

Suggerimento

La documentazione dettagliata dell'API relativa a questa discussione è disponibile all'indirizzo:

Suggerimento

La documentazione dettagliata dell'API relativa a questa discussione è disponibile all'indirizzo:

Completamento della chat nel kernel semantico

Il completamento della chat è fondamentalmente un protocollo per un'interazione basata su chat con un modello di intelligenza artificiale in cui la cronologia delle chat viene mantenuta e presentata al modello con ogni richiesta. Il Semantic Kernel offre un framework unificato per integrare le funzionalità di completamento della chat di vari modelli di intelligenza artificiale.

Un ChatCompletionAgent può sfruttare uno di questi servizi di intelligenza artificiale per generare risposte, sia indirizzate a un utente che a un altro agente.

Preparazione dell'ambiente di sviluppo

Per procedere con lo sviluppo di un ChatCompletionAgent, è necessario configurare l'ambiente di sviluppo con i pacchetti appropriati.

Aggiungere il pacchetto Microsoft.SemanticKernel.Agents.Core al progetto:

dotnet add package Microsoft.SemanticKernel.Agents.Core --prerelease

Installare il pacchetto semantic-kernel:

pip install semantic-kernel

Importante

A seconda del servizio di intelligenza artificiale usato come parte di ChatCompletionAgent, potrebbe essere necessario installare pacchetti aggiuntivi. Verificare la presenza degli elementi aggiuntivi necessari nella pagina seguente

<dependency>
    <groupId>com.microsoft.semantic-kernel</groupId>
    <artifactId>semantickernel-agents-core</artifactId>
    <version>[LATEST]</version>
</dependency>

Creazione di un ChatCompletionAgent

Un ChatCompletionAgent è fondamentalmente basato su servizi di intelligenza artificiale . Di conseguenza, la creazione di un ChatCompletionAgent inizia con l'inizializzazione di un'istanza di Kernel che contiene uno o più servizi di completamento chat, seguita dall'instanziazione dell'agente con un riferimento a quell'istanza di Kernel.

// Initialize a Kernel with a chat-completion service
IKernelBuilder builder = Kernel.CreateBuilder();

builder.AddAzureOpenAIChatCompletion(/*<...configuration parameters>*/);

Kernel kernel = builder.Build();

// Create the agent
ChatCompletionAgent agent =
    new()
    {
        Name = "SummarizationAgent",
        Instructions = "Summarize user input",
        Kernel = kernel
    };

Esistono due modi per creare un ChatCompletionAgent:

1. Fornendo direttamente il servizio di completamento della chat

from semantic_kernel.agents import ChatCompletionAgent

# Create the agent by directly providing the chat completion service
agent = ChatCompletionAgent(
    service=AzureChatCompletion(),  # your chat completion service instance
    name="<agent name>",
    instructions="<agent instructions>",
)

2. Creando prima un kernel, aggiungendo il servizio, quindi fornendo il kernel

# Define the kernel
kernel = Kernel()

# Add the chat completion service to the kernel
kernel.add_service(AzureChatCompletion())

# Create the agent using the kernel
agent = ChatCompletionAgent(
  kernel=kernel, 
  name="<agent name>", 
  instructions="<agent instructions>",
)

Il primo metodo è utile quando si dispone già di un servizio di completamento della chat pronto. Il secondo metodo è utile quando è necessario un kernel che gestisce più servizi o funzionalità aggiuntive.

// Initialize a Kernel with a chat-completion service
var chatCompletion = OpenAIChatCompletion.builder()
        .withOpenAIAsyncClient(client) // OpenAIAsyncClient with configuration parameters
        .withModelId(MODEL_ID)
        .build();

var kernel = Kernel.builder()
        .withAIService(ChatCompletionService.class, chatCompletion)
        .build();

// Create the agent
var agent = ChatCompletionAgent.builder()
        .withKernel(kernel)
        .build();

Selezione del servizio di intelligenza artificiale

Non è diverso dall'uso diretto dei servizi di intelligenza artificiale del kernel semantico; un ChatCompletionAgent supporta la specifica di un selettore di servizio. Un selettore di servizi identifica quale servizio di intelligenza artificiale indirizzare quando il Kernel contiene più di uno.

Annotazioni

Se sono presenti più servizi di intelligenza artificiale e non viene fornito alcun selettore di servizio, viene applicata la stessa logica predefinita per l'agente che si trova quando si usa un servizio di intelligenza artificiale all'esterno di Agent Framework

IKernelBuilder builder = Kernel.CreateBuilder();

// Initialize multiple chat-completion services.
builder.AddAzureOpenAIChatCompletion(/*<...service configuration>*/, serviceId: "service-1");
builder.AddAzureOpenAIChatCompletion(/*<...service configuration>*/, serviceId: "service-2");

Kernel kernel = builder.Build();

ChatCompletionAgent agent =
    new()
    {
        Name = "<agent name>",
        Instructions = "<agent instructions>",
        Kernel = kernel,
        Arguments = // Specify the service-identifier via the KernelArguments
          new KernelArguments(
            new OpenAIPromptExecutionSettings() 
            { 
              ServiceId = "service-2" // The target service-identifier.
            })
    };
from semantic_kernel.connectors.ai.open_ai import (
    AzureChatCompletion,
    AzureChatPromptExecutionSettings,
)

# Define the Kernel
kernel = Kernel()

# Add the AzureChatCompletion AI Service to the Kernel
kernel.add_service(AzureChatCompletion(service_id="service1"))
kernel.add_service(AzureChatCompletion(service_id="service2"))

settings = AzureChatPromptExecutionSettings(service_id="service2")

# Create the agent
agent = ChatCompletionAgent(
  kernel=kernel, 
  name="<agent name>", 
  instructions="<agent instructions>",
  arguments=KernelArguments(settings=settings)
)

Funzionalità attualmente non disponibile in Java.

Conversare con ChatCompletionAgent

Conversare con il tuo ChatCompletionAgent si basa su un'istanza di ChatHistory, non è diverso dall'interagire con un servizio di intelligenza artificiale per il completamento delle chat.

È sufficiente richiamare l'agente con il messaggio dell'utente.

// Define agent
ChatCompletionAgent agent = ...;

// Generate the agent response(s)
await foreach (ChatMessageContent response in agent.InvokeAsync(new ChatMessageContent(AuthorRole.User, "<user input>")))
{
  // Process agent response(s)...
}

È anche possibile usare un AgentThread per avere conversazione con l'agente. In questo caso viene usato un oggetto ChatHistoryAgentThread.

Il ChatHistoryAgentThread può anche accettare un oggetto facoltativo ChatHistory come input, attraverso il suo costruttore, nel caso si riprenda una conversazione precedente. (non visualizzato)

// Define agent
ChatCompletionAgent agent = ...;

AgentThread thread = new ChatHistoryAgentThread();

// Generate the agent response(s)
await foreach (ChatMessageContent response in agent.InvokeAsync(new ChatMessageContent(AuthorRole.User, "<user input>"), thread))
{
  // Process agent response(s)...
}

Esistono diversi modi per conversare con un ChatCompletionAgent.

Il modo più semplice consiste nel chiamare e attendere get_response:

# Define agent
agent = ChatCompletionAgent(...)

# Generate the agent response
response = await agent.get_response(messages="user input")
# response is an `AgentResponseItem[ChatMessageContent]` object

Se si vuole che l'agente mantenga la cronologia delle conversazioni tra le chiamate, è possibile passarla come ChatHistoryAgentThread segue:


# Define agent
agent = ChatCompletionAgent(...)

# Generate the agent response(s)
response = await agent.get_response(messages="user input")

# Generate another response, continuing the conversation thread from the first response.
response2 = await agent.get_response(messages="user input", thread=response.thread)
# process agent response(s)

La chiamata al metodo invoke restituisce un AsyncIterable di AgentResponseItem[ChatMessageContent].

# Define agent
agent = ChatCompletionAgent(...)

# Define the thread
thread = ChatHistoryAgentThread()

# Generate the agent response(s)
async for response in agent.invoke(messages="user input", thread=thread):
  # process agent response(s)

Il ChatCompletionAgent supporta anche lo streaming in cui il metodo invoke_stream restituisce un AsyncIterable di StreamingChatMessageContent:

# Define agent
agent = ChatCompletionAgent(...)

# Define the thread
thread = ChatHistoryAgentThread()

# Generate the agent response(s)
async for response in agent.invoke_stream(messages="user input", thread=thread):
  # process agent response(s)
ChatCompletionAgent agent = ...;

// Generate the agent response(s)
agent.invokeAsync(new ChatMessageContent<>(AuthorRole.USER, "<user input>")).block();

È anche possibile usare un AgentThread per avere conversazione con l'agente. In questo caso viene usato un oggetto ChatHistoryAgentThread.

Il ChatHistoryAgentThread può anche accettare un ChatHistory oggetto come input, tramite il relativo costruttore, se si riprende una conversazione precedente. (non visualizzato)

// Define agent
ChatCompletionAgent agent = ...;

AgentThread thread = new ChatHistoryAgentThread();

// Generate the agent response(s)
agent.invokeAsync(new ChatMessageContent<>(AuthorRole.USER, "<user input>"), thread).block();

Gestione dei messaggi intermedi con un ChatCompletionAgent

Il kernel ChatCompletionAgent semantico è progettato per richiamare un agente che soddisfa le query o le domande degli utenti. Durante la chiamata, l'agente può eseguire strumenti per derivare la risposta finale. Per accedere ai messaggi intermedi generati durante questo processo, i chiamanti possono fornire una funzione di callback che gestisce le istanze di FunctionCallContent o FunctionResultContent.

La documentazione per il callback di ChatCompletionAgent sarà presto disponibile.

La configurazione del on_intermediate_message callback all'interno agent.invoke(...) o agent.invoke_stream(...) consente al chiamante di ricevere messaggi intermedi generati durante il processo di simulazione della risposta finale dell'agente.

import asyncio
from typing import Annotated

from semantic_kernel.agents.chat_completion.chat_completion_agent import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import AzureChatCompletion
from semantic_kernel.contents import FunctionCallContent, FunctionResultContent
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.functions import kernel_function


# Define a sample plugin for the sample
class MenuPlugin:
    """A sample Menu Plugin used for the concept sample."""

    @kernel_function(description="Provides a list of specials from the menu.")
    def get_specials(self) -> Annotated[str, "Returns the specials from the menu."]:
        return """
        Special Soup: Clam Chowder
        Special Salad: Cobb Salad
        Special Drink: Chai Tea
        """

    @kernel_function(description="Provides the price of the requested menu item.")
    def get_item_price(
        self, menu_item: Annotated[str, "The name of the menu item."]
    ) -> Annotated[str, "Returns the price of the menu item."]:
        return "$9.99"


# This callback function will be called for each intermediate message
# Which will allow one to handle FunctionCallContent and FunctionResultContent
# If the callback is not provided, the agent will return the final response
# with no intermediate tool call steps.
async def handle_intermediate_steps(message: ChatMessageContent) -> None:
    for item in message.items or []:
        if isinstance(item, FunctionCallContent):
            print(f"Function Call:> {item.name} with arguments: {item.arguments}")
        elif isinstance(item, FunctionResultContent):
            print(f"Function Result:> {item.result} for function: {item.name}")
        else:
            print(f"{message.role}: {message.content}")


async def main() -> None:
    agent = ChatCompletionAgent(
        service=AzureChatCompletion(),
        name="Assistant",
        instructions="Answer questions about the menu.",
        plugins=[MenuPlugin()],
    )

    # Create a thread for the agent
    # If no thread is provided, a new thread will be
    # created and returned with the initial response
    thread: ChatHistoryAgentThread = None

    user_inputs = [
        "Hello",
        "What is the special soup?",
        "How much does that cost?",
        "Thank you",
    ]

    for user_input in user_inputs:
        print(f"# User: '{user_input}'")
        async for response in agent.invoke(
            messages=user_input,
            thread=thread,
            on_intermediate_message=handle_intermediate_steps,
        ):
            print(f"# {response.role}: {response}")
            thread = response.thread


if __name__ == "__main__":
    asyncio.run(main())

Di seguito viene illustrato l'output di esempio del processo di chiamata dell'agente:

User: 'Hello'
AuthorRole.ASSISTANT: Hi there! How can I assist you today?
User: 'What is the special soup?'
Function Call:> MenuPlugin-get_specials with arguments: {}
Function Result:> 
        Special Soup: Clam Chowder
        Special Salad: Cobb Salad
        Special Drink: Chai Tea
        for function: MenuPlugin-get_specials
AuthorRole.ASSISTANT: The special soup today is Clam Chowder. Would you like to know anything else from the menu?
User: 'How much does that cost?'
Function Call:> MenuPlugin-get_item_price with arguments: {"menu_item":"Clam Chowder"}
Function Result:> $9.99 for function: MenuPlugin-get_item_price
AuthorRole.ASSISTANT: The Clam Chowder costs $9.99. Would you like to know more about the menu or anything else?
User: 'Thank you'
AuthorRole.ASSISTANT: You're welcome! If you have any more questions, feel free to ask. Enjoy your day!

Funzionalità attualmente non disponibile in Java.

Specifica dichiarativa

La documentazione sull'uso delle specifiche dichiarative sarà presto disponibile.

Importante

Questa funzionalità si trova nella fase sperimentale. Le funzionalità in questa fase sono in fase di sviluppo e soggette a cambiamenti prima di passare alla fase di anteprima o alla versione candidata al rilascio.

L'ChatCompletionAgent può essere istanziato direttamente da una specifica dichiarativa YAML. Questo approccio consente di definire le proprietà principali dell'agente, le istruzioni e le funzioni disponibili (plug-in) in modo strutturato e portabile. Usando YAML è possibile descrivere il nome, la descrizione, la richiesta di istruzioni, il set di strumenti e i parametri del modello dell'agente in un singolo documento, rendendo facilmente controllabile e riproducibile la configurazione dell'agente.

Annotazioni

Tutti gli strumenti o le funzioni specificati nel file YAML dichiarativo devono esistere già nell'istanza del kernel al momento della creazione dell'agente. Il caricatore dell'agente non crea nuove funzioni dalla specifica; cerca invece i plug-in e le funzioni a cui si fa riferimento in base ai relativi identificatori nel kernel. Se un plug-in o una funzione richiesta non è presente nel kernel, durante la costruzione dell'agente verrà generato un errore.

Esempio: Creazione di un oggetto ChatCompletionAgent da una specifica YAML

import asyncio
from typing import Annotated

from semantic_kernel import Kernel
from semantic_kernel.agents import AgentRegistry, ChatHistoryAgentThread
from semantic_kernel.agents.chat_completion.chat_completion_agent import ChatCompletionAgent
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.functions import kernel_function

# Define a plugin with kernel functions
class MenuPlugin:
    @kernel_function(description="Provides a list of specials from the menu.")
    def get_specials(self) -> Annotated[str, "Returns the specials from the menu."]:
        return """
        Special Soup: Clam Chowder
        Special Salad: Cobb Salad
        Special Drink: Chai Tea
        """

    @kernel_function(description="Provides the price of the requested menu item.")
    def get_item_price(
        self, menu_item: Annotated[str, "The name of the menu item."]
    ) -> Annotated[str, "Returns the price of the menu item."]:
        return "$9.99"

# YAML spec for the agent
AGENT_YAML = """
type: chat_completion_agent
name: Assistant
description: A helpful assistant.
instructions: Answer the user's questions using the menu functions.
tools:
  - id: MenuPlugin.get_specials
    type: function
  - id: MenuPlugin.get_item_price
    type: function
model:
  options:
    temperature: 0.7
"""

USER_INPUTS = [
    "Hello",
    "What is the special soup?",
    "What does that cost?",
    "Thank you",
]

async def main():
    kernel = Kernel()
    kernel.add_plugin(MenuPlugin(), plugin_name="MenuPlugin")

    agent: ChatCompletionAgent = await AgentRegistry.create_from_yaml(
        AGENT_YAML, kernel=kernel, service=OpenAIChatCompletion()
    )

    thread: ChatHistoryAgentThread | None = None

    for user_input in USER_INPUTS:
        print(f"# User: {user_input}")
        response = await agent.get_response(user_input, thread=thread)
        print(f"# {response.name}: {response}")
        thread = response.thread

    await thread.delete() if thread else None

if __name__ == "__main__":
    asyncio.run(main())

Questa funzionalità non è disponibile.

Guida pratica

Per un esempio completo per un ChatCompletionAgent, consultare:

Passaggi successivi