Semantic Kernel入门系列:通过依赖注入管理对象和插件

semantic,kernel · 浏览次数 : 0

小编点评

前言 在本章中,我们将讨论如何在 Semantic Kernel 中使用 Dependency Injection(依赖注入)来实现框架的初始化工作。与之前的手动创建 Kernel 对象的方式不同,我们现在将借助依赖注入来实现。 实战:定义 Native Plugins 我们将使用官方文档中的 LightPlugins 插件作为示例来演示依赖注入在 Semantic Kernel 中的实现。 ```java public class LightPlugin { public bool IsOn { get; set; } = false; [KernelFunction] [Description("Gets the state of the light.")] public string GetState() => IsOn ? "on" : "off"; [KernelFunction] [Description("Changes the state of the light.")] public string ChangeState(bool newState) { this.IsOn = newState; var state = GetState(); // Print the state to the console Console.WriteLine($"[Light is now {state}]"); return state; } } ``` 这个插件包含两个方法:一个用于获取当前灯的状态,另一个用于改变灯的状态。为了在 Semantic Kernel 中使用此插件,我们需要注册它并为其提供一个键。 创建 Kernel 对象 在之前的示例中,我们都是通过 `Kernel.CreateBuilder().AddOpenAIChatCompletion()` 方法创建一个 Kernel 对象。现在,我们将使用依赖注入来完成这个过程。 ```java var kernel = Kernel.CreateBuilder() .AddOpenAIChatCompletion(modelId: config.ModelId, apiKey: config.ApiKey) .Build(); ``` 在 API 项目的开发中,依赖注入可以帮助我们更好地管理依赖关系和对象的复用。依赖注入注入 Kernel 依赖有两种方式: 1. 使用 `KernelServiceCollectionExtensions` 提供的 `AddKernel` 扩展方法。 2. 自己创建 `Kernel` 实例,例如:`Kernel kernel = new ServiceCollection() ...`.AddKernel() ...;` 通过源码,我们可以看到这两种方式基本上没有区别。我们在这里将以第一种方式为例进行演示。 ```java IServiceCollection services = new ServiceCollection(); // 添加会话服务到 IOC 容器 services.AddKernel().AddOpenAIChatCompletion(modelId: config.ModelId, apiKey: config.ApiKey); services.AddSingleton(sp => KernelPluginFactory.CreateFromType(serviceProvider: sp)); var kernel = services.BuildServiceProvider().GetRequiredService(); ``` 通过上述代码,我们可以了解在依赖注入中注册 Kernel 对象和插件的步骤。所有的依赖项都将被注册到 `IServiceCollection` 容器中。在 Semantic Kernel 中,使用依赖注入的服务和插件通常作为 Singleton(单例)注册,以便在不同的 Kernel 实例之间重用/共享。 而内核通常以 Transient(瞬态)的方式注册,这样可以确保每个内核实例不会受到执行其他任务的内核更改影响。当在其他项目中使用时,我们可以通过在构造函数中获取内核实例,然后用内核实例获取服务实例。 ```java var chatCompletionService = kernel.GetRequiredService(); IChatCompletionService instance =(kernel as IServiceProvider).getService(); ``` 在本章的实战部分,我们将使用依赖注入运行这个插件。然后通过模拟用户输入来查看插件如何响应。

正文

前言

本章讲一下在Semantic Kernel中使用DependencyInject(依赖注入),在之前的章节我们都是通过手动创建Kernel对象来完成框架的初始化工作,今天我们用依赖注入的方式来实现。

实战

定义Native Plugins

我们用官网的LightPlugins插件来演示依赖注入在SK中的使用

public class LightPlugin
{
    public bool IsOn { get; set; } = false;

#pragma warning disable CA1024 // Use properties where appropriate
    [KernelFunction]
    [Description("Gets the state of the light.")]
    public string GetState() => IsOn ? "on" : "off";
#pragma warning restore CA1024 // Use properties where appropriate

    [KernelFunction]
    [Description("Changes the state of the light.'")]
    public string ChangeState(bool newState)
    {
        this.IsOn = newState;
        var state = GetState();

        // Print the state to the console
        Console.WriteLine($"[Light is now {state}]");

        return state;
    }
}

这个插件有两个方法一个是获取当前灯的状态,第二个是改变灯的状态

创建kernel对象

在之前我们的演示中都是通过Kernel对象提供的CreateBuilder方法来创建Kernel对象。

    var kernel = Kernel.CreateBuilder().
      AddOpenAIChatCompletion(modelId: config.ModelId, apiKey: config.ApiKey)
        .Build();

在api项目的开发中,依靠依赖注入的方式更容易管理依赖项,以及对象的复用

依赖注入注入Kernel依赖

有两种方式可以用依赖注入创建Kernel对象,第一种是借助于KernelServiceCollectionExtensions累提供的AddKernel扩展方法,第二种就是自己Kernel kernel = new(services.BuildServiceProvider());或者services.AddTransient<Kernel>();

AddKernel源码

    /// </returns>
    /// <remarks>
    /// Both services are registered as transient, as both objects are mutable.
    /// </remarks>
    public static IKernelBuilder AddKernel(this IServiceCollection services)
    {
        Verify.NotNull(services);

        // Register a KernelPluginCollection to be populated with any IKernelPlugins that have been
        // directly registered in DI. It's transient because the Kernel will store the collection
        // directly, and we don't want two Kernel instances to hold on to the same mutable collection.
        services.AddTransient<KernelPluginCollection>();

        // Register the Kernel as transient. It's mutable and expected to be mutated by consumers,
        // such as via adding event handlers, adding plugins, storing state in its Data collection, etc.
        services.AddTransient<Kernel>();

        // Create and return a builder that can be used for adding services and plugins
        // to the IServiceCollection.
        return new KernelBuilder(services);
    }

通过源码我们可以看出来,这两种方式基本上没区别,第二种AddKernel实际上是简化了我们第二种的步骤,我们就用第一种举例演示

//依赖注入
{
    IServiceCollection services = new ServiceCollection();
    //会话服务注册到IOC容器
    services.AddKernel().AddOpenAIChatCompletion(modelId: config.ModelId, apiKey: config.ApiKey, httpClient: client);
    services.AddSingleton<KernelPlugin>(sp => KernelPluginFactory.CreateFromType<LightPlugin>(serviceProvider: sp));
    var kernel = services.BuildServiceProvider().GetRequiredService<Kernel>();

这就是在依赖注入中注册Kernel对象和插件的步骤,依赖项都会被注册到IServiceCollection

Semantic Kernel使用的服务插件通常作为Singleton单例注册到依赖注入容器中,以便它们可以在各种Kernel之间重用/共享。Kernel通常注册为Transient瞬态,以便每个实例不受处理其他任务的Kernel所做更改的影响。

在项目中使用时,我们可以通过在构造函数中获取Kernel对象的实例,用Kernel对象来获取服务实例

var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

IChatCompletionService 实例也可以通过 IServiceProvider 来获取,您可以灵活地使用更适合您要求的方法。

实战

我们用依赖注入跑一下LightPlugin插件

    // Create chat history
    var history = new ChatHistory();

    // Get chat completion service
    var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

    // Start the conversation
    Console.Write("User > ");
    string? userInput;
    while ((userInput = Console.ReadLine()) is not null)
    {
        // Add user input
        history.AddUserMessage(userInput);

        // Enable auto function calling
        OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
        {
            ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
        };

        // Get the response from the AI
        var result = await chatCompletionService.GetChatMessageContentAsync(
            history,
            executionSettings: openAIPromptExecutionSettings,
            kernel: kernel);

        // Print the results
        Console.WriteLine("Assistant > " + result);

        // Add the message from the agent to the chat history
        history.AddMessage(result.Role, result.Content ?? string.Empty);

        // Get user input again
        Console.Write("User > ");
    }

输出:

User > 当前灯光的状态
Assistant > 当前灯光的状态是关闭的。
User > 帮我开个灯
[Light is now on]
Assistant > 已经成功为您点亮了灯。

最后

本文Demo用的大模型月之暗面的moonshot-v1-8k

  "Endpoint": "https://api.moonshot.cn",
  "ModelId": "moonshot-v1-8k",

原则上任何支持OpenAI function calling 格式的都可以使用。

通过本章的学习,我们深入了解了在Semantic Kernel中利用依赖注入的方式来管理Kernel对象和插件,使得项目开发更加灵活和高效。

参考文献

Using Semantic Kernel with Dependency Injection

示例代码

本文源代码

与Semantic Kernel入门系列:通过依赖注入管理对象和插件相似的内容:

Semantic Kernel入门系列:通过依赖注入管理对象和插件

前言 本章讲一下在Semantic Kernel中使用DependencyInject(依赖注入),在之前的章节我们都是通过手动创建Kernel对象来完成框架的初始化工作,今天我们用依赖注入的方式来实现。 实战 定义Native Plugins 我们用官网的LightPlugins插件来演示依赖注入

Semantic Kernel入门系列:利用Handlebars创建Prompts functions

引言 本章我们将学习通过Handlebars Prompts Template来创建Prompts functions。 什么是Handlebars? Handlebars是一个流行的 JavaScript 模板引擎,它允许你通过在 HTML 中使用简单的占位符来创建动态的 HTML。 它使用模板和

Semantic Kernel入门系列:利用YAML定义prompts functions

引言 在上一章节我们熟悉了prompts functions(提示函数)的创建,我们了解了PromptTemplateConfig中各个属性的简单使用。Semantic Kernel允许我们利用多种方式去创建prompts包括native functions,prompts functions或者也

.NET周报 【4月第3期 2023-04-15】

国内文章 Semantic Kernel 入门系列:📅 Planner 规划器 https://www.cnblogs.com/xbotter/p/semantic_kernel_introduction_planner.html 本文介绍了Semantic Kernel的一个核心能力,即实现目标

(1)semantic-kernel入门课程

(1)semantic-kernel入门课程 获取OpenAI在线API 由于在国内的限制,我们并没有办法直接访问OpenAI,所以下面的教程我们会推荐使用https://api.token-ai.cn,然后您需要在这个网站中注册一个账号,并且创建一个令牌(最好是设置无限额度和无过期时间),创建好的

入门Semantic Kernel:OneApi集成与HelloWorld

引言 从这一章节开始正式进入我们的 Semantic Kernel 的学习之旅了。 什么是Semantic Kernel? Semantic Kernel是一个轻量级的开源框架,通过 Semantic Kernel 可以快速使用不同编程语言(C#/Python/Java)结合 LLMs(OpenAI

探索Semantic Kernel内置插件:深入了解HttpPlugin的应用

前言 上一章我们熟悉了Semantic Kernel中的内置插件和对ConversationSummaryPlugin插件进行了实战,本章我们讲解一下另一个常用的内置插件HttpPlugin的应用。 上一章对ConversationSummaryPlugin总结进行了调整之后,顺便给Semantic

探索Semantic Kernel内置插件:深入了解ConversationSummaryPlugin的应用

前言 经过前几章的学习我们已经熟悉了Semantic Kernel 插件的概念,以及基于Prompts构造的Semantic Plugins和基于本地方法构建的Native Plugins。本章我们来讲解一下在Semantic Kernel 中内置的一些插件,让我们避免重复造轮子。 内置插件 Sem

.NET 6+Semantic Kernel快速接入OpenAI接口

Semantic Kernel 与 LangChain 类似,但 Semantic Kernel 是为应用开发开发人员创建的SDK项目,它支持.NET, Python 以及 Java,但是对.NET支持最成熟(微软自家孩子嘛),可以让你的应用很轻易的集成AI大语言模型。今天我们快速地使用Semant...

深入学习Semantic Kernel:创建和配置prompts functions

引言 上一章我们熟悉了一下 Semantic Kernel 的理论知识,Kernel 创建以及简单的Sample熟悉了一下 SK 的基本使用。在Semantic Kernel中的 kernel functions由两部分组成第一部分是prompts functions(提示函数),第二部分Nativ