Gewusst wie: Koordinieren der Agentzusammenarbeit mithilfe des Agentgruppenchats (Experimental)
Warnung
Das semantische Kernel-Agent-Framework ist experimentell, befindet sich noch in der Entwicklung und unterliegt Änderungen.
Übersicht
In diesem Beispiel erfahren Sie, wie Sie mithilfe des Agent-Gruppenchats die Sortierung von zwei verschiedenen Agents koordinieren, die an der Überprüfung und Umschreibung von vom Benutzer bereitgestellten Inhalten arbeiten. Jedem Agent wird eine unterschiedliche Rolle zugewiesen:
- Prüfer: Überprüft und stellt Die Richtung zu Writer bereit.
- Writer: Aktualisiert Benutzerinhalte basierend auf der Bearbeitereingabe .
Der Ansatz wird schrittweise auf ein hohes Licht auf die wichtigsten Teile des Codierungsprozesses aufgeteilt.
Erste Schritte
Bevor Sie mit der Featurecodierung fortfahren, stellen Sie sicher, dass Ihre Entwicklungsumgebung vollständig eingerichtet und konfiguriert ist.
Erstellen Sie zunächst ein Konsolenprojekt . Schließen Sie dann die folgenden Paketverweise ein, um sicherzustellen, dass alle erforderlichen Abhängigkeiten verfügbar sind.
Verwenden Sie den dotnet
Befehl, um Paketabhängigkeiten aus der Befehlszeile hinzuzufügen:
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
Wenn Sie NuGet-Pakete in Visual Studio verwalten, stellen Sie sicher, dass
Include prerelease
sie aktiviert ist.
Die Projektdatei (.csproj
) sollte die folgenden PackageReference
Definitionen enthalten:
<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>
Das Agent Framework ist experimentell und erfordert eine Warnunterdrückung. Dies kann als Eigenschaft in der Projektdatei adressiert werden (.csproj
):
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007;IDE1006;SKEXP0001;SKEXP0110;OPENAI001</NoWarn>
</PropertyGroup>
import asyncio
import os
import copy
import pyperclip # Install via pip
from semantic_kernel.agents import AgentGroupChat, ChatCompletionAgent
from semantic_kernel.agents.strategies.selection.kernel_function_selection_strategy import (
KernelFunctionSelectionStrategy,
)
from semantic_kernel.agents.strategies.termination.kernel_function_termination_strategy import (
KernelFunctionTerminationStrategy,
)
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import AzureChatCompletion
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.functions.kernel_function_from_prompt import KernelFunctionFromPrompt
from semantic_kernel.kernel import Kernel
Agents sind derzeit in Java nicht verfügbar.
Konfiguration
In diesem Beispiel ist konfigurationseinstellung erforderlich, um eine Verbindung mit Remotediensten herzustellen. Sie müssen Einstellungen für Open AI oder Azure Open AI definieren.
# Open AI
dotnet user-secrets set "OpenAISettings:ApiKey" "<api-key>"
dotnet user-secrets set "OpenAISettings:ChatModel" "gpt-4o"
# Azure Open AI
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"
Die folgende Klasse wird in allen Agent-Beispielen verwendet. Achten Sie darauf, sie in Ihr Projekt einzuschließen, um die ordnungsgemäße Funktionalität sicherzustellen. Diese Klasse dient als grundlegende Komponente für die folgenden Beispiele.
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();
}
}
Die schnellste Möglichkeit, mit der richtigen Konfiguration zu beginnen, um den Beispielcode auszuführen, besteht darin, eine .env
Datei im Stammverzeichnis Ihres Projekts zu erstellen (wo Ihr Skript ausgeführt wird).
Konfigurieren Sie die folgenden Einstellungen in Ihrer .env
Datei für Azure OpenAI oder OpenAI:
AZURE_OPENAI_API_KEY="..."
AZURE_OPENAI_ENDPOINT="https://..."
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME="..."
AZURE_OPENAI_API_VERSION="..."
OPENAI_API_KEY="sk-..."
OPENAI_ORG_ID=""
OPENAI_CHAT_MODEL_ID=""
Nach der Konfiguration werden die entsprechenden KI-Dienstklassen die erforderlichen Variablen abholen und während der Instanziierung verwenden.
Agents sind derzeit in Java nicht verfügbar.
Codieren
Der Codierungsprozess für dieses Beispiel umfasst:
- Setup – Initialisieren von Einstellungen und dem Plug-In.
- Agentdefinition – Erstellen Sie die beiden Chatabschluss-Agent-Instanzen (Prüfer und Writer).
- Chatdefinition – Erstellen sie den Agentgruppenchat und die zugehörigen Strategien.
- Die Chatschleife – Schreiben Sie die Schleife , die die Benutzer-/Agent-Interaktion steuert.
Der vollständige Beispielcode wird im Abschnitt "Final " bereitgestellt. Weitere Informationen finden Sie in diesem Abschnitt für die vollständige Implementierung.
Setup
Vor dem Erstellen eines Chat-Vervollständigungs-Agents müssen die Konfigurationseinstellungen, Plug-Ins und Kernel initialisiert werden.
Instanziieren Sie die Settings
Klasse, auf die im vorherigen Konfigurationsabschnitt verwiesen wird.
Settings settings = new();
Agents sind derzeit in Java nicht verfügbar.
Initialisieren Sie nun eine Kernel
Instanz mit einer IChatCompletionService
.
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
Kernel kernel = builder.Build();
Initialisieren des Kernelobjekts:
kernel = Kernel()
Agents sind derzeit in Java nicht verfügbar.
Lassen Sie uns auch eine zweite Kernelinstanz über Klonen erstellen und ein Plug-In hinzufügen, mit dem der Umbruch aktualisierte Inhalte auf dem Clipboard platzieren kann.
Kernel toolKernel = kernel.Clone();
toolKernel.Plugins.AddFromType<ClipboardAccess>();
tool_kernel = copy.deepcopy(kernel)
tool_kernel.add_plugin(ClipboardAccess(), plugin_name="clipboard")
Agents sind derzeit in Java nicht verfügbar.
Das Zwischenablage-Plug-In kann als Teil des Beispiels definiert werden.
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();
}
}
Hinweis: Wir nutzen ein Python-Paket namens Pyperclip. Bitte installieren Sie pip.
class ClipboardAccess:
@kernel_function
def set_clipboard(content: str):
if not content.strip():
return
pyperclip.copy(content)
Agents sind derzeit in Java nicht verfügbar.
Agentdefinition
Deklarieren wir die Agentnamen so, dass const
auf sie in Den Strategien für Agentgruppenchat verwiesen werden kann:
const string ReviewerName = "Reviewer";
const string WriterName = "Writer";
REVIEWER_NAME = "Reviewer"
WRITER_NAME = "Writer"
Agents sind derzeit in Java nicht verfügbar.
Das Definieren des Prüfer-Agents verwendet das Muster, das in How-To: Chat Completion Agent untersucht wird.
Hier erhält der Prüfer die Rolle, auf Benutzereingaben zu reagieren, die Richtung zum Writer-Agent bereitzustellen und das Ergebnis des Writer-Agents zu überprüfen.
ChatCompletionAgent agentReviewer =
new()
{
Name = ReviewerName,
Instructions =
"""
Your responsiblity 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(
service_id=REVIEWER_NAME,
kernel=_create_kernel_with_chat_completion(REVIEWER_NAME),
name=REVIEWER_NAME,
instructions="""
Your responsiblity 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.
""",
)
Agents sind derzeit in Java nicht verfügbar.
Der Writer-Agent ist ähnlich, erfordert jedoch keine Spezifikation der Ausführungseinstellungen , da er nicht mit einem Plug-In konfiguriert ist.
Hier erhält der Writer eine Einzelaufgabe, folgt der Richtung, und schreiben Sie den Inhalt neu.
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,
};
agent_writer = ChatCompletionAgent(
service_id=COPYWRITER_NAME,
kernel=_create_kernel_with_chat_completion(COPYWRITER_NAME),
name=COPYWRITER_NAME,
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.
""",
)
Agents sind derzeit in Java nicht verfügbar.
Chatdefinition
Zum Definieren des Agentgruppenchats müssen die Strategien für die Auswahl der Agent-Aktivierung berücksichtigt und ermittelt werden, wann die Chatschleife beendet werden soll. Für beide Aspekte definieren wir eine Kernel-Eingabeaufforderungsfunktion.
Der erste Grund für die Auswahl des Agents :
Die Verwendung AgentGroupChat.CreatePromptFunctionForStrategy
bietet einen praktischen Mechanismus, um die HTML-Codierung des Nachrichtenparameters zu vermeiden.
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"""
Determine which participant takes the next turn in a conversation based on the the most recent participant.
State only the name of the participant to take the next turn.
No participant should take more than one turn in a row.
Choose only from these participants:
- {REVIEWER_NAME}
- {COPYWRITER_NAME}
Always follow these rules when selecting the next participant:
- After user input, it is {COPYWRITER_NAME}'s turn.
- After {COPYWRITER_NAME} replies, it is {REVIEWER_NAME}'s turn.
- After {REVIEWER_NAME} provides feedback, it is {COPYWRITER_NAME}'s turn.
History:
{{{{$history}}}}
""",
)
Agents sind derzeit in Java nicht verfügbar.
Die zweite wird ausgewertet, wann die Chatschleife beendet werden soll:
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 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:
{{{{$history}}}}
""",
)
Agents sind derzeit in Java nicht verfügbar.
Beide Strategien erfordern nur Kenntnisse über die neueste Chatnachricht. Dadurch wird die Tokennutzung reduziert und die Leistung verbessert:
ChatHistoryTruncationReducer historyReducer = new(1);
**ChatHistoryReducer is coming soon to Python.**
Agents sind derzeit in Java nicht verfügbar.
Schließlich sind wir bereit, alles in unserer Definition des Agent-Gruppenchats zusammenzubringen.
Das Erstellen AgentGroupChat
umfasst Folgendes:
- Schließen Sie beide Agents in den Konstruktor ein.
- Definieren Sie eine
KernelFunctionSelectionStrategy
Verwendung der zuvor definiertenKernelFunction
undKernel
Instanz. - Definieren Sie eine
KernelFunctionTerminationStrategy
Verwendung der zuvor definiertenKernelFunction
undKernel
Instanz.
Beachten Sie, dass jede Strategie für die Analyse des KernelFunction
Ergebnisses verantwortlich ist.
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!");
Das Erstellen AgentGroupChat
umfasst Folgendes:
- Schließen Sie beide Agents in den Konstruktor ein.
- Definieren Sie eine
KernelFunctionSelectionStrategy
Verwendung der zuvor definiertenKernelFunction
undKernel
Instanz. - Definieren Sie eine
KernelFunctionTerminationStrategy
Verwendung der zuvor definiertenKernelFunction
undKernel
Instanz.
Beachten Sie, dass jede Strategie für die Analyse des KernelFunction
Ergebnisses verantwortlich ist.
chat = AgentGroupChat(
agents=[agent_writer, agent_reviewer],
selection_strategy=KernelFunctionSelectionStrategy(
function=selection_function,
kernel=_create_kernel_with_chat_completion("selection"),
result_parser=lambda result: str(result.value[0]) if result.value is not None else COPYWRITER_NAME,
agent_variable_name="agents",
history_variable_name="history",
),
termination_strategy=KernelFunctionTerminationStrategy(
agents=[agent_reviewer],
function=termination_function,
kernel=_create_kernel_with_chat_completion("termination"),
result_parser=lambda result: TERMINATION_KEYWORD in str(result.value[0]).lower(),
history_variable_name="history",
maximum_iterations=10,
),
)
Agents sind derzeit in Java nicht verfügbar.
Die Chatschleife
Schließlich können wir die Interaktion zwischen dem Benutzer und dem Agent-Gruppenchat koordinieren. Erstellen Sie zunächst eine leere Schleife.
Hinweis: Im Gegensatz zu den anderen Beispielen wird kein externer Verlauf oder Thread verwaltet. Der Agentgruppenchat verwaltet den Aufgezeichneten Unterhaltungen intern.
bool isComplete = false;
do
{
} while (!isComplete);
is_complete: bool = False
while not is_complete:
# operational logic
Agents sind derzeit in Java nicht verfügbar.
Lassen Sie uns nun Benutzereingaben innerhalb der vorherigen Schleife erfassen. In diesem Fall:
- Leere Eingabe wird ignoriert.
- Der Begriff
EXIT
signalisiert, dass die Unterhaltung abgeschlossen ist. - Der Begriff
RESET
löscht den Verlauf des Agentgruppenchats . - Jeder Begriff, der beginnt
@
, wird als Dateipfad behandelt, dessen Inhalt als Eingabe bereitgestellt wird. - Gültige Eingaben werden der Agentgruppe Chaty als Benutzernachricht hinzugefügt.
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("[Converation 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));
user_input = input("User:> ")
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
if user_input.startswith("@") and len(input) > 1:
file_path = input[1:]
try:
if not os.path.exists(file_path):
print(f"Unable to access file: {file_path}")
continue
with open(file_path) as file:
user_input = file.read()
except Exception:
print(f"Unable to access file: {file_path}")
continue
await chat.add_chat_message(ChatMessageContent(role=AuthorRole.USER, content=user_input))
Agents sind derzeit in Java nicht verfügbar.
Um die Agent-Zusammenarbeit als Reaktion auf Benutzereingaben zu initieren und die Agentantworten anzuzeigen, rufen Sie den Agent-Gruppenchat auf. Stellen Sie jedoch zuerst sicher, dass Sie den Status "Abschluss" von jedem vorherigen Aufruf zurücksetzen.
Hinweis: Dienstfehler werden abgefangen und angezeigt, um zu vermeiden, dass die Unterhaltungsschleife abstürzt.
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 }));
}
}
}
chat.is_complete = False
async for response in chat.invoke():
print(f"# {response.role} - {response.name or '*'}: '{response.content}'")
if chat.is_complete:
is_complete = True
break
Agents sind derzeit in Java nicht verfügbar.
Endphase
Alle Schritte zusammenzuführen, haben wir den endgültigen Code für dieses Beispiel. Die vollständige Implementierung wird unten bereitgestellt.
// Copyright (c) Microsoft. All rights reserved.
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 responsiblity 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 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,
};
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("[Converation 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();
}
}
}
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import os
from semantic_kernel.agents import AgentGroupChat, ChatCompletionAgent
from semantic_kernel.agents.strategies.selection.kernel_function_selection_strategy import (
KernelFunctionSelectionStrategy,
)
from semantic_kernel.agents.strategies.termination.kernel_function_termination_strategy import (
KernelFunctionTerminationStrategy,
)
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import AzureChatCompletion
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.functions.kernel_function_from_prompt import KernelFunctionFromPrompt
from semantic_kernel.kernel import Kernel
###################################################################
# 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. #
###################################################################
class ClipboardAccess:
@kernel_function
def set_clipboard(content: str):
if not content.strip():
return
pyperclip.copy(content)
REVIEWER_NAME = "Reviewer"
COPYWRITER_NAME = "Writer"
def _create_kernel_with_chat_completion(service_id: str) -> Kernel:
kernel = Kernel()
kernel.add_service(AzureChatCompletion(service_id=service_id))
return kernel
async def main():
agent_reviewer = ChatCompletionAgent(
service_id=REVIEWER_NAME,
kernel=_create_kernel_with_chat_completion(REVIEWER_NAME),
name=REVIEWER_NAME,
instructions="""
Your responsiblity 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.
""",
)
agent_writer = ChatCompletionAgent(
service_id=COPYWRITER_NAME,
kernel=_create_kernel_with_chat_completion(COPYWRITER_NAME),
name=COPYWRITER_NAME,
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.
""",
)
selection_function = KernelFunctionFromPrompt(
function_name="selection",
prompt=f"""
Determine which participant takes the next turn in a conversation based on the the most recent participant.
State only the name of the participant to take the next turn.
No participant should take more than one turn in a row.
Choose only from these participants:
- {REVIEWER_NAME}
- {COPYWRITER_NAME}
Always follow these rules when selecting the next participant:
- After user input, it is {COPYWRITER_NAME}'s turn.
- After {COPYWRITER_NAME} replies, it is {REVIEWER_NAME}'s turn.
- After {REVIEWER_NAME} provides feedback, it is {COPYWRITER_NAME}'s turn.
History:
{{{{$history}}}}
""",
)
TERMINATION_KEYWORD = "yes"
termination_function = KernelFunctionFromPrompt(
function_name="termination",
prompt=f"""
Examine the RESPONSE and determine whether the content has been deemed satisfactory.
If 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:
{{{{$history}}}}
""",
)
chat = AgentGroupChat(
agents=[agent_writer, agent_reviewer],
selection_strategy=KernelFunctionSelectionStrategy(
function=selection_function,
kernel=_create_kernel_with_chat_completion("selection"),
result_parser=lambda result: str(result.value[0]) if result.value is not None else COPYWRITER_NAME,
agent_variable_name="agents",
history_variable_name="history",
),
termination_strategy=KernelFunctionTerminationStrategy(
agents=[agent_reviewer],
function=termination_function,
kernel=_create_kernel_with_chat_completion("termination"),
result_parser=lambda result: TERMINATION_KEYWORD in str(result.value[0]).lower(),
history_variable_name="history",
maximum_iterations=10,
),
)
is_complete: bool = False
while not is_complete:
user_input = input("User:> ")
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
if user_input.startswith("@") and len(input) > 1:
file_path = input[1:]
try:
if not os.path.exists(file_path):
print(f"Unable to access file: {file_path}")
continue
with open(file_path) as file:
user_input = file.read()
except Exception:
print(f"Unable to access file: {file_path}")
continue
await chat.add_chat_message(ChatMessageContent(role=AuthorRole.USER, content=user_input))
async for response in chat.invoke():
print(f"# {response.role} - {response.name or '*'}: '{response.content}'")
if chat.is_complete:
is_complete = True
break
if __name__ == "__main__":
asyncio.run(main())
Agents sind derzeit in Java nicht verfügbar.