Procedura: Coordinare la collaborazione degli agenti tramite la chat di gruppo degli agenti
Avviso
Il framework dell'agente del kernel semantico è sperimentale, ancora in fase di sviluppo ed è soggetto a modifiche.
Panoramica
In questo esempio verrà illustrato come usare La chat del gruppo di agenti per coordinare la collborazione di due agenti diversi che lavorano per esaminare e riscrivere il contenuto fornito dall'utente. A ogni agente viene assegnato un ruolo distinto:
- Revisore: rivede e fornisce la direzione per writer.
- Writer: aggiorna il contenuto utente in base all'input del revisore .
L'approccio verrà suddiviso passo dopo passo fino alla luce elevata delle parti chiave del processo di codifica.
Introduzione
Prima di procedere con la codifica delle funzionalità, assicurarsi che l'ambiente di sviluppo sia completamente configurato e configurato.
Per iniziare, creare un progetto console . Includere quindi i riferimenti al pacchetto seguenti per assicurarsi che tutte le dipendenze necessarie siano disponibili.
Per aggiungere le dipendenze dei pacchetti dalla riga di comando, usare il dotnet
comando :
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
Se si gestiscono pacchetti NuGet in Visual Studio, verificare che
Include prerelease
sia selezionata.
Il file di progetto (.csproj
) deve contenere le definizioni seguenti 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>
Agent Framework è sperimentale e richiede l'eliminazione degli avvisi. Ciò può essere risolto come proprietà nel file di progetto (.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
Gli agenti non sono attualmente disponibili in Java.
Impostazione
Questo esempio richiede l'impostazione di configurazione per connettersi ai servizi remoti. Sarà necessario definire le impostazioni per Open AI o 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"
La classe seguente viene usata in tutti gli esempi di Agent. Assicurarsi di includerlo nel progetto per garantire una funzionalità appropriata. Questa classe funge da componente di base per gli esempi seguenti.
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();
}
}
Il modo più rapido per iniziare a usare la configurazione corretta per eseguire il codice di esempio consiste nel creare un .env
file nella radice del progetto (dove viene eseguito lo script).
Configurare le impostazioni seguenti nel .env
file per Azure OpenAI o 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=""
Dopo la configurazione, le rispettive classi di servizi di intelligenza artificiale rileveranno le variabili necessarie e le useranno durante la creazione di istanze.
Gli agenti non sono attualmente disponibili in Java.
Scrittura del codice
Il processo di codifica per questo esempio prevede:
- Installazione : inizializzazione delle impostazioni e del plug-in.
- Definizione agente: creare le due istanze dell'agente di completamento chat (revisore e writer).
- Definizione chat: creare la chat del gruppo di agenti e le strategie associate.
- Ciclo chat - Scrivere il ciclo che determina l'interazione utente/agente.
Il codice di esempio completo viene fornito nella sezione Finale . Fare riferimento a questa sezione per l'implementazione completa.
Attrezzaggio
Prima di creare qualsiasi agente di completamento chat, è necessario inizializzare le impostazioni di configurazione, i plug-in e il kernel .
Creare un'istanza della Settings
classe a cui si fa riferimento nella sezione Configurazione precedente.
Settings settings = new();
Gli agenti non sono attualmente disponibili in Java.
Inizializzare ora un'istanza Kernel
di con un oggetto IChatCompletionService
.
IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddAzureOpenAIChatCompletion(
settings.AzureOpenAI.ChatModelDeployment,
settings.AzureOpenAI.Endpoint,
new AzureCliCredential());
Kernel kernel = builder.Build();
Inizializzare l'oggetto kernel:
kernel = Kernel()
Gli agenti non sono attualmente disponibili in Java.
Si creerà anche una seconda istanza del kernel tramite clonazione e si aggiungerà un plug-in che consentirà al reivew di inserire contenuto aggiornato nella clip-board.
Kernel toolKernel = kernel.Clone();
toolKernel.Plugins.AddFromType<ClipboardAccess>();
tool_kernel = copy.deepcopy(kernel)
tool_kernel.add_plugin(ClipboardAccess(), plugin_name="clipboard")
Gli agenti non sono attualmente disponibili in Java.
Il plug-in Appunti può essere definito come parte dell'esempio.
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();
}
}
Nota: si sta sfruttando un pacchetto Python denominato pyperclip. Installare sta usando pip.
class ClipboardAccess:
@kernel_function
def set_clipboard(content: str):
if not content.strip():
return
pyperclip.copy(content)
Gli agenti non sono attualmente disponibili in Java.
Definizione agente
Si dichiarino ora i nomi degli agenti in const
modo che possano essere referenziati nelle strategie di Chat del gruppo di agenti:
const string ReviewerName = "Reviewer";
const string WriterName = "Writer";
REVIEWER_NAME = "Reviewer"
COPYWRITER_NAME = "Writer"
Gli agenti non sono attualmente disponibili in Java.
La definizione dell'agente revisore usa il modello esaminato in Procedura: Agente di completamento chat.
In questo caso, il revisore ha il ruolo di rispondere all'input dell'utente, fornendo la direzione all'agente writer e verificando il risultato dell'agente 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()
})
};
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.
""",
)
Gli agenti non sono attualmente disponibili in Java.
L'agente writer è simile, ma non richiede la specifica di Impostazioni di esecuzione perché non è configurata con un plug-in.
Qui a Writer viene assegnata un'attività a scopo singolo, seguire la direzione e riscrivere il contenuto.
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.
""",
)
Gli agenti non sono attualmente disponibili in Java.
Definizione chat
Per definire la chat del gruppo di agenti è necessario prendere in considerazione le strategie per selezionare il turno dell'agente e determinare quando uscire dal ciclo chat. Per entrambe queste considerazioni, verrà definita una funzione prompt del kernel.
Primo motivo della selezione di Agent :
L'uso AgentGroupChat.CreatePromptFunctionForStrategy
di fornisce un meccanismo pratico per evitare la codifica HTML del parametro del messaggio.
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}}}}
""",
)
Gli agenti non sono attualmente disponibili in Java.
Il secondo valuterà quando uscire dal ciclo chat :
const string TerminationToken = "yes";
KernelFunction terminationFunction =
AgentGroupChat.CreatePromptFunctionForStrategy(
$$$"""
Examine the RESPONSE and determine whether the content has been deemed satisfactory.
If content is satisfactory, respond with a single word without explanation: {{{TerminationToken}}}.
If specific suggestions are being provided, it is not satisfactory.
If no correction is suggested, it is satisfactory.
RESPONSE:
{{$lastmessage}}
""",
safeParameterNames: "lastmessage");
TERMINATION_KEYWORD = "yes"
termination_function = KernelFunctionFromPrompt(
function_name="termination",
prompt=f"""
Examine the RESPONSE and determine whether the content has been deemed satisfactory.
If 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}}}}
""",
)
Gli agenti non sono attualmente disponibili in Java.
Entrambe queste strategie richiederanno solo la conoscenza del messaggio di chat più recente. In questo modo si riduce l'utilizzo dei token e si migliorano le prestazioni:
ChatHistoryTruncationReducer historyReducer = new(1);
**ChatHistoryReducer is coming soon to Python.**
Gli agenti non sono attualmente disponibili in Java.
Infine, siamo pronti a riunire tutti gli elementi nella definizione della chat del gruppo di agenti.
La creazione AgentGroupChat
prevede:
- Includere entrambi gli agenti nel costruttore.
- Definire un
KernelFunctionSelectionStrategy
oggetto usando l'istanza eKernelFunction
definitaKernel
in precedenza. - Definire un
KernelFunctionTerminationStrategy
oggetto usando l'istanza eKernelFunction
definitaKernel
in precedenza.
Si noti che ogni strategia è responsabile dell'analisi del KernelFunction
risultato.
AgentGroupChat chat =
new(agentReviewer, agentWriter)
{
ExecutionSettings = new AgentGroupChatSettings
{
SelectionStrategy =
new KernelFunctionSelectionStrategy(selectionFunction, kernel)
{
// Always start with the editor agent.
InitialAgent = agentReviewer,
// Save tokens by only including the final response
HistoryReducer = historyReducer,
// The prompt variable name for the history argument.
HistoryVariableName = "lastmessage",
// Returns the entire result value as a string.
ResultParser = (result) => result.GetValue<string>() ?? agentReviewer.Name
},
TerminationStrategy =
new KernelFunctionTerminationStrategy(terminationFunction, kernel)
{
// Only evaluate for editor's response
Agents = [agentReviewer],
// Save tokens by only including the final response
HistoryReducer = historyReducer,
// The prompt variable name for the history argument.
HistoryVariableName = "lastmessage",
// Limit total number of turns
MaximumIterations = 12,
// Customer result parser to determine if the response is "yes"
ResultParser = (result) => result.GetValue<string>()?.Contains(TerminationToken, StringComparison.OrdinalIgnoreCase) ?? false
}
}
};
Console.WriteLine("Ready!");
La creazione AgentGroupChat
prevede:
- Includere entrambi gli agenti nel costruttore.
- Definire un
KernelFunctionSelectionStrategy
oggetto usando l'istanza eKernelFunction
definitaKernel
in precedenza. - Definire un
KernelFunctionTerminationStrategy
oggetto usando l'istanza eKernelFunction
definitaKernel
in precedenza.
Si noti che ogni strategia è responsabile dell'analisi del KernelFunction
risultato.
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,
),
)
Gli agenti non sono attualmente disponibili in Java.
Ciclo chat
Infine, siamo in grado di coordinare l'interazione tra l'utente e la chat del gruppo di agenti. Per iniziare, creare un ciclo vuoto.
Nota: a differenza degli altri esempi, non viene gestita alcuna cronologia o thread esterno. Agent Group Chat gestisce internamente la cronologia delle conversazioni.
bool isComplete = false;
do
{
} while (!isComplete);
is_complete: bool = False
while not is_complete:
# operational logic
Gli agenti non sono attualmente disponibili in Java.
Ora si acquisisce l'input dell'utente all'interno del ciclo precedente. In questo caso:
- L'input vuoto verrà ignorato
- Il termine
EXIT
segnalerà che la conversazione è stata completata - Il termine
RESET
cancella la cronologia della chat del gruppo di agenti - Qualsiasi termine che inizia con
@
verrà considerato come un percorso di file il cui contenuto verrà fornito come input - L'input valido verrà aggiunto a Agent Group Chaty come messaggio utente .
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))
Gli agenti non sono attualmente disponibili in Java.
Per initare la collaborazione agente in risposta all'input dell'utente e visualizzare le risposte dell'agente , richiamare la chat del gruppo di agenti. Tuttavia, assicurarsi di reimpostare lo stato di completamento da qualsiasi chiamata precedente.
Nota: gli errori del servizio vengono rilevati e visualizzati per evitare l'arresto anomalo del ciclo di conversazione.
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
Gli agenti non sono attualmente disponibili in Java.
Finale
Riunire tutti i passaggi, è disponibile il codice finale per questo esempio. Di seguito è riportata l'implementazione completa.
Provare a usare questi input suggeriti:
- Ciao
- {"message: "ciao mondo"}
- {"message": "ciao mondo"}
- Semantic Kernel (SK) è un SDK open source che consente agli sviluppatori di creare e orchestrare flussi di lavoro di intelligenza artificiale complessi che coinvolgono l'elaborazione del linguaggio naturale (NLP) e i modelli di Machine Learning. Offre una piattaforma flessibile per l'integrazione di funzionalità di intelligenza artificiale come la ricerca semantica, il riepilogo del testo e i sistemi di dialogo nelle applicazioni. Con SK, è possibile combinare facilmente diversi servizi e modelli di intelligenza artificiale, definire le relazioni tra loro e orchestrare le interazioni tra di esse.
- fare questi due paragrafi
- Grazie
- @.\WomensSuffrage.txt
- Va bene, ma è adatto per il mio professore universitario?
// 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())
Gli agenti non sono attualmente disponibili in Java.