Files
semantic-kernel/samples/dotnet/kernel-syntax-examples/Example12_SequentialPlanner.cs
T
af03e00a79 Feature serviceregistry (#993)
### Motivation, Context and Description

This PR introduces several changes aimed to simplify the kernel and
enhancing its extensibility for AI services management. The changes
include the following:

1. Kernel simplification and extensibility: The code responsible for AI
services management, such as registration and resolution, has been moved
out of the Kernel and KernelConfig classes into a new class called
AIServiceProvider. This restructuring promotes the principle of Single
Responsibility, ensuring that each class has a clear and focused
purpose.

2. Hosting apps customization: With the introduction of the
AIServiceProvider, hosting applications now have the flexibility to
provide their own implementation of the provider if needed. This
promotes extensibility, allowing the hosting apps to tailor the AI
service management according to their specific requirements.

3. AI services registration improvements: A new method called
WithAiService has been added, which enables the registration of any new
AI service that is not currently supported.

4. Improved kernel builder interface: The Add* methods for AI services
registration have been moved from the KernelConfig class to the
KernelBuilder. This change aligns the interface with other KernelBilder
methods, such as WithLogger and WithMemory, creating a more cohesive and
intuitive kernel builder interface.

---------

Co-authored-by: Shawn Callegari <[email protected]>
Co-authored-by: Lee Miller <[email protected]>
2023-05-22 15:21:48 -07:00

242 lines
10 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Planning;
using Microsoft.SemanticKernel.Planning.Sequential;
using RepoUtils;
using Skills;
// ReSharper disable CommentTypo
// ReSharper disable once InconsistentNaming
internal static class Example12_SequentialPlanner
{
public static async Task RunAsync()
{
await PoetrySamplesAsync();
await EmailSamplesAsync();
await BookSamplesAsync();
await MemorySampleAsync();
}
private static async Task PoetrySamplesAsync()
{
Console.WriteLine("======== Sequential Planner - Create and Execute Poetry Plan ========");
var kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_SERVICE_ID"),
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.Build();
string folder = RepoFiles.SampleSkillsPath();
kernel.ImportSemanticSkillFromDirectory(folder,
"SummarizeSkill",
"WriterSkill");
var planner = new SequentialPlanner(kernel);
var plan = await planner.CreatePlanAsync("Write a poem about John Doe, then translate it into Italian.");
// Original plan:
// Goal: Write a poem about John Doe, then translate it into Italian.
// Steps:
// - WriterSkill.ShortPoem INPUT='John Doe is a friendly guy who likes to help others and enjoys reading books.' =>
// - WriterSkill.Translate language='Italian' INPUT='' =>
Console.WriteLine("Original plan:");
Console.WriteLine(plan.ToPlanString());
var result = await kernel.RunAsync(plan);
Console.WriteLine("Result:");
Console.WriteLine(result.Result);
}
private static async Task EmailSamplesAsync()
{
Console.WriteLine("======== Sequential Planner - Create and Execute Email Plan ========");
var kernel = InitializeKernelAndPlanner(out var planner, 512);
kernel.ImportSkill(new EmailSkill(), "email");
// Load additional skills to enable planner to do non-trivial asks.
string folder = RepoFiles.SampleSkillsPath();
kernel.ImportSemanticSkillFromDirectory(folder,
"SummarizeSkill",
"WriterSkill");
var plan = await planner.CreatePlanAsync("Summarize an input, translate to french, and e-mail to John Doe");
// Original plan:
// Goal: Summarize an input, translate to french, and e-mail to John Doe
// Steps:
// - SummarizeSkill.Summarize INPUT='' =>
// - WriterSkill.Translate language='French' INPUT='' => TRANSLATED_SUMMARY
// - email.GetEmailAddress INPUT='John Doe' => EMAIL_ADDRESS
// - email.SendEmail INPUT='$TRANSLATED_SUMMARY' email_address='$EMAIL_ADDRESS' =>
Console.WriteLine("Original plan:");
Console.WriteLine(plan.ToPlanString());
var input =
"Once upon a time, in a faraway kingdom, there lived a kind and just king named Arjun. " +
"He ruled over his kingdom with fairness and compassion, earning him the love and admiration of his people. " +
"However, the kingdom was plagued by a terrible dragon that lived in the nearby mountains and terrorized the nearby villages, " +
"burning their crops and homes. The king had tried everything to defeat the dragon, but to no avail. " +
"One day, a young woman named Mira approached the king and offered to help defeat the dragon. She was a skilled archer " +
"and claimed that she had a plan to defeat the dragon once and for all. The king was skeptical, but desperate for a solution, " +
"so he agreed to let her try. Mira set out for the dragon's lair and, with the help of her trusty bow and arrow, " +
"she was able to strike the dragon with a single shot through its heart, killing it instantly. The people rejoiced " +
"and the kingdom was at peace once again. The king was so grateful to Mira that he asked her to marry him and she agreed. " +
"They ruled the kingdom together, ruling with fairness and compassion, just as Arjun had done before. They lived " +
"happily ever after, with the people of the kingdom remembering Mira as the brave young woman who saved them from the dragon.";
await ExecutePlanAsync(kernel, plan, input, 5);
}
private static async Task BookSamplesAsync()
{
Console.WriteLine("======== Sequential Planner - Create and Execute Book Creation Plan ========");
var kernel = InitializeKernelAndPlanner(out var planner);
// Load additional skills to enable planner to do non-trivial asks.
string folder = RepoFiles.SampleSkillsPath();
kernel.ImportSemanticSkillFromDirectory(folder, "WriterSkill");
kernel.ImportSemanticSkillFromDirectory(folder, "MiscSkill");
var originalPlan = await planner.CreatePlanAsync("Create a book with 3 chapters about a group of kids in a club called 'The Thinking Caps.'");
// Original plan:
// Goal: Create a book with 3 chapters about a group of kids in a club called 'The Thinking Caps.'
// Steps:
// - WriterSkill.NovelOutline chapterCount='3' INPUT='A group of kids in a club called 'The Thinking Caps' that solve mysteries and puzzles using their creativity and logic.' endMarker='<!--===ENDPART===-->' => OUTLINE
// - MiscSkill.ElementAtIndex count='3' INPUT='$OUTLINE' index='0' => CHAPTER_1_SYNOPSIS
// - WriterSkill.NovelChapter chapterIndex='1' previousChapter='' INPUT='$CHAPTER_1_SYNOPSIS' theme='Children's mystery' => RESULT__CHAPTER_1
// - MiscSkill.ElementAtIndex count='3' INPUT='$OUTLINE' index='1' => CHAPTER_2_SYNOPSIS
// - WriterSkill.NovelChapter chapterIndex='2' previousChapter='$CHAPTER_1_SYNOPSIS' INPUT='$CHAPTER_2_SYNOPSIS' theme='Children's mystery' => RESULT__CHAPTER_2
// - MiscSkill.ElementAtIndex count='3' INPUT='$OUTLINE' index='2' => CHAPTER_3_SYNOPSIS
// - WriterSkill.NovelChapter chapterIndex='3' previousChapter='$CHAPTER_2_SYNOPSIS' INPUT='$CHAPTER_3_SYNOPSIS' theme='Children's mystery' => RESULT__CHAPTER_3
Console.WriteLine("Original plan:");
Console.WriteLine(originalPlan.ToPlanString());
Stopwatch sw = new();
sw.Start();
await ExecutePlanAsync(kernel, originalPlan);
}
private static async Task MemorySampleAsync()
{
Console.WriteLine("======== Sequential Planner - Create and Execute Plan using Memory ========");
var kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.WithAzureTextEmbeddingGenerationService(
Env.Var("AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_EMBEDDINGS_ENDPOINT"),
Env.Var("AZURE_OPENAI_EMBEDDINGS_KEY"))
.WithMemoryStorage(new VolatileMemoryStore())
.Build();
string folder = RepoFiles.SampleSkillsPath();
kernel.ImportSemanticSkillFromDirectory(folder,
"SummarizeSkill",
"WriterSkill",
"CalendarSkill",
"ChatSkill",
"ChildrensBookSkill",
"ClassificationSkill",
"CodingSkill",
"FunSkill",
"IntentDetectionSkill",
"MiscSkill",
"QASkill");
kernel.ImportSkill(new EmailSkill(), "email");
kernel.ImportSkill(new StaticTextSkill(), "statictext");
kernel.ImportSkill(new TextSkill(), "text");
kernel.ImportSkill(new Microsoft.SemanticKernel.CoreSkills.TextSkill(), "coretext");
var goal = "Create a book with 3 chapters about a group of kids in a club called 'The Thinking Caps.'";
var planner = new SequentialPlanner(kernel, new SequentialPlannerConfig { RelevancyThreshold = 0.78 });
var plan = await planner.CreatePlanAsync(goal);
Console.WriteLine("Original plan:");
Console.WriteLine(plan.ToPlanString());
}
private static IKernel InitializeKernelAndPlanner(out SequentialPlanner planner, int maxTokens = 1024)
{
var kernel = new KernelBuilder()
.WithLogger(ConsoleLogger.Log)
.WithAzureTextCompletionService(
Env.Var("AZURE_OPENAI_DEPLOYMENT_NAME"),
Env.Var("AZURE_OPENAI_ENDPOINT"),
Env.Var("AZURE_OPENAI_KEY"))
.Build();
planner = new SequentialPlanner(kernel, new SequentialPlannerConfig { MaxTokens = maxTokens });
return kernel;
}
private static async Task<Plan> ExecutePlanAsync(
IKernel kernel,
Plan plan,
string input = "",
int maxSteps = 10)
{
Stopwatch sw = new();
sw.Start();
// loop until complete or at most N steps
try
{
for (int step = 1; plan.HasNextStep && step < maxSteps; step++)
{
if (string.IsNullOrEmpty(input))
{
await plan.InvokeNextStepAsync(kernel.CreateNewContext());
// or await kernel.StepAsync(plan);
}
else
{
plan = await kernel.StepAsync(input, plan);
}
if (!plan.HasNextStep)
{
Console.WriteLine($"Step {step} - COMPLETE!");
Console.WriteLine(plan.State.ToString());
break;
}
Console.WriteLine($"Step {step} - Results so far:");
Console.WriteLine(plan.State.ToString());
}
}
catch (KernelException e)
{
Console.WriteLine($"Step - Execution failed:");
Console.WriteLine(e.Message);
}
sw.Stop();
Console.WriteLine($"Execution complete in {sw.ElapsedMilliseconds} ms!");
return plan;
}
}