Compartilhar via


Explorando o kernel semântico ChatCompletionAgent

Dica

A documentação detalhada da API relacionada a esta discussão está disponível em:

Dica

A documentação detalhada da API relacionada a esta discussão está disponível em:

Dica

A documentação detalhada da API relacionada a esta discussão está disponível em:

Conclusão de bate-papo no Kernel Semântico

A conclusão do chat é fundamentalmente um protocolo para uma interação baseada em chat com um modelo de IA em que o histórico de chat é mantido e apresentado ao modelo com cada solicitação. Os serviços de IA do Kernel Semântico oferecem uma estrutura unificada para integrar os recursos de preenchimento de chat de vários modelos de IA.

É ChatCompletionAgent possível aproveitar qualquer um desses serviços de IA para gerar respostas, sejam elas direcionadas a um usuário ou a outro agente.

Preparando seu ambiente de desenvolvimento

Para continuar desenvolvendo um ChatCompletionAgent, configure seu ambiente de desenvolvimento com os pacotes apropriados.

Adicione o pacote de Microsoft.SemanticKernel.Agents.Core ao seu projeto:

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

Instalar o pacote semantic-kernel:

pip install semantic-kernel

Importante

Dependendo de qual Serviço de IA você usa como parte do ChatCompletionAgent, talvez seja necessário instalar pacotes extras. Verifique se há o extra necessário na página a seguir

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

Criando um ChatCompletionAgent

Um ChatCompletionAgent é fundamentalmente baseado em uns serviços de IA. Para criar um ChatCompletionAgent, primeiro é necessário criar uma instância Kernel que contenha um ou mais serviços de conclusão de chat e, em seguida, o agente é instanciado com referência a essa instância 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
    };

Há duas maneiras de criar um ChatCompletionAgent:

1. Fornecendo o serviço de conclusão de chat diretamente

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. Criando primeiro um Kernel, adicionando o serviço a ele e fornecendo o 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>",
)

O primeiro método é útil quando você já tem um serviço de conclusão de chat pronto. O segundo método é benéfico quando você precisa de um kernel que gerencia vários serviços ou funcionalidades adicionais.

// 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();

Seleção de serviço de IA

Não é diferente usar diretamente os serviços de IA do Kernel Semântico; um ChatCompletionAgent dá suporte à especificação de um seletor de serviço. Um seletor de serviço identifica qual serviço de IA deve ser direcionado quando o Kernel contém mais de um.

Observação

Se vários serviços de IA estiverem presentes e nenhum seletor de serviço for fornecido, a mesma lógica padrão será aplicada ao agente que você encontrará ao usar um serviço de IA fora do 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)
)

Recurso atualmente indisponível em Java.

Conversando com ChatCompletionAgent

Conversar com seu ChatCompletionAgent é baseado em uma instância de ChatHistory, semelhante a interagir com um serviço de IA de Conclusão de Chat.

Você pode simplesmente invocar o agente com sua mensagem de usuário.

// 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)...
}

Você também pode usar um AgentThread para ter uma conversa com seu agente. Aqui estamos usando um ChatHistoryAgentThread.

O ChatHistoryAgentThread também pode aceitar um objeto ChatHistory opcional como entrada, por meio de seu construtor, para retomar uma conversa anterior. (não mostrado)

// 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)...
}

Há várias maneiras de conversar com o ChatCompletionAgent.

O mais fácil é chamar e aguardar 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 você quiser que o agente mantenha o histórico de conversas entre invocações, você poderá passá-lo da ChatHistoryAgentThread seguinte maneira:


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

Chamar o método invoke retorna um AsyncIterable de 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)

O ChatCompletionAgent também dá suporte ao streaming em que o método invoke_stream retorna uma AsyncIterable de 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();

Você também pode usar um AgentThread para ter uma conversa com seu agente. Aqui estamos usando um ChatHistoryAgentThread.

Ele ChatHistoryAgentThread também pode receber um ChatHistory objeto como entrada, através de seu construtor, caso esteja retomando uma conversa anterior. (não mostrado)

// Define agent
ChatCompletionAgent agent = ...;

AgentThread thread = new ChatHistoryAgentThread();

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

Manipulando mensagens intermediárias com um ChatCompletionAgent

O Kernel ChatCompletionAgent Semântico foi projetado para invocar um agente que atende a consultas ou perguntas do usuário. Durante a invocação, o agente pode executar ferramentas para derivar a resposta final. Para acessar mensagens intermediárias produzidas durante este processo, os chamadores podem fornecer uma função de callback que lida com instâncias de FunctionCallContent ou FunctionResultContent.

A documentação de callback para o ChatCompletionAgent estará disponível em breve.

Configurar o on_intermediate_message retorno de chamada dentro de agent.invoke(...) ou agent.invoke_stream(...) permite que o chamador receba mensagens intermediárias geradas durante o processo de formulação da resposta final do 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())

O seguinte demonstra a saída de exemplo do processo de invocação do 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!

Recurso atualmente indisponível em Java.

Especificação declarativa

A documentação sobre como usar especificações declarativas será fornecida em breve.

Importante

Esse recurso está em estágio experimental. Os recursos nesta fase estão em desenvolvimento e sujeitos a alterações antes de avançar para a versão preliminar ou para o estágio de candidato a lançamento.

O ChatCompletionAgent pode ser instanciado diretamente a partir de uma especificação declarativa YAML. Essa abordagem permite definir as principais propriedades, instruções e funções disponíveis do agente (plug-ins) de maneira estruturada e portátil. Usando YAML, você pode descrever o nome do agente, a descrição, o prompt de instrução, o conjunto de ferramentas e os parâmetros de modelo em um único documento, tornando a configuração do agente facilmente auditável e reproduzível.

Observação

Todas as ferramentas ou funções especificadas no YAML declarativo já devem existir na instância do Kernel no momento em que o agente é criado. O carregador de agente não cria novas funções a partir da especificação; Em vez disso, ele pesquisa os plug-ins referenciados e as funções por seus identificadores no kernel. Se um plug-in ou função necessário não estiver presente no kernel, um erro será gerado durante a construção do agente.

Exemplo: criando um ChatCompletionAgent a partir de uma especificação 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())

Esse recurso não está disponível.

Como Fazer

Para obter um exemplo completo para um ChatCompletionAgent, consulte:

Próximas etapas