NSDT工具推荐Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割

这是对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翻译整理,转载请标明出处