Procédure : Coordonner la collaboration de l’agent à l’aide de la conversation de groupe d’agents
Essentiel
Cette fonctionnalité est à l’étape expérimentale. Les fonctionnalités à ce stade sont toujours en cours de développement et soumises à des modifications avant de passer à la phase de préversion ou de version candidate.
Vue d’ensemble
Dans cet exemple, nous allons découvrir comment utiliser AgentGroupChat
pour coordonner la collaboration de deux agents différents qui travaillent pour passer en revue et réécrire le contenu fourni par l’utilisateur. Chaque agent est affecté à un rôle distinct :
- Réviseur : révise et donne des orientations au Rédacteur.
- Enregistreur : met à jour le contenu utilisateur en fonction de l’entrée du réviseur .
L’approche sera décomposée pas à pas pour éclairer les principales parties du processus de codage.
Mise en route
Avant de commencer le codage des fonctionnalités, vérifiez que votre environnement de développement est entièrement configuré et prêt.
Conseil / Astuce
Cet exemple utilise un fichier texte facultatif dans le cadre du traitement. Si vous souhaitez l’utiliser, vous pouvez le télécharger ici. Placez le fichier dans votre répertoire de travail de code.
Commencez par créer un projet console . Ensuite, incluez les références de package suivantes pour vous assurer que toutes les dépendances requises sont disponibles.
Pour ajouter des dépendances de package à partir de la ligne de commande, utilisez la dotnet
commande :
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
Si vous gérez des packages NuGet dans Visual Studio, assurez-vous que l'option
Include prerelease
est cochée.
Le fichier projet (.csproj
) doit contenir les définitions suivantes PackageReference
:
<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>
Le Agent Framework
est expérimental et nécessite une suppression des avertissements. Cela peut être traité en tant que propriété dans le fichier projet (.csproj
) :
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
Conseil / Astuce
Cet exemple utilise un fichier texte facultatif dans le cadre du traitement. Si vous souhaitez l’utiliser, vous pouvez le télécharger ici. Placez le fichier dans votre répertoire de travail de code.
Commencez par installer le package Python du noyau sémantique.
pip install semantic-kernel
Ajoutez ensuite les importations requises.
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
Les agents sont actuellement indisponibles en Java.
Configuration
Cet exemple nécessite un paramètre de configuration pour se connecter aux services distants. Vous devez définir des paramètres pour 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"
La classe suivante est utilisée dans tous les exemples agent. Veillez à l’inclure dans votre projet pour garantir une fonctionnalité appropriée. Cette classe sert de composant fondamental pour les exemples qui suivent.
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();
}
}
Le moyen le plus rapide de bien démarrer avec la configuration appropriée pour exécuter l’exemple de code consiste à créer un .env
fichier à la racine de votre projet (où votre script est exécuté). L’exemple nécessite que vous ayez des ressources Azure OpenAI ou OpenAI disponibles.
Configurez les paramètres suivants dans votre .env
fichier pour 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=""
Une fois configurées, les classes de service IA respectives récupèrent les variables requises et les utilisent pendant l’instanciation.
Les agents sont actuellement indisponibles en Java.
Codage
Le processus de codage de cet exemple implique :
- Configuration : initialisation des paramètres et du plug-in.
-
Agent
Définition : créez les deux instances deChatCompletionAgent
(Réviseur et Writer). -
Chat Définition - Créer les
AgentGroupChat
et les stratégies associées. - Boucle de conversation : écrivez la boucle qui pilote l’interaction utilisateur/agent.
L’exemple de code complet est fourni dans la section Finale . Reportez-vous à cette section pour l’implémentation complète.
Programme d’installation
Avant de créer une ChatCompletionAgent
, les paramètres de configuration, les plug-ins et les Kernel
doivent être initialisés.
Instanciez la Settings
classe référencée dans la section Configuration précédente.
Settings settings = new();
Les agents sont actuellement indisponibles en Java.
Initialisez maintenant une Kernel
instance avec un IChatCompletionService
.
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
Kernel kernel = builder.Build();
Initialisez l’objet noyau :
kernel = Kernel()
Les agents sont actuellement indisponibles en Java.
Nous allons également créer une deuxième instance de Kernel
via clonage et ajouter un plug-in qui permettra au processus de révision de placer le contenu mis à jour sur le presse-papiers.
Kernel toolKernel = kernel.Clone();
toolKernel.Plugins.AddFromType<ClipboardAccess>();
::: zone-end
Les agents sont actuellement indisponibles en Java.
Le plug-in Presse-papiers peut être défini comme partie de l’exemple.
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();
}
}
Les agents sont actuellement indisponibles en Java.
Définition de l’agent
Déclarons les noms des agents en tant que const
afin qu'ils puissent être référencés dans les stratégies AgentGroupChat
:
const string ReviewerName = "Reviewer";
const string WriterName = "Writer";
Nous allons déclarer les noms des agents en tant que « Réviseur » et « Rédacteur ».
REVIEWER_NAME = "Reviewer"
COPYWRITER_NAME = "Writer"
Les agents sont actuellement indisponibles en Java.
La définition de l’agent Réviseur utilise le modèle exploré dans How-To : Chat Completion Agent.
Ici, le réviseur a le rôle de répondre aux entrées utilisateur, de fournir une direction à l’agent Writer et de vérifier le résultat de l’agent 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.
""",
)
Les agents sont actuellement indisponibles en Java.
L'agent Writer est similaire, mais ne nécessite pas la spécification des paramètres d’exécution , étant donné qu’il n’est pas configuré avec un module complémentaire.
Ici, l'écrivain reçoit une tâche à usage unique : suivre la direction et réécrire le contenu.
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,
};
L’agent Writer est similaire. On lui confie une tâche unique, en suivant les instructions et en réécrivant le contenu.
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.
""",
)
Les agents sont actuellement indisponibles en Java.
Définition du tchat
La définition du AgentGroupChat
nécessite de prendre en compte les stratégies de sélection du tour de Agent
et de déterminer quand quitter la boucle de Chat. Pour ces deux considérations, nous allons définir une fonction d’invite de noyau.
La première à réfléchir sur la sélection de Agent
:
L’utilisation de AgentGroupChat.CreatePromptFunctionForStrategy
fournit un mécanisme pratique pour éviter encodage HTML le paramètre de 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}}}}
"""
)
Les agents sont actuellement indisponibles en Java.
La deuxième évalue quand quitter la boucle Chat :
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}}}}
"""
)
Les agents sont actuellement indisponibles en Java.
Ces deux stratégies n’auront besoin que de connaître le message de conversation le plus récent. Cela permet de réduire l’utilisation des jetons et d’améliorer les performances :
ChatHistoryTruncationReducer historyReducer = new(1);
history_reducer = ChatHistoryTruncationReducer(target_count=1)
Les agents sont actuellement indisponibles en Java.
Enfin, nous sommes prêts à rassembler tout dans notre définition AgentGroupChat
.
La création AgentGroupChat
implique :
- Incluez les deux agents dans le constructeur.
- Définissez une
KernelFunctionSelectionStrategy
en utilisant l’instanceKernelFunction
et l’instanceKernel
précédemment définie. - Définissez une
KernelFunctionTerminationStrategy
en utilisant l’instanceKernelFunction
et l’instanceKernel
précédemment définie.
Notez que chaque stratégie est responsable de l’analyse du KernelFunction
résultat.
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!");
La création AgentGroupChat
implique :
- Incluez les deux agents dans le constructeur.
- Définissez une
KernelFunctionSelectionStrategy
en utilisant l’instanceKernelFunction
et l’instanceKernel
précédemment définie. - Définissez une
KernelFunctionTerminationStrategy
en utilisant l’instanceKernelFunction
et l’instanceKernel
précédemment définie.
Notez que chaque stratégie est responsable de l’analyse du KernelFunction
résultat.
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,
),
)
Le lastmessage
history_variable_name
correspond à la KernelFunctionSelectionStrategy
et à l’invite KernelFunctionTerminationStrategy
qui ont été définies ci-dessus. C'est là que le dernier message est placé lors de l'affichage de l'invite.
Les agents sont actuellement indisponibles en Java.
Boucle de conversation
Enfin, nous sommes en mesure de coordonner l’interaction entre l’utilisateur et le AgentGroupChat
. Commencez par créer une boucle vide.
Remarque : Contrairement aux autres exemples, aucun historique externe ou thread n’est géré.
AgentGroupChat
gère l’historique des conversations en interne.
bool isComplete = false;
do
{
} while (!isComplete);
is_complete: bool = False
while not is_complete:
# operational logic
Les agents sont actuellement indisponibles en Java.
Nous allons maintenant capturer l’entrée utilisateur dans la boucle précédente. Dans ce cas :
- L’entrée vide est ignorée
- Le terme
EXIT
signale que la conversation est terminée - Le terme
RESET
effacera l’historique desAgentGroupChat
- Tout terme commençant par
@
sera traité comme un chemin d’accès de fichier dont le contenu sera fourni comme entrée - Une entrée valide est ajoutée au
AgentGroupChat
en tant que message utilisateur .
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));
Nous allons maintenant capturer l’entrée utilisateur dans la boucle précédente. Dans ce cas :
- L’entrée vide est ignorée.
- Le terme
exit
signale que la conversation est terminée. - Le terme
reset
effacera l’historiqueAgentGroupChat
. - Tout terme commençant par
@
sera traité comme un chemin d’accès de fichier dont le contenu sera fourni comme entrée. - Une entrée valide est ajoutée au
AgentGroupChat
en tant que message utilisateur .
La logique d’opération à l’intérieur de la boucle while ressemble à ceci :
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)
Les agents sont actuellement indisponibles en Java.
Pour lancer la collaboration Agent
en réponse à l’entrée de l’utilisateur et afficher les réponses Agent
, appelez la AgentGroupChat
; Toutefois, veillez d’abord à réinitialiser l’état d’achèvement à partir d’un appel antérieur.
Remarque : Les échecs de service sont interceptés et affichés pour éviter de bloquer la boucle de conversation.
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
Les agents sont actuellement indisponibles en Java.
Finale
En rassemblant toutes les étapes, nous obtenons le code final de cet exemple. L’implémentation complète est fournie ci-dessous.
Essayez d’utiliser ces entrées suggérées :
- Salut
- {"message : « hello world"}
- {"message » : « hello world"}
- Le noyau sémantique (SK) est un SDK open source qui permet aux développeurs de créer et d’orchestrer des flux de travail IA complexes qui impliquent un traitement en langage naturel (NLP) et des modèles Machine Learning. Il fournit une plateforme flexible pour intégrer des fonctionnalités IA telles que la recherche sémantique, le résumé de texte et les systèmes de dialogue dans les applications. Avec sk, vous pouvez facilement combiner différents services et modèles IA, définir leurs relations et orchestrer les interactions entre eux.
- faire de cela deux paragraphes
- Merci
- @.\DroitDeVoteDesFemmes.txt
- c’est bon, mais est-il prêt pour mon professeur d’université ?
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();
}
}
}
En rassemblant toutes les étapes, nous avons maintenant le code final pour cet exemple. L’implémentation complète est indiquée ci-dessous.
Vous pouvez essayer d’utiliser l’une des entrées suggérées. À mesure que la conversation de l’agent commence, les agents échangent des messages pour plusieurs itérations jusqu’à ce que l’agent réviseur soit satisfait du travail du rédacteur. La boucle while
garantit que la conversation continue, même si la conversation est initialement considérée comme terminée, en réinitialisant l’indicateur de is_complete
pour False
.
- Les roses sont rouges, les violettes sont bleues.
- Le noyau sémantique (SK) est un SDK open source qui permet aux développeurs de créer et d’orchestrer des flux de travail IA complexes qui impliquent un traitement en langage naturel (NLP) et des modèles Machine Learning. Il fournit une plateforme flexible pour intégrer des fonctionnalités IA telles que la recherche sémantique, le résumé de texte et les systèmes de dialogue dans les applications. Avec sk, vous pouvez facilement combiner différents services et modèles IA, définir leurs relations et orchestrer les interactions entre eux.
- Transformer cela en deux paragraphes
- Merci
- @WomensSuffrage.txt
- C’est bon, mais est-ce prêt pour mon professeur d’université ?
Conseil / Astuce
Vous pouvez référencer n’importe quel fichier en fournissant @<file_path_to_file>
. Pour référencer le texte « WomensSuffrage » ci-dessus, téléchargez-le ici et placez-le dans votre répertoire de travail actuel. Vous pouvez ensuite le référencer avec @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())
Vous trouverez peut-être le code complet , comme indiqué ci-dessus, dans notre dépôt.
Les agents sont actuellement indisponibles en Java.