【semantic Kernel】Plugin(插件)
插件
插件是Semantic Kernel的核心组件。通过插件,你可以将现有的API封装成一个集合,供AI调用,从而赋予AI原本无法执行的操作能力。在背后,Semantic Kernel利用了最新大型语言模型(LLM)中的原生功能——Function Calling,来帮助LLM进行规划并调用API。通过函数调用,LLM可以请求特定函数的执行。Semantic Kernel会将这个请求传递给代码中的对应函数,并将结果返回给LLM,供其生成最终响应。并不是所有的AI SDK都有类似插件的概念(大多数只提供函数或工具)。但在企业应用场景中,插件非常有用,因为它们封装了一组功能,契合企业开发者常用的服务和API开发方式。插件还支持依赖注入。在插件的构造函数中,你可以注入执行任务所需的服务(例如数据库连接、HTTP客户端等)。而没有插件功能的SDK通常难以实现这一点。
在 Semantic Kernel 中定义 Plugins 插件有两种方式,第一种是通过模版定义插件也叫Semantic Plugins(语义插件),第二种是通过函数创建插件也叫 Native Plugins(本地插件)
Function
通过prompt创建Prompts Function
var template = "当前北京的天气?";
var function = kernel.CreateFunctionFromPrompt(template);
通过Delegate来建Kernel Function
源码:
public static KernelFunction CreateFunctionFromMethod(
this Kernel kernel,
Delegate method,
string? functionName = null,
string? description = null,
IEnumerable<KernelParameterMetadata>? parameters = null,
KernelReturnParameterMetadata? returnParameter = null)
{
Verify.NotNull(kernel);
Verify.NotNull(method);
return KernelFunctionFactory.CreateFromMethod(method.Method, method.Target, functionName, description, parameters, returnParameter, kernel.LoggerFactory);
}
创建 Kernel Function
var kernelfunction = kernel.CreateFunctionFromMethod((string city) => { return $"{city} 好玩的地方有八达岭长城,故宫,恭王府等"; },
functionName: "GetTourismClassic", description: "获取城市的经典",
[
new KernelParameterMetadata(name:"city") {
Description="城市名"
}]);
混合函数
更多的时候,可以把本地函数和语义函数混合起来做用,下面例子就是把本地函数以插件形式注册到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>();
}
}
OpenAIPromptExecutionSettings
KernelFunction excuseFunction = kernel.CreateFunctionFromPrompt(promptTemplate,
new OpenAIPromptExecutionSettings() { MaxTokens = 100, Temperature = 0.4, TopP = 1 });
下面这些参数是用来精细控制OpenAI GPT模型在文本生成过程中的行为的:
- max_tokens:这个参数定义了模型输出的最大词数(或者说是token数)。Token不仅仅是单词,还包括标点符号和空格等。这个限制帮助控制生成内容的长度。
- temperature:这个参数用于控制输出的随机性或者创造性。温度值在0到1之间,较低的温度(如0.2或0.3)会让模型的输出更加可预测和稳定,而较高的温度(如0.8或1)会增加输出的随机性和多样性,但也可能导致文本的连贯性和相关性下降。
- top_p (Nucleus Sampling):这个参数控制模型在选择下一个词时考虑的范围。例如,如果top_p设置为0.9,模型将只从概率累积到90%的那部分词中选择下一个词。这通常有助于保持文本的相关性同时还能保持一定的创意自由。
- presence_penalty 和 frequency_penalty:这两个参数用于增加输出的多样性和降低重复性。presence_penalty增加了已出现过的词再次出现的代价,有助于避免重复同一主题或词汇。frequency_penalty类似,但它是基于词出现的频率来增加代价,频繁出现的词在后续生成中被选中的概率将降低。这些参数的组合可以帮助调整生成文本的风格和质量,以适应不同的应用场景和需求。
OpenAIPromptExecutionSettings继承自PromptExecutionSettings,也可以使用如下方式传参:
var settings = new PromptExecutionSettings{
ExtensionData = new Dictionary<string, object> {
["max_tokens"] = 1000,
["temperature"] = 0.2,
["top_p"] = 0.8,
["presence_penalty"] = 0.0,
["frequency_penalty"] = 0.0
}
};
Sermantic Plugins
通过模版定义插件
我们知道可以通过Prompts(提示词工程)可以和LLMs进行对话,我们在处理一系列特定业务过程中,可能不止一个Prompts,可能是一组Prompts的集合。我们可以把这些针对业务能力的Prompts集合放到Semantic Kernel的插件集合内。
通过文件创建插件:
mkdir -p Plugins/DevOps/GenerateKubernetesYaml
在文件夹中分别添加 skprompt.txt 与 config.json 文件
提示词配置文件 skprompt.txt,需要输入的变量是 input
INSTRUCTIONS:
Generate YAML files to create a kubernetes deployment according to the given description.
RULES:
- All YAML files must be complete
- YAML files must be listed one by one
- Every file is indicated with "FILE:" followed by its path
- Every file content must begin and end with #----#
DESCRIPTION:{{$input}}
请求大模型 api 的配置文件 config.json
{
"schema": 1,
"description": "Create kubernetes YAML files for given devops task",
"type": "completion",
"completion": {
"max_tokens": 1000,
"temperature": 0.5,
"top_p": 1,
"presence_penalty": 0,
"frequency_penalty": 0
},
"input": {
"parameters": [
{
"name": "input",
"description": "The description of the deployment to be be created",
"defaultValue": "keycloak with postgres backend and prod mode activated. nginx-ingress is used to forward calls to keycloak. ingress uses tls termination."
}
]
}
}
添加 Plugin
Program 中调用 AddFromPromptDirectory 方法添加 plugin,注意参数中的路径是 plugin 的路径,不是 prompt function 的路径
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion("gpt-3.5-turbo", apiKey);
builder.Plugins.AddFromPromptDirectory("Plugins/DevOps");
var kernel = builder.Build();
控制台输出 plugin 信息,看看插件是否已成功添加
foreach (var plugin in kernel.Plugins)
{
Console.WriteLine("plugin: " + plugin.Name);
foreach (var function in plugin)
{
Console.WriteLine(" - prompt function: " + function.Name);
}
}
Native Plugins
根据类型创建插件
SK源码:
public static KernelPlugin ImportPluginFromType<T>(this Kernel kernel, string? pluginName = null)
{
KernelPlugin plugin = CreatePluginFromType<T>(kernel, pluginName);
kernel.Plugins.Add(plugin);
return plugin;
}
在 Semantic Kernel 中定义函数插件,需要用到两个特性KernelFunction和Description
KernelFunction特性把函数标记为一个SK的Native function;Description给函数和参数以及返回值加描述,方便LLMs能够更好的理解。
public class WeatherPlugin
{
public static string GetWeather => "WeatherSearch";
[KernelFunction, Description("根据城市查询天气")]
public string WeatherSearch([Description("城市名")] string city)
{
return $"{city}, 25℃,天气晴朗。";
}
}
Kernel添加插件
kernel.ImportPluginFromType<WeatherPlugin>();
调用
var getWeatherFunc = kernel.Plugins.GetFunction(nameof(WeatherPlugin), WeatherPlugin.GetWeather);
var weatherContent = await getWeatherFunc.InvokeAsync(kernel, new() { ["city"] = "北京" });
Console.WriteLine(weatherContent.ToString());
输出
北京, 25℃,天气晴朗。
这是手动调用的方式当然也可以IChatCompletionService会话模式自动调用。
根据对象创建
主要用到了ImportPluginFromObject这个扩展方法
public static KernelPlugin ImportPluginFromObject(this Kernel kernel, object target, string? pluginName = null)
{
KernelPlugin plugin = CreatePluginFromObject(kernel, target, pluginName);
kernel.Plugins.Add(plugin);
return plugin;
}
根据方法组 创建对象的实例
kernel.ImportPluginFromFunctions("TourismClassicPlugin", [function1,function2]);
var getTourismClassic = kernel.Plugins.GetFunction("TourismClassicPlugin", "GetTourismClassic");
var weatherContent = await getTourismClassic.InvokeAsync(kernel, new() { ["city"] = "北京" });
Console.WriteLine(weatherContent.ToString());
输出
北京 好玩的地方有八达岭长城,故宫,恭王府等
扩展
上面介绍的都是在Sk中创建Native Plugins常用的方法,还有一些用法,比如:
- ImportPluginFromApiManifestAsync OpenAPI 功能相关
- ImportPluginFromOpenAIAsync 通过 OpenAI 的 ChatGPT 格式为 OpenAI 插件创建一个插件
- CreatePluginFromOpenApiAsync 从 OpenAPI v3 端点创建插件
- ImportPluginFromGrpcFile 从 gRPC 文档导入
- 其他
SK内置插件
https://www.cnblogs.com/ruipeng/p/18251740
https://www.cnblogs.com/ruipeng/p/18266195

浙公网安备 33010602011771号