-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
.Net: Function Calling Stepwise Planner (#3434)
### Motivation and Context Stepwise planner built on OpenAI function calling ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [x] I didn't break anyone 😄 --------- Co-authored-by: Ben Thomas <[email protected]> Co-authored-by: Mark Wallace <[email protected]>
- Loading branch information
1 parent
43d1ee7
commit 01fdb69
Showing
20 changed files
with
706 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
dotnet/samples/KernelSyntaxExamples/Example66_FunctionCallingStepwisePlanner.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using Microsoft.SemanticKernel; | ||
using Microsoft.SemanticKernel.Planners; | ||
using Microsoft.SemanticKernel.Plugins.Core; | ||
using Plugins; | ||
using RepoUtils; | ||
|
||
// ReSharper disable once InconsistentNaming | ||
public static class Example66_FunctionCallingStepwisePlanner | ||
{ | ||
public static async Task RunAsync() | ||
{ | ||
string[] questions = new string[] | ||
{ | ||
"What is the current hour number, plus 5?", | ||
"What is 387 minus 22? Email the solution to John and Mary.", | ||
"Write a limerick, translate it to Spanish, and send it to Jane", | ||
}; | ||
|
||
var kernel = InitializeKernel(); | ||
|
||
var config = new FunctionCallingStepwisePlannerConfig | ||
{ | ||
MaxIterations = 15, | ||
MaxTokens = 4000, | ||
}; | ||
var planner = new FunctionCallingStepwisePlanner(kernel, config); | ||
|
||
foreach (var question in questions) | ||
{ | ||
FunctionCallingStepwisePlannerResult result = await planner.ExecuteAsync(question); | ||
Console.WriteLine($"Q: {question}\nA: {result.FinalAnswer}"); | ||
|
||
// You can uncomment the line below to see the planner's process for completing the request. | ||
// Console.WriteLine($"Chat history:\n{result.ChatHistory?.AsJson()}"); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Initialize the kernel and load plugins. | ||
/// </summary> | ||
/// <returns>A kernel instance</returns> | ||
private static IKernel InitializeKernel() | ||
{ | ||
IKernel kernel = new KernelBuilder() | ||
.WithLoggerFactory(ConsoleLogger.LoggerFactory) | ||
.WithAzureOpenAIChatCompletionService( | ||
TestConfiguration.AzureOpenAI.ChatDeploymentName, | ||
TestConfiguration.AzureOpenAI.Endpoint, | ||
TestConfiguration.AzureOpenAI.ApiKey) | ||
.Build(); | ||
|
||
kernel.ImportFunctions(new EmailPlugin(), "EmailPlugin"); | ||
kernel.ImportFunctions(new MathPlugin(), "MathPlugin"); | ||
kernel.ImportFunctions(new TimePlugin(), "TimePlugin"); | ||
|
||
return kernel; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,12 @@ public string GetEmailAddress( | |
// Sensitive data, logging as trace, disabled by default | ||
logger?.LogTrace("Returning hard coded email for {0}", input); | ||
|
||
return "[email protected]"; | ||
return input switch | ||
{ | ||
"Jane" => "[email protected]", | ||
"Paul" => "[email protected]", | ||
"Mary" => "[email protected]", | ||
_ => "[email protected]", | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
dotnet/src/IntegrationTests/Planners/StepwisePlanner/FunctionCallingStepwisePlannerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Logging.Abstractions; | ||
using Microsoft.SemanticKernel; | ||
using Microsoft.SemanticKernel.Planners; | ||
using Microsoft.SemanticKernel.Plugins.Core; | ||
using Microsoft.SemanticKernel.Plugins.Web; | ||
using Microsoft.SemanticKernel.Plugins.Web.Bing; | ||
using SemanticKernel.IntegrationTests.TestSettings; | ||
using Xunit; | ||
using Xunit.Abstractions; | ||
|
||
namespace SemanticKernel.IntegrationTests.Planners.StepwisePlanner; | ||
|
||
public sealed class FunctionCallingStepwisePlannerTests : IDisposable | ||
{ | ||
private readonly string _bingApiKey; | ||
|
||
public FunctionCallingStepwisePlannerTests(ITestOutputHelper output) | ||
{ | ||
this._loggerFactory = NullLoggerFactory.Instance; | ||
this._testOutputHelper = new RedirectOutput(output); | ||
|
||
// Load configuration | ||
this._configuration = new ConfigurationBuilder() | ||
.AddJsonFile(path: "testsettings.json", optional: false, reloadOnChange: true) | ||
.AddJsonFile(path: "testsettings.development.json", optional: true, reloadOnChange: true) | ||
.AddEnvironmentVariables() | ||
.AddUserSecrets<StepwisePlannerTests>() | ||
.Build(); | ||
|
||
string? bingApiKeyCandidate = this._configuration["Bing:ApiKey"]; | ||
Assert.NotNull(bingApiKeyCandidate); | ||
this._bingApiKey = bingApiKeyCandidate; | ||
} | ||
|
||
[Theory(Skip = "Requires model deployment that supports function calling.")] | ||
[InlineData("What is the tallest mountain on Earth? How tall is it?", "Everest")] | ||
[InlineData("What is the weather in Seattle?", "Seattle")] | ||
public async Task CanExecuteStepwisePlanAsync(string prompt, string partialExpectedAnswer) | ||
{ | ||
// Arrange | ||
bool useEmbeddings = false; | ||
IKernel kernel = this.InitializeKernel(useEmbeddings); | ||
var bingConnector = new BingConnector(this._bingApiKey); | ||
var webSearchEnginePlugin = new WebSearchEnginePlugin(bingConnector); | ||
kernel.ImportFunctions(webSearchEnginePlugin, "WebSearch"); | ||
kernel.ImportFunctions(new TimePlugin(), "time"); | ||
|
||
var planner = new FunctionCallingStepwisePlanner( | ||
kernel, | ||
new FunctionCallingStepwisePlannerConfig() { MaxIterations = 10 }); | ||
|
||
// Act | ||
var planResult = await planner.ExecuteAsync(prompt); | ||
|
||
// Assert - should contain the expected answer | ||
Assert.NotNull(planResult); | ||
Assert.NotEqual(string.Empty, planResult.FinalAnswer); | ||
Assert.Contains(partialExpectedAnswer, planResult.FinalAnswer, StringComparison.InvariantCultureIgnoreCase); | ||
Assert.True(planResult.Iterations > 0); | ||
Assert.True(planResult.Iterations <= 10); | ||
} | ||
|
||
private IKernel InitializeKernel(bool useEmbeddings = false) | ||
{ | ||
AzureOpenAIConfiguration? azureOpenAIConfiguration = this._configuration.GetSection("AzureOpenAI").Get<AzureOpenAIConfiguration>(); | ||
Assert.NotNull(azureOpenAIConfiguration); | ||
|
||
AzureOpenAIConfiguration? azureOpenAIEmbeddingsConfiguration = this._configuration.GetSection("AzureOpenAIEmbeddings").Get<AzureOpenAIConfiguration>(); | ||
Assert.NotNull(azureOpenAIEmbeddingsConfiguration); | ||
|
||
var builder = new KernelBuilder() | ||
.WithLoggerFactory(this._loggerFactory) | ||
.WithRetryBasic(); | ||
|
||
builder.WithAzureOpenAIChatCompletionService( | ||
deploymentName: azureOpenAIConfiguration.ChatDeploymentName!, | ||
endpoint: azureOpenAIConfiguration.Endpoint, | ||
apiKey: azureOpenAIConfiguration.ApiKey); | ||
|
||
if (useEmbeddings) | ||
{ | ||
builder.WithAzureOpenAITextEmbeddingGenerationService( | ||
deploymentName: azureOpenAIEmbeddingsConfiguration.DeploymentName, | ||
endpoint: azureOpenAIEmbeddingsConfiguration.Endpoint, | ||
apiKey: azureOpenAIEmbeddingsConfiguration.ApiKey); | ||
} | ||
|
||
var kernel = builder.Build(); | ||
|
||
return kernel; | ||
} | ||
|
||
private readonly ILoggerFactory _loggerFactory; | ||
private readonly RedirectOutput _testOutputHelper; | ||
private readonly IConfigurationRoot _configuration; | ||
|
||
public void Dispose() | ||
{ | ||
this.Dispose(true); | ||
GC.SuppressFinalize(this); | ||
} | ||
|
||
~FunctionCallingStepwisePlannerTests() | ||
{ | ||
this.Dispose(false); | ||
} | ||
|
||
private void Dispose(bool disposing) | ||
{ | ||
if (disposing) | ||
{ | ||
if (this._loggerFactory is IDisposable ld) | ||
{ | ||
ld.Dispose(); | ||
} | ||
|
||
this._testOutputHelper.Dispose(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
36 changes: 36 additions & 0 deletions
36
dotnet/src/Planners/Planners.OpenAI/Planners.OpenAI.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<!-- THIS PROPERTY GROUP MUST COME FIRST --> | ||
<AssemblyName>Microsoft.SemanticKernel.Planners.OpenAI</AssemblyName> | ||
<RootNamespace>Microsoft.SemanticKernel.Planners</RootNamespace> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" /> | ||
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/src/InternalUtilities.props" /> | ||
|
||
<PropertyGroup> | ||
<!-- NuGet Package Settings --> | ||
<Title>Semantic Kernel - Planners</Title> | ||
<Description>Semantic Kernel OpenAI Planners.</Description> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<EmbeddedResource Include="Stepwise\InitialPlanPrompt.txt"> | ||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||
</EmbeddedResource> | ||
<EmbeddedResource Include="Stepwise\StepPrompt.txt"> | ||
<CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||
</EmbeddedResource> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\Connectors\Connectors.AI.OpenAI\Connectors.AI.OpenAI.csproj" /> | ||
<ProjectReference Include="..\..\Functions\Functions.OpenAPI\Functions.OpenAPI.csproj" /> | ||
<ProjectReference Include="..\..\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" /> | ||
<ProjectReference Include="..\..\SemanticKernel.Core\SemanticKernel.Core.csproj" /> | ||
<ProjectReference Include="..\..\Extensions\TemplateEngine.Basic\TemplateEngine.Basic.csproj" /> | ||
<ProjectReference Include="..\Planners.Core\Planners.Core.csproj" /> | ||
</ItemGroup> | ||
</Project> |
Oops, something went wrong.