Agent 开发实战 01:C# 使用 Semantic Kernel 构建 AIAgent

一、开源地址

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包

bash 复制代码
Microsoft.SemanticKernel
Microsoft.SemanticKernel.Connectors.OpenAI

三、基础方法

学习关键词:

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

}
相关推荐
znnnk1 小时前
【AI应用】从 Prompt 到 Skill:AI 到底“会什么”?
ai·prompt·ai编程·ai应用·skill
ruleslol2 小时前
Function Calling 讲解
java·ai
RobinDevNotes2 小时前
TensorRT 与 ONNX Runtime 推理全解析
搜索引擎·ai·大模型·推理引擎
LuTshoes2 小时前
AI Agent 相关介绍
人工智能·ai
俊哥V2 小时前
每日 AI 研究简报 · 2026-09-14
人工智能·ai
严同学正在努力3 小时前
认识 SQL Server 的 T-SQL 语法
数据库·人工智能·ai·oracle·dba
果粒蹬i3 小时前
AI 不只回答问题:用 Hermes Agent 把微信指令接到 Windows 电脑上
人工智能·ai
宁渡AI大模型3 小时前
河南宁渡科技有限公司|宁渡课堂 AI 全栈面试分享,大模型 API 开发与流式输出面试题
人工智能·python·ai·大模型
ruleslol3 小时前
System Prompt(系统提示词) 的用法
java·ai