NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - AI模型在线查看 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 - 3D道路快速建模
这是对Semantic Kernel某些功能的快速指南,Semantic Kernel是一种软件开发工具包,可简化将大型语言模型(LLM)集成到你自己的应用程序中的过程。
1、Semantic Kernel简介
Semantic Kernel 处于预发布阶段,这意味着它还不完整,并且即将进行大量改进。 然而,它是与 OpenAI 和 Azure OpenAI 提供的大型语言模型 (LLM) 交互的好工具。 可以通过 NuGet 将它作为一个库添加到你的 .NET 项目中,一旦安装,它就会使与这些 API 交互的复杂业务变得简单。
我使用C# 进行开发,但 Semantic Kernel 支持多种语言:C#、Python等。
Semantic Kernel理解两种操作:
- 语义函数——以人类语言指令表达的能力
- 原生函数 - 编写为可在本地执行的代码的附加功能
这些函数组合在一起成为技能(Skills) - 技能可以具有任意数量的语义或本机函数。
Semantic Kernel 还有一个 规划器(Planner),它可以决定将哪些函数链接在一起以完成 Ask(用人类语言向它提出的请求)。
警告:本文的所有内容都应视为简化的介绍。 在Semantic Kernel的表面下发生了很多我还没有完全理解的事情。 例如,我对 Planner 正在做什么有一些非常初步的猜测——但它可能比这复杂得多。
Semantic Kernel还有一些与内存和连接器相关的非常有趣的功能,我还没有接触过。 我会把它保存在以后的帖子中……
2、Semantic Kernel快速上手
如果你想跳转到可以尝试的解决方案,请查看此存储库 。你需要做的就是为它提供一个 OpenAI 密钥以开始使用。 运行时它会为你执行一个快速测试,调用 Planner 来安排一个计划,该计划使用解决方案中定义的多个函数。
接下来是对上述仓库中代码所做事情的快速浏览……
3、Semantic Kernel配置
一切都从 SKE.Program.Main()
开始——这个控制台应用程序的入口点。
首先,它从 .env
和 .secret.env
文件中检索配置,并使用该配置构建内核。
// configure and create the semantic kernel
var config = RetrieveConfiguration();
var kernel = BuildAndConfigureKernel(config);
private static IKernel BuildAndConfigureKernel(IConfigurationRoot config)
{
var kernel = Kernel.Builder.Build();
kernel.Config.AddOpenAITextCompletionService(
config["serviceId"]!,
config["modelId"]!,
config["apiKey"]!);
return kernel;
}
Semantic Kernel配置非常简单——它只需知道应该使用的服务和文本完成模型,以及允许访问的 API 密钥。
4、导入技能
然后使用 Kernel 导入项目中定义的技能(Skills),以及 Planner - Semantic Kernel 提供的特殊技能。
// import defined skills, and the planner
kernel.ImportAllSemanticSkills("Skills");
kernel.ImportAllNativeSkills();
var planner = kernel.ImportPlannerSkill();
各种 Import* 方法被定义为 SKE.KernelSkillsExtensions 中 IKernel 的扩展方法。
语义函数(Semantic Functions)是从它们在技能目录树中的位置导入的。 每个技能都有自己的目录,每个函数都有一个目录。
在此解决方案中,所有技能都分组在技能目录中:
kernel.ImportSemanticSkillFromDirectory(scanDirectory, skill);
每个技能包含两个文件:
- skprompt.txt - 表示此功能的文本完成模型的说明
- config.json - 用于微调功能的各种信息
config.json 是可选的,但是在该文件中的所有信息中,描述文本对 Planner 很重要,因为它需要它来理解函数的作用。
例如 skprompt.txt
:
Reverse the meaning of the following, giving the opposite effect to the original intention.
---Begin Text---
{{$input}}
---End Text---
以及 config.json :
{
"schema": 1,
"type": "completion",
"description": "Reverse the meaning of the text",
"completion": {
"max_tokens": 512,
"temperature": 0.0,
"top_p": 0.0,
"presence_penalty": 0.0,
"frequency_penalty": 0.0
},
"input": {
"parameters": [
{
"name": "input",
"description": "Text to reverse the meaning of",
"defaultValue": ""
}
]
}
}
相反,通过提供一个类中的对象来导入原生函数(Native Functions),该类包含许多标有 [SKFunction]
注解的方法。
在此解决方案中,它们通过反射检测 - 然后实例化并导入每个标识为 Skill 的类:
kernel.ImportSkill(skillObject!, type.Name!);
在这个解决方案中,我们定义了一个简单的原生函数:
public class CharacterManipulationSkill
{
[SKFunction("Return the text in all uppercase (aka capitals)")]
public string Uppercase(string input)
=> input.ToUpper();
}
上面代码的作用很容易理解。 请注意,SKFunction 注释包含函数的描述作为其参数。 Planner 需要它来理解函数,以便将其合并到任何计划中。
最后,导入 Planner 本身:
kernel.ImportSkill(new PlannerSkill(kernel));
这很有趣——PlannerSkill 被传递给内核的一个实例,我相信这是因为它需要能够了解内核导入了哪些技能,以便能够对它们进行推理。
6、制定计划
一旦配置好并具备 Planner 可用的所有技能,代码就会继续准备一个请求 - 一个对内核的简单语言请求。
这个解决方案使用一个非常简单、固定的请求来帮助说明如何一起使用这些技能:
// provide a simple input and ask as an example
var input = "Yesterday I went to the London Prompt Engineers jam at Newspeak House. Brilliant to meet folks who want to experiment with the new technologies, and a great opportunity to muck out with Semantic Kernel. Can't recommend highly enough!";
var ask = $"Reverse the following and then deliver it as cockney rhyming slag in all caps: {input}";
构建请求后,可以调用 Planner 来创建计划:
// create plan for the given ask
var plan = await kernel.CreatePlanAsync(planner, ask);
同样,这发生在扩展方法中——这次是异步调用,因为内核将调用远程 LLM 来完成计划:
await kernel.RunAsync(ask, planner["CreatePlan"]);
7、执行计划
最后,要求Kernel执行计划:
// execute the plan
var result = await kernel.ExecutePlanAsync(planner, plan);
同样,这包含在扩展方法中,但这隐藏了相当多的复杂性:
public static async Task<string?> ExecutePlanAsync(this IKernel kernel, IDictionary<string, ISKFunction> planner, SKContext plan, int maxSteps = 10)
{
string? result = null;
var executionResults = plan;
var partialResults = new List<string>();
int step = 1;
稍作设置后,它会开始一个 while 循环——当计划完成或超过允许的最大步数时,它会停止……
while (!executionResults.Variables.ToPlan().IsComplete && step < maxSteps)
{
它执行计划中找到的下一步(存储在 executionResults 中)。 这些执行结果兼作执行步骤的输出,以及有关计划本身执行的信息。
// execute the next step found in execution results
var results = await kernel.RunAsync(executionResults.Variables, planner["ExecutePlan"]);
然后,假设执行成功,当前步骤的输出被捕获到 result 中,并且也被添加到 partialResults 列表中(主要用于调试目的)。
如果执行失败,这就是该方法抛出 PlanExecutionException 的地方,其中包含当前可用的所有调试信息。
if (results.Variables.ToPlan().IsSuccessful)
{
result = results.Variables.ToPlan().Result;
partialResults.Add(result);
}
else
{
throw new PlanExecutionException($"Step {step} execution failed.", plan, step, partialResults);
}
最后将当前步骤的执行结果放入executionResults中,用于循环迭代时提供下一步的计划。
// iterate - using the execution results as the input for the next step
executionResults = results;
step++;
}
当循环完成后,如果没有抛出异常,那么result中应该有一个最终的结果……
return result;
}
Program.Main 的最后一行将最终结果打印到控制台:
Console.WriteLine($"Result:\n\n{result}");
8、结束语
与机器学习的所有其他应用程序一样,使用大型语言模型来解决问题也伴随着障碍和警告。
花时间阅读随Semantic Kernel文档提供的 Responsible AI Transparency Note 是值得的。
AI系统不仅包括技术,还包括将使用它的人、将受其影响的人以及部署它的环境。 创建适合其预期目的的系统需要了解技术的工作原理、其功能和局限性以及如何实现最佳性能。
原文链接:BUILDING AI INTO YOUR APPLICATION WITH SEMANTIC KERNEL
BimAnt翻译整理,转载请标明出处