久趣下载站

当前位置: 首页 » 游戏攻略 » 在Semantic Kernel中使用依赖注入

在Semantic Kernel中使用依赖注入

本章介绍了在Semantic Kernel中使用Dependency Injection(依赖注入)的方法。在之前的章节中,我们手动创建Kernel对象来完成框架的初始化工作。现在,我们将使用依赖注入的方式来实现这一过程。

实战

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

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

#pragma warning disable CA1024 // Use properties where appropriate
    [KernelFunction]
    [Description("获取灯的状态。")]
    public string GetState() => IsOn ? "开启" : "关闭";
#pragma warning restore CA1024 // Use properties where appropriate

    [KernelFunction]
    [Description("改变灯的状态。")]
    public string ChangeState(bool newState)
    {
        this.IsOn = newState;
        var state = GetState();

        // 打印状态到控制台
        Console.WriteLine($"[灯现在{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对象或使用services.AddTransient<Kernel>()方法。


AddKernel源码

public static IKernelBuilder AddKernel(this IServiceCollection services)
{
    Verify.NotNull(services);

    // 注册一个KernelPluginCollection,用于存储直接在DI中注册的任何IKernelPlugins。它是瞬态的,因为Kernel将直接存储该集合,我们不希望两个Kernel实例持有相同的可变集合。
    services.AddTransient<KernelPluginCollection>();

    // 注册Kernel为瞬态。它是可变的,预计将被使用者改变,例如通过添加事件处理程序、添加插件、在其Data集合中存储状态等。
    services.AddTransient<Kernel>();

    // 创建并返回一个可用于向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插件。

// 创建聊天记录
var history = new ChatHistory();

// 获取聊天完成服务
var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

// 开始对话
Console.Write("用户 > ");
string? userInput;
while ((userInput = Console.ReadLine()) is not null)
{
    // 添加用户输入
    history.AddUserMessage(userInput);

    // 启用自动函数调用
    OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
    {
        ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
    };

    // 从AI获取响应
    var result = await chatCompletionService.GetChatMessageContentAsync(
        history,
        executionSettings: openAIPromptExecutionSettings,
        kernel: kernel);

    // 打印结果
    Console.WriteLine("助手 > " + result);

    // 将代理的消息添加到聊天记录中
    history.AddMessage(result.Role, result.Content ?? string.Empty);

    // 再次获取用户输入
    Console.Write("用户 > ");
}


输出:

用户 > 当前灯光的状态
助手 > 当前灯光的状态是关闭的。
用户 > 帮我开个灯
[灯现在开启]
助手 > 已经成功为您点亮了灯。

最后

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

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

原则上,任何支持OpenAI函数调用格式的模型都可以使用。

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


参考文献

Using Semantic Kernel with Dependency Injection


示例代码

本文源代码

猜你喜欢
本类排行