前言

本章讲一下在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入门系列:通过依赖注入管理对象和插件的更多相关文章

  1. Semantic Kernel 入门系列:🍋Connector连接器

    当我们使用Native Function的时候,除了处理一些基本的逻辑操作之外,更多的还是需要进行外部数据源和服务的对接,要么是获取相关的数据,要么是保存输出结果.这一过程在Semantic Kern ...

  2. Semantic Kernel 入门系列:🔥Kernel 内核和🧂Skills 技能

    理解了LLM的作用之后,如何才能构造出与LLM相结合的应用程序呢? 首先我们需要把LLM AI的能力和原生代码的能力区分开来,在Semantic Kernel(以下简称SK),LLM的能力称为 sem ...

  3. Semantic Kernel 入门系列:🥑Memory内存

    了解的运作原理之后,就可以开始使用Semantic Kernel来制作应用了. Semantic Kernel将embedding的功能封装到了Memory中,用来存储上下文信息,就好像电脑的内存一样 ...

  4. Semantic Kernel 入门系列:🛸LLM降临的时代

    不论你是否关心,不可否认,AGI的时代即将到来了. 在这个突如其来的时代中,OpenAI的ChatGPT无疑处于浪潮之巅.而在ChatGPT背后,我们不能忽视的是LLM(Large Language ...

  5. Semantic Kernel 入门系列:🪄LLM的魔法

    ChatGPT 只是LLM 的小试牛刀,让人类能够看到的是机器智能对于语言系统的理解和掌握. 如果只是用来闲聊,而且只不过是将OpenAI的接口封装一下,那么市面上所有的ChatGPT的换皮应用都差不 ...

  6. Semantic Kernel 入门系列:💬Semantic Function

    如果把提示词也算作一种代码的话,那么语义技能所带来的将会是全新编程方式,自然语言编程. 通常情况下一段prompt就可以构成一个Semantic Function,如此这般简单,如果我们提前可以组织好 ...

  7. Semantic Kernel 入门系列:💾Native Function

    语义的归语义,语法的归语法. 基础定义 最基本的Native Function定义只需要在方法上添加 SKFunction 的特性即可. using Microsoft.SemanticKernel. ...

  8. Semantic Kernel 入门系列:🥑突破提示词的限制

    无尽的上下文 LLM的语言理解和掌握能力在知识内容的解读和总结方面提供了强大的能力. 但是由于训练数据本身来自于公共领域,也就注定了无法在一些小众或者私有的领域能够足够的好的应答. 因此如何给LLM ...

  9. Semantic Kernel 入门系列:📅 Planner 计划管理

    Semantic Kernel 的一个核心能力就是实现"目标导向"的AI应用. 目标导向 "目标导向"听起来是一个比较高大的词,但是却是实际生活中我们处理问题的 ...

  10. 拥抱.NET Core系列:依赖注入(2)

    上一篇"拥抱.NET Core系列:依赖注入(1)"大体介绍了服务注册.获取和生命周期,这一篇来做一些补充. 由于内容跨度大(.NET Core.ASP.NET Core),所以文 ...

随机推荐

  1. 第 7章 Python 爬虫框架 Scrapy(上)

    第 7章 Python 爬虫框架 Scrapy(上) 编写爬虫可以看成行军打仗,基本的角色有两个:士兵和将军,士兵冲锋陷阵,而将军更多地是调兵遣将.框架就像一个将军,里面包含了爬虫的全部流程.异常处理 ...

  2. 阿里云PolarDB开源数据库社区与 Tapdata 联合共建开放数据技术生态

    ​简介:近日,阿里云PolarDB开源数据库社区宣布将与 Tapdata 联合共建开放数据技术生态. 近日,阿里云PolarDB开源数据库社区宣布将与 Tapdata 联合共建开放数据技术生态.在此之 ...

  3. 10种编程语言实现Y组合子

    简介: Y组合子是Lambda演算的一部分,也是函数式编程的理论基础.它是一种方法/技巧,在没有赋值语句的前提下定义递归的匿名函数,即仅仅通过Lambda表达式这个最基本的"原子" ...

  4. 新型DDoS来袭 | 基于STUN协议的DDoS反射攻击分析

    简介: 作为新型反射类型,目前仍存绕过防御可能性. 阿里云安全近期发现利用STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)服务发起的DDoS反 ...

  5. 基于 Scheduled SQL 对 VPC FlowLog 实现细粒度时间窗口分析

    简介: 针对VPC FlowLog的五元组和捕获窗口信息,在分析时使用不同时间窗口精度,可能得到不一样的流量特征,本文介绍一种方法将原始采集日志的时间窗口做拆分,之后重新聚合为新的日志做分析,达到更细 ...

  6. 剑指企业级云原生,阿里云 CNFS 如何破局容器持久化存储困境

    简介: 云原生趋势下,应用容器化比例正在快速增长,Kubernetes 也已成为云原生时代新的基础设施. 据 Forrester 预测,到 2022 年, 全球企业及组织在生产环境运行容器化应用.观察 ...

  7. OLAP系列之分析型数据库clickhouse备份方式(五)

    一.常见备份方式 1.1 备份方式 备份方式 特点 物理文件备份 对物理文件进行拷贝,备份期间禁止数据写入 dump数据导入导出 备份方式灵活,但备份速度慢 快照表备份 制作_bak表进行备份 FRE ...

  8. STM32定时器原理

    一.简介 不同的芯片定时器的数量不同,STM32F10x中一共有11个定时器,其中2个高级控制定时器,4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器. 基本定时器:TIM6. ...

  9. SpringBoot实现WebSocket发送接收消息 + Vue实现SocketJs接收发送消息

    SpringBoot实现WebSocket发送接收消息 + Vue实现SocketJs接收发送消息 参考: 1.https://www.mchweb.net/index.php/dev/887.htm ...

  10. python3解析FreeSWITCH会议室列表信息

    操作系统 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 Python版本:3.9.12 进行FreeSWITCH会议室相关功能开发过程中,会遇到需要解析会议室列表信息并进行 ...