Udostępnij za pośrednictwem


Instrukcje: koordynowanie współpracy agenta przy użyciu czatu grupy agentów

Ważne

Ta funkcja znajduje się na etapie eksperymentalnym. Funkcje na tym etapie są opracowywane i mogą ulec zmianie przed przejściem do etapu wersji zapoznawczej lub etapu kandydata do wydania.

Przegląd

W tym przykładzie dowiesz się, jak używać AgentGroupChat do koordynowania współpracy dwóch różnych agentów pracujących w celu przeglądania i ponownego zapisywania zawartości udostępnionej przez użytkownika. Każdy agent ma przypisaną odrębną rolę:

  • Recenzent: przegląda i daje wskazówki Pisarzowi.
  • Autor: aktualizuje zawartość użytkownika na podstawie danych wejściowych recenzenta.

Podejście to zostanie podzielone krok po kroku na kluczowe elementy procesu kodowania.

Wprowadzenie

Przed kontynuowaniem tworzenia funkcji upewnij się, że środowisko deweloperskie jest w pełni przygotowane i skonfigurowane.

Wskazówka

W tym przykładzie używany jest opcjonalny plik tekstowy w ramach przetwarzania. Jeśli chcesz go użyć, możesz pobrać go tutaj. Umieść plik w katalogu roboczym kodu.

Zacznij od utworzenia projektu konsolowego. Następnie dołącz następujące odwołania do pakietu, aby upewnić się, że wszystkie wymagane zależności są dostępne.

Aby dodać zależności pakietów z wiersza polecenia, użyj dotnet polecenia :

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

Jeśli zarządzasz pakietami NuGet w programie Visual Studio, upewnij się, że Include prerelease jest zaznaczone.

Plik projektu (.csproj) powinien zawierać następujące PackageReference definicje:

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

Agent Framework jest eksperymentalna i wymaga tłumienia ostrzeżeń. Może to zostać rozwiązane jako właściwość w pliku projektu (.csproj):

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

Wskazówka

W tym przykładzie używany jest opcjonalny plik tekstowy w ramach przetwarzania. Jeśli chcesz go użyć, możesz pobrać go tutaj. Umieść plik w katalogu roboczym kodu.

Zacznij od zainstalowania pakietu Semantic Kernel dla języka Python.

pip install semantic-kernel

Następnie dodaj wymagane importy.

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

Funkcja jest obecnie niedostępna w języku Java.

Konfiguracja

Ten przykład wymaga ustawienia konfiguracji w celu nawiązania połączenia z usługami zdalnymi. Musisz zdefiniować ustawienia dla OpenAI lub 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"

Poniższa klasa jest używana we wszystkich przykładach Agenta. Pamiętaj, aby uwzględnić go w projekcie, aby zapewnić odpowiednią funkcjonalność. Ta klasa służy jako podstawowy składnik dla poniższych przykładów.

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

Najszybszym sposobem rozpoczęcia pracy z właściwą konfiguracją uruchamiania przykładowego kodu jest utworzenie .env pliku w katalogu głównym projektu (w którym jest uruchamiany skrypt). Próbka wymaga dostępności zasobów Azure OpenAI lub OpenAI.

Skonfiguruj następujące ustawienia w pliku .env dla Azure OpenAI lub usługi 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=""

Po skonfigurowaniu odpowiednie klasy usług AI będą pobierać wymagane zmienne i wykorzystywać je podczas inicjalizacji.

Funkcja jest obecnie niedostępna w języku Java.

Kodowanie

Proces kodowania dla tego przykładu obejmuje:

  1. Konfiguracja — inicjowanie ustawień i wtyczki.
  2. Agent Definicja - Utwórz dwie instancje ChatCompletionAgent (Recenzent i Pisarz).
  3. Definicja czatu — tworzenie AgentGroupChat i skojarzonych strategii.
  4. Pętla czatu — zapis pętli, która napędza interakcję użytkownika/agenta.

Pełny przykładowy kod znajduje się w sekcji Final. Zapoznaj się z sekcją dotyczącą pełnej implementacji.

Konfiguracja

Przed utworzeniem dowolnego ChatCompletionAgent należy zainicjować ustawienia konfiguracji, wtyczki oraz Kernel.

Utwórz wystąpienie klasy Settings, do której odwołano się w sekcji Konfiguracja.

Settings settings = new();

Funkcja jest obecnie niedostępna w języku Java.

Teraz zainicjuj wystąpienie Kernel z IChatCompletionService.

IKernelBuilder builder = Kernel.CreateBuilder();

builder.AddAzureOpenAIChatCompletion(
	settings.AzureOpenAI.ChatModelDeployment,
	settings.AzureOpenAI.Endpoint,
	new AzureCliCredential());

Kernel kernel = builder.Build();

Zainicjuj obiekt jądra:

kernel = Kernel()

Funkcja jest obecnie niedostępna w języku Java.

Utwórzmy również drugie wystąpienie Kernel za pomocą klonowania i dodajmy wtyczkę, która umożliwi przeglądowi umieszczenie zaktualizowanej zawartości na tablicy clip-board.

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

Funkcja jest obecnie niedostępna w języku Java.

Wtyczka Schowka może być zdefiniowana jako część przykładu.

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

Funkcja jest obecnie niedostępna w języku Java.

Definicja agenta

Zadeklarujmy nazwy agentów jako const, aby mogły być odwoływane w strategiach AgentGroupChat.

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

Zadeklarujemy nazwy agentów jako "Recenzent" i "Autor".

REVIEWER_NAME = "Reviewer"
COPYWRITER_NAME = "Writer"

Funkcja jest obecnie niedostępna w języku Java.

Definiowanie agenta Recenzenta wykorzystuje wzorzec omówiony w Instrukcja: Agent do uzupełniania czatu.

W tym miejscu recenzent otrzymuje rolę reagowania na dane wejściowe użytkownika, zapewniając kierunek agentowi modułu zapisywania i weryfikując wynik agenta modułu zapisywania.

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

Funkcja jest obecnie niedostępna w języku Java.

Agent Writer jest podobny, ale nie wymaga specyfikacji ustawień wykonania, ponieważ nie jest skonfigurowany z wtyczką.

W tym miejscu Writer otrzymuje jedno zadanie: postępowanie zgodnie ze wskazówkami i przepisanie zawartości.

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

Agent Piszący jest podobny. Otrzymuje zadanie jednokrotnego użytku, postępuj zgodnie z instrukcją i ponownie napisz zawartość.

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

Funkcja jest obecnie niedostępna w języku Java.

Definicja czatu

Zdefiniowanie AgentGroupChat wymaga rozważenia strategii wyboru Agent tury i określenia, kiedy należy zamknąć pętlę czatu . W przypadku obu tych zagadnień zdefiniujemy Funkcję Monitu Jądra.

Pierwszy aspekt do rozważenia przy wyborze Agent:

Użycie AgentGroupChat.CreatePromptFunctionForStrategy zapewnia wygodny mechanizm umożliwiający uniknięcie kodowania HTML parametru komunikatu.

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}}}}
"""
)

Funkcja jest obecnie niedostępna w języku Java.

Drugi oceni, kiedy zamknąć pętlę czatu:

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}}}}
"""
)

Funkcja jest obecnie niedostępna w języku Java.

Obie strategie będą wymagały tylko znajomości najnowszej wiadomości czatu. Spowoduje to zmniejszenie użycia tokenu i zwiększenie wydajności:

ChatHistoryTruncationReducer historyReducer = new(1);
history_reducer = ChatHistoryTruncationReducer(target_count=1)

Funkcja jest obecnie niedostępna w języku Java.

Na koniec jesteśmy gotowi, aby zebrać wszystko w naszej definicji AgentGroupChat.

Tworzenie AgentGroupChat obejmuje:

  1. Uwzględnij obu agentów w konstruktorze.
  2. Zdefiniuj KernelFunctionSelectionStrategy przy użyciu wcześniej zdefiniowanego wystąpienia KernelFunction i Kernel.
  3. Zdefiniuj KernelFunctionTerminationStrategy przy użyciu wcześniej zdefiniowanego wystąpienia KernelFunction i Kernel.

Zwróć uwagę, że każda strategia jest odpowiedzialna za analizowanie KernelFunction wyniku.

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!");

Tworzenie AgentGroupChat obejmuje:

  1. Uwzględnij obu agentów w konstruktorze.
  2. Zdefiniuj KernelFunctionSelectionStrategy przy użyciu wcześniej zdefiniowanego wystąpienia KernelFunction i Kernel.
  3. Zdefiniuj KernelFunctionTerminationStrategy przy użyciu wcześniej zdefiniowanego wystąpienia KernelFunction i Kernel.

Zwróć uwagę, że każda strategia jest odpowiedzialna za analizowanie KernelFunction wyniku.

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

lastmessage history_variable_name odpowiada KernelFunctionSelectionStrategy i monitowi KernelFunctionTerminationStrategy zdefiniowanemu powyżej. W tym miejscu podczas renderowania monitu jest umieszczany ostatni komunikat.

Funkcja jest obecnie niedostępna w języku Java.

Pętla czatu

Na koniec możemy koordynować interakcję między użytkownikiem a AgentGroupChat. Zacznij od utworzenia pustej pętli.

Uwaga: w przeciwieństwie do innych przykładów nie jest zarządzana żadna historia zewnętrzna ani wątek . AgentGroupChat zarządza historią konwersacji wewnętrznie.

bool isComplete = false;
do
{

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

Funkcja jest obecnie niedostępna w języku Java.

Teraz przechwyć dane wejściowe użytkownika w ramach poprzedniej pętli. W tym przypadku:

  • Puste dane wejściowe zostaną zignorowane
  • EXIT Termin sygnalizuje, że konwersacja zostanie ukończona
  • Termin RESET wyczyści historię AgentGroupChat
  • Każdy termin rozpoczynający się od @ będzie traktowany jako ścieżka pliku, którego zawartość zostanie podana jako dane wejściowe
  • Prawidłowe dane wejściowe zostaną dodane do AgentGroupChat jako komunikat użytkownika .
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));

Teraz przechwyć dane wejściowe użytkownika w ramach poprzedniej pętli. W tym przypadku:

  • Puste dane wejściowe zostaną zignorowane.
  • Termin exit sygnalizuje zakończenie konwersacji.
  • Termin reset wyczyści historię AgentGroupChat.
  • Każdy termin rozpoczynający się od @ będzie traktowany jako ścieżka pliku, której zawartość będzie dostarczana jako dane wejściowe.
  • Prawidłowe dane wejściowe zostaną dodane do AgentGroupChat jako komunikat użytkownika .

Logika operacji wewnątrz pętli while wygląda następująco:

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)

Funkcja jest obecnie niedostępna w języku Java.

Aby zainicjować współpracę Agent w odpowiedzi na dane wejściowe użytkownika i wyświetlić odpowiedzi Agent, wywołaj AgentGroupChat, jednak najpierw należy zresetować stan zakończenia z wcześniejszego wywołania.

Uwaga: błędy usługi są przechwytywane i wyświetlane, aby uniknąć awarii pętli konwersacji.

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

Funkcja jest obecnie niedostępna w języku Java.

Końcowa

Łącząc wszystkie kroki, mamy końcowy kod dla tego przykładu. Pełną implementację podano poniżej.

Spróbuj użyć tych sugerowanych danych wejściowych:

  1. Cześć
  2. {"message: "witaj świecie"}
  3. {"message": "witaj świecie"}
  4. Semantyczne jądro (SK) to zestaw SDK typu open source, który umożliwia deweloperom tworzenie i organizowanie złożonych przepływów pracy sztucznej inteligencji obejmujących przetwarzanie języka naturalnego (NLP) i modele uczenia maszynowego. Zapewnia ona elastyczną platformę do integrowania funkcji sztucznej inteligencji, takich jak wyszukiwanie semantyczne, podsumowanie tekstu i systemy dialogu w aplikacjach. Dzięki sk można łatwo łączyć różne usługi i modele sztucznej inteligencji, definiować ich relacje i organizować interakcje między nimi.
  5. podziel to na dwa akapity
  6. Dziękuję
  7. @.\WomensSuffrage.txt
  8. To jest dobre, ale czy nadaje się dla mojego wykładowcy na uniwersytecie?
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();
        }
    }
}

Łącząc wszystkie kroki, mamy teraz końcowy kod dla tego przykładu. Kompletna implementacja jest pokazana poniżej.

Możesz spróbować użyć jednego z sugerowanych danych wejściowych. Po rozpoczęciu rozmowy tekstowej z agentem, agenci będą wysyłać wiadomości przez kilka iteracji, dopóki agent recenzujący nie będzie zadowolony z pracy copywritera. Pętla while zapewnia kontynuację konwersacji, nawet jeśli czat początkowo jest uznawany za zakończony, poprzez zresetowanie flagi is_complete do False.

  1. Róże są czerwone, fiołki są niebieskie.
  2. Semantyczne jądro (SK) to zestaw SDK typu open source, który umożliwia deweloperom tworzenie i organizowanie złożonych przepływów pracy sztucznej inteligencji obejmujących przetwarzanie języka naturalnego (NLP) i modele uczenia maszynowego. Zapewnia ona elastyczną platformę do integrowania funkcji sztucznej inteligencji, takich jak wyszukiwanie semantyczne, podsumowanie tekstu i systemy dialogu w aplikacjach. Dzięki sk można łatwo łączyć różne usługi i modele sztucznej inteligencji, definiować ich relacje i organizować interakcje między nimi.
  3. Podziel to na dwa akapity
  4. Dziękuję
  5. @WomensSuffrage.txt
  6. To jest dobre, ale czy jest gotowe dla mojego profesora na uczelni?

Wskazówka

Możesz odwołać się do dowolnego pliku, podając @<file_path_to_file>. Aby odwołać się do wspomnianego powyżej tekstu "WomensSuffrage", pobierz go stąd i umieść w aktualnym katalogu roboczym. Następnie możesz odwołać się do niego za pomocą @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())

W naszym repozytorium możesz znaleźć pełny kod , jak pokazano powyżej.

Funkcja jest obecnie niedostępna w języku Java.