Практическое руководство. Координация совместной работы агента с помощью группового чата агента (экспериментальный)
Предупреждение
Платформа агента семантического ядра является экспериментальной, по-прежнему в разработке и подлежит изменению.
Обзор
В этом примере мы рассмотрим, как использовать групповой чат агента для координации сортировки двух разных агентов, работающих для просмотра и перезаписи предоставленного пользователем содержимого. Каждому агенту назначена отдельная роль:
- Рецензент: рецензировать и предоставляет направление записи.
- Запись: обновляет содержимое пользователя на основе входных данных рецензента .
Этот подход будет разбит на шаге, чтобы обеспечить высокий уровень освещения ключевых частей процесса программирования.
Начало работы
Прежде чем продолжить программирование компонентов, убедитесь, что среда разработки полностью настроена и настроена.
Начните с создания проекта консоли . Затем включите следующие ссылки на пакеты, чтобы убедиться, что доступны все необходимые зависимости.
Чтобы добавить зависимости пакета из командной строки, используйте dotnet
команду:
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
При управлении пакетами NuGet в Visual Studio убедитесь, что
Include prerelease
установлен флажок.
Файл проекта (.csproj
) должен содержать следующие PackageReference
определения:
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="<stable>" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="<stable>" />
<PackageReference Include="Microsoft.SemanticKernel.Agents.Core" Version="<latest>" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="<latest>" />
</ItemGroup>
Платформа агента является экспериментальной и требует подавления предупреждений. Это может быть устранено в виде свойства в файле проекта (.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
Агенты в настоящее время недоступны в Java.
Настройка
Этот пример требует настройки для подключения к удаленным службам. Вам потребуется определить параметры для Open AI или Azure Open AI.
# 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"
Следующий класс используется во всех примерах агента. Не забудьте включить его в проект, чтобы обеспечить правильную функциональность. Этот класс служит базовым компонентом для приведенных ниже примеров.
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();
}
}
Самый быстрый способ начать работу с правильной конфигурацией для запуска примера кода — создать .env
файл в корне проекта (где выполняется скрипт).
Настройте следующие параметры в .env
файле для Azure OpenAI или 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=""
После настройки соответствующие классы служб ИИ будут собирать необходимые переменные и использовать их во время создания экземпляра.
Агенты в настоящее время недоступны в Java.
Написание кода
Процесс написания кода для этого примера включает в себя:
- Настройка — инициализация параметров и подключаемый модуль.
- Определение агента— создание двух экземпляров агента завершения чата (рецензент и запись).
- Определение чата— создание группового чата агента и связанных стратегий.
- Цикл чата — запись цикла , который управляет взаимодействием пользователя или агента.
Полный пример кода представлен в заключительном разделе. См. этот раздел для полной реализации.
Настройка
Перед созданием агента завершения чата параметры конфигурации, подключаемые модули и ядро должны быть инициализированы.
Создайте Settings
экземпляр класса, на который ссылается предыдущий раздел конфигурации .
Settings settings = new();
Агенты в настоящее время недоступны в Java.
Теперь инициализировать Kernel
экземпляр с помощью .IChatCompletionService
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
Kernel kernel = builder.Build();
Инициализировать объект ядра:
kernel = Kernel()
Агенты в настоящее время недоступны в Java.
Давайте также создадим второй экземпляр ядра с помощью клонирования и добавим подключаемый модуль, который позволит повторно разместить обновленное содержимое на буфере обмена.
Kernel toolKernel = kernel.Clone();
toolKernel.Plugins.AddFromType<ClipboardAccess>();
tool_kernel = copy.deepcopy(kernel)
tool_kernel.add_plugin(ClipboardAccess(), plugin_name="clipboard")
Агенты в настоящее время недоступны в Java.
Подключаемый модуль буфера обмена может быть определен как часть примера.
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();
}
}
Примечание. Мы будем использовать пакет Python под названием pyperclip. Установите pip.
class ClipboardAccess:
@kernel_function
def set_clipboard(content: str):
if not content.strip():
return
pyperclip.copy(content)
Агенты в настоящее время недоступны в Java.
Определение агента
Давайте объявим имена агентов так, чтобы const
они могли ссылаться в стратегиях группового чата агента:
const string ReviewerName = "Reviewer";
const string WriterName = "Writer";
REVIEWER_NAME = "Reviewer"
WRITER_NAME = "Writer"
Агенты в настоящее время недоступны в Java.
При определении агента рецензента используется шаблон, рассмотренный в руководстве. Агент завершения чата.
Здесь рецензент получает роль реагирования на входные данные пользователя, предоставление направления агенту записи и проверка результата агента записи.
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.
""",
)
Агенты в настоящее время недоступны в Java.
Агент записи аналогичен, но не требует спецификации параметров выполнения, так как он не настроен с подключаемым модулем.
Здесь модуль записи получает задачу с одним назначением, следуйте указаниям и перезаписи содержимого.
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.
""",
)
Агенты в настоящее время недоступны в Java.
Определение чата
Определение чата группы агентов требует рассмотрения стратегий выбора поворота агента и определения момента выхода из цикла чата. Для обоих этих соображений мы определим функцию запроса ядра.
Первая причина выбора агента :
Использование AgentGroupChat.CreatePromptFunctionForStrategy
обеспечивает удобный механизм, чтобы избежать кодирования HTML-файла парамтером сообщения.
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}}}}
""",
)
Агенты в настоящее время недоступны в Java.
Второй будет оценивать время выхода из цикла чата:
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}}}}
""",
)
Агенты в настоящее время недоступны в Java.
Обе эти стратегии потребуют только знания о последнем сообщении чата . Это уменьшит использование маркеров и поможет повысить производительность:
ChatHistoryTruncationReducer historyReducer = new(1);
**ChatHistoryReducer is coming soon to Python.**
Агенты в настоящее время недоступны в Java.
Наконец, мы готовы объединить все в определении группового чата агента.
Создание AgentGroupChat
включает в себя:
- Включите оба агента в конструктор.
- Определите
KernelFunctionSelectionStrategy
использование ранее определенногоKernelFunction
иKernel
экземпляра. - Определите
KernelFunctionTerminationStrategy
использование ранее определенногоKernelFunction
иKernel
экземпляра.
Обратите внимание, что каждая стратегия отвечает за анализ KernelFunction
результата.
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!");
Создание AgentGroupChat
включает в себя:
- Включите оба агента в конструктор.
- Определите
KernelFunctionSelectionStrategy
использование ранее определенногоKernelFunction
иKernel
экземпляра. - Определите
KernelFunctionTerminationStrategy
использование ранее определенногоKernelFunction
иKernel
экземпляра.
Обратите внимание, что каждая стратегия отвечает за анализ KernelFunction
результата.
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,
),
)
Агенты в настоящее время недоступны в Java.
Цикл чата
Наконец, мы можем координировать взаимодействие между пользователем и групповым чатом агента. Сначала создайте пустой цикл.
Примечание. В отличие от других примеров, внешний журнал или поток не управляется. Группа агентов управляет журналом бесед внутренне.
bool isComplete = false;
do
{
} while (!isComplete);
is_complete: bool = False
while not is_complete:
# operational logic
Агенты в настоящее время недоступны в Java.
Теперь давайте зафиксируем входные данные пользователя в предыдущем цикле. В данном случае:
- Пустые входные данные будут игнорироваться
- Термин
EXIT
сигнализирует о завершении беседы - Термин
RESET
очищает журнал чата группы агентов - Любой термин, начинающийся с
@
, будет рассматриваться как путь к файлу, содержимое которого будет предоставлено в качестве входных данных. - Допустимые входные данные будут добавлены в группу агента Чати в качестве сообщения пользователя .
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))
Агенты в настоящее время недоступны в Java.
Чтобы инициализация совместной работы агента в ответ на входные данные пользователя и отображение ответов агента, вызовите чат группы агентов. Однако сначала необходимо сбросить состояние завершения с любого предыдущего вызова.
Примечание. Сбои служб перехватываются и отображаются, чтобы избежать сбоя цикла беседы.
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
Агенты в настоящее время недоступны в Java.
Завершение
В этом примере приведен окончательный код для всех шагов. Полная реализация представлена ниже.
// 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())
Агенты в настоящее время недоступны в Java.