// Copyright (c) Microsoft. All rights reserved. using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Agents; using Microsoft.SemanticKernel.ChatCompletion; using Plugins; using Resources; namespace GettingStarted; /// /// Demonstrate creation of with a , /// and then eliciting its response to explicit user messages. /// public class Step02_Plugins(ITestOutputHelper output) : BaseAgentsTest(output) { [Theory] [InlineData(true)] [InlineData(false)] public async Task UseChatCompletionWithPlugin(bool useChatClient) { // Define the agent ChatCompletionAgent agent = CreateAgentWithPlugin( plugin: KernelPluginFactory.CreateFromType(), instructions: "Answer questions about the menu.", name: "Host", useChatClient: useChatClient); // Create the chat history thread to capture the agent interaction. ChatHistoryAgentThread thread = new(); // Respond to user input, invoking functions where appropriate. await InvokeAgentAsync(agent, thread, "Hello"); await InvokeAgentAsync(agent, thread, "What is the special soup and its price?"); await InvokeAgentAsync(agent, thread, "What is the special drink and its price?"); await InvokeAgentAsync(agent, thread, "Thank you"); } [Theory] [InlineData(true)] [InlineData(false)] public async Task UseChatCompletionWithPluginEnumParameter(bool useChatClient) { // Define the agent ChatCompletionAgent agent = CreateAgentWithPlugin( KernelPluginFactory.CreateFromType(), useChatClient: useChatClient); // Create the chat history thread to capture the agent interaction. ChatHistoryAgentThread thread = new(); // Respond to user input, invoking functions where appropriate. await InvokeAgentAsync(agent, thread, "Create a beautiful red colored widget for me."); } [Theory] [InlineData(true)] [InlineData(false)] public async Task UseChatCompletionWithPromptFunction(bool useChatClient) { // Define prompt function KernelFunction promptFunction = KernelFunctionFactory.CreateFromPrompt( promptTemplate: """ Count the number of vowels in INPUT and report as a markdown table. INPUT: {{$input}} """, description: "Counts the number of vowels"); // Define the agent ChatCompletionAgent agent = CreateAgentWithPlugin( KernelPluginFactory.CreateFromFunctions("AgentPlugin", [promptFunction]), instructions: "You job is to only and always analyze the vowels in the user input without confirmation.", useChatClient: useChatClient); // Add a filter to the agent's kernel to log function invocations. agent.Kernel.FunctionInvocationFilters.Add(new PromptFunctionFilter()); // Create the chat history thread to capture the agent interaction. ChatHistoryAgentThread thread = new(); // Respond to user input, invoking functions where appropriate. await InvokeAgentAsync(agent, thread, "Who would know naught of art must learn, act, and then take his ease."); } [Theory] [InlineData(true)] [InlineData(false)] public async Task UseChatCompletionWithTemplateExecutionSettings(bool useChatClient) { // Read the template resource string autoInvokeYaml = EmbeddedResource.Read("AutoInvokeTools.yaml"); PromptTemplateConfig templateConfig = KernelFunctionYaml.ToPromptTemplateConfig(autoInvokeYaml); KernelPromptTemplateFactory templateFactory = new(); // Define the agent: // Execution-settings with auto-invocation of plugins defined via the config. ChatCompletionAgent agent = new(templateConfig, templateFactory) { Kernel = this.CreateKernelWithChatCompletion(useChatClient, out var chatClient), }; agent.Kernel.Plugins.AddFromType(); // Create the chat history thread to capture the agent interaction. ChatHistoryAgentThread thread = new(); // Respond to user input, invoking functions where appropriate. await InvokeAgentAsync(agent, thread, "Create a beautiful red colored widget for me."); chatClient?.Dispose(); } [Theory] [InlineData(true)] [InlineData(false)] public async Task UseChatCompletionWithManualFunctionCalling(bool useChatClient) { // Define the agent ChatCompletionAgent agent = CreateAgentWithPlugin( KernelPluginFactory.CreateFromType(), functionChoiceBehavior: FunctionChoiceBehavior.Auto(autoInvoke: false), useChatClient: useChatClient); /// Create the chat history thread to capture the agent interaction. ChatHistoryAgentThread thread = new(); // Respond to user input, invoking functions where appropriate. await InvokeAgentAsync(agent, thread, "What is the special soup and its price?"); await InvokeAgentAsync(agent, thread, "What is the special drink and its price?"); } private ChatCompletionAgent CreateAgentWithPlugin( KernelPlugin plugin, string? instructions = null, string? name = null, FunctionChoiceBehavior? functionChoiceBehavior = null, bool useChatClient = false) { ChatCompletionAgent agent = new() { Instructions = instructions, Name = name, Kernel = this.CreateKernelWithChatCompletion(useChatClient, out _), Arguments = new KernelArguments(new PromptExecutionSettings() { FunctionChoiceBehavior = functionChoiceBehavior ?? FunctionChoiceBehavior.Auto() }), }; // Initialize plugin and add to the agent's Kernel (same as direct Kernel usage). agent.Kernel.Plugins.Add(plugin); return agent; } private async Task InvokeAgentAsync(ChatCompletionAgent agent, ChatHistoryAgentThread thread, string input) { ChatMessageContent message = new(AuthorRole.User, input); this.WriteAgentChatMessage(message); await foreach (ChatMessageContent response in agent.InvokeAsync(message, thread)) { this.WriteAgentChatMessage(response); Task[] functionResults = await ProcessFunctionCalls(response, agent.Kernel).ToArrayAsync(); thread.ChatHistory.Add(response); foreach (ChatMessageContent functionResult in functionResults.Select(result => result.Result.ToChatMessage())) { this.WriteAgentChatMessage(functionResult); thread.ChatHistory.Add(functionResult); } } } private async IAsyncEnumerable> ProcessFunctionCalls(ChatMessageContent response, Kernel kernel) { foreach (FunctionCallContent functionCall in response.Items.OfType()) { yield return functionCall.InvokeAsync(kernel); } } private sealed class PromptFunctionFilter : IFunctionInvocationFilter { public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func next) { System.Console.WriteLine($"INVOKING: {context.Function.Name}"); await next.Invoke(context); System.Console.WriteLine($"RESULT: {context.Result}"); } } }