Semantic Kernel .NET AI 开发
大约 10 分钟约 3041 字
Semantic Kernel .NET AI 开发
简介
Semantic Kernel(SK)是微软推出的 AI 编排框架,核心目标是把大模型能力自然融入现有 .NET 应用,而不是让 AI 逻辑游离在业务系统之外。它特别适合已经有 ASP.NET Core、依赖注入、配置中心、日志体系和企业服务边界的团队,用来构建知识问答、Copilot、Agent、工作流助手和工具调用场景。
特点
实现
初始化 Kernel 与聊天服务
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(
modelId: "gpt-4o-mini",
apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY")!
);
var kernel = builder.Build();
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage("你是一个 .NET 企业应用助手,回答时优先给出可执行建议。");
history.AddUserMessage("解释 ASP.NET Core 中间件管道和过滤器有什么区别?");
var reply = await chatService.GetChatMessageContentAsync(history);
Console.WriteLine(reply.Content);// 在 ASP.NET Core 中注册 Kernel
builder.Services.AddSingleton(sp =>
{
var kernelBuilder = Kernel.CreateBuilder();
kernelBuilder.AddOpenAIChatCompletion(
modelId: "gpt-4o-mini",
apiKey: builder.Configuration["OpenAI:ApiKey"]!
);
return kernelBuilder.Build();
});{
"OpenAI": {
"ApiKey": "your-api-key",
"Model": "gpt-4o-mini"
}
}Prompt 模板与结构化输入
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
var promptTemplate = """
你是一个企业架构师。
项目背景:{{project}}
系统约束:{{constraints}}
用户问题:{{question}}
请从架构、稳定性、成本三个维度给出建议。
""";
var function = kernel.CreateFunctionFromPrompt(
promptTemplate,
templateFormat: "handlebars",
promptTemplateFactory: new HandlebarsPromptTemplateFactory()
);
var result = await kernel.InvokeAsync(function, new KernelArguments
{
["project"] = "一个 .NET 8 电商平台",
["constraints"] = "已有 RabbitMQ、Redis、SQL Server,团队熟悉 ASP.NET Core",
["question"] = "如何设计订单异步通知架构?"
});
Console.WriteLine(result.GetValue<string>());// 你也可以把 Prompt 看成“可版本化的业务模板”
// 生产环境建议:
// 1. Prompt 放入文件或配置中心
// 2. 建立版本号
// 3. 为每次变更记录评估结果Plugin 开发与自动函数调用
using ComponentModel = System.ComponentModel;
using Microsoft.SemanticKernel;
public class OrderPlugin
{
private readonly IOrderService _orderService;
public OrderPlugin(IOrderService orderService)
{
_orderService = orderService;
}
[KernelFunction("query_order")]
[ComponentModel.Description("根据订单号查询订单详情")]
public async Task<string> QueryOrderAsync(
[ComponentModel.Description("订单号")] string orderId)
{
var order = await _orderService.GetByIdAsync(orderId);
if (order is null) return $"未找到订单 {orderId}";
return $"订单 {order.Id},状态:{order.Status},金额:{order.Amount},创建时间:{order.CreatedAt:yyyy-MM-dd HH:mm:ss}";
}
[KernelFunction("cancel_order")]
[ComponentModel.Description("取消指定订单")]
public async Task<string> CancelOrderAsync(
[ComponentModel.Description("订单号")] string orderId,
[ComponentModel.Description("取消原因")] string reason)
{
await _orderService.CancelAsync(orderId, reason);
return $"订单 {orderId} 已取消,原因:{reason}";
}
}var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion("gpt-4o-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY")!);
var kernel = builder.Build();
kernel.Plugins.AddFromType<OrderPlugin>("OrderPlugin");
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var settings = new OpenAIPromptExecutionSettings
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
Temperature = 0.2
};
var history = new ChatHistory();
history.AddSystemMessage("你是订单助手,只能根据工具返回结果回答。不要编造。\n");
history.AddUserMessage("请查询订单 ORD-2024-0001,如果状态是待支付就取消它。");
var response = await chatService.GetChatMessageContentAsync(history, settings, kernel);
Console.WriteLine(response.Content);// 自动函数调用适合:
// 1. 工单查询
// 2. 用户资料检索
// 3. 审批流程辅助
// 4. 内部知识问答 + 操作建议
// 不适合直接把高风险操作无审批暴露给模型Filter、审计与安全控制
using Microsoft.SemanticKernel;
public class LoggingFilter : IFunctionInvocationFilter
{
private readonly ILogger<LoggingFilter> _logger;
public LoggingFilter(ILogger<LoggingFilter> logger)
{
_logger = logger;
}
public async Task OnFunctionInvocationAsync(
FunctionInvocationContext context,
Func<FunctionInvocationContext, Task> next)
{
_logger.LogInformation(
"SK function invoke: plugin={Plugin}, function={Function}",
context.Function.PluginName,
context.Function.Name
);
await next(context);
_logger.LogInformation(
"SK function completed: function={Function}, resultType={ResultType}",
context.Function.Name,
context.Result?.GetType().Name
);
}
}builder.Services.AddSingleton<IFunctionInvocationFilter, LoggingFilter>();// 对危险函数增加审批/白名单过滤
public class ApprovalFilter : IFunctionInvocationFilter
{
public async Task OnFunctionInvocationAsync(
FunctionInvocationContext context,
Func<FunctionInvocationContext, Task> next)
{
var dangerousFunctions = new[] { "delete_user", "refund_order", "shutdown_device" };
if (dangerousFunctions.Contains(context.Function.Name))
{
throw new InvalidOperationException($"函数 {context.Function.Name} 需要人工审批,当前已阻止自动执行。");
}
await next(context);
}
}与 RAG / 向量检索结合
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.InMemory;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.Memory;
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion("gpt-4o-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY")!);
builder.AddOpenAITextEmbeddingGeneration("text-embedding-3-small", Environment.GetEnvironmentVariable("OPENAI_API_KEY")!);
var kernel = builder.Build();
var embeddingService = kernel.GetRequiredService<ITextEmbeddingGenerationService>();
var memoryStore = new VolatileMemoryStore();
var memory = new SemanticTextMemory(memoryStore, embeddingService);
await memory.SaveInformationAsync("company-docs", id: "refund-policy", text: "退款政策:签收后7天内可申请退款,虚拟商品除外。", description: "退款规则");
await memory.SaveInformationAsync("company-docs", id: "invoice-policy", text: "发票规则:企业客户支持专票,个人用户默认电子发票。", description: "发票规则");
await foreach (var item in memory.SearchAsync("company-docs", "退款后发票怎么处理", limit: 3))
{
Console.WriteLine($"id={item.Metadata.Id}, relevance={item.Relevance}, text={item.Metadata.Text}");
}// 生产环境可替换为更稳定的向量库:
// 1. Azure AI Search
// 2. Qdrant
// 3. Redis
// 4. pgvector / Elasticsearch vectorASP.NET Core 中落地 AI 接口
app.MapPost("/api/copilot/architecture", async (
ArchitectureQuestionRequest request,
Kernel kernel) =>
{
var chat = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
history.AddSystemMessage("你是企业架构 Copilot,回答时请优先给出结构化建议。\n");
history.AddUserMessage($"问题:{request.Question}\n上下文:{request.Context}");
var response = await chat.GetChatMessageContentAsync(history);
return Results.Ok(new
{
answer = response.Content,
traceId = Guid.NewGuid().ToString("N")
});
});
public record ArchitectureQuestionRequest(string Question, string Context);优点
缺点
总结
Semantic Kernel 最适合已经有 .NET 工程基础的团队,把大模型能力嵌入到现有应用中,而不是从零搭一个全新的 AI 技术栈。真正落地时,重点不是把模型调通,而是把 Prompt、Plugin、权限、审计、RAG、评估和回退机制放进同一条工程链路里。
关键知识点
- Kernel 是执行容器,Plugin 是工具能力,Filter 是治理层。
- 自动函数调用很强,但必须配权限控制和审计日志。
- Prompt 模板应版本化,不应散落在代码里。
- RAG 不是“可选增强”,而是很多企业问答场景的基础能力。
项目落地视角
- 内部工单助手:查工单、查订单、查用户信息。
- 企业知识问答:RAG 检索规章制度、产品文档、FAQ。
- 审批辅助:模型总结上下文,但最终审批仍由人工决策。
- 客服 Copilot:给坐席建议,不直接替代最终回复。
常见误区
- 只做一个聊天 Demo,就认为已经完成企业级落地。
- 把高风险函数直接暴露给自动调用,没有审批边界。
- 不做评估集、不做日志审计,出现错误无法回放。
- 认为接了 Semantic Kernel,就自动拥有 Agent 能力和稳定性。
进阶路线
- 深入学习 SK 的 function calling、planner、memory 和 agent 模式。
- 结合向量数据库、缓存、消息队列构建更完整 AI 服务架构。
- 为 Prompt、RAG、函数调用建立自动评估流水线。
- 与 ASP.NET Core 中间件、认证授权、OpenTelemetry 打通。
适用场景
- .NET 企业应用接入 AI 能力。
- 知识助手、客服助手、开发助手。
- 内部运营 Copilot、流程辅助决策。
- 需要函数调用、RAG 和工具编排的智能应用。
落地建议
- 从低风险、可回退、可评估的场景开始接入 SK。
- 所有工具调用都要有日志、权限、超时和兜底策略。
- 把 Prompt、Embedding、知识库分块策略纳入版本管理。
- 先做“辅助决策”,再考虑“自动执行”。
排错清单
- 回复不稳定时,先看 Prompt 是否过长或上下文不清晰。
- 工具调用异常时,先检查插件注册、参数描述和函数签名。
- RAG 效果差时,先查检索与召回,不要只怪模型。
- 线上问题回放不了时,优先补齐日志、traceId 和输入输出审计。
复盘问题
- 当前项目接入 AI,真正想解决的是查询、生成,还是流程编排?
- 哪些工具调用是可以自动执行的,哪些必须人工确认?
- 你的 AI 接口是否具备失败回退和权限边界?
- 如果今天模型输出错误,你是否能快速回放并定位原因?
多轮对话与上下文管理
// 多轮对话管理
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var history = new ChatHistory();
// 系统提示
history.AddSystemMessage("你是一个 .NET 技术顾问。回答时给出具体代码示例。");
history.AddUserMessage("什么是依赖注入?");
var reply1 = await chatService.GetChatMessageContentAsync(history);
history.AddAssistantMessage(reply1.Content!);
// 多轮上下文自动带入
history.AddUserMessage("如何在 ASP.NET Core 中注册 scoped 服务?");
var reply2 = await chatService.GetChatMessageContentAsync(history);
// 上下文窗口管理 — 防止 Token 超限
public class ConversationManager
{
private readonly ChatHistory _history = new();
private readonly int _maxTokens;
public ConversationManager(int maxTokens = 4000)
{
_maxTokens = maxTokens;
}
public void AddMessage(AuthorRole role, string content)
{
_history.AddMessage(role, content);
TrimIfNeeded();
}
private void TrimIfNeeded()
{
// 保留系统提示,裁剪最早的对话
while (EstimateTokens(_history) > _maxTokens && _history.Count > 1)
{
// 跳过索引 0(系统提示)
if (_history.Count > 1 && _history[1].Role == AuthorRole.User)
_history.RemoveAt(1);
if (_history.Count > 1 && _history[1].Role == AuthorRole.Assistant)
_history.RemoveAt(1);
}
}
private int EstimateTokens(ChatHistory history)
{
// 简化估算:中文约 1.5 字符/token,英文约 4 字符/token
int total = 0;
foreach (var msg in history)
total += msg.Content?.Length ?? 0;
return (int)(total / 2.5);
}
public ChatHistory GetHistory() => _history;
}Agent 模式与 Planner
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
// 自动函数调用 + 多步骤推理
var builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion("gpt-4o-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY")!);
var kernel = builder.Build();
// 注册多个 Plugin
kernel.Plugins.AddFromType<WeatherPlugin>("Weather");
kernel.Plugins.AddFromType<CalculatorPlugin>("Calculator");
kernel.Plugins.AddFromType<DateTimePlugin>("DateTime");
public class WeatherPlugin
{
[KernelFunction("get_weather")]
[ComponentModel.Description("获取指定城市的当前天气")]
public async Task<string> GetWeatherAsync(
[ComponentModel.Description("城市名称")] string city)
{
// 模拟天气查询
return $"{city}:晴,温度 25°C,湿度 60%";
}
}
public class CalculatorPlugin
{
[KernelFunction("calculate")]
[ComponentModel.Description("执行数学计算")]
public string Calculate(
[ComponentModel.Description("数学表达式")] string expression)
{
// 简化示例,生产环境需要安全的表达式解析器
return $"计算结果:{expression}";
}
}
public class DateTimePlugin
{
[KernelFunction("current_time")]
[ComponentModel.Description("获取当前日期和时间")]
public string GetCurrentTime()
{
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
}
// Agent 模式:让模型自主决策调用哪些工具
var chatService = kernel.GetRequiredService<IChatCompletionService>();
var settings = new OpenAIPromptExecutionSettings
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
Temperature = 0.2,
MaxTokens = 2000
};
var history = new ChatHistory();
history.AddSystemMessage("你是一个智能助手,可以使用天气、计算器和日期时间工具回答问题。");
history.AddUserMessage("北京现在天气怎么样?如果温度超过 20 度,帮我计算 15 乘以 8 等于多少。");
var response = await chatService.GetChatMessageContentAsync(history, settings, kernel);
Console.WriteLine(response.Content);内置 Plugin 与 Connector
// Semantic Kernel 内置了丰富的 Connector 和 Plugin
// 1. 使用 Bing Search Plugin
builder.AddBingSearchPlugin("your-bing-api-key");
// 2. 使用 HTTP REST API 作为 Plugin
kernel.Plugins.AddFromFunction(
"StockPlugin",
"查询股票价格",
async (string symbol) =>
{
using var http = new HttpClient();
var response = await http.GetStringAsync($"https://api.example.com/stock/{symbol}");
return response;
}
);
// 3. 使用 OpenAI File Service
builder.AddOpenAIFileService(Environment.GetEnvironmentVariable("OPENAI_API_KEY")!);
// 4. 配置多个模型(不同任务用不同模型)
builder.AddOpenAIChatCompletion("gpt-4o", apiKey, serviceId: "premium");
builder.AddOpenAIChatCompletion("gpt-4o-mini", apiKey, serviceId: "fast");
// 按场景选择模型
var premiumChat = kernel.GetRequiredService<IChatCompletionService>("premium");
var fastChat = kernel.GetRequiredService<IChatCompletionService>("fast");错误处理与重试策略
// 生产环境的错误处理策略
public class ResilientChatService
{
private readonly IChatCompletionService _chatService;
private readonly ILogger<ResilientChatService> _logger;
private readonly int _maxRetries = 3;
public ResilientChatService(
IChatCompletionService chatService,
ILogger<ResilientChatService> logger)
{
_chatService = chatService;
_logger = logger;
}
public async Task<string?> GetSafeResponseAsync(
ChatHistory history,
Kernel kernel,
string? fallbackMessage = "抱歉,服务暂时不可用,请稍后重试。")
{
for (int attempt = 1; attempt <= _maxRetries; attempt++)
{
try
{
var settings = new OpenAIPromptExecutionSettings
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
Temperature = 0.2
};
var response = await _chatService.GetChatMessageContentAsync(
history, settings, kernel);
if (string.IsNullOrWhiteSpace(response.Content))
{
_logger.LogWarning("模型返回空内容,第 {Attempt} 次重试", attempt);
continue;
}
return response.Content;
}
catch (HttpOperationException ex) when (ex.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
_logger.LogWarning("限流,等待后重试,第 {Attempt} 次", attempt);
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
catch (Exception ex)
{
_logger.LogError(ex, "AI 调用失败,第 {Attempt} 次", attempt);
if (attempt == _maxRetries)
return fallbackMessage;
await Task.Delay(TimeSpan.FromSeconds(2 * attempt));
}
}
return fallbackMessage;
}
}// 生产环境常见问题与应对
// 1. Token 超限
// 解决:监控输入 Token 数,超过阈值时裁剪上下文
// 2. 模型限流(429)
// 解决:指数退避重试 + 多模型备选
// 3. 工具调用循环
// 解决:设置最大工具调用轮次
var settings = new OpenAIPromptExecutionSettings
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
MaximumAutoInvokeAttempts = 5 // 最多 5 轮工具调用
};
// 4. 输出格式不稳定
// 解决:在 Prompt 中明确输出格式要求,或使用 JSON Mode