
一、开源地址Semantic Kernel 是微软开源.NET 优先 AI 框架可接入 DeepSeek 等模型快速开发带插件、RAG 记忆的 AI Agent。GitHub - microsoft/semantic-kernel: Integrate cutting-edge LLM technology quickly and easily into your apps · GitHub二、安装Nuget包Microsoft.SemanticKernel Microsoft.SemanticKernel.Connectors.OpenAI三、基础方法学习关键词Plugin插件把 C# 方法 / 提示词封装成可被大模型调用的能力ChatHistory聊天历史保存多轮对话消息维持短期上下文记忆VectorStore向量存储存放文本向量用于 RAG 知识库检索Embedding文本嵌入将文字转为向量实现语义相似度匹配RAG检索增强生成从知识库检索相关片段辅助大模型回答1.初始化// 1. 创建内核 var builder Kernel.CreateBuilder(); // 接入兼容OpenAI接口(DeepSeek等) builder.AddOpenAIChatCompletion( modelId: deepseek-chat, apiKey: xxx, endpoint: new Uri(https://api.deepseek.com) ); var kernel builder.Build();2.插件// 定义插件类 public class WeatherPlugin { [KernelFunction, Description(查询指定城市的当前气温)] public string GetWeather([Description(城市名称)] string city) { // 你的业务逻辑 return ${city} 当前26℃多云; } } // 注册插件到kernel kernel.Plugins.AddFromTypeWeatherPlugin(); // 开启自动调用插件LLM自己判断要不要调用C#方法 var settings new OpenAIPromptExecutionSettings { ToolCallBehavior ToolCallBehavior.AutoInvokeKernelFunctions, // ✅ 自动执行插件 MaxTokens 1024 }; // 执行LLM会自动调用GetWeather var result await kernel.InvokePromptAsync(肇庆今天天气怎么样?, new KernelArguments(settings)); Console.WriteLine(result.ToString());3.对话记忆内存记忆ChatHistory 就是对话记忆保存在内存适合聊天场景自动拼接历史消息。// 聊天历史容器存放对话记忆 ChatHistory chatHistory new ChatHistory(); // 第一轮对话 chatHistory.AddUserMessage(介绍肇庆特产); var chatSettings new OpenAIPromptExecutionSettings { MaxTokens 1024 }; // 聊天补全服务 var chatService kernel.GetRequiredServiceIChatCompletionService(); var chatResult await chatService.GetChatMessageContentAsync(chatHistory, chatSettings, kernel); // 把AI回答追加进历史形成记忆 chatHistory.Add(chatResult); Console.WriteLine(chatResult.Content); // 第二轮带上下文提问能记住上一轮对话 chatHistory.AddUserMessage(挑3个简单描述); var chatResult2 await chatService.GetChatMessageContentAsync(chatHistory, chatSettings, kernel); chatHistory.Add(chatResult2);4.持久化记忆 / 向量记忆// 简易内存向量存储Demo实际项目换Redis/PG向量库 using Microsoft.SemanticKernel.Memory; using Microsoft.SemanticKernel.Connectors.OpenAI; var memoryBuilder new MemoryBuilder(); memoryBuilder.WithOpenAITextEmbeddingGeneration(text-embedding-model, apikey, new Uri(url)); memoryBuilder.WithMemoryStore(new VolatileMemoryStore()); // 内存向量库重启丢失 var memory memoryBuilder.Build(); // 1. 写入知识库 await memory.SaveInformationAsync( collection: zhaoqing, // 知识库分组 text: 肇庆裹蒸粽冬叶糯米制作, id: doc1 ); // 2. 检索根据问题查找相关片段RAG var searchResult memory.SearchAsync(肇庆有什么美食, zhaoqing, limit:2); await foreach(var item in searchResult) { Console.WriteLine(item.Metadata.Text); }5.流失输出var settings new OpenAIPromptExecutionSettings { MaxTokens 512 }; // 流式迭代返回chunk await foreach (var chunk in kernel.InvokePromptStreamingAsync(介绍端砚, new KernelArguments(settings))) { Console.Write(chunk.ToString()); }四、拦截器一共有三个拦截器代码给出前两个IFunctionInvocationFilter拦截所有插件函数执行捕获调用前后信息。IPromptRenderFilter拦截提示词模板渲染查看 / 修改最终 Prompt。IAutoFunctionInvocationFilter仅拦截 Agent 自动工具调用可终止多轮调用。/// summary /// 记录调用插件AOP切片 /// /summary public class FunctionLogFilter : IFunctionInvocationFilter { public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, FuncFunctionInvocationContext, Task next) { // 调用前打印 var pluginName context.Function.PluginName; var funcName context.Function.Name; if (pluginName ! null) { Console.WriteLine($【SK调用】准备执行插件方法{pluginName}.{funcName}); // 可选打印参数 foreach (var arg in context.Arguments) { Console.WriteLine($--参数 {arg.Key} {arg.Value}); } } // 执行原始函数必须调用next否则不会执行插件方法 await next(context); // 调用后打印 if (pluginName ! null) { Console.WriteLine($【SK调用完成】返回结果{context.Result}); } } } /// summary /// 记录Prompt渲染AOP切片 /// /summary public class PromptRenderLogFilter : IPromptRenderFilter { public async Task OnPromptRenderAsync(PromptRenderContext context, FuncPromptRenderContext, Task next) { // 执行渲染把 {{$变量}} 替换成真实值 await next(context); // 渲染完成后传给LLM之前 Console.WriteLine($【Prompt渲染完成】 :{context.RenderedPrompt}); // 你可以在这里修改prompt // context.RenderedPrompt renderedPrompt \n请用简短语言回答; } }五、封装的类不断更新中...public class EasyKernel { private Kernel _kernel new Kernel(); private OpenAIPromptExecutionSettings _settings new OpenAIPromptExecutionSettings(); public EasyKernel(string apiKey,string modelId deepseek-flash, string endpointhttps://api.deepseek.com) { _kernel Kernel.CreateBuilder() .AddOpenAIChatCompletion( modelId: modelId, apiKey: apiKey, endpoint: new Uri(endpoint) ) .Build(); _settings new OpenAIPromptExecutionSettings { ToolCallBehavior ToolCallBehavior.AutoInvokeKernelFunctions, }; } /// summary /// AI回答 /// /summary /// typeparam nameT需要返回Json则传入T/typeparam /// param namequestion问题/param /// param namemaxWords最大回答字数/param /// returns/returns public async Taskstring AskAIAsync(string question,int? maxWords null) { var res await _kernel.InvokePromptAsync(GetQuestion(question,maxWords), new KernelArguments(_settings)); return res.ToString(); } /// summary /// AI回答(流式) /// /summary /// typeparam nameT需要返回Json则传入T/typeparam /// param namequestion问题/param /// param namemaxWords最大回答字数/param /// returns/returns public async Task AskAIStreamingAsync(string question,Actionstring funcs ,int? maxWords null) { await foreach (var chunk in _kernel.InvokePromptStreamingAsync(GetQuestion(question, maxWords), new KernelArguments(_settings))) { funcs(chunk.ToString()); } } /// summary /// AI回答(返回Json) /// /summary public async TaskT AskAIAsyncT(string question,int? maxWords null) where T : new() { var settings _settings; settings.ResponseFormat json_object; Type targetType typeof(T); bool isList targetType.IsGenericType targetType.GetGenericTypeDefinition() typeof(List); string templateJson; string formatDesc; if (isList) { Type itemType targetType.GetGenericArguments()[0]; object itemInstance Activator.CreateInstance(itemType)!; templateJson JsonConvert.SerializeObject(itemInstance); formatDesc $输出JSON数组**绝对不能外层套{{}}对象只输出[]数组**数组内每一项结构参考下面模板\n{templateJson}; } else { templateJson JsonConvert.SerializeObject(new T()); formatDesc $输出JSON对象结构参考下面模板\n{templateJson}; } string prompt GetQuestion(question, maxWords) formatDesc; var res await _kernel.InvokePromptAsync(prompt, new KernelArguments(settings)); return JsonConvert.DeserializeObjectT(res.ToString()) ?? new T(); } /// summary /// 获取问题及字数限制 /// /summary private string GetQuestion(string question, int? maxWords) { StringBuilder sb new StringBuilder($问题{question}); if (maxWords!null) { sb.AppendLine($字数限制:{maxWords}); } return sb.ToString(); } }