Semantic Kernel 入门系列:📅 Planner 计划管理
阅读原文时间:2023年07月08日阅读:2

Semantic Kernel 的一个核心能力就是实现“目标导向”的AI应用。

“目标导向”听起来是一个比较高大的词,但是却是实际生活中我们处理问题的基本方法和原则。

顾名思义,这种方法的核心就是先确定目标,然后在寻找实现目标的方法和步骤。这对于人来说的是很自然的事情,但是对于机器则不然。一大堆的指令和控制逻辑其实都是在完成另外一种产出导向的结果。所有的流程和过程都需要提前预定义好,然后期待一个结果的产出。

如今,借助 LLM AI 的力量,我们可以轻松的实现目标导向的过程。

在 Semantic Kernel中,Planner就用于这项工作。

我们可以提前准备好所需的Skill,根据设定好的最终目标,通过Planner,可以将目标分解为需要执行的任务列表,并且可以指定好对应的参数传递,然后逐个任务执行,从而实现最终目标。

为了让LLM AI更好的理解我们所提供的技能,需要明确地配置好每个技能本身的描述和参数描述。

对于的Semantic Function来说,可以在config.json中配置。以下是一个Translate的配置。

需要注意的就是 descriptioninput 参数,最终会成为的LLM是否选择使用的判断标准。而这一切都是基于语义化的理解。

{
    "schema": 1,
    "type": "completion",
    "description": "Translate the input into the specified language",
    "completion": {
        "max_tokens": 256,
        "temperature": 0.0,
        "top_p": 0.0,
        "presence_penalty": 0.0,
        "frequency_penalty": 0.0
    },
    "input": {
        "parameters": [
        {
            "name": "input",
            "description": "input text",
            "defaultValue": ""
        },
        {
            "name":"language",
            "description":"the specified language",
            "defaultValue":"English"
        }
        ]
    }
}

对于Native Function ,可以使用特性进行声明,其实 SKFunction 中用于添加Function的描述, SKFunctionContextParameter 用于添加参数的说明。

public class EmailSKill {
    [SKFunction("Send email conten to receiver")]
    [SKFunctionContextParameter(Name ="content", Description = "email content")]
    [SKFunctionContextParameter(Name ="receiver", Description = "the email address of receiver")]
    public void SendTo(SKContext context){
        var email = context["content"];
        var receiver = context["receiver"];
        Console.WriteLine(
$"""
mail to: {receiver}
{email}
""");
    }
}

准备好技能之后,就可以将这些技能导入到Kernel中,准备后续时候。

作为示例,这里导入了三个功能:

  1. MySkill.WriteText : 文案写作
  2. MySkill.Translate :文本翻译
  3. email.SendTo : 邮件发送

然后我们就可以开始使用 Planner了。

Planner本身也是 Semantic Kernel中的一个Skill,融合了Semantic Function 和 Native Function。

和通常的Skill一样,导入 PlannerSkill 后即可使用。

var planner = kernel.ImportSkill(new PlannerSkill(kernel),"plan");

指定好任务目标。

var goal = "The PowerBlog is about to release a new product, please write a chinese press release about the new product and send it to mail@example.com";

然后就可以执行 CreatePlan 创建一个Plan。

var plan = await kernel.RunAsync(goal,planner["CreatePlan"]);

此时可以使用Plan.PathString查看当前的任务编排。

Plan所有状态的结果都会保存在Context中,所以可以通过 context.Variables.ToPlan() 方法获取Plan对象。

plan.Variables.ToPlan().PlanString.Dump("Create Plan");
/*
<goal>
The PowerBlog is about to release a new product, please write a chinese press release about the new product and send it to mail@example.com
</goal>
<plan>
  <function.MySkill.WriteText input="The PowerBlog is about to release a new product" setContextVariable="PRESS_RELEASE"/>
  <function.MySkill.Translate input="$PRESS_RELEASE" language="Chinese" setContextVariable="TRANSLATED_PRESS_RELEASE"/>
  <function.email.SendTo content="$TRANSLATED_PRESS_RELEASE" receiver="mail@example.com"/>
</plan>
*/

创建好了 Plan 就可以使用 ExecutePlan 执行了。

由于每次只能执行一个Function,所以我们需要通过循环来执行所有Function。

同时根据Plan的状态,判断是否执行成功,是否执行完成。

Plan 有两种状态,一种是 IsSuccessful ,表示当前的Plan是否执行成功。

另一个是 IsComplete,表示整个Plan是否执行完成。

async Task<SKContext> ExecutePlanAsync(IKernel kernel, SKContext plan)
{
    var executionResults = plan;
    while (!executionResults.Variables.ToPlan().IsComplete)
    {
        var result = await kernel.RunAsync(executionResults.Variables, planner["ExecutePlan"]);
        var planResult = result.Variables.ToPlan();
        if (planResult.IsSuccessful)
        {
            if (planResult.IsComplete)
            {
                break;
            }
        }
        else
        {
            break;
        }
        executionResults = result;
    }
    return executionResults;
}

var result = await ExecutePlanAsync(kernel,plan);
// output:
/*
mail to: mail@example.com

介绍PowerBlog——专为企业和企业家设计的终极博客平台。我们的新产品旨在帮助您轻松创建、管理和优化博客内容。

PowerBlog是为想要创建与竞争对手不同的专业博客的企业和企业家完美的解决方案。通过我们的直观用户界面,您可以快速创建和管理博客文章,优化SEO内容,并跟踪博客的性能。

我们的新产品还包括强大的功能,如自动内容策划、社交媒体整合和分析工具。通过这些功能,您可以轻松监控博客的性能,并就内容策略做出明智的决定。

PowerBlog是为想要创建与竞争对手不同的专业博客的企业和企业家完美的解决方案。通过我们的直观用户界面,您可以快速创建和管理博客文章,优化SEO内容,并跟踪博客的性能。

我们很高兴向客户提供这款新产品,期待帮助您创建一个成功的博客。今天就注册,开始创建与竞争对手不同的内容吧。
*/

最后根据状态判断Plan最后执行是否成功,并从plan.Result 中获取最后的输出结果。

if(!result.Variables.ToPlan().IsSuccessful) {
    result.Variables.ToPlan().Result.Dump("Complete!");
}else {
    result.Variables.ToPlan().Result.Dump("Error");
}
/*
:Complete!:

介绍PowerBlog——专为企业和企业家设计的终极博客平台。我们的新产品旨在帮助您轻松创建、管理和优化博客内容。

PowerBlog是为想要创建与竞争对手不同的专业博客的企业和企业家完美的解决方案。通过我们的直观用户界面,您可以快速创建和管理博客文章,优化SEO内容,并跟踪博客的性能。

我们的新产品还包括强大的功能,如自动内容策划、社交媒体整合和分析工具。通过这些功能,您可以轻松监控博客的性能,并就内容策略做出明智的决定。

PowerBlog是为想要创建与竞争对手不同的专业博客的企业和企业家完美的解决方案。通过我们的直观用户界面,您可以快速创建和管理博客文章,优化SEO内容,并跟踪博客的性能。

我们很高兴向客户提供这款新产品,期待帮助您创建一个成功的博客。今天就注册,开始创建与竞争对手不同的内容吧。
*/

至此,我们就掌握了Semantic Kernel 当前所有的核心概念和基本使用方法。


参考资料:

  1. Planner in Semantic Kernel | Microsoft Learn
  2. semantic-kernel/05-using-the-planner.ipynb at main · microsoft/semantic-kernel · GitHub
  3. What is Semantic Kernel? | Microsoft Learn