// Copyright (c) Microsoft. All rights reserved. using System.Text.Json; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.OpenAI; using Microsoft.SemanticKernel.Data; using Microsoft.SemanticKernel.Plugins.Web.Bing; namespace GettingStartedWithTextSearch; /// /// This example shows how to use for Function Calling. /// public class Step3_Search_With_FunctionCalling(ITestOutputHelper output) : BaseTest(output) { /// /// Show how to create a default from an and use it with /// function calling to have the LLM include grounding context in it's response. /// [Fact] public async Task FunctionCallingWithBingTextSearchAsync() { // Create a kernel with OpenAI chat completion IKernelBuilder kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey); kernelBuilder.Services.AddSingleton(this.Output); kernelBuilder.Services.AddSingleton(); Kernel kernel = kernelBuilder.Build(); // Create a search service with Bing search var textSearch = new BingTextSearch(new(TestConfiguration.Bing.ApiKey)); // Build a text search plugin with Bing search and add to the kernel var searchPlugin = textSearch.CreateWithSearch("SearchPlugin"); kernel.Plugins.Add(searchPlugin); // Invoke prompt and use text search plugin to provide grounding information OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }; KernelArguments arguments = new(settings); Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic Kernel?", arguments)); } /// /// Show how to create a default from an and use it with /// function calling and have the LLM include links in the final response. /// [Fact] public async Task FunctionCallingWithBingTextSearchIncludingCitationsAsync() { // Create a kernel with OpenAI chat completion IKernelBuilder kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey); Kernel kernel = kernelBuilder.Build(); // Create a search service with Bing search var textSearch = new BingTextSearch(new(TestConfiguration.Bing.ApiKey)); // Build a text search plugin with Bing search and add to the kernel var searchPlugin = textSearch.CreateWithGetTextSearchResults("SearchPlugin"); kernel.Plugins.Add(searchPlugin); // Invoke prompt and use text search plugin to provide grounding information OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }; KernelArguments arguments = new(settings); Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic Kernel? Include citations to the relevant information where it is referenced in the response.", arguments)); } #pragma warning disable CS0618 // Suppress obsolete warnings for legacy TextSearchOptions/TextSearchFilter usage /// /// Show how to create a default from an and use it with /// function calling to have the LLM include grounding context from the Microsoft Dev Blogs site in it's response. /// [Fact] public async Task FunctionCallingWithBingTextSearchUsingDevBlogsSiteAsync() { // Create a kernel with OpenAI chat completion IKernelBuilder kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey); Kernel kernel = kernelBuilder.Build(); // Create a search service with Bing search var textSearch = new BingTextSearch(new(TestConfiguration.Bing.ApiKey)); // Build a text search plugin with Bing search and add to the kernel var filter = new TextSearchFilter().Equality("site", "devblogs.microsoft.com"); var searchOptions = new TextSearchOptions() { Filter = filter }; var searchPlugin = KernelPluginFactory.CreateFromFunctions( "SearchPlugin", "Search Microsoft Developer Blogs site only", [textSearch.CreateGetTextSearchResults(searchOptions: searchOptions)]); kernel.Plugins.Add(searchPlugin); // Invoke prompt and use text search plugin to provide grounding information OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }; KernelArguments arguments = new(settings); Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic Kernel? Include citations to the relevant information where it is referenced in the response.", arguments)); } /// /// Show how to create a default from an and use it with /// function calling to have the LLM include grounding context from the Microsoft Dev Blogs site in it's response. /// [Fact] public async Task FunctionCallingWithBingTextSearchUsingSiteArgumentAsync() { // Create a kernel with OpenAI chat completion IKernelBuilder kernelBuilder = Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion( modelId: TestConfiguration.OpenAI.ChatModelId, apiKey: TestConfiguration.OpenAI.ApiKey); Kernel kernel = kernelBuilder.Build(); // Create a search service with Bing search var textSearch = new BingTextSearch(new(TestConfiguration.Bing.ApiKey)); // Build a text search plugin with Bing search and add to the kernel var searchPlugin = KernelPluginFactory.CreateFromFunctions("SearchPlugin", "Search specified site", [CreateSearchBySite(textSearch)]); kernel.Plugins.Add(searchPlugin); // Invoke prompt and use text search plugin to provide grounding information OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }; KernelArguments arguments = new(settings); Console.WriteLine(await kernel.InvokePromptAsync("What is the Semantic Kernel? Only include results from techcommunity.microsoft.com. Include citations to the relevant information where it is referenced in the response.", arguments)); } #region private private sealed class FunctionInvocationFilter(ITestOutputHelper output) : IFunctionInvocationFilter { public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func next) { if (context.Function.PluginName == "SearchPlugin") { output.WriteLine($"{context.Function.Name}:{JsonSerializer.Serialize(context.Arguments)}\n"); } await next(context); } } private static KernelFunction CreateSearchBySite(BingTextSearch textSearch, TextSearchFilter? filter = null) { var options = new KernelFunctionFromMethodOptions() { FunctionName = "Search", Description = "Perform a search for content related to the specified query and optionally from the specified domain.", Parameters = [ new KernelParameterMetadata("query") { Description = "What to search for", IsRequired = true }, new KernelParameterMetadata("count") { Description = "Number of results", IsRequired = false, DefaultValue = 2 }, new KernelParameterMetadata("skip") { Description = "Number of results to skip", IsRequired = false, DefaultValue = 0 }, new KernelParameterMetadata("site") { Description = "Only return results from this domain", IsRequired = false, DefaultValue = 2 }, ], ReturnParameter = new() { ParameterType = typeof(KernelSearchResults) }, }; return textSearch.CreateSearch(options); } #pragma warning restore CS0618 #endregion }