文章目录
- 一、引言
- 二、开发Semantic Kernel插件
- 三、实战
- 3.1 时间信息插件
- 3.2 小部件工厂插件
- 3.3 初始化Semantic Kernel实例
- 3.4 四个实战示例
- 3.4.1 模型幻觉
- 3.4.2 给模型提供时间信息
- 3.4.3 AI自动调用函数
- 3.4.4 AI自动调用和使用枚举
- 四、结论
一、引言
在上一篇入门文章《探索Semantic Kernel:开启AI编程新篇章》中,我们了解了Semantic Kernel的基础知识,包括如何创建内核实例、配置AI模型以及执行基本的AI任务。本文将作为进阶篇,重点介绍如何开发Semantic Kernel插件,并在实际应用中调用这些插件。
二、开发Semantic Kernel插件
Semantic Kernel插件是扩展AI模型功能的模块,它们可以封装特定领域的知识和功能,使得AI模型能够执行更复杂的任务。在本教程中,我们将开发两个插件:TimeInformation和WidgetFactory。
三、实战
3.1 时间信息插件
TimeInformation插件提供了一个函数,用于获取当前的UTC时间。
/// <summary>
/// 返回当前时间的插件
/// </summary>
public class TimeInformation
{
[KernelFunction]
[Description("获取当前UTC时间")]
public string GetCurrentUtcTime() => DateTime.UtcNow.ToString("R");
}
3.2 小部件工厂插件
WidgetFactory插件提供了一个函数,用于根据指定的类型和颜色创建小部件。
/// <summary>
/// 创建部件的插件
/// </summary>
public class WidgetFactory
{
[KernelFunction]
[Description("创建指定类型和颜色的部件")]
public WidgetDetails CreateWidget([Description("要创建的小部件的类型")] WidgetType widgetType, [Description("要创建的小部件颜色")] WidgetColor[] widgetColors)
{
var colors = string.Join('-', widgetColors.Select(c => c.GetDisplayName()).ToArray());
return new()
{
SerialNumber = $"{widgetType}-{colors}-{Guid.NewGuid()}",
Type = widgetType,
Colors = widgetColors
};
}
}
为了使插件能够处理不同的小部件类型和颜色,我们定义了两个枚举类型:WidgetType和WidgetColor。
WidgetDetails类用于存储小部件的详细信息,包括序列号、类型和颜色。
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum WidgetType
{
[Description("有用的小部件。")]
Useful,
[Description("装饰性的小部件。")]
Decorative
}
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum WidgetColor
{
[Description("创建红色物品时使用")]
Red,
[Description("创建绿色物品时使用")]
Green,
[Description("创建蓝色物品时使用")]
Blue
}
public class WidgetDetails
{
public string SerialNumber { get; init; }
public WidgetType Type { get; init; }
public WidgetColor[] Colors { get; init; }
}
/// <summary>
/// 枚举扩展方法
/// </summary>
public static class EnumExtensions
{
private static readonly ConcurrentDictionary<Enum, string> DisplayNameCache = new ConcurrentDictionary<Enum, string>();
/// <summary>
/// 获取枚举字段值的属性。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="enumValue"></param>
/// <returns></returns>
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Fields are never trimmed for enum types.")]
public static T GetAttributeOfType<T>(this Enum enumValue) where T : Attribute
{
FieldInfo field = enumValue.GetType().GetField(enumValue.ToString(), BindingFlags.Static | BindingFlags.Public);
if (field == null)
{
return null;
}
return field.GetCustomAttributes<T>(inherit: false).FirstOrDefault();
}
/// <summary>
/// 获取enum的显示名称
/// </summary>
/// <param name="enumValue"></param>
/// <returns></returns>
public static string GetDisplayName(this Enum enumValue)
{
return DisplayNameCache.GetOrAdd(enumValue, delegate (Enum e)
{
DisplayAttribute attributeOfType = e.GetAttributeOfType<DisplayAttribute>();
return (attributeOfType != null) ? attributeOfType.Name : e.ToString();
});
}
}
3.3 初始化Semantic Kernel实例
#pragma warning disable SKEXP0001, SKEXP0010, SKEXP0050, SKEXP0020, ASP0000
//使用的模型
string model = "gpt-4o-mini";
//使用的openai代理地址,这里也可以使用国产模型和地址。只要是openai格式即可
string endpointKey = "https://xie.openai.com/v1";
//openai 密钥
string apiKey = "sk-";
// 创建一个带有OpenAI聊天完成的内核
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion(
modelId: model,
endpoint: new Uri(endpointKey),
apiKey: apiKey);
//添加打印时间信息插件
kernelBuilder.Plugins.AddFromType<TimeInformation>();
//添加构建颜色、类型部件插件
kernelBuilder.Plugins.AddFromType<WidgetFactory>();
Kernel kernel = kernelBuilder.Build();
3.4 四个实战示例
3.4.1 模型幻觉
//示例1:用一个提示来调用内核,该提示要求AI提供它无法提供的信息,并可能产生幻觉
Console.WriteLine("------------------------示例1 模型无法提供的实时信息-------------------------------");
Console.WriteLine(await kernel.InvokePromptAsync("离圣诞节还有几天?"));
Console.WriteLine();
3.4.2 给模型提供时间信息
//示例2:使用模板提示调用内核,该提示调用插件并显示结果
Console.WriteLine("------------------------示例2 使用插件结合问题,给模型提供时间信息--------------------------------");
Console.WriteLine(await kernel.InvokePromptAsync("当前时间为: {{TimeInformation.GetCurrentUtcTime}}。 离圣诞节还有几天?"));
Console.WriteLine();
3.4.3 AI自动调用函数
//示例3:使用提示符调用内核,并允许AI自动调用函数
#pragma warning disable SKEXP0001 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
OpenAIPromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };
#pragma warning restore SKEXP0001 // 类型仅用于评估,在将来的更新中可能会被更改或删除。取消此诊断以继续。
Console.WriteLine("------------------------示例3 模型自动评估是否调用函数获取时间--------------------------------");
Console.WriteLine(await kernel.InvokePromptAsync("离圣诞节还有几天?解释你的想法。", new(settings)));
Console.WriteLine();
3.4.4 AI自动调用和使用枚举
//示例4:用提示符调用内核,并允许AI自动调用使用枚举的函数
Console.WriteLine("------------------------示例4 模型自动调用函数生成对应颜色组件- -----------------------------");
Console.WriteLine("--------------------示例4.1红色----------------------------");
//因枚举中有红色,AI自动识别并创建红色组件
Console.WriteLine(await kernel.InvokePromptAsync("为我创建一个红色小部件。", new(settings)));
Console.WriteLine();
Console.WriteLine("--------------------示例4.2浅绿色----------------------------");
//因枚举中有绿色,AI自动识别并创建绿色组件
Console.WriteLine(await kernel.InvokePromptAsync("为我创建一个浅绿色小部件。", new(settings)));
Console.WriteLine();
Console.WriteLine("--------------------示例4.3蓝色----------------------------");
//因枚举中有蓝色,AI自动识别并创建蓝色组件
Console.WriteLine(await kernel.InvokePromptAsync("为我创建一个蓝色小部件。", new(settings)));
Console.WriteLine();
// 因枚举中没有紫色,AI会告知用户只能在系统提供的三种颜色中选取
Console.WriteLine("--------------------示例4.4紫色----------------------------");
Console.WriteLine(await kernel.InvokePromptAsync("为我创建一个紫色小部件。", new(settings)));
四、结论
通过开发和调用Semantic Kernel插件,我们可以将自定义功能和业务逻辑集成到AI模型中,从而创建更加强大和灵活的应用程序。这些插件不仅可以提高开发效率,还可以帮助我们更好地控制AI模型的行为。
在下一篇文章中,我们将探讨如何将Semantic Kernel与其他技术栈和服务集成,以构建更加强大和全面的AI解决方案。
参考资料:
微软文档:https://learn.microsoft.com/en-us/semantic-kernel/overview/
Github:https://github.com/microsoft/semantic-kernel