Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Important
Il s’agit d’un document archivé.
Important
Cette fonctionnalité est à l’étape expérimentale, mais n’est plus conservée. Pour consulter un remplacement, consultez la Group Chat Orchestration et le guide Migrating from AgentChat to Group Chat Orchestration.
Aperçu
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 : examine le travail 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.
Pour commencer
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
Include prereleaseest coché.
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 la 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
Fonctionnalité actuellement indisponible en Java.
Paramétrage
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.
Fonctionnalité actuellement indisponible en Java.
Codage
Le processus de codage de cet exemple implique :
- Configuration : initialisation des paramètres et du plug-in.
-
AgentDéfinition : créez les deux instances deChatCompletionAgent(Réviseur et Writer). -
Chat Définition - Créer les
AgentGroupChatet 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.
Configuration
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();
Fonctionnalité actuellement indisponible 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()
Fonctionnalité actuellement indisponible 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>();
Fonctionnalité actuellement indisponible 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();
}
}
Fonctionnalité actuellement indisponible 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"
Fonctionnalité actuellement indisponible 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.
""",
)
Fonctionnalité actuellement indisponible en Java.
L’agent Writer est similaire, mais ne nécessite pas la spécification des paramètres d’exécution, car il n’est pas configuré avec un plug-in.
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 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,
};
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.
""",
)
Fonctionnalité actuellement indisponible 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}}}}
"""
)
Fonctionnalité actuellement indisponible 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}}}}
"""
)
Fonctionnalité actuellement indisponible 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)
Fonctionnalité actuellement indisponible 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
KernelFunctionSelectionStrategyen utilisant l’instanceKernelFunctionet l’instanceKernelprécédemment définie. - Définissez une
KernelFunctionTerminationStrategyen utilisant l’instanceKernelFunctionet l’instanceKernelpré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
KernelFunctionSelectionStrategyen utilisant l’instanceKernelFunctionet l’instanceKernelprécédemment définie. - Définissez une
KernelFunctionTerminationStrategyen utilisant l’instanceKernelFunctionet l’instanceKernelpré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 lastmessagehistory_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.
Fonctionnalité actuellement indisponible 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é.
AgentGroupChatgère l’historique des conversations en interne.
bool isComplete = false;
do
{
} while (!isComplete);
is_complete: bool = False
while not is_complete:
# operational logic
Fonctionnalité actuellement indisponible 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
EXITsignale que la conversation est terminée - Le terme
RESETeffacera 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
AgentGroupChaten 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
exitsignale que la conversation est terminée. - Le terme
reseteffacera 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
AgentGroupChaten 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)
Fonctionnalité actuellement indisponible 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
Fonctionnalité actuellement indisponible en Java.
Finale
En rassemblant toutes les étapes, nous obtenons le code final pour 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.
Fonctionnalité actuellement indisponible en Java.