Agent 开发实战 :C# 使用 Semantic Kernel 构建 AIAgent
2026/9/16 7:24:04 网站建设 项目流程

一、开源地址

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

三、基础方法

学习关键词:

  1. Plugin(插件):把 C# 方法 / 提示词封装成可被大模型调用的能力
  2. ChatHistory(聊天历史):保存多轮对话消息,维持短期上下文记忆
  3. VectorStore(向量存储):存放文本向量,用于 RAG 知识库检索
  4. Embedding(文本嵌入):将文字转为向量,实现语义相似度匹配
  5. 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.AddFromType<WeatherPlugin>(); // 开启自动调用插件(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.GetRequiredService<IChatCompletionService>(); 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()); }

四、拦截器

一共有三个拦截器,代码给出前两个:

  1. IFunctionInvocationFilter:拦截所有插件函数执行,捕获调用前后信息。
  2. IPromptRenderFilter:拦截提示词模板渲染,查看 / 修改最终 Prompt。
  3. IAutoFunctionInvocationFilter:仅拦截 Agent 自动工具调用,可终止多轮调用。
/// <summary> /// 记录调用插件(AOP切片) /// </summary> public class FunctionLogFilter : IFunctionInvocationFilter { public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func<FunctionInvocationContext, 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, Func<PromptRenderContext, 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 endpoint="https://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 name="T">需要返回Json则传入T</typeparam> /// <param name="question">问题</param> /// <param name="maxWords">最大回答字数</param> /// <returns></returns> public async Task<string> AskAIAsync(string question,int? maxWords = null) { var res = await _kernel.InvokePromptAsync(GetQuestion(question,maxWords), new KernelArguments(_settings)); return res.ToString(); } /// <summary> /// AI回答(流式) /// </summary> /// <typeparam name="T">需要返回Json则传入T</typeparam> /// <param name="question">问题</param> /// <param name="maxWords">最大回答字数</param> /// <returns></returns> public async Task AskAIStreamingAsync(string question,Action<string> 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 Task<T> AskAIAsync<T>(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.DeserializeObject<T>(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(); } }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询