在SK框架中,Planner用于根据输入创建执行计划。SK框架会自动搜索注册到kernel中的所有可用的function,然后阅读这些function的说明,最后利用这些function搭建可用的执行计划。注意,在使用Planner之前,必须先把所有Function(不管是Native Function还是 SK Function)先注册到Kernel,并附上相应的function说明以便于AI识别。
其中,Native Function采用System.ComponentModel.DescriptionAttribute来标注函数和参数说明,SK Function则在config中定义。参考定义如下:
{
"schema": 1,
"description": "Generate a funny limerick about a person",
"execution_settings": {
"default": {
"max_tokens": 100,
"temperature": 0.7,
"top_p": 0,
"presence_penalty": 0,
"frequency_penalty": 0
}
},
"input_variables": [
{
"name": "name",
"description": "",
"default": "Bob"
},
{
"name": "input",
"description": "",
"default": "Dogs"
}
]
}
SK框架中,目前提供了两种Planner: HandlebarsPlanner 和 FunctionCallingStepwisePlanner,前者用于根据目标制定一系列执行计划并执行。后者是逐步执行器,他将逐步执行生成的计划,并参考过程中的执行结果,调整执行任务。
HandlebarsPlanner
HandlebarsPlanner源码如下:
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using HandlebarsDotNet.Helpers.Enums;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
using Microsoft.SemanticKernel.Text;
namespace Microsoft.SemanticKernel.Planning.Handlebars;
/// <summary>
/// Represents a Handlebars planner.
/// </summary>
public sealed class HandlebarsPlanner
{
/// <summary>
/// Represents static options for all Handlebars Planner prompt templates.
/// </summary>
public static readonly HandlebarsPromptTemplateOptions PromptTemplateOptions = new()
{
// Options for built-in Handlebars helpers
Categories = new Category[] {
Category.DateTime },
UseCategoryPrefix = false,
// Custom helpers
RegisterCustomHelpers = HandlebarsPromptTemplateExtensions.RegisterCustomCreatePlanHelpers,
};
/// <summary>
/// Initializes a new instance of the <see cref="HandlebarsPlanner"/> class.
/// </summary>
/// <param name="options">Configuration options for Handlebars Planner.</param>
public HandlebarsPlanner(HandlebarsPlannerOptions? options = default)
{
this._options = options ?? new HandlebarsPlannerOptions();
this._templateFactory = new HandlebarsPromptTemplateFactory(options: PromptTemplateOptions);
this._options.ExcludedPlugins.Add("Planner_Excluded");
}
/// <summary>Creates a plan for the specified goal.</summary>
/// <param name="kernel">The <see cref="Kernel"/> containing services, plugins, and other state for use throughout the operation.</param>
/// <param name="goal">The goal for which a plan should be created.</param>
/// <param name="arguments"> Optional. Context arguments to pass to the planner. </param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>The created plan.</returns>
/// <exception cref="ArgumentNullException"><paramref name="goal"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="goal"/> is empty or entirely composed of whitespace.</exception>
/// <exception cref="KernelException">A plan could not be created.</exception>
public Task<HandlebarsPlan> CreatePlanAsync(Kernel kernel, string goal, KernelArguments? arguments = null, CancellationToken cancellationToken = default)
{
Verify.NotNullOrWhiteSpace(goal);
var logger = kernel.LoggerFactory.CreateLogger(typeof(HandlebarsPlanner)) ?? NullLogger.Instance;
return PlannerInstrumentation.CreatePlanAsync(
static (HandlebarsPlanner planner, Kernel kernel, string goal, KernelArguments? arguments, CancellationToken cancellationToken)
=> planner.CreatePlanCoreAsync(kernel, goal, arguments, cancellationToken),
this, kernel, goal, arguments, logger, cancellationToken);
}
#region private
private readonly HandlebarsPlannerOptions _options;
private readonly HandlebarsPromptTemplateFactory _templateFactory;
private async Task<HandlebarsPlan> CreatePlanCoreAsync(Kernel kernel, string goal, KernelArguments? arguments, CancellationToken cancellationToken = default)
{
string? createPlanPrompt = null;
ChatMessageContent? modelResults = null;
try
{
// Get CreatePlan prompt template
var functionsMetadata = await kernel.Plugins.GetFunctionsAsync(this._options, null, null, cancellationToken).ConfigureAwait(false);
var availableFunctions = this.GetAvailableFunctionsManual(functionsMetadata, out var complexParameterTypes, out var complexParameterSchemas);
createPlanPrompt = await this.GetHandlebarsTemplateAsync(kernel, goal, arguments, availableFunctions, complexParameterTypes, complexParameterSchemas, cancellationToken).ConfigureAwait(false);
ChatHistory chatMessages = this.GetChatHistoryFromPrompt(createPlanPrompt);
// Get the chat completion results
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
modelResults = await chatCompletionService.GetChatMessageContentAsync(chatMessages, executionSettings: this._options.ExecutionSettings, cancellationToken: cancellationToken).ConfigureAwait(false);
// Regex breakdown:
// (```\s*handlebars){1}\s*: Opening backticks, starting boundary for HB template
// ((([^`]|`(?!``))+): Any non-backtick character or one backtick character not followed by 2 more consecutive backticks
// (\s*```){1}: Closing backticks, closing boundary for HB template
MatchCollection matches = Regex.Matches(modelResults.Content, @"(```\s*handlebars){1}\s*(([^`]|`(?!``))+)(\s*```){1}", RegexOptions.Multiline);
if (matches.Count < 1)
{
throw new KernelException($"[{
HandlebarsPlannerErrorCodes.InvalidTemplate}] Could not find the plan in the results. Additional helpers or input may be required.\n\nPlanner output:\n{
modelResults.Content}");
}
else if (matches.Count > 1)
{
throw new KernelException($"[{
HandlebarsPlannerErrorCodes.InvalidTemplate}] Identified multiple Handlebars templates in model response. Please try again.\n\nPlanner output:\n{
modelResults.Content}");
}
var planTemplate = matches[0].Groups[2].Value.Trim();
planTemplate = MinifyHandlebarsTemplate(planTemplate);
return new HandlebarsPlan(planTemplate, createPlanPrompt);
}
catch (KernelException ex)
{
throw new PlanCreationException(
"CreatePlan failed. See inner exception for details.",
createPlanPrompt,
modelResults,
ex
)