// Copyright (c) Microsoft. All rights reserved. using System.Text.Json; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Agents; using Microsoft.SemanticKernel.Agents.Orchestration; using Microsoft.SemanticKernel.Agents.Orchestration.GroupChat; using Microsoft.SemanticKernel.Agents.Runtime.InProcess; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; namespace GettingStarted.Orchestration; /// /// Demonstrates how to use the /// with a group chat manager that uses a chat completion service to /// control the flow of the conversation. /// public class Step03b_GroupChatWithAIManager(ITestOutputHelper output) : BaseOrchestrationTest(output) { [Fact] public async Task GroupChatWithAIManagerAsync() { // Define the agents ChatCompletionAgent farmer = this.CreateChatCompletionAgent( name: "Farmer", description: "A rural farmer from Southeast Asia.", instructions: """ You're a farmer from Southeast Asia. Your life is deeply connected to land and family. You value tradition and sustainability. You are in a debate. Feel free to challenge the other participants with respect. """); ChatCompletionAgent developer = this.CreateChatCompletionAgent( name: "Developer", description: "An urban software developer from the United States.", instructions: """ You're a software developer from the United States. Your life is fast-paced and technology-driven. You value innovation, freedom, and work-life balance. You are in a debate. Feel free to challenge the other participants with respect. """); ChatCompletionAgent teacher = this.CreateChatCompletionAgent( name: "Teacher", description: "A retired history teacher from Eastern Europe", instructions: """ You're a retired history teacher from Eastern Europe. You bring historical and philosophical perspectives to discussions. You value legacy, learning, and cultural continuity. You are in a debate. Feel free to challenge the other participants with respect. """); ChatCompletionAgent activist = this.CreateChatCompletionAgent( name: "Activist", description: "A young activist from South America.", instructions: """ You're a young activist from South America. You focus on social justice, environmental rights, and generational change. You are in a debate. Feel free to challenge the other participants with respect. """); ChatCompletionAgent spiritual = this.CreateChatCompletionAgent( name: "SpiritualLeader", description: "A spiritual leader from the Middle East.", instructions: """ You're a spiritual leader from the Middle East. You provide insights grounded in religion, morality, and community service. You are in a debate. Feel free to challenge the other participants with respect. """); ChatCompletionAgent artist = this.CreateChatCompletionAgent( name: "Artist", description: "An artist from Africa.", instructions: """ You're an artist from Africa. You view life through creative expression, storytelling, and collective memory. You are in a debate. Feel free to challenge the other participants with respect. """); ChatCompletionAgent immigrant = this.CreateChatCompletionAgent( name: "Immigrant", description: "An immigrant entrepreneur from Asia living in Canada.", instructions: """ You're an immigrant entrepreneur from Asia living in Canada. You balance trandition with adaption. You focus on family success, risk, and opportunity. You are in a debate. Feel free to challenge the other participants with respect. """); ChatCompletionAgent doctor = this.CreateChatCompletionAgent( name: "Doctor", description: "A doctor from Scandinavia.", instructions: """ You're a doctor from Scandinavia. Your perspective is shaped by public health, equity, and structured societal support. You are in a debate. Feel free to challenge the other participants with respect. """); // Create a monitor to capturing agent responses (via ResponseCallback) // to display at the end of this sample. (optional) // NOTE: Create your own callback to capture responses in your application or service. OrchestrationMonitor monitor = new(); // Define the orchestration const string topic = "What does a good life mean to you personally?"; Kernel kernel = this.CreateKernelWithChatCompletion(); GroupChatOrchestration orchestration = new( new AIGroupChatManager( topic, kernel.GetRequiredService()) { MaximumInvocationCount = 5 }, farmer, developer, teacher, activist, spiritual, artist, immigrant, doctor) { LoggerFactory = this.LoggerFactory, ResponseCallback = monitor.ResponseCallback, }; // Start the runtime InProcessRuntime runtime = new(); await runtime.StartAsync(); // Run the orchestration Console.WriteLine($"\n# INPUT: {topic}\n"); OrchestrationResult result = await orchestration.InvokeAsync(topic, runtime); string text = await result.GetValueAsync(TimeSpan.FromSeconds(ResultTimeoutInSeconds * 3)); Console.WriteLine($"\n# RESULT: {text}"); await runtime.RunUntilIdleAsync(); } private sealed class AIGroupChatManager(string topic, IChatCompletionService chatCompletion) : GroupChatManager { private static class Prompts { public static string Termination(string topic) => $""" You are mediator that guides a discussion on the topic of '{topic}'. You need to determine if the discussion has reached a conclusion. If you would like to end the discussion, please respond with True. Otherwise, respond with False. """; public static string Selection(string topic, string participants) => $""" You are mediator that guides a discussion on the topic of '{topic}'. You need to select the next participant to speak. Here are the names and descriptions of the participants: {participants}\n Please respond with only the name of the participant you would like to select. """; public static string Filter(string topic) => $""" You are mediator that guides a discussion on the topic of '{topic}'. You have just concluded the discussion. Please summarize the discussion and provide a closing statement. """; } /// public override ValueTask> FilterResults(ChatHistory history, CancellationToken cancellationToken = default) => this.GetResponseAsync(history, Prompts.Filter(topic), cancellationToken); /// public override ValueTask> SelectNextAgent(ChatHistory history, GroupChatTeam team, CancellationToken cancellationToken = default) => this.GetResponseAsync(history, Prompts.Selection(topic, team.FormatList()), cancellationToken); /// public override ValueTask> ShouldRequestUserInput(ChatHistory history, CancellationToken cancellationToken = default) => ValueTask.FromResult(new GroupChatManagerResult(false) { Reason = "The AI group chat manager does not request user input." }); /// public override async ValueTask> ShouldTerminate(ChatHistory history, CancellationToken cancellationToken = default) { GroupChatManagerResult result = await base.ShouldTerminate(history, cancellationToken); if (!result.Value) { result = await this.GetResponseAsync(history, Prompts.Termination(topic), cancellationToken); } return result; } private async ValueTask> GetResponseAsync(ChatHistory history, string prompt, CancellationToken cancellationToken = default) { OpenAIPromptExecutionSettings executionSettings = new() { ResponseFormat = typeof(GroupChatManagerResult) }; ChatHistory request = [.. history, new ChatMessageContent(AuthorRole.System, prompt)]; ChatMessageContent response = await chatCompletion.GetChatMessageContentAsync(request, executionSettings, kernel: null, cancellationToken); string responseText = response.ToString(); return JsonSerializer.Deserialize>(responseText) ?? throw new InvalidOperationException($"Failed to parse response: {responseText}"); } } }