Semantic Kernel 入門系列:💾Native Function

2023-04-12 06:00:46


語意的歸語意,語法的歸語法。

基礎定義

最基本的Native Function定義只需要在方法上新增 SKFunction 的特性即可。

using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.Orchestration;

namespace MySkillsDirectory;

public class MyCSharpSkill
{
    [SKFunction("Return the first row of a qwerty keyboard")]
    public string Qwerty(string input)
    {
        return "qwertyuiop";
    }

    [SKFunction("Return a string that's duplicated")]
    public string DupDup(string text)
    {
        return text + text;
    }
}

預設情況下只需要傳遞一個string 引數就行,如果需要多個引數的話,和Semantic Function一樣,也是使用Context,不過這裡傳進去是 SKContext。在方法上使用 SKFunctionContextParameter宣告一下引數,可以提供一定的說明,同時的有需要的話,可以設定引數的預設值。

using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.Orchestration;

namespace MySkillsDirectory;

public class MyCSharpSkill
{
    [SKFunction("Return a string that's duplicated")]
    public string DupDup(string text)
    {
        return text + text;
    }

    [SKFunction("Joins a first and last name together")]
    [SKFunctionContextParameter(Name = "firstname", Description = "Informal name you use")]
    [SKFunctionContextParameter(Name = "lastname", Description = "More formal name you use")]
    public string FullNamer(SKContext context)
    {
        return context["firstname"] + " " + context["lastname"];
    }
}

呼叫的時候,一樣使用 ContextVariables.

using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Orchestration;

using MySkillsDirectory;

// ... instantiate a kernel as myKernel

var myContext = new ContextVariables(); 
myContext.Set("firstname","Sam");
myContext.Set("lastname","Appdev");

var myCshSkill = myKernel.ImportSkill ( new MyCSharpSkill(), "MyCSharpSkill");
var myOutput = await myKernel.RunAsync(myContext,myCshSkill["FullNamer"]);

Console.WriteLine(myOutput);

當然非同步的方法也是支援的。這樣的話,就可以處理一些像是網路請求,資料庫存取、檔案讀寫等操作了。

using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.Orchestration;

public class MyCSharpSkill
{
    [SKFunction("Return the first row of a qwerty keyboard")]
    public string Qwerty(string input)
    {
        return "qwertyuiop";
    }

    [SKFunction("Return the second row of a qwerty keyboard")]
    [SKFunctionName("Asdfg")]
    public async Task<string> AsdfgAsync(string input)
    {
        await ...do something asynchronous...

        return "asdfghjkl";
    }

這裡針對 AsdfgAsync 新增了一個 SKFunctionName 的特性,主要是為了使Function name 好看一些,避免 MyCSharpSkill.AsdfgAsync 這樣。

混合呼叫

和 Semantic Function中能夠呼叫 Native Function一樣,在 Native Function也可以呼叫Semantic Function,其中主要使用的還是 SKContext.

using Microsoft.SemanticKernel.SkillDefinition;
using Microsoft.SemanticKernel.Orchestration;

namespace MySkillsDirectory;

public class MyCSharpSkill
{
    [SKFunction("Tell me a joke in one line of text")]
    [SKFunctionName("TellAJokeInOneLine")]
    public async Task<string> TellAJokeInOneLineAsync(SKContext context)
    {
        // Fetch a semantic function previously loaded into the kernel
        ISKFunction joker1 = context.Func("funSkill", "joker");

        // OR Fetch a semantic function previously loaded into the kernel
        ISKFunction joker2 = context.Skills.GetSemanticFunction("funSkill", "joker");

        var joke = await joker1.InvokeAsync();

        return joke.Result.ReplaceLineEndings(" ");
    }
}

這裡並沒有限制是 Semantic Function 還是Native Function,所以甚至可以完全使用Native Function編排技能呼叫,除了引數的定義和提取有些費勁以外,其他的幾乎沒什麼問題,畢竟返回值都是string,這也就貫徹了Text is the universal wire protocol,即便是程式碼也得將就一下。

一些核心技能

Semantic Kernel 中大部分的能力都是有技能提供的,例如Semantic Kernel的一個核心元件Planner,其實就是一個Semantic Skill,另外官方提供了一些Core SKill,基本是日常比較常用的。具體可以參考https://github.com/microsoft/semantic-kernel/tree/main/dotnet/src/SemanticKernel/CoreSkills

和自行定義的Native Function一樣的,只需要使用ImportSkill就行了

using Microsoft.SemanticKernel.CoreSkills;

// ( You want to instantiate a kernel and configure it first )

myKernel.ImportSkill(new TimeSkill(), "time");

const string ThePromptTemplate = @"
Today is: {{time.Date}}
Current time is: {{time.Time}}

Answer to the following questions using JSON syntax, including the data used.
Is it morning, afternoon, evening, or night (morning/afternoon/evening/night)?
Is it weekend time (weekend/not weekend)?";

var myKindOfDay = myKernel.CreateSemanticFunction(ThePromptTemplate, maxTokens: 150);

var myOutput = await myKindOfDay.InvokeAsync();
Console.WriteLine(myOutput);

至此,Semantic Kernel 的基礎能力就學習得差不多了。


參考資料:

  1. https://learn.microsoft.com/en-us/semantic-kernel/howto/nativefunctions
  2. https://learn.microsoft.com/en-us/semantic-kernel/howto/coreskills
  3. https://github.com/microsoft/semantic-kernel/tree/main/dotnet/src/SemanticKernel/CoreSkills