Como fazer: Coordenar a colaboração do agente usando o bate-papo de grupo do agente
Importante
Esta característica encontra-se em fase experimental. As funcionalidades nesta fase continuam em desenvolvimento, estando sujeitas a alterações antes de avançarem para a versão de pré-visualização ou versão candidata a lançamento.
Descriçã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. A cada agente é atribuída uma função distinta:
- Revisor: Revisa e fornece direção ao Writer.
- Writer: Atualiza o conteúdo do usuário com base na entrada 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 programação de funcionalidades, verifique se o ambiente de desenvolvimento está totalmente preparado e configurado.
Dica
Este exemplo usa um arquivo de texto opcional como parte do processamento. Se quiser usá-lo, pode descarregá-lo aqui. Coloque o arquivo no diretório de trabalho do código.
Comece criando um projeto de 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 a partir 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, verifique se
Include prerelease
está marcado.
O ficheiro 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 de 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 quiser usá-lo, pode descarregá-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
Os agentes estão atualmente indisponíveis em Java.
Configuração
Este exemplo requer definição de configuração para se conectar a serviços remotos. Você precisará definir configurações para OpenAI ou 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 agente. Certifique-se de incluí-lo em seu projeto para garantir a funcionalidade adequada. Esta classe serve como um componente fundamental para os exemplos que se seguem.
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 o script é executado). O exemplo requer que você tenha recursos do Azure OpenAI ou OpenAI disponíveis.
Configure as seguintes definições no ficheiro .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=""
Uma vez configuradas, as respetivas classes de serviço de IA pegarão as variáveis necessárias e as usarão durante a instanciação.
Os agentes estão atualmente indisponíveis em Java.
Codificação
O processo de codificação para este exemplo envolve:
- Configuração - Inicializando as configurações e o plug-in.
-
Agent
Definição - Crie as duas instânciasChatCompletionAgent
(Revisor e Autor). -
Chat Definição - Criar o
AgentGroupChat
e as estratégias associadas. - The Chat Loop - Escreva o loop que impulsiona a interação usuário/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 Kernel
devem ser inicializados.
Instancie a classe Settings
referenciada na secção anterior Configuração.
Settings settings = new();
Os agentes estão atualmente indisponíveis em Java.
Agora, inicialize uma Kernel
instância com um IChatCompletionService
.
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
Kernel kernel = builder.Build();
Inicialize o objeto do kernel:
kernel = Kernel()
Os agentes estão atualmente indisponíveis em Java.
Também vamos criar uma segunda instância Kernel
por meio de clonagem e adicionar um plug-in que permitirá que a revisão coloque conteúdo atualizado na área de transferência.
Kernel toolKernel = kernel.Clone();
toolKernel.Plugins.AddFromType<ClipboardAccess>();
::: fim de zona
Os agentes estão atualmente indisponíveis em Java.
O plug-in da Área de Transferência pode ser definido como parte da amostra.
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();
}
}
Os agentes estão atualmente indisponíveis em Java.
Definição do agente
Vamos declarar os nomes dos agentes como const
para que eles possam ser referenciados em AgentGroupChat
estratégias:
const string ReviewerName = "Reviewer";
const string WriterName = "Writer";
Declararemos os nomes dos agentes como "Revisor" e "Escritor".
REVIEWER_NAME = "Reviewer"
COPYWRITER_NAME = "Writer"
Os agentes estão atualmente indisponíveis em Java.
A definição do agente Revisor usa o padrão explorado em How-To: Chat Completion Agent.
Aqui, o revisor recebe a função de responder à entrada do usuário, fornecer orientação ao agente do Writer e verificar o resultado do agente do 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.
""",
)
Os agentes estão atualmente indisponíveis em Java.
O agente do Writer é semelhante, mas não requer a especificação das Configurações de Execução , pois não está configurado com um plug-in.
Aqui o escritor recebe uma tarefa de propósito único, siga a direção e reescreva o conteúdo.
ChatCompletionAgent agentWriter =
new()
{
Name = WriterName,
Instructions =
"""
Your sole responsiblity 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. É atribuída 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.
""",
)
Os agentes estão atualmente indisponíveis em Java.
Definição de Chat
Definir o AgentGroupChat
requer considerar as estratégias para selecionar o turno de Agent
e determinar quando sair do loop de do Chat. Para ambas as considerações, definiremos uma Função de Prompt do Kernel.
O primeiro a raciocinar sobre a seleção de Agent
Ao usar AgentGroupChat.CreatePromptFunctionForStrategy
, é fornecido um mecanismo conveniente para evitar a codificação HTML do parâmetro message.
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}}}}
"""
)
Os agentes estão atualmente indisponíveis 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}}}}
"""
)
Os agentes estão atualmente indisponíveis em Java.
Ambas as estratégias exigirão apenas o 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)
Os agentes estão atualmente indisponíveis em Java.
Finalmente, estamos prontos para reunir tudo na nossa definição AgentGroupChat
.
A criação AgentGroupChat
envolve:
- Inclua ambos os agentes no método construtor.
- Defina um
KernelFunctionSelectionStrategy
usando a instância previamente definida deKernelFunction
eKernel
. - Defina um
KernelFunctionTerminationStrategy
usando a instância previamente definida deKernelFunction
eKernel
.
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!");
A criação de AgentGroupChat
envolve:
- Inclua ambos os agentes no construtor.
- Defina um
KernelFunctionSelectionStrategy
usando a instância previamente definida deKernelFunction
eKernel
. - Defina um
KernelFunctionTerminationStrategy
usando a instância previamente definida deKernelFunction
eKernel
.
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 lastmessage
history_variable_name
corresponde ao KernelFunctionSelectionStrategy
e ao prompt KernelFunctionTerminationStrategy
que foi definido acima. É aqui que a última mensagem é colocada ao renderizar o prompt.
Os agentes estão atualmente indisponíveis em Java.
O Loop do Chat
Finalmente, somos capazes de coordenar a interação entre o usuário e o AgentGroupChat
. Comece criando um loop vazio.
Nota: Ao contrário dos outros exemplos, nenhum histórico externo ou thread é gerenciado.
AgentGroupChat
gerencia o histórico de conversas internamente.
bool isComplete = false;
do
{
} while (!isComplete);
is_complete: bool = False
while not is_complete:
# operational logic
Os agentes estão atualmente indisponíveis em Java.
Agora vamos capturar a entrada do usuário dentro do loop anterior. Neste caso:
- A entrada vazia será ignorada
- O termo
EXIT
sinalizará que a conversa está concluída - O termo
RESET
vai limpar o histórico doAgentGroupChat
- Qualquer termo que comece com
@
será tratado como um caminho de arquivo cujo conteúdo será fornecido como entrada - Uma entrada válida será adicionada ao
AgentGroupChat
como uma mensagem de utilizador .
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 dentro do loop anterior. Neste caso:
- A entrada vazia será ignorada.
- O termo
exit
sinalizará que a conversa está completa. - O termo
reset
vai limpar a históriaAgentGroupChat
. - Qualquer termo que comece com
@
será tratado como um caminho de arquivo cujo conteúdo será fornecido como entrada. - Uma entrada válida será adicionada ao
AgentGroupChat
como uma mensagem de utilizador .
A lógica de operação dentro do loop while se parece com:
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)
Os agentes estão atualmente indisponíveis em Java.
Para iniciar a colaboração Agent
em resposta à entrada do utilizador e mostrar as respostas Agent
, invoque o AgentGroupChat
; no entanto, primeiro certifique-se de redefinir o estado de conclusão de qualquer chamada anterior.
Nota: Falhas de serviço estão sendo detetadas e exibidas para evitar travar o 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
Os agentes estão atualmente indisponíveis em Java.
Final
Juntando todas as etapas, temos o código final para este exemplo. A implementação completa é fornecida abaixo.
Tente usar estas sugestões de dados de entrada:
- Olá
- {"mensagem: "Olá mundo"}
- {"mensagem": "Olá mundo"}
- O Semantic Kernel (SK) é um SDK de código aberto que permite aos desenvolvedores criar e orquestrar fluxos de trabalho complexos de IA que envolvem processamento de linguagem natural (NLP) e modelos de aprendizado de máquina. 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 facilmente combinar diferentes serviços e modelos de IA, definir seus relacionamentos e orquestrar interações entre eles.
- Divida isto em dois parágrafos
- Obrigado
- Sufrágio feminino
- Está bom, mas será que está pronto para o meu professor universitário?
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();
}
}
}
Juntando todas as etapas, temos agora o código final para este exemplo. A implementação completa é mostrada abaixo.
Você pode tentar usar uma das entradas sugeridas. À medida que o bate-papo do agente começa, os agentes trocarão mensagens por várias iterações até que o agente revisor esteja satisfeito com o trabalho do redator. O loop de while
garante que a conversa continue, mesmo que o bate-papo seja inicialmente considerado completo, redefinindo o sinalizador de is_complete
para False
.
- Rozes são vermelhos, violetz são azuis.
- O Semantic Kernel (SK) é um SDK de código aberto que permite aos desenvolvedores criar e orquestrar fluxos de trabalho complexos de IA que envolvem processamento de linguagem natural (NLP) e modelos de aprendizado de máquina. 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 facilmente combinar diferentes serviços e modelos de IA, definir seus relacionamentos e orquestrar interações entre eles.
- Faça estes dois parágrafos
- Obrigado
- @WomensSuffrage.txt
- Está bom, mas será que está pronto para o meu professor universitário?
Dica
Você pode fazer referência a qualquer arquivo fornecendo @<file_path_to_file>
. Para fazer referência ao texto "WomensSuffrage" acima, faça o download aqui e coloque-o no seu diretório de trabalho presente. Em seguida, podes consultá-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, como mostrado acima, em nosso repositório.
Os agentes estão atualmente indisponíveis em Java.