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();
    }

}
相关推荐
玫瑰互动GEO1 小时前
GEO优化学习九级模型:开发者从认知层切入
人工智能·ai·ai搜索·gem·生成式引擎优化·gem优化
VIP_CQCRE2 小时前
用 Ace Data Cloud 的 gpt-image-2,把 AI 图像生成接入真实业务场景
ai·aigc·api·图像生成·acedatacloud
满脑子技术的前端工程师3 小时前
第六章 TypeScript MCP Server:独立综合项目与能力验收
ai
EchoMind-Henry3 小时前
Muse外设两条接入路,成本该怎么算
人工智能·ai
Jo乔戈里6 小时前
免费本地搜图软件
图像处理·python·搜索引擎·ai
全栈练习生12 小时前
大模型原理之 Softmax
python·ai
唯鹿12 小时前
Jev初体验记录
人工智能·ai·jev
网络毒刘13 小时前
MCP 资源与提示(resources/prompts)实战:不只 tools,把只读上下文结构化喂给 Agent
人工智能·ai·cursor
数商思语行14 小时前
从BA、产品、实施或开发转做FDE,先补哪种能力
人工智能·ai·供应链·商业分析·ontology·本体·fde
我是神614 小时前
俄罗斯恢复软件r.saver汉化版
ai·数据恢复