Semantic Kernel 实战:.NET 插件与大模型编排

在 .NET 生态里要把大模型真正用进业务,靠手写 HTTP 调用只能解决”能对话”,却搞不定”会调用工具、能记住上下文、可编排多步任务”。Semantic Kernel 是微软开源的 SDK,它把大模型、插件函数、记忆与规划器统一成一套可组合的运行时。本文用 C# 实战,带你从零跑通 Semantic Kernel 的插件式 AI 编排,并说清它和 Spring AI、LangChain4j 的取舍。

一、什么是 Semantic Kernel:.NET 原生的 AI 编排 SDK

Semantic Kernel(简称 SK)不是一个”框架套框架”的重型中间件,而是一层轻量的编排内核:你向它注册模型、函数和记忆,它负责在运行时把自然语言请求路由到合适的函数,再把结果回填给模型。对 .NET 团队最友好的点是——它原生支持 C# 特性,插件就是普通类加特性标注,无需额外 DSL。

相比直接调 OpenAI 的 chat/completions 接口,SK 的价值在三层:其一,函数即工具,本地 C# 方法可被模型自动调用;其二,连接器抽象,换 Azure OpenAI、OpenAI、Ollama 只改一行配置;其三,规划能力,复杂目标可拆成多个函数链式执行。

二、核心概念:Kernel、插件与连接器

理解三个核心对象就够了:

  • Kernel:运行时容器,持有模型服务、插件与记忆。
  • Plugin(插件):一组被模型可见的函数,可以是 C# 方法(原生函数),也可以是提示词模板(提示函数)。
  • Connector(连接器):对接具体模型供应商的适配层,决定往哪发请求、用什么鉴权。

记忆(Memory)与向量连接器则负责 RAG:把文档切块、向量化后存起来,问答时先检索再生成,避免模型”一本正经胡说”。

三、五分钟跑通:用 Semantic Kernel 调用大模型

新建一个控制台项目,安装官方包后,下面这段就能让模型回答一句话。注意这里用 OpenAI 连接器,把 ApiKey 换成你自己的密钥即可。

// dotnet add package Microsoft.SemanticKernel
using Microsoft.SemanticKernel;

var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(
    modelId: "gpt-4o-mini",
    apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")!);

Kernel kernel = builder.Build();

string prompt = "用一句话解释什么是向量数据库。";
string answer = await kernel.InvokePromptAsync(prompt);
Console.WriteLine(answer);

这就是最朴素的用法:InvokePromptAsync 直接把提示词发给模型。但要进入”编排”阶段,关键在插件。

四、插件即函数:用 [KernelFunction] 暴露能力

SK 的精髓是:把你的业务方法标注成模型可调用的函数。下面写一个”查天气”的插件,模型在回答时若需要天气数据,会自己决定调用它。

public class WeatherPlugin
{
    [KernelFunction("get_weather")]
    [Description("根据城市名称返回当前天气摘要")]
    public string GetWeather(
        [Description("城市名称,例如 上海")] string city)
    {
        // 实际项目里这里调气象 API,示例返回模拟数据
        return $"{city} 今天多云,气温 22℃,东南风 3 级。";
    }
}

// 注册插件到 Kernel
builder.Plugins.AddFromType<WeatherPlugin>();

[KernelFunction] 的函数名和 [Description] 描述会原样送给模型作为”工具清单”,所以描述要写清楚何时用、参数是什么,模型才能正确触发。

五、自动函数调用:让模型自己决定调哪个插件

注册好插件后,开启自动函数调用,模型就能在对话中自主选择并执行函数,再把结果融进最终回答。这和你用 Spring AI 的 Tool Calling 落地大模型 思路一致,区别在于 SK 用 C# 原生标注,无需额外配置类。也可参考 Function Calling 实战 理解底层协议。

var settings = new OpenAIPromptExecutionSettings
{
    ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};

var result = await kernel.InvokePromptAsync(
    "上海现在天气怎么样?适合穿短袖吗?",
    new(settings));

Console.WriteLine(result);

运行后你会看到:模型识别到需要天气,自动调用 get_weather("上海"),拿到结果后再用自然语言给出”建议穿薄外套”这类带推理的回答。整个过程我们没有写任何 if/else 去判断该调哪个函数。

六、函数流式输出与中文处理

生产环境里,让回答像打字机一样逐字流出能显著降低等待感。SK 支持用 await foreach 消费流式增量;中文要注意编码器显式指定 UTF-8,避免控制台乱码。

await foreach (var chunk in kernel.InvokePromptStreamingAsync<StreamingTextContent>(
    "用三句话介绍 Semantic Kernel 的规划能力。"))
{
    Console.Write(chunk.Text);
}

流式输出同样兼容函数调用:模型边思考边可能触发插件,SK 会在内部完成函数执行后继续流出最终文本。如果你还做 Java 后端,SSE 流式推送 一文讲透了服务端如何把这类流推到前端。

七、检索增强(RAG):接入向量与记忆

当模型需要”你公司内部的文档”,就得上 RAG。SK 提供内存向量存储与连接器,本地 demo 用 VolatileVectorStore 即可,生产可换成 PGVector 或 Azure AI Search。下面是把一段文本灌入记忆并检索的骨架:

// 使用 Microsoft.SemanticKernel.Connectors.OpenAI 的 text embedding
var memory = new KernelMemoryBuilder()
    .WithOpenAITextEmbeddingGeneration("text-embedding-3-small", apiKey)
    .WithOpenAIChatCompletion("gpt-4o-mini", apiKey)
    .Build();

await memory.ImportTextAsync("Semantic Kernel 支持 C#、Python 与 Java。");
var ans = await memory.AskAsync("Semantic Kernel 支持哪些语言?");
Console.WriteLine(ans.Result);

这里借用了独立的 Kernel Memory 组件(与 SK 同源),它把切块、向量化、检索、生成封装成一步 AskAsync,比自己拼 HuggingFace + 向量库省心得多。想用 Ollama 本地模型跑这套流程,可参照 Dify + Ollama 私有化部署 的思路准备本地推理服务。

八、Semantic Kernel 与 Spring AI / LangChain4j 怎么选

三者定位相似——都是”给业务代码接大模型”的编排层,差别主要在语言栈与生态。一张表看清:

维度Semantic KernelSpring AILangChain4j
主语言.NET / C#Java / SpringJava
插件写法[KernelFunction] 标注@Tool 注解@ToolSpecification
规划能力内置 Planner需自行编排中等
适合团队已有 .NET 后端Spring 微服务轻量 Java 项目

结论很直接:栈里是 C# 就选 SK,是 Java Spring 就选 Spring AI;若想要更贴近 LangChain Python 生态的 Java 体验,LangChain4j 实战 那篇给了完整示例。三者函数调用语义互通,迁移成本主要在标注语法。

九、生产落地清单

  • 密钥放配置:ApiKey 走环境变量或 Key Vault,切勿硬编码进仓库。
  • 函数描述要诚实:模型靠 [Description] 判断调用时机,含糊的描述会导致误调用。
  • 加超时与重试:模型与向量库都可能抖动,套一层 Polly 之类的弹性策略。
  • 控制函数数量:一次性暴露几十个函数会让模型”选择困难”,按场景拆成多个小插件。
  • 日志可观测:记录每次函数调用的入参与返回,出问题才好复盘。

Semantic Kernel 的魅力在于”轻”:你不必引入整套 AI 平台,就能让现有 .NET 服务拥有调用工具、记忆上下文、编排多步任务的能力。把它当作连接业务代码与大模型的桥梁,先把一个真实插件跑通,再逐步往 RAG 和规划器扩展,是最稳的切入点。

上一篇 Kong 网关实战:API 网关部署与插件治理
下一篇 Quarkus 实战:GraalVM 原生镜像与云原生 Java 微服务
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]