Compartilhar via


Instruções: coordenar a colaboração do agente usando o chat de grupo do agente

Importante

Este é um documento arquivado.

Importante

Esse recurso está em estágio experimental, mas não é mais mantido. Para uma substituição, consulte a Orquestração de Chat em Grupo e o guia de migração Migrando do AgentChat para a Orquestração de Chat em Grupo.

Visão geral

Neste exemplo, exploraremos como usar AgentGroupChat para coordenar a colaboração de dois agentes diferentes que trabalham para revisar e reescrever o conteúdo fornecido pelo usuário. Cada agente recebe uma função distinta:

  • Revisor: Revisa e fornece orientação ao Escritor.
  • Escritor: atualiza o conteúdo do usuário com base nas instruções do Revisor.

A abordagem será dividida passo a passo para destacar as principais partes do processo de codificação.

Introdução

Antes de prosseguir com a codificação de recursos, verifique se o ambiente de desenvolvimento está totalmente instalado e configurado.

Dica

Este exemplo usa um arquivo de texto opcional como parte do processamento. Se você quiser usá-lo, poderá baixá-lo aqui. Coloque o arquivo no diretório de trabalho do código.

Comece criando um projeto do Console. Em seguida, inclua as seguintes referências de pacote para garantir que todas as dependências necessárias estejam disponíveis.

Para adicionar dependências de pacote da linha de comando, use o dotnet comando:

dotnet add package Azure.Identity
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Binder
dotnet add package Microsoft.Extensions.Configuration.UserSecrets
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
dotnet add package Microsoft.SemanticKernel.Connectors.AzureOpenAI
dotnet add package Microsoft.SemanticKernel.Agents.Core --prerelease

Se estiver gerenciando pacotes NuGet no Visual Studio, assegure-se de que Include prerelease está marcada.

O arquivo de projeto (.csproj) deve conter as seguintes PackageReference definições:

  <ItemGroup>
    <PackageReference Include="Azure.Identity" Version="<stable>" />
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="<stable>" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="<stable>" />
    <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="<stable>" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="<stable>" />
    <PackageReference Include="Microsoft.SemanticKernel.Agents.Core" Version="<latest>" />
    <PackageReference Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="<latest>" />
  </ItemGroup>

O Agent Framework é experimental e requer supressão de aviso. Isso pode ser abordado como uma propriedade no arquivo do projeto (.csproj):

  <PropertyGroup>
    <NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
  </PropertyGroup>

Dica

Este exemplo usa um arquivo de texto opcional como parte do processamento. Se você quiser usá-lo, poderá baixá-lo aqui. Coloque o arquivo no diretório de trabalho do código.

Comece instalando o pacote Python do Kernel Semântico.

pip install semantic-kernel

Em seguida, adicione as importações necessárias.

import asyncio
import os

from semantic_kernel import Kernel
from semantic_kernel.agents import AgentGroupChat, ChatCompletionAgent
from semantic_kernel.agents.strategies import (
    KernelFunctionSelectionStrategy,
    KernelFunctionTerminationStrategy,
)
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatHistoryTruncationReducer
from semantic_kernel.functions import KernelFunctionFromPrompt

Recurso atualmente indisponível em Java.

Configuração

Este exemplo requer uma definição de configuração para se conectar a serviços remotos. Você precisará definir as configurações do OpenAI ou do Azure OpenAI.

# OpenAI
dotnet user-secrets set "OpenAISettings:ApiKey" "<api-key>"
dotnet user-secrets set "OpenAISettings:ChatModel" "gpt-4o"

# Azure OpenAI
dotnet user-secrets set "AzureOpenAISettings:ApiKey" "<api-key>" # Not required if using token-credential
dotnet user-secrets set "AzureOpenAISettings:Endpoint" "<model-endpoint>"
dotnet user-secrets set "AzureOpenAISettings:ChatModelDeployment" "gpt-4o"

A classe a seguir é usada em todos os exemplos de Agent. Certifique-se de incluí-lo em seu projeto para garantir a funcionalidade adequada. Essa classe serve como um componente fundamental para os exemplos a seguir.

using System.Reflection;
using Microsoft.Extensions.Configuration;

namespace AgentsSample;

public class Settings
{
    private readonly IConfigurationRoot configRoot;

    private AzureOpenAISettings azureOpenAI;
    private OpenAISettings openAI;

    public AzureOpenAISettings AzureOpenAI => this.azureOpenAI ??= this.GetSettings<Settings.AzureOpenAISettings>();
    public OpenAISettings OpenAI => this.openAI ??= this.GetSettings<Settings.OpenAISettings>();

    public class OpenAISettings
    {
        public string ChatModel { get; set; } = string.Empty;
        public string ApiKey { get; set; } = string.Empty;
    }

    public class AzureOpenAISettings
    {
        public string ChatModelDeployment { get; set; } = string.Empty;
        public string Endpoint { get; set; } = string.Empty;
        public string ApiKey { get; set; } = string.Empty;
    }

    public TSettings GetSettings<TSettings>() =>
        this.configRoot.GetRequiredSection(typeof(TSettings).Name).Get<TSettings>()!;

    public Settings()
    {
        this.configRoot =
            new ConfigurationBuilder()
                .AddEnvironmentVariables()
                .AddUserSecrets(Assembly.GetExecutingAssembly(), optional: true)
                .Build();
    }
}

A maneira mais rápida de começar com a configuração adequada para executar o código de exemplo é criar um .env arquivo na raiz do seu projeto (onde seu script é executado). O exemplo requer que você tenha recursos do Azure OpenAI ou OpenAI disponíveis.

Configure as seguintes definições no seu arquivo .env para Azure OpenAI ou OpenAI:

AZURE_OPENAI_API_KEY="..."
AZURE_OPENAI_ENDPOINT="https://<resource-name>.openai.azure.com/"
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="..."
AZURE_OPENAI_API_VERSION="..."

OPENAI_API_KEY="sk-..."
OPENAI_ORG_ID=""
OPENAI_CHAT_MODEL_ID=""

Depois de configuradas, as respectivas classes de serviço de IA selecionarão as variáveis necessárias e as usarão durante a instanciação.

Recurso atualmente indisponível em Java.

Codificação

O processo de codificação para este exemplo envolve:

  1. Configuração - Inicializando as configurações e o plug-in.
  2. Agent Definição – Criar as duas instâncias de ChatCompletionAgent (Reviewer e Writer).
  3. Definição de Chat - Criar AgentGroupChat e estratégias associadas.
  4. O Ciclo de Chat - Escreva o ciclo que impulsiona a interação entre usuário e agente.

O código de exemplo completo é fornecido na seção Final . Consulte essa seção para obter a implementação completa.

Configuração

Antes de criar qualquer ChatCompletionAgent, as configurações, os plug-ins e os Kernel devem ser inicializados.

Instancie a classe referenciada na seção Configuração anterior.

Settings settings = new();

Recurso atualmente indisponível em Java.

Agora inicialize uma instância de Kernel com um IChatCompletionService.

IKernelBuilder builder = Kernel.CreateBuilder();

builder.AddAzureOpenAIChatCompletion(
	settings.AzureOpenAI.ChatModelDeployment,
	settings.AzureOpenAI.Endpoint,
	new AzureCliCredential());

Kernel kernel = builder.Build();

Inicialize o objeto kernel:

kernel = Kernel()

Recurso atualmente indisponível em Java.

Vamos também criar uma segunda instância Kernel clonando via e adicionar um plug-in que permitirá que a revisão coloque o conteúdo atualizado na área de transferência.

Kernel toolKernel = kernel.Clone();
toolKernel.Plugins.AddFromType<ClipboardAccess>();

Recurso atualmente indisponível em Java.

O plugin Clipboard pode ser definido como parte do exemplo.

private sealed class ClipboardAccess
{
    [KernelFunction]
    [Description("Copies the provided content to the clipboard.")]
    public static void SetClipboard(string content)
    {
        if (string.IsNullOrWhiteSpace(content))
        {
            return;
        }

        using Process clipProcess = Process.Start(
            new ProcessStartInfo
            {
                FileName = "clip",
                RedirectStandardInput = true,
                UseShellExecute = false,
            });

        clipProcess.StandardInput.Write(content);
        clipProcess.StandardInput.Close();
    }
}

Recurso atualmente indisponível em Java.

Definição de agente

Vamos declarar os nomes dos agentes como const para que possam ser mencionados em estratégias AgentGroupChat.

const string ReviewerName = "Reviewer";
const string WriterName = "Writer";

Declararemos os nomes de agente como "Revisor" e "Escritor".

REVIEWER_NAME = "Reviewer"
COPYWRITER_NAME = "Writer"

Recurso atualmente indisponível em Java.

A definição do agente Revisor usa o padrão explorado em Guia: Agente de Conclusão de Chat.

Aqui, o Revisor recebe a função de responder à entrada do usuário, fornecer orientação ao agente Writer e verificar o resultado do agente Writer.

ChatCompletionAgent agentReviewer =
    new()
    {
        Name = ReviewerName,
        Instructions =
            """
            Your responsibility is to review and identify how to improve user provided content.
            If the user has providing input or direction for content already provided, specify how to address this input.
            Never directly perform the correction or provide example.
            Once the content has been updated in a subsequent response, you will review the content again until satisfactory.
            Always copy satisfactory content to the clipboard using available tools and inform user.

            RULES:
            - Only identify suggestions that are specific and actionable.
            - Verify previous suggestions have been addressed.
            - Never repeat previous suggestions.
            """,
        Kernel = toolKernel,
        Arguments =
            new KernelArguments(
                new AzureOpenAIPromptExecutionSettings() 
                { 
                    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() 
                })
    };
agent_reviewer = ChatCompletionAgent(
        kernel=kernel,
        name=REVIEWER_NAME,
        instructions="""
Your responsibility is to review and identify how to improve user provided content.
If the user has provided input or direction for content already provided, specify how to address this input.
Never directly perform the correction or provide an example.
Once the content has been updated in a subsequent response, review it again until it is satisfactory.

RULES:
- Only identify suggestions that are specific and actionable.
- Verify previous suggestions have been addressed.
- Never repeat previous suggestions.
""",
)

Recurso atualmente indisponível em Java.

O agente gravador é semelhante, mas não requer a especificação de Configurações de Execução, pois ele não está configurado com um plug-in.

Aqui, o escritor recebe uma tarefa de propósito único, segue a direção e reescreve o conteúdo.

ChatCompletionAgent agentWriter =
    new()
    {
        Name = WriterName,
        Instructions =
            """
            Your sole responsibility is to rewrite content according to review suggestions.

            - Always apply all review direction.
            - Always revise the content in its entirety without explanation.
            - Never address the user.
            """,
        Kernel = kernel,
    };

O agente Writer é semelhante. É dada uma tarefa de propósito único: siga as instruções e reescreva o conteúdo.

agent_writer = ChatCompletionAgent(
        kernel=kernel,
        name=WRITER_NAME,
        instructions="""
Your sole responsibility is to rewrite content according to review suggestions.
- Always apply all review directions.
- Always revise the content in its entirety without explanation.
- Never address the user.
""",
    )

Recurso atualmente indisponível em Java.

Definição de bate-papo

Definir o AgentGroupChat requer considerar as estratégias para selecionar o turno Agent e determinar quando sair do loop de Chat. Para ambas as considerações, definiremos uma função de prompt do kernel.

O primeiro a raciocinar sobre a seleção de Agent:

O uso de AgentGroupChat.CreatePromptFunctionForStrategy fornece um mecanismo conveniente para evitar codificação HTML o parâmetro de mensagem.

KernelFunction selectionFunction =
    AgentGroupChat.CreatePromptFunctionForStrategy(
        $$$"""
        Examine the provided RESPONSE and choose the next participant.
        State only the name of the chosen participant without explanation.
        Never choose the participant named in the RESPONSE.

        Choose only from these participants:
        - {{{ReviewerName}}}
        - {{{WriterName}}}

        Always follow these rules when choosing the next participant:
        - If RESPONSE is user input, it is {{{ReviewerName}}}'s turn.
        - If RESPONSE is by {{{ReviewerName}}}, it is {{{WriterName}}}'s turn.
        - If RESPONSE is by {{{WriterName}}}, it is {{{ReviewerName}}}'s turn.

        RESPONSE:
        {{$lastmessage}}
        """,
        safeParameterNames: "lastmessage");
selection_function = KernelFunctionFromPrompt(
    function_name="selection", 
    prompt=f"""
Examine the provided RESPONSE and choose the next participant.
State only the name of the chosen participant without explanation.
Never choose the participant named in the RESPONSE.

Choose only from these participants:
- {REVIEWER_NAME}
- {WRITER_NAME}

Rules:
- If RESPONSE is user input, it is {REVIEWER_NAME}'s turn.
- If RESPONSE is by {REVIEWER_NAME}, it is {WRITER_NAME}'s turn.
- If RESPONSE is by {WRITER_NAME}, it is {REVIEWER_NAME}'s turn.

RESPONSE:
{{{{$lastmessage}}}}
"""
)

Recurso atualmente indisponível em Java.

O segundo avaliará quando sair do loop de bate-papo :

const string TerminationToken = "yes";

KernelFunction terminationFunction =
    AgentGroupChat.CreatePromptFunctionForStrategy(
        $$$"""
        Examine the RESPONSE and determine whether the content has been deemed satisfactory.
        If content is satisfactory, respond with a single word without explanation: {{{TerminationToken}}}.
        If specific suggestions are being provided, it is not satisfactory.
        If no correction is suggested, it is satisfactory.

        RESPONSE:
        {{$lastmessage}}
        """,
        safeParameterNames: "lastmessage");
termination_keyword = "yes"

termination_function = KernelFunctionFromPrompt(
    function_name="termination", 
    prompt=f"""
Examine the RESPONSE and determine whether the content has been deemed satisfactory.
If the content is satisfactory, respond with a single word without explanation: {termination_keyword}.
If specific suggestions are being provided, it is not satisfactory.
If no correction is suggested, it is satisfactory.

RESPONSE:
{{{{$lastmessage}}}}
"""
)

Recurso atualmente indisponível em Java.

Ambas as estratégias exigirão apenas conhecimento da mensagem de bate-papo mais recente. Isso reduzirá o uso de tokens e ajudará a melhorar o desempenho:

ChatHistoryTruncationReducer historyReducer = new(1);
history_reducer = ChatHistoryTruncationReducer(target_count=1)

Recurso atualmente indisponível em Java.

Por fim, estamos prontos para reunir tudo em nossa definição de AgentGroupChat.

Criar AgentGroupChat envolve:

  1. Inclua ambos os agentes no construtor.
  2. Defina um KernelFunctionSelectionStrategy usando a instância KernelFunction e Kernel definidas anteriormente.
  3. Defina um KernelFunctionTerminationStrategy usando a instância KernelFunction e Kernel definidas anteriormente.

Observe que cada estratégia é responsável por analisar o KernelFunction resultado.

AgentGroupChat chat =
    new(agentReviewer, agentWriter)
    {
        ExecutionSettings = new AgentGroupChatSettings
        {
            SelectionStrategy =
                new KernelFunctionSelectionStrategy(selectionFunction, kernel)
                {
                    // Always start with the editor agent.
                    InitialAgent = agentReviewer,
                    // Save tokens by only including the final response
                    HistoryReducer = historyReducer,
                    // The prompt variable name for the history argument.
                    HistoryVariableName = "lastmessage",
                    // Returns the entire result value as a string.
                    ResultParser = (result) => result.GetValue<string>() ?? agentReviewer.Name
                },
            TerminationStrategy =
                new KernelFunctionTerminationStrategy(terminationFunction, kernel)
                {
                    // Only evaluate for editor's response
                    Agents = [agentReviewer],
                    // Save tokens by only including the final response
                    HistoryReducer = historyReducer,
                    // The prompt variable name for the history argument.
                    HistoryVariableName = "lastmessage",
                    // Limit total number of turns
                    MaximumIterations = 12,
                    // Customer result parser to determine if the response is "yes"
                    ResultParser = (result) => result.GetValue<string>()?.Contains(TerminationToken, StringComparison.OrdinalIgnoreCase) ?? false
                }
        }
    };

Console.WriteLine("Ready!");

Criar AgentGroupChat envolve:

  1. Inclua ambos os agentes no construtor.
  2. Defina um KernelFunctionSelectionStrategy usando a instância KernelFunction e Kernel definidas anteriormente.
  3. Defina um KernelFunctionTerminationStrategy usando a instância KernelFunction e Kernel definidas anteriormente.

Observe que cada estratégia é responsável por analisar o KernelFunction resultado.

chat = AgentGroupChat(
    agents=[agent_reviewer, agent_writer],
    selection_strategy=KernelFunctionSelectionStrategy(
        initial_agent=agent_reviewer,
        function=selection_function,
        kernel=kernel,
        result_parser=lambda result: str(result.value[0]).strip() if result.value[0] is not None else WRITER_NAME,
        history_variable_name="lastmessage",
        history_reducer=history_reducer,
    ),
    termination_strategy=KernelFunctionTerminationStrategy(
        agents=[agent_reviewer],
        function=termination_function,
        kernel=kernel,
        result_parser=lambda result: termination_keyword in str(result.value[0]).lower(),
        history_variable_name="lastmessage",
        maximum_iterations=10,
        history_reducer=history_reducer,
    ),
)

O lastmessagehistory_variable_name corresponde ao KernelFunctionSelectionStrategy e ao prompt KernelFunctionTerminationStrategy definido acima. É aqui que a última mensagem é colocada ao renderizar o prompt.

Recurso atualmente indisponível em Java.

O loop de bate-papo

Finalmente, podemos coordenar a interação entre o usuário e o AgentGroupChat. Comece criando um loop vazio.

Observação: ao contrário dos outros exemplos, nenhum histórico ou thread externo é gerenciado. AgentGroupChat gerencia o histórico da conversa internamente.

bool isComplete = false;
do
{

} while (!isComplete);
is_complete: bool = False
while not is_complete:
    # operational logic

Recurso atualmente indisponível em Java.

Agora vamos capturar a entrada do usuário no loop anterior. Nesse caso:

  • A entrada vazia será ignorada
  • O termo EXIT sinalizará que a conversa foi concluída
  • O termo RESET limpará o histórico de AgentGroupChat
  • Qualquer termo que comece com @ será tratado como um caminho de arquivo cujo conteúdo será fornecido como entrada
  • A entrada válida será adicionada à AgentGroupChat como uma mensagem de de usuário.
Console.WriteLine();
Console.Write("> ");
string input = Console.ReadLine();
if (string.IsNullOrWhiteSpace(input))
{
    continue;
}
input = input.Trim();
if (input.Equals("EXIT", StringComparison.OrdinalIgnoreCase))
{
    isComplete = true;
    break;
}

if (input.Equals("RESET", StringComparison.OrdinalIgnoreCase))
{
    await chat.ResetAsync();
    Console.WriteLine("[Conversation has been reset]");
    continue;
}

if (input.StartsWith("@", StringComparison.Ordinal) && input.Length > 1)
{
    string filePath = input.Substring(1);
    try
    {
        if (!File.Exists(filePath))
        {
            Console.WriteLine($"Unable to access file: {filePath}");
            continue;
        }
        input = File.ReadAllText(filePath);
    }
    catch (Exception)
    {
        Console.WriteLine($"Unable to access file: {filePath}");
        continue;
    }
}

chat.AddChatMessage(new ChatMessageContent(AuthorRole.User, input));

Agora vamos capturar a entrada do usuário no loop anterior. Nesse caso:

  • A entrada vazia será ignorada.
  • O termo exit sinalizará que a conversa está concluída.
  • O termo reset limpará o histórico de AgentGroupChat.
  • Qualquer termo começando com @ será tratado como um caminho de arquivo cujo conteúdo será fornecido como entrada.
  • A entrada válida será adicionada à AgentGroupChat como uma mensagem de de usuário.

A lógica de operação dentro do loop while tem a seguinte aparência:

print()
user_input = input("User > ").strip()
if not user_input:
    continue

if user_input.lower() == "exit":
    is_complete = True
    break

if user_input.lower() == "reset":
    await chat.reset()
    print("[Conversation has been reset]")
    continue

# Try to grab files from the script's current directory
if user_input.startswith("@") and len(user_input) > 1:
    file_name = user_input[1:]
    script_dir = os.path.dirname(os.path.abspath(__file__))
    file_path = os.path.join(script_dir, file_name)
    try:
        if not os.path.exists(file_path):
            print(f"Unable to access file: {file_path}")
            continue
        with open(file_path, "r", encoding="utf-8") as file:
            user_input = file.read()
    except Exception:
        print(f"Unable to access file: {file_path}")
        continue

# Add the current user_input to the chat
await chat.add_chat_message(message=user_input)

Recurso atualmente indisponível em Java.

Para iniciar a colaboração Agent em resposta à entrada do usuário e exibir as respostas Agent, invoque o AgentGroupChat; no entanto, primeiro certifique-se de redefinir o estado Completion de qualquer invocação anterior.

Observação: as falhas de serviço estão sendo detectadas e exibidas para evitar falhas no loop de conversa.

chat.IsComplete = false;

try
{
    await foreach (ChatMessageContent response in chat.InvokeAsync())
    {
        Console.WriteLine();
        Console.WriteLine($"{response.AuthorName.ToUpperInvariant()}:{Environment.NewLine}{response.Content}");
    }
}
catch (HttpOperationException exception)
{
    Console.WriteLine(exception.Message);
    if (exception.InnerException != null)
    {
        Console.WriteLine(exception.InnerException.Message);
        if (exception.InnerException.Data.Count > 0)
        {
            Console.WriteLine(JsonSerializer.Serialize(exception.InnerException.Data, new JsonSerializerOptions() { WriteIndented = true }));
        }
    }
}
try:
    async for response in chat.invoke():
        if response is None or not response.name:
            continue
        print()
        print(f"# {response.name.upper()}:\n{response.content}")
except Exception as e:
    print(f"Error during chat invocation: {e}")

# Reset the chat's complete flag for the new conversation round.
chat.is_complete = False

Recurso atualmente indisponível em Java.

Final

Juntando todas as etapas, temos o código final para este exemplo. A implementação completa é fornecida abaixo.

Tente usar estas entradas sugeridas:

  1. Oi
  2. {"mensagem: "olá, mundo"}
  3. {"message": "olá mundo"}
  4. O SK (Kernel Semântico) é um SDK de software livre que permite aos desenvolvedores criar e orquestrar fluxos de trabalho complexos de IA que envolvem NLP (processamento de linguagem natural) e modelos de machine learning. Ele fornece uma plataforma flexível para integrar recursos de IA, como pesquisa semântica, resumo de texto e sistemas de diálogo em aplicativos. Com o SK, você pode combinar facilmente diferentes serviços e modelos de IA, definir suas relações e orquestrar interações entre eles.
  5. transformar isto em dois parágrafos
  6. Obrigado
  7. @.\SufrágioFeminino.txt
  8. Está bom, mas será que está pronto para o meu professor da faculdade?
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.Chat;
using Microsoft.SemanticKernel.Agents.History;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;

namespace AgentsSample;

public static class Program
{
    public static async Task Main()
    {
        // Load configuration from environment variables or user secrets.
        Settings settings = new();

        Console.WriteLine("Creating kernel...");
        IKernelBuilder builder = Kernel.CreateBuilder();

        builder.AddAzureOpenAIChatCompletion(
            settings.AzureOpenAI.ChatModelDeployment,
            settings.AzureOpenAI.Endpoint,
            new AzureCliCredential());

        Kernel kernel = builder.Build();

        Kernel toolKernel = kernel.Clone();
        toolKernel.Plugins.AddFromType<ClipboardAccess>();


        Console.WriteLine("Defining agents...");

        const string ReviewerName = "Reviewer";
        const string WriterName = "Writer";

        ChatCompletionAgent agentReviewer =
            new()
            {
                Name = ReviewerName,
                Instructions =
                    """
                    Your responsibility is to review and identify how to improve user provided content.
                    If the user has providing input or direction for content already provided, specify how to address this input.
                    Never directly perform the correction or provide example.
                    Once the content has been updated in a subsequent response, you will review the content again until satisfactory.
                    Always copy satisfactory content to the clipboard using available tools and inform user.

                    RULES:
                    - Only identify suggestions that are specific and actionable.
                    - Verify previous suggestions have been addressed.
                    - Never repeat previous suggestions.
                    """,
                Kernel = toolKernel,
                Arguments = new KernelArguments(new AzureOpenAIPromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() })
            };

        ChatCompletionAgent agentWriter =
            new()
            {
                Name = WriterName,
                Instructions =
                    """
                    Your sole responsibility is to rewrite content according to review suggestions.

                    - Always apply all review direction.
                    - Always revise the content in its entirety without explanation.
                    - Never address the user.
                    """,
                Kernel = kernel,
            };

        KernelFunction selectionFunction =
            AgentGroupChat.CreatePromptFunctionForStrategy(
                $$$"""
                Examine the provided RESPONSE and choose the next participant.
                State only the name of the chosen participant without explanation.
                Never choose the participant named in the RESPONSE.

                Choose only from these participants:
                - {{{ReviewerName}}}
                - {{{WriterName}}}

                Always follow these rules when choosing the next participant:
                - If RESPONSE is user input, it is {{{ReviewerName}}}'s turn.
                - If RESPONSE is by {{{ReviewerName}}}, it is {{{WriterName}}}'s turn.
                - If RESPONSE is by {{{WriterName}}}, it is {{{ReviewerName}}}'s turn.

                RESPONSE:
                {{$lastmessage}}
                """,
                safeParameterNames: "lastmessage");

        const string TerminationToken = "yes";

        KernelFunction terminationFunction =
            AgentGroupChat.CreatePromptFunctionForStrategy(
                $$$"""
                Examine the RESPONSE and determine whether the content has been deemed satisfactory.
                If content is satisfactory, respond with a single word without explanation: {{{TerminationToken}}}.
                If specific suggestions are being provided, it is not satisfactory.
                If no correction is suggested, it is satisfactory.

                RESPONSE:
                {{$lastmessage}}
                """,
                safeParameterNames: "lastmessage");

        ChatHistoryTruncationReducer historyReducer = new(1);

        AgentGroupChat chat =
            new(agentReviewer, agentWriter)
            {
                ExecutionSettings = new AgentGroupChatSettings
                {
                    SelectionStrategy =
                        new KernelFunctionSelectionStrategy(selectionFunction, kernel)
                        {
                            // Always start with the editor agent.
                            InitialAgent = agentReviewer,
                            // Save tokens by only including the final response
                            HistoryReducer = historyReducer,
                            // The prompt variable name for the history argument.
                            HistoryVariableName = "lastmessage",
                            // Returns the entire result value as a string.
                            ResultParser = (result) => result.GetValue<string>() ?? agentReviewer.Name
                        },
                    TerminationStrategy =
                        new KernelFunctionTerminationStrategy(terminationFunction, kernel)
                        {
                            // Only evaluate for editor's response
                            Agents = [agentReviewer],
                            // Save tokens by only including the final response
                            HistoryReducer = historyReducer,
                            // The prompt variable name for the history argument.
                            HistoryVariableName = "lastmessage",
                            // Limit total number of turns
                            MaximumIterations = 12,
                            // Customer result parser to determine if the response is "yes"
                            ResultParser = (result) => result.GetValue<string>()?.Contains(TerminationToken, StringComparison.OrdinalIgnoreCase) ?? false
                        }
                }
            };

        Console.WriteLine("Ready!");

        bool isComplete = false;
        do
        {
            Console.WriteLine();
            Console.Write("> ");
            string input = Console.ReadLine();
            if (string.IsNullOrWhiteSpace(input))
            {
                continue;
            }
            input = input.Trim();
            if (input.Equals("EXIT", StringComparison.OrdinalIgnoreCase))
            {
                isComplete = true;
                break;
            }

            if (input.Equals("RESET", StringComparison.OrdinalIgnoreCase))
            {
                await chat.ResetAsync();
                Console.WriteLine("[Conversation has been reset]");
                continue;
            }

            if (input.StartsWith("@", StringComparison.Ordinal) && input.Length > 1)
            {
                string filePath = input.Substring(1);
                try
                {
                    if (!File.Exists(filePath))
                    {
                        Console.WriteLine($"Unable to access file: {filePath}");
                        continue;
                    }
                    input = File.ReadAllText(filePath);
                }
                catch (Exception)
                {
                    Console.WriteLine($"Unable to access file: {filePath}");
                    continue;
                }
            }

            chat.AddChatMessage(new ChatMessageContent(AuthorRole.User, input));

            chat.IsComplete = false;

            try
            {
                await foreach (ChatMessageContent response in chat.InvokeAsync())
                {
                    Console.WriteLine();
                    Console.WriteLine($"{response.AuthorName.ToUpperInvariant()}:{Environment.NewLine}{response.Content}");
                }
            }
            catch (HttpOperationException exception)
            {
                Console.WriteLine(exception.Message);
                if (exception.InnerException != null)
                {
                    Console.WriteLine(exception.InnerException.Message);
                    if (exception.InnerException.Data.Count > 0)
                    {
                        Console.WriteLine(JsonSerializer.Serialize(exception.InnerException.Data, new JsonSerializerOptions() { WriteIndented = true }));
                    }
                }
            }
        } while (!isComplete);
    }

    private sealed class ClipboardAccess
    {
        [KernelFunction]
        [Description("Copies the provided content to the clipboard.")]
        public static void SetClipboard(string content)
        {
            if (string.IsNullOrWhiteSpace(content))
            {
                return;
            }

            using Process clipProcess = Process.Start(
                new ProcessStartInfo
                {
                    FileName = "clip",
                    RedirectStandardInput = true,
                    UseShellExecute = false,
                });

            clipProcess.StandardInput.Write(content);
            clipProcess.StandardInput.Close();
        }
    }
}

Reunindo todas as etapas, agora temos o código final para este exemplo. A implementação completa é mostrada abaixo.

Você pode tentar usar uma das entradas sugeridas. Quando o chat dos agentes começa, os agentes trocarão várias mensagens até que o agente revisor esteja satisfeito com o trabalho do redator. O loop while garante que a conversa continue, mesmo que o chat seja inicialmente considerado concluído, redefinindo o sinalizador is_complete para False.

  1. Rozes são vermelhos, violetas são azuis.
  2. O SK (Kernel Semântico) é um SDK de software livre que permite aos desenvolvedores criar e orquestrar fluxos de trabalho complexos de IA que envolvem NLP (processamento de linguagem natural) e modelos de machine learning. Ele fornece uma plataforma flexível para integrar recursos de IA, como pesquisa semântica, resumo de texto e sistemas de diálogo em aplicativos. Com o SK, você pode combinar facilmente diferentes serviços e modelos de IA, definir suas relações e orquestrar interações entre eles.
  3. Divida isso em dois parágrafos
  4. Obrigado
  5. @WomensSuffrage.txt
  6. Está bom, mas está adequado para o meu professor da faculdade?

Dica

Você pode referenciar qualquer arquivo fornecendo @<file_path_to_file>. Para fazer referência ao texto "WomensSuffrage" acima, baixe-o aqui e coloque-o no diretório de trabalho atual. Em seguida, você pode referenciá-lo com @WomensSuffrage.txt.

import asyncio
import os

from semantic_kernel import Kernel
from semantic_kernel.agents import AgentGroupChat, ChatCompletionAgent
from semantic_kernel.agents.strategies import (
    KernelFunctionSelectionStrategy,
    KernelFunctionTerminationStrategy,
)
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatHistoryTruncationReducer
from semantic_kernel.functions import KernelFunctionFromPrompt

"""
The following sample demonstrates how to create a simple,
agent group chat that utilizes a Reviewer Chat Completion
Agent along with a Writer Chat Completion Agent to
complete a user's task.
"""

# Define agent names
REVIEWER_NAME = "Reviewer"
WRITER_NAME = "Writer"


def create_kernel() -> Kernel:
    """Creates a Kernel instance with an Azure OpenAI ChatCompletion service."""
    kernel = Kernel()
    kernel.add_service(service=AzureChatCompletion())
    return kernel


async def main():
    # Create a single kernel instance for all agents.
    kernel = create_kernel()

    # Create ChatCompletionAgents using the same kernel.
    agent_reviewer = ChatCompletionAgent(
        kernel=kernel,
        name=REVIEWER_NAME,
        instructions="""
Your responsibility is to review and identify how to improve user provided content.
If the user has provided input or direction for content already provided, specify how to address this input.
Never directly perform the correction or provide an example.
Once the content has been updated in a subsequent response, review it again until it is satisfactory.

RULES:
- Only identify suggestions that are specific and actionable.
- Verify previous suggestions have been addressed.
- Never repeat previous suggestions.
""",
    )

    agent_writer = ChatCompletionAgent(
        kernel=kernel,
        name=WRITER_NAME,
        instructions="""
Your sole responsibility is to rewrite content according to review suggestions.
- Always apply all review directions.
- Always revise the content in its entirety without explanation.
- Never address the user.
""",
    )

    # Define a selection function to determine which agent should take the next turn.
    selection_function = KernelFunctionFromPrompt(
        function_name="selection",
        prompt=f"""
Examine the provided RESPONSE and choose the next participant.
State only the name of the chosen participant without explanation.
Never choose the participant named in the RESPONSE.

Choose only from these participants:
- {REVIEWER_NAME}
- {WRITER_NAME}

Rules:
- If RESPONSE is user input, it is {REVIEWER_NAME}'s turn.
- If RESPONSE is by {REVIEWER_NAME}, it is {WRITER_NAME}'s turn.
- If RESPONSE is by {WRITER_NAME}, it is {REVIEWER_NAME}'s turn.

RESPONSE:
{{{{$lastmessage}}}}
""",
    )

    # Define a termination function where the reviewer signals completion with "yes".
    termination_keyword = "yes"

    termination_function = KernelFunctionFromPrompt(
        function_name="termination",
        prompt=f"""
Examine the RESPONSE and determine whether the content has been deemed satisfactory.
If the content is satisfactory, respond with a single word without explanation: {termination_keyword}.
If specific suggestions are being provided, it is not satisfactory.
If no correction is suggested, it is satisfactory.

RESPONSE:
{{{{$lastmessage}}}}
""",
    )

    history_reducer = ChatHistoryTruncationReducer(target_count=5)

    # Create the AgentGroupChat with selection and termination strategies.
    chat = AgentGroupChat(
        agents=[agent_reviewer, agent_writer],
        selection_strategy=KernelFunctionSelectionStrategy(
            initial_agent=agent_reviewer,
            function=selection_function,
            kernel=kernel,
            result_parser=lambda result: str(result.value[0]).strip() if result.value[0] is not None else WRITER_NAME,
            history_variable_name="lastmessage",
            history_reducer=history_reducer,
        ),
        termination_strategy=KernelFunctionTerminationStrategy(
            agents=[agent_reviewer],
            function=termination_function,
            kernel=kernel,
            result_parser=lambda result: termination_keyword in str(result.value[0]).lower(),
            history_variable_name="lastmessage",
            maximum_iterations=10,
            history_reducer=history_reducer,
        ),
    )

    print(
        "Ready! Type your input, or 'exit' to quit, 'reset' to restart the conversation. "
        "You may pass in a file path using @<path_to_file>."
    )

    is_complete = False
    while not is_complete:
        print()
        user_input = input("User > ").strip()
        if not user_input:
            continue

        if user_input.lower() == "exit":
            is_complete = True
            break

        if user_input.lower() == "reset":
            await chat.reset()
            print("[Conversation has been reset]")
            continue

        # Try to grab files from the script's current directory
        if user_input.startswith("@") and len(user_input) > 1:
            file_name = user_input[1:]
            script_dir = os.path.dirname(os.path.abspath(__file__))
            file_path = os.path.join(script_dir, file_name)
            try:
                if not os.path.exists(file_path):
                    print(f"Unable to access file: {file_path}")
                    continue
                with open(file_path, "r", encoding="utf-8") as file:
                    user_input = file.read()
            except Exception:
                print(f"Unable to access file: {file_path}")
                continue

        # Add the current user_input to the chat
        await chat.add_chat_message(message=user_input)

        try:
            async for response in chat.invoke():
                if response is None or not response.name:
                    continue
                print()
                print(f"# {response.name.upper()}:\n{response.content}")
        except Exception as e:
            print(f"Error during chat invocation: {e}")

        # Reset the chat's complete flag for the new conversation round.
        chat.is_complete = False


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

Você pode encontrar o código de completo, conforme mostrado acima, em nosso repositório.

Recurso atualmente indisponível em Java.