Semantic Kernel 内核详解企业级 AI 应用真正的挑战在于认知与行动如何让 AI 理解复杂的业务上下文如何让它自主调用外部工具如何编排多步骤的推理任务Semantic Kernel简称 SK正是微软为 .NET 生态打造的大语言模型LLM编排框架。它不仅仅是 OpenAI 的 .NET SDK 封装更是一个内核——一个将 LLM 的推理能力与现有 C# 业务逻辑深度融合的桥梁。掌握 SK 的核心抽象将其作为构建企业智能体的基石。将深入 Semantic Kernel 的内核从生命周期管理、提示词工程、函数调用到插件化架构带你构建一个可扩展、可测试、可观测的 AI 编排层。1 Kernel 对象生命周期与依赖注入集成1.1 Kernel 是什么Kernel是 Semantic Kernel 的核心容器它聚合了三个关键组件AI 服务如聊天补全、文本嵌入插件封装为业务能力的函数集合记忆向量存储用于 RAG每个 Kernel 实例代表一个独立的 AI 运行时环境。在 ASP.NET Core 应用中我们需要谨慎管理 Kernel 的生命周期确保线程安全、资源复用以及与依赖注入容器的集成。1.2 构建 Kernel从 Builder 模式开始SK 推荐使用KernelBuilder来构建实例。一个典型的构建流程包括添加 AI 服务OpenAI、Azure OpenAI、本地模型等注册插件从类型、对象或目录加载配置日志和 HttpClient 工厂usingMicrosoft.SemanticKernel;varbuilderKernel.CreateBuilder();// 添加 Azure OpenAI 服务builder.AddAzureOpenAIChatCompletion(deploymentName:...,endpoint:https://openai.com/,apiKey:your-api-key);// 添加 OpenAI 服务可选支持多模型builder.AddOpenAIChatCompletion(modelId:...,endpoint:...,apiKey:your-openai-key);// 添加嵌入生成服务用于语义内存builder.AddAzureOpenAITextEmbeddingGeneration(deploymentName:text-embedding-ada-002,endpoint:...,apiKey:...);// 添加插件builder.Plugins.AddFromTypeTimePlugin();// 从类型加载插件builder.Plugins.AddFromObject(newEmailPlugin());// 从实例加载// 配置日志builder.Services.AddLogging(configureconfigure.AddConsole());// 使用 IHttpClientFactory推荐builder.Services.AddHttpClient();varkernelbuilder.Build();1.3 在 ASP.NET Core 中集成 KernelKernel 对象是线程安全的可以被多个请求共享。但考虑到不同的请求可能需要不同的 AI 配置如不同的部署名称、插件集更常见的做法是将 Kernel 注册为Scoped服务并在每个请求中根据用户上下文动态构建。方案一注册为单例简单场景如果所有请求共享相同的模型和插件可以将 Kernel 注册为单例减少构建开销。// Program.csbuilder.Services.AddSingleton(sp{varkernelBuilderKernel.CreateBuilder();kernelBuilder.AddAzureOpenAIChatCompletion(deploymentName:config[AI:LLM:DeploymentName],endpoint:config[AI:LLM:Endpoint],apiKey:config[AI:LLM:ApiKey]);kernelBuilder.Plugins.AddFromTypeTimePlugin();kernelBuilder.Services.AddLogging();returnkernelBuilder.Build();});方案二注册为 Scoped动态配置当不同租户需要不同的模型或插件时可以使用工厂模式在请求范围内动态构建 Kernel。// 注册一个 KernelFactorybuilder.Services.AddScopedIKernelFactory,KernelFactory();// 在服务中注入工厂按需创建publicclassChatService{privatereadonlyIKernelFactory_kernelFactory;publicChatService(IKernelFactorykernelFactory)_kernelFactorykernelFactory;publicasyncTaskstringChatAsync(stringuserInput,stringtenantId){varkernel_kernelFactory.CreateForTenant(tenantId);returnawaitkernel.InvokePromptAsync(userInput);}}1.4 资源清理与生命周期管理Kernel 本身不持有非托管资源但它内部使用的 HttpClient 等对象需要妥善管理。通过IHttpClientFactory注册的 HttpClient 会自动复用和清理无需担心。但如果 Kernel 内部创建了需要释放的对象如某些插件中的数据库连接应实现IDisposable并通过kernelBuilder.Services.AddSingletonT(...)注册由 DI 容器管理生命周期。2 提示词模板引擎与函数调用Function Calling2.1 提示词模板语法Semantic Kernel 提供了一套强大的提示词模板引擎支持变量、函数调用、循环等特性。模板语法以{{$variable}}引用上下文变量以{{namespace.functionName}}调用插件函数。基础示例varprompt You are an AI assistant.Answer the users questionusingthe provided context.Context:{{$context}}Question:{{$userInput}}Answer:;varresultawaitkernel.InvokePromptAsync(prompt,new(){[context]The user is asking about Semantic Kernel.,[userInput]What is SK?});调用插件函数在模板中直接调用插件函数可以实现动态数据注入。// 定义插件publicclassWeatherPlugin{[KernelFunction][Description(Get the current weather for a city)]publicstringGetWeather(stringcity){return$The weather in{city}is sunny, 25°C.;}}// 注册插件kernel.Plugins.AddFromTypeWeatherPlugin();// 提示词中调用插件varpromptWhats the weather in {{$city}}? {{WeatherPlugin.GetWeather $city}};varresultawaitkernel.InvokePromptAsync(prompt,new(){[city]London});2.2 函数调用Function Calling原理大语言模型原生支持函数调用Function Calling能力。模型可以根据用户输入自主决定调用哪个函数并生成符合函数签名的 JSON 参数。Semantic Kernel 将这个能力封装为KernelFunction并自动处理函数调用的往返过程。手动定义函数// 使用 KernelFunction 特性标注方法publicclassEmailPlugin{[KernelFunction][Description(Send an email to the specified recipient)]publicasyncTaskstringSendEmailAsync([Description(Recipient email address)]stringto,[Description(Email subject)]stringsubject,[Description(Email body)]stringbody){// 实际发送逻辑awaitTask.Delay(100);// 模拟异步操作return$Email sent to{to};}}让模型自动调用当调用InvokePromptAsync时如果提示词中包含了需要函数调用的逻辑SK 会自动与 LLM 协商执行函数并将结果返回给模型。kernel.Plugins.AddFromTypeEmailPlugin();varpromptSend an email to johnexample.com with subject Hello and body How are you?;varresultawaitkernel.InvokePromptAsync(prompt);// 模型会决定调用 SendEmailAsync 函数最终返回 Email sent to johnexample.com强制函数调用某些场景下我们希望模型必须调用特定函数。可以通过KernelArguments的ExecutionSettings指定函数名称。varargumentsnewKernelArguments{[function_call]EmailPlugin-SendEmailAsync};varresultawaitkernel.InvokePromptAsync(prompt,arguments);2.3 高级模板技巧循环与条件模板引擎支持简单的循环和条件语句通过 Handlebars 风格的语法需启用 Handlebars 插件。自动函数参数注入SK 会自动将函数参数与上下文变量匹配无需手动传递。流式输出使用InvokePromptStreamingAsync可以实时获取 LLM 的流式响应适合聊天场景。awaitforeach(varupdateinkernel.InvokePromptStreamingAsync(prompt)){Console.Write(update);}3 插件化架构将现有 C# 业务逻辑封装为 AI 能力插件Plugin是 Semantic Kernel 的核心扩展点。通过插件我们可以将任何现有的 C# 方法、API、数据库操作等暴露给 AI让 LLM 能够调用这些能力来完成复杂任务。3.1 插件的三种定义方式1. 从类加载推荐使用[KernelFunction]特性标记方法SK 会自动发现并注册。publicclassOrderPlugin{privatereadonlyIOrderRepository_orderRepository;publicOrderPlugin(IOrderRepositoryorderRepository){_orderRepositoryorderRepository;}[KernelFunction][Description(Get recent orders for a user)]publicasyncTaskListOrderGetRecentOrdersAsync([Description(User ID)]stringuserId,[Description(Number of days to look back)]intdays30){returnawait_orderRepository.GetRecentOrdersAsync(userId,days);}}注册时需要将依赖的服务也注册到 DI 容器中。2. 从对象加载对于已有实例可以直接添加到插件集合。varorderPluginnewOrderPlugin(orderRepository);kernel.Plugins.AddFromObject(orderPlugin);3. 从目录加载OpenAPI/SwaggerSK 支持从 OpenAPI 文档自动生成插件可以快速集成第三方 REST API。// 1. 注册 OpenAPI 插件自动生成设备查询函数builder.Plugins.AddFromOpenApiAsync(pluginName:DeviceDataPlatform,openApiDocumentPath:https://factory-api/device-platform/swagger.json,executionParameters:newOpenApiFunctionExecutionParameters{// 处理 API Key 认证AuthCallbackasync(request,cancellationToken){request.Headers.Add(X-API-Key,GetApiKeyFromSecretStore());awaitTask.CompletedTask;},// 只导入我们关心的操作避免 AI 产生幻觉OperationsToExcludenewListstring{POST_/write-data,DELETE_/history}});3.2 插件的描述与参数说明为了让 LLM 正确选择并调用插件我们必须提供清晰、详细的描述插件类可以添加[Description]特性说明插件的整体用途。方法[KernelFunction]的Description参数描述该方法的功能。参数使用[Description]描述每个参数的含义和格式。良好的描述能够显著提升函数调用的准确性。例如[KernelFunction,Description(Calculates the total price including tax and shipping for a given product)]publicdecimalCalculateTotalPrice([Description(Product identifier (SKU))]stringsku,[Description(Quantity, must be positive integer)]intquantity,[Description(Promo code, optional)]stringpromoCodenull){// ...}3.3 依赖注入与插件实例化在 ASP.NET Core 中插件通常会依赖其他服务如数据库、HttpClient。我们可以利用 DI 容器来创建插件实例确保依赖关系被正确解析。方案一手动解析适用于简单场景varorderPluginsp.GetRequiredServiceOrderPlugin();kernel.Plugins.AddFromObject(orderPlugin);方案二使用插件工厂更优雅SK 提供了IPlugin接口我们可以自定义一个PluginLoader从容器中获取插件实例。publicclassKernelFactory{privatereadonlyIServiceProvider_services;publicKernelFactory(IServiceProviderservices)_servicesservices;publicKernelCreateKernel(){varbuilderKernel.CreateBuilder();// 添加 AI 服务...// 从 DI 容器中获取插件并添加varorderPlugin_services.GetRequiredServiceOrderPlugin();builder.Plugins.AddFromObject(orderPlugin);varemailPlugin_services.GetRequiredServiceEmailPlugin();builder.Plugins.AddFromObject(emailPlugin);returnbuilder.Build();}}3.4 插件的最佳实践单一职责每个插件应该聚焦于一个明确的业务领域如订单、用户、库存。异步友好插件方法应返回Task或ValueTask避免阻塞。幂等性如果可能让插件方法具备幂等性因为 LLM 可能会重试调用。安全校验插件方法内部必须进行权限校验因为 LLM 可能被诱导执行危险操作如删除数据。可观测性在插件方法中添加日志和遥测记录调用来源、参数和执行耗时。3.5 插件与 RAG 的结合插件不仅用于函数调用还可以用于检索增强生成RAG。通过插件我们可以将向量数据库查询、搜索引擎结果等包装为函数让 LLM 在回答问题时自主决定是否检索外部知识。publicclassKnowledgeBasePlugin{privatereadonlyIVectorDatabase_vectorDb;publicKnowledgeBasePlugin(IVectorDatabasevectorDb)_vectorDbvectorDb;[KernelFunction][Description(Search the internal knowledge base for relevant information)]publicasyncTaskstringSearchKnowledgeBaseAsync([Description(Search query)]stringquery){varembeddingawaitGenerateEmbeddingAsync(query);varresultsawait_vectorDb.SimilaritySearchAsync(embedding,topK:3);returnstring.Join(\n,results.Select(rr.Content));}}然后在提示词中让模型自主决定何时调用SearchKnowledgeBaseAsync。这样模型既能利用自身知识又能按需获取最新信息。总结深入 Semantic Kernel 的内核掌握了Kernel 的生命周期管理如何在 ASP.NET Core 中集成和复用 Kernel 实例。提示词模板引擎使用变量、函数调用和流式输出构建动态提示词。函数调用机制让 LLM 自主选择并调用 C# 方法实现 AI 与业务逻辑的无缝对接。插件化架构将现有代码封装为插件利用依赖注入和描述特性构建可扩展的 AI 能力层。