一、开源地址
Semantic Kernel 是微软开源.NET 优先 AI 框架,可接入 DeepSeek 等模型,快速开发带插件、RAG 记忆的 AI Agent。
二、安装Nuget包
bash
Microsoft.SemanticKernel
Microsoft.SemanticKernel.Connectors.OpenAI
三、基础方法
学习关键词:
- Plugin(插件):把 C# 方法 / 提示词封装成可被大模型调用的能力
- ChatHistory(聊天历史):保存多轮对话消息,维持短期上下文记忆
- VectorStore(向量存储):存放文本向量,用于 RAG 知识库检索
- Embedding(文本嵌入):将文字转为向量,实现语义相似度匹配
- RAG(检索增强生成):从知识库检索相关片段,辅助大模型回答
1.初始化
cs
// 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.插件
cs
// 定义插件类
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 就是对话记忆,保存在内存;适合聊天场景,自动拼接历史消息。
cs
// 聊天历史容器,存放对话记忆
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.持久化记忆 / 向量记忆
cs
// 简易内存向量存储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.流失输出
cs
var settings = new OpenAIPromptExecutionSettings { MaxTokens = 512 };
// 流式迭代返回chunk
await foreach (var chunk in kernel.InvokePromptStreamingAsync("介绍端砚", new KernelArguments(settings)))
{
Console.Write(chunk.ToString());
}
四、封装的类
不断更新中...
cs
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();
}
}