Semantic Kernel 入門系列:📅 Planner 規劃器

2023-04-16 21:00:22

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了。

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 [email protected]";

然後就可以執行 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 [email protected]
</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="[email protected]"/>
</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: [email protected]

介紹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