semantic kernel 是一个轻量级 ai 编排框架,将 llm 调用、提示词工程、函数编排和记忆管理抽象为可复用组件;而直接调用 openai api 需手动处理 prompt 拼接、json 解析、重试及上下文管理。

什么是 Semantic Kernel,它和直接调用 OpenAI API 有什么区别
Semantic Kernel(SK)不是模型,也不是 API 封装库,而是一个轻量级的「AI 编排框架」——它帮你把 LLM 调用、提示词工程、函数编排 和 记忆管理 组织成可复用、可测试、可插拔的组件。直接调用 OpenAIClient 时,你得自己拼接 prompt、解析 JSON、处理重试、管理上下文;而 SK 把这些抽象成 Kernel、Function、Plugin 和 ChatHistory 等概念,重点在「让 AI 行为像传统函数一样被定义和组合」。
它不替代 Microsoft.SemanticKernel NuGet 包之外的底层 SDK,而是构建在其之上。目前(v1.0+)已稳定支持 .NET 6/7/8,但对 .NET Framework 无支持。
快速跑通第一个 SK 应用:三步完成 Hello World
别从复杂插件或 RAG 开始。先验证环境是否就绪,再扩展:
- 安装
Microsoft.SemanticKernel(建议 v1.0.0-rc1 或更高)和对应连接器,如Microsoft.SemanticKernel.Connectors.OpenAI - 初始化
Kernel时必须显式注册一个IChatCompletionService,比如new OpenAIChatCompletionService("gpt-4o", "your-key", "https://api.openai.com/v1");漏掉这步会报InvalidOperationException: No chat completion service registered - 最简调用不是写 plugin,而是用
kernel.InvokePromptAsync("你好,你是谁?")—— 这会绕过函数编排,直连 LLM,适合快速验证 token 和权限
示例片段:
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion("gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY")!)
.Build();
<p>var result = await kernel.InvokePromptAsync("用中文一句话介绍你自己");
Console.WriteLine(result.GetValue<string>()); // 注意:result 是 FunctionResult,需显式 .GetValue<T>()如何定义并调用自定义语义函数(Semantic Function)
语义函数 = 带描述的 prompt 模板,由 SK 解析后注入变量、自动补全系统消息。它不是 C# 方法,不能传参对象,只接受字符串输入并返回字符串输出(内部仍走 LLM 调用)。
- 模板中用
{{input}}接收主输入,用{{myVar}}接收命名参数(需在InvokeAsync时以new() { ["myVar"] = "xxx" }方式传入) - 必须提供
description字符串,否则 SK 在自动规划(planning)时无法理解该函数用途 - 不要试图在模板里写 C# 逻辑(如 if/for),SK 不执行它们;所有逻辑应放在 native function(C# 方法)里
- 常见错误:
KernelException: Function 'xxx' not found,通常是因为没用kernel.Plugins.AddFromPrompt...注册,或插件名/函数名含空格、特殊字符
例如定义一个格式化时间的语义函数:
var plugin = kernel.Plugins.AddFromPrompt(
"TimeFormatter",
"Format current time as: {{input}} (e.g., 'HH:mm:ss')",
new PromptTemplateConfig
{
Description = "Formats current time using a given format string"
});
<p>var result = await kernel.InvokeAsync(plugin["FormatTime"], new() { ["input"] = "yyyy-MM-dd HH:mm" });什么时候该用 Native Function 而不是 Semantic Function
当你要执行确定性操作(查数据库、发 HTTP 请求、调本地算法)、需要强类型参数/返回值、或要复用现有业务逻辑时,必须用 native function —— 它是标准 C# 方法,通过 [KernelFunction] 特性导出给 SK 使用。
- 方法必须是
public,返回值建议为Task<string></string>或string(SK 自动 await),参数类型限于基础类型或KernelArguments - 不要在 native function 里做耗时同步阻塞操作(如
Thread.Sleep),否则会拖慢整个异步 pipeline - 若需访问
Kernel实例(比如调另一个插件),可通过构造函数注入,但注意生命周期:native function 实例默认是 singleton,Kernel是 transient,需用Func<kernel t></kernel>延迟获取 - 常见坑:
KernelFunction方法体里抛异常,SK 默认吞掉并返回空字符串,需手动检查result.Metadata.TryGetValue("Exception", out var ex)
比如封装一个本地天气查询:
public class WeatherService
{
[KernelFunction]
public async Task<string> GetWeatherAsync(string city)
{
// 实际调用 HttpClient...
return $"Weather in {city}: Sunny, 26°C";
}
}
<p>kernel.Plugins.AddFromObject(new WeatherService(), "Weather");真正难的不是写第一个函数,而是设计 plugin 边界:哪些逻辑放 native、哪些放 semantic、哪些该拆成多个小 function——这直接影响后续 chain、plan 和调试成本。别一开始就追求“全自动 planning”,先手动 compose 几个函数跑通流程,再逐步放开 control。










