Freigeben über


Anleitung: Koordinieren der Agent-Zusammenarbeit mithilfe des Agent-Gruppenchats

Von Bedeutung

Dies ist ein archiviertes Dokument.

Von Bedeutung

Dieses Feature befindet sich in der experimentellen Phase, wird aber nicht mehr beibehalten. Einen Ersatz finden Sie unter " Group Chat Orchestration" und dem Migrationsleitfaden für die Migration von AgentChat zu "Group Chat Orchestration".

Überblick

In diesem Beispiel werden wir untersuchen, wie Sie AgentGroupChat verwenden, um die Zusammenarbeit von zwei verschiedenen Agents zu 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 gibt die Richtung an den Autor/die Autorin vor.
  • Writer: Aktualisiert Benutzerinhalte basierend auf der Bearbeitereingabe.

Der Ansatz wird schrittweise erklärt, um die wichtigsten Teile des Codierungsprozesses hervorzuheben.

Erste Schritte

Bevor Sie mit der Featurecodierung fortfahren, stellen Sie sicher, dass Ihre Entwicklungsumgebung vollständig eingerichtet und konfiguriert ist.

Tipp

In diesem Beispiel wird eine optionale Textdatei als Teil der Verarbeitung verwendet. Wenn Sie es verwenden möchten, können Sie es hierherunterladen. Platzieren Sie die Datei in Ihrem Codearbeitsverzeichnis.

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 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>

Die Agent Framework ist experimentell und erfordert die Unterdrückung von Warnmeldungen. Dies kann als Eigenschaft in der Projektdatei adressiert werden (.csproj):

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

Tipp

In diesem Beispiel wird eine optionale Textdatei als Teil der Verarbeitung verwendet. Wenn Sie es verwenden möchten, können Sie es hierherunterladen. Platzieren Sie die Datei in Ihrem Codearbeitsverzeichnis.

Installieren Sie zunächst das Semantic Kernel Python-Paket.

pip install semantic-kernel

Fügen Sie als Nächstes die erforderlichen Importe hinzu.

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

Das Feature ist 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 OpenAI oder Azure OpenAI definieren.

# 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"

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). Das Beispiel erfordert, dass Azure OpenAI- oder OpenAI-Ressourcen verfügbar sind.

Konfigurieren Sie die folgenden Einstellungen in Ihrer .env Datei für Azure OpenAI oder 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=""

Nach der Konfiguration werden die entsprechenden KI-Dienstklassen die erforderlichen Variablen abholen und während der Instanziierung verwenden.

Das Feature ist derzeit in Java nicht verfügbar.

Codieren

Der Codierungsprozess für dieses Beispiel umfasst:

  1. Setup – Initialisieren von Einstellungen und dem Plug-In.
  2. Agent Definition – Erstellen Sie die beiden ChatCompletionAgent Instanzen (Prüfer und Schreiber).
  3. Chat Definition – Erstellen Sie die AgentGroupChat und die zugehörigen Strategien.
  4. Die Chatschleife – Schreiben Sie die Schleife , die die Benutzer-/Agent-Interaktion steuert.

Der vollständige Beispielcode wird im Abschnitt "Final " bereitgestellt. Lesen Sie diesen Abschnitt für die vollständige Implementierung.

Konfiguration

Vor der Erstellung von ChatCompletionAgentmüssen die Konfigurationseinstellungen, Plug-Ins und Kernel initialisiert werden.

Instanziieren Sie die Settings Klasse, auf die im vorherigen Konfigurationsabschnitt verwiesen wird.

Settings settings = new();

Das Feature ist 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()

Das Feature ist derzeit in Java nicht verfügbar.

Lassen Sie uns auch eine zweite Kernel Instanz über Klonen erstellen und ein Plug-In hinzufügen, mit dem die Überprüfung aktualisierte Inhalte auf dem Clipboard platzieren kann.

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

Das Feature ist derzeit in Java nicht verfügbar.

Das Zwischenablage-Plugin kann möglicherweise 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();
    }
}

Das Feature ist derzeit in Java nicht verfügbar.

Definition des Agenten

Deklarieren wir die Agentnamen als const, damit sie in AgentGroupChat Strategien referenziert werden können:

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

Wir deklarieren die Agentnamen als "Reviewer" und "Writer".

REVIEWER_NAME = "Reviewer"
COPYWRITER_NAME = "Writer"

Das Feature ist 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 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.
""",
)

Das Feature ist 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 hat der Writer die Aufgabe, eine Anweisung zu befolgen und den Inhalt umzuschreiben.

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,
    };

Der Writer-Agent ist ähnlich. Es wird eine einmalige Aufgabe gestellt: Befolgen Sie die Anweisungen und überarbeiten Sie den Inhalt.

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.
""",
    )

Das Feature ist derzeit in Java nicht verfügbar.

Definition von Chat

Zum Definieren der AgentGroupChat müssen die Strategien zur Auswahl der Agent Runde berücksichtigt werden und der Zeitpunkt bestimmt werden, zu dem die Chat-Schleife zu beenden ist. Für beide Aspekte definieren wir eine Kernel-Eingabeaufforderungsfunktion.

Der Erste, der über die Auswahl von Agent nachdenkt.

Die Verwendung von AgentGroupChat.CreatePromptFunctionForStrategy bietet einen praktischen Mechanismus, um HTML-Codierung den Nachrichtenparameter 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"""
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}}}}
"""
)

Das Feature ist derzeit in Java nicht verfügbar.

Die zweite wird auswerten, wann man die Chatschleife verlassen 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 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}}}}
"""
)

Das Feature ist 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);
history_reducer = ChatHistoryTruncationReducer(target_count=1)

Das Feature ist derzeit in Java nicht verfügbar.

Schließlich sind wir bereit, alles in unserer AgentGroupChat Definition zusammenzubringen.

Das Erstellen AgentGroupChat umfasst Folgendes:

  1. Schließen Sie beide Agenten in den Konstruktor ein.
  2. Definieren Sie eine KernelFunctionSelectionStrategy unter Verwendung von der zuvor definierten KernelFunction und der Kernel Instanz.
  3. Definieren Sie eine KernelFunctionTerminationStrategy unter Verwendung von der zuvor definierten KernelFunction und der Kernel 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:

  1. Schließen Sie beide Agenten in den Konstruktor ein.
  2. Definieren Sie eine KernelFunctionSelectionStrategy unter Verwendung von der zuvor definierten KernelFunction und der Kernel Instanz.
  3. Definieren Sie eine KernelFunctionTerminationStrategy unter Verwendung von der zuvor definierten KernelFunction und der Kernel Instanz.

Beachten Sie, dass jede Strategie für die Analyse des KernelFunction Ergebnisses verantwortlich ist.

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,
    ),
)

Die lastmessagehistory_variable_name entspricht dem KernelFunctionSelectionStrategy und der KernelFunctionTerminationStrategy Eingabeaufforderung, die oben definiert wurde. Hier wird die letzte Nachricht beim Rendern der Eingabeaufforderung platziert.

Das Feature ist derzeit in Java nicht verfügbar.

Die Chatschleife

Schließlich können wir die Interaktion zwischen dem Benutzer und dem AgentGroupChatkoordinieren. Beginnen Sie damit, eine leere Schleife zu erstellen.

Hinweis: Im Gegensatz zu den anderen Beispielen wird keine externe Historie oder Thread verwaltet. AgentGroupChat verwaltet den Konversationsverlauf intern.

bool isComplete = false;
do
{

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

Das Feature ist 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 Ausdruck RESET wird den Verlauf von AgentGroupChat löschen.
  • Jeder Begriff, der beginnt @ , wird als Dateipfad behandelt, dessen Inhalt als Eingabe bereitgestellt wird.
  • Gültige Eingaben werden der AgentGroupChat 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("[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));

Lassen Sie uns nun Benutzereingaben innerhalb der vorherigen Schleife erfassen. In diesem Fall:

  • Leere Eingaben werden ignoriert.
  • Der Begriff exit signalisiert, dass die Unterhaltung abgeschlossen ist.
  • Der Ausdruck reset wird den AgentGroupChat Verlauf löschen.
  • Jeder Begriff, der mit @ beginnt, wird als Dateipfad behandelt, dessen Inhalt als Eingabe bereitgestellt wird.
  • Gültige Eingaben werden der AgentGroupChat als Benutzernachricht hinzugefügt.

Die Vorgangslogik innerhalb der While-Schleife sieht wie folgt aus:

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)

Das Feature ist derzeit in Java nicht verfügbar.

Rufen Sie die Agent auf, um die Agent Zusammenarbeit als Reaktion auf die Benutzereingabe zu starten und die AgentGroupChat Antworten anzuzeigen. Stellen Sie jedoch sicher, dass Sie den Zustand der Completion von allen vorherigen Aufrufen zurücksetzen.

Hinweis: Dienstfehler werden erfasst und angezeigt, um zu verhindern, 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 }));
        }
    }
}
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

Das Feature ist derzeit in Java nicht verfügbar.

Finale

Wenn wir alle Schritte zusammenführen, erhalten wir den endgültigen Code für dieses Beispiel. Die vollständige Implementierung wird unten bereitgestellt.

Versuchen Sie, diese vorgeschlagenen Eingaben zu verwenden:

  1. Hallo
  2. {"message: "Hallo Welt"}
  3. {"message": "hallo Welt"}
  4. Semantic Kernel (SK) ist ein Open-Source-SDK, mit dem Entwickler komplexe KI-Workflows erstellen und koordinieren können, die nlP (Natural Language Processing) und Machine Learning-Modelle umfassen. Sie bietet eine flexible Plattform für die Integration von KI-Funktionen wie semantische Suche, Textzusammenfassung und Dialogsysteme in Anwendungen. Mit SK können Sie verschiedene KI-Dienste und -Modelle einfach kombinieren, ihre Beziehungen definieren und Interaktionen zwischen ihnen koordinieren.
  5. Machen Sie daraus zwei Absätze.
  6. Vielen Dank
  7. @.\WomensSuffrage.txt
  8. Ist gut, aber ist es geeignet für meinen Hochschulprofessor?
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();
        }
    }
}

Wenn Sie alle Schritte zusammenführen, haben wir nun den endgültigen Code für dieses Beispiel. Die vollständige Implementierung wird unten gezeigt.

Sie können versuchen, eine der vorgeschlagenen Eingaben zu verwenden. Sobald der Chat zwischen den Agenten beginnt, tauschen die Agenten Nachrichten für mehrere Iterationen aus, bis der überprüfende Agent mit der Arbeit des Texters zufrieden ist. Die while Schleife stellt sicher, dass die Unterhaltung fortgesetzt wird, auch wenn der Chat anfangs als abgeschlossen angesehen wird, indem das is_complete Flag auf Falsezurückgesetzt wird.

  1. Rosen sind rot, Violette sind blau.
  2. Semantic Kernel (SK) ist ein Open-Source-SDK, mit dem Entwickler komplexe KI-Workflows erstellen und koordinieren können, die nlP (Natural Language Processing) und Machine Learning-Modelle umfassen. Sie bietet eine flexible Plattform für die Integration von KI-Funktionen wie semantische Suche, Textzusammenfassung und Dialogsysteme in Anwendungen. Mit SK können Sie verschiedene KI-Dienste und -Modelle einfach kombinieren, ihre Beziehungen definieren und Interaktionen zwischen ihnen koordinieren.
  3. Machen Sie daraus zwei Absätze.
  4. Vielen Dank
  5. @WomensSuffrage.txt
  6. Es ist gut, aber ist es bereit für meinen Hochschulprofessor?

Tipp

Sie können auf eine beliebige Datei verweisen, indem Sie @<file_path_to_file>angeben. Um auf den Text "WomensSuffrage" von oben zu verweisen, laden Sie ihn hier herunter und platzieren Sie ihn in Ihrem aktuellen Arbeitsverzeichnis. Anschließend können Sie mit @WomensSuffrage.txtdarauf verweisen.

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())

Möglicherweise finden Sie den vollständigen Code, wie oben gezeigt, in unserem Repository.

Das Feature ist derzeit in Java nicht verfügbar.