前言
本章講一下在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
示例程式碼
本文原始碼