• Semantic Kernel中的Functions
  • 发布于 1周前
  • 41 热度
    0 评论
  • APAC
  • 18 粉丝 36 篇博客
  •   

Semantic Kernel中的Functions被设计为可被动态调用的“插件”,用于处理某些特定的操作。例如,一个Function可以是用于获取天气信息的API请求,或者是用于计算复杂数学表达式的函数。在SK中,Functions通常分为以下两种类型:本地函数和语义函数。


Native Functions(本地函数)
这些是直接用编程语言(如Python、C#等)编写的代码函数,可以执行任意的编程逻辑。开发者可以将本地函数注册到Semantic Kernel中,使其成为LLM可以调用的操作。例如,取当前UTC时间。
var timer = new Timer();
var nativeFunction = kernel.CreateFunctionFromMethod(typeof(Timer).GetMethod("GetCurrentTime"), target: timer);
var result = await nativeFunction.InvokeAsync(kernel);
var time = result.GetValue<DateTime>();
Console.WriteLine("当前UTC时间:{0}", time);
class Timer
{
    [KernelFunction]
    public DateTime GetCurrentTime()
    {
        // 堆代码 duidaima.com
        return DateTime.UtcNow;
    }
}
Semantic Functions(语义函数):
这些是由自然语言描述的函数,通常由大语言模型(LLM)来执行。例如,一个语义函数可以是“给出一个国家(参数)前五大城市”。Semantic Kernel使用LLM来解释和执行这些语义函数。
using System.Text.Json;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.ComponentModel;
using System.Globalization;

var chatModelId = "gpt-4o";
var key = File.ReadAllText(@"C:\GPT\key.txt");

var kernel = Kernel.CreateBuilder()
   .AddOpenAIChatCompletion(chatModelId, key)
   .Build();

var promptFunctionDescribe = "请给出{{$parameter}}的前五大城市的人口数量和经济规模";
var promptFunction = kernel.CreateFunctionFromPrompt(promptFunctionDescribe);
var result = await kernel.InvokeAsync(promptFunction, new() { ["parameter"] = "日本" });
Console.WriteLine(result.GetValue<string>());
Console.WriteLine(JsonSerializer.Serialize(result.Metadata?["Usage"]));
混合函数:
更多的时候,可以把本地函数和语义函数混合起来做用,下面例子就是把本地函数以插件形式注册到Kernel的插件类型集合中,这样就可以在普通的本地函数中调用语义函数了。下面例子是先中数据查询出数据,然后再用语义函数来总结。
kernel.ImportPluginFromType<DataBase>();
var salesAnalysisFunctions = kernel.ImportPluginFromType<SalesAnalysis>();
var result = await kernel.InvokeAsync<string>(salesAnalysisFunctions["Analysis"]);
Console.WriteLine(result);

class SalesAnalysis
{
    [KernelFunction]
    public async Task<string?> AnalysisAsync(Kernel kernel)
    {
        var data = await kernel.InvokeAsync<string>(nameof(DataBase), "GetSalesData", arguments: new() { ["month"] = "July" });
        Console.WriteLine("查询数据如下:\r\n{0}", data);
        Console.WriteLine("分析中……");
        var result = await kernel.InvokePromptAsync("请根据下面的json数据,综合每个人的销售总额和件数,以及它们的关系,给出销售能力评价。要求:不需要给出分析过程,只用以控制台表格形式给出分析结果,评价用中文书写。数据:{{$data}}", new() { ["data"] = data });
        return result.GetValue<string>();
    }
}
public class DataBase
{
    List<SalesRecord> salesData = new List<SalesRecord>
        {
            // Alice's sales data
            new SalesRecord("Alice", "January", 50, 1000.50m),
            new SalesRecord("Alice", "February", 45, 950.75m),
            new SalesRecord("Alice", "March", 60, 1200.30m),
            new SalesRecord("Alice", "April", 55, 1100.40m),
            new SalesRecord("Alice", "May", 70, 1500.80m),
            new SalesRecord("Alice", "June", 65, 1300.00m),
            new SalesRecord("Alice", "July", 75, 1550.25m),
            new SalesRecord("Alice", "August", 80, 1600.00m),
            new SalesRecord("Alice", "September", 85, 1700.75m),
            new SalesRecord("Alice", "October", 90, 1800.90m),
            new SalesRecord("Alice", "November", 95, 1900.60m),
            new SalesRecord("Alice", "December", 100, 2000.00m),

            // Bob's sales data
            new SalesRecord("Bob", "January", 30, 600.00m),
            new SalesRecord("Bob", "February", 35, 700.50m),
            new SalesRecord("Bob", "March", 40, 800.25m),
            new SalesRecord("Bob", "April", 32, 640.75m),
            new SalesRecord("Bob", "May", 38, 760.00m),
            new SalesRecord("Bob", "June", 42, 820.60m),
            new SalesRecord("Bob", "July", 47, 940.90m),
            new SalesRecord("Bob", "August", 50, 1000.00m),
            new SalesRecord("Bob", "September", 55, 1100.30m),
            new SalesRecord("Bob", "October", 60, 1200.50m),
            new SalesRecord("Bob", "November", 65, 1300.75m),
            new SalesRecord("Bob", "December", 70, 1400.00m),

            // Charlie's sales data
            new SalesRecord("Charlie", "January", 25, 500.00m),
            new SalesRecord("Charlie", "February", 28, 560.00m),
            new SalesRecord("Charlie", "March", 35, 700.00m),
            new SalesRecord("Charlie", "April", 30, 600.00m),
            new SalesRecord("Charlie", "May", 40, 800.00m),
            new SalesRecord("Charlie", "June", 45, 900.00m),
            new SalesRecord("Charlie", "July", 50, 5000.00m),
            new SalesRecord("Charlie", "August", 55, 1100.00m),
            new SalesRecord("Charlie", "September", 60, 1200.00m),
            new SalesRecord("Charlie", "October", 65, 1300.00m),
            new SalesRecord("Charlie", "November", 70, 1400.00m),
            new SalesRecord("Charlie", "December", 75, 1500.00m),

            // Diana's sales data
            new SalesRecord("Diana", "January", 40, 800.00m),
            new SalesRecord("Diana", "February", 42, 840.00m),
            new SalesRecord("Diana", "March", 45, 900.00m),
            new SalesRecord("Diana", "April", 50, 1000.00m),
            new SalesRecord("Diana", "May", 55, 1100.00m),
            new SalesRecord("Diana", "June", 60, 1200.00m),
            new SalesRecord("Diana", "July", 65, 1300.00m),
            new SalesRecord("Diana", "August", 70, 1400.00m),
            new SalesRecord("Diana", "September", 75, 1500.00m),
            new SalesRecord("Diana", "October", 80, 1600.00m),
            new SalesRecord("Diana", "November", 85, 1700.00m),
            new SalesRecord("Diana", "December", 90, 1800.00m),

            // Ethan's sales data
            new SalesRecord("Ethan", "January", 15, 300.00m),
            new SalesRecord("Ethan", "February", 18, 360.00m),
            new SalesRecord("Ethan", "March", 20, 400.00m),
            new SalesRecord("Ethan", "April", 25, 500.00m),
            new SalesRecord("Ethan", "May", 30, 600.00m),
            new SalesRecord("Ethan", "June", 35, 700.00m),
            new SalesRecord("Ethan", "July", 40, 800.00m),
            new SalesRecord("Ethan", "August", 45, 900.00m),
            new SalesRecord("Ethan", "September", 50, 1000.00m),
            new SalesRecord("Ethan", "October", 55, 1100.00m),
            new SalesRecord("Ethan", "November", 60, 1200.00m),
            new SalesRecord("Ethan", "December", 65, 1300.00m)
        };
    [KernelFunction]
    public async Task<string> GetSalesDataAsync(string month)
    {
        var salesRecords = salesData.Where(s => s.Month == month);
        if (salesRecords == null)
        {
            return "No sales data found for the specified name and month.";
        }
        return JsonSerializer.Serialize(salesRecords);
    }
    public class SalesRecord
    {
        public string Name { get; set; }
        public string Month { get; set; }
        public int Quantity { get; set; }
        public decimal Amount { get; set; }

        public SalesRecord(string name, string month, int quantity, decimal amount)
        {
            Name = name;
            Month = month;
            Quantity = quantity;
            Amount = amount;
        }
    }
}
最终结果如下:

其实用FuncationCalling更优雅,后续文章介绍……
用户评论