Semantic Kernel也能充当MCP Client

背景

笔者之前,分别写过两篇关于Semantic Kernel(下简称SK)相关的博客,最近模型上下文协议(下称MCP)大火,实际上了解过SK的小伙伴,一看到 MCP的一些具体呈现,会发现,Client 调用 Server的方式,和SK调用插件的过程很像,实际操作了一下,发现确实是可以的。

也就是说,如果我们之前的项目里用到SK做过Agent相关的模块,如今也可以丝滑的让其充当MCP Client的角色,去使用更多MCP生态的东西,而不需要做更多的改动。

虽然SK是为AI Agent的发展而诞生的,但好框架就是好框架,没想到它和MCP也这么契合。

本篇,建立在《再尝Semantic Kernel,Planning特性很香》的基础上,再次扩展一下MCP相关的介绍。

注意:本篇不会深入介绍MCP相关的概念,架构等等前置内容,主要还是通过案例说明SK和MCP
Server之间的联系,建议不熟悉MCP相关内容的小伙伴,先登录MCP官网进行了解。

创建一个MCP Server

在MCP的官方介绍文档里已经有C#的官方SDK了,这里我们参照它官方的例子,先来一个MCP Server。

Tips:官方的案例已经非常简洁和完善了,建议没搞过MCP的小伙伴上手试一下,虽然案例很简单,一看就能明白,但那真正跑通的获得感,还是得自己动手试一下才能体会到。

构建服务

这一步我觉得大家还是直接看官方文档更清楚,我这里不在赘述

传送门👉:https://modelcontextprotocol.io/quickstart/server#building-your-server-5

需要注意的是,我这里的Server是使用SSE的传输方式。

目前SSE的方式官方已经声明会逐步被Streamable Http的形式替代,但目前还是Built-in状态,本地调试的话还可以使用stdio的方式,这也是Claude Desktop,Cline之类客户端工具支持的方式,这点大家按需设定即可,这部分内容可以参考这里👉:https://mcp-framework.com/docs/Transports/transports-overview。

编写Tool

定义一个class,然后标记上MCPServer的特定属性,这部分官网也有介绍,我就直接上代码了

[McpServerToolType]
public static class WeatherTools
{[McpServerTool(Name = "GetWeather"), Description("获取当前城市的天气")]public static async Task<string> GetWeather(HttpClient client,[Description("中国的城市编码adcode")] string adcode){if (string.IsNullOrEmpty(adcode)){return "adcode不能为空";}string gdKey = ConfigHelper.GetAppSetting("GaoDeKey");var jsonElement = await client.GetFromJsonAsync<JsonElement>($"/v3/weather/weatherInfo?key={gdKey}&city={adcode}&extensions=base");var lives = jsonElement.GetProperty("lives").EnumerateArray();if (!lives.Any()){return "当前城市天气获取失败";}return string.Join("\n--\n",lives.Select(live =>{var city =  $"{live.GetProperty("province").GetString()}--{live.GetProperty("city").GetString()}";var weather = live.GetProperty("weather").GetString();var temperature = live.GetProperty("temperature").GetString();var windPower = live.GetProperty("windpower").GetString();var humidity = live.GetProperty("humidity").GetString();return $"城市:{city}\n天气:{weather}\n温度:{temperature}°C\n风力:{windPower}级\n湿度:{humidity}%";}));}
}

我这里,没有使用官方案例里的天气接口,而是改成了高德的天气接口,因为一会儿还要演示一下SK调用MCP Server的能力,除了调用本地的Server,高德还有一个云端的MCP Server,非常好用,稍后一并介绍一下,正好就连天气接口也改成高德的。

编写完成后,启动我们的Server服务。

dotnet run

验证

Tool编写完成后,可以先使用一些软件或者工具类的MCP Client验证一下,开发阶段,这些工具还是非常有必要的,它的角色定位就像我们用到的数据库管理工具,比如SSMS,Pg Admin,DBeaver等。

我这里使用的是官方的MCP Inspector,本地只要有node和npx环境即可。

另外,因为要经常测试一些Server,建议把Python和Python包管理工具uv也安装一下。

然后,我们启动Inspector

npx @modelcontextprotocol/inspector node build/index.js

在这里插入图片描述

启动之后,控制台会监听一个端口,然后在浏览器打开,然后配置好我们的Server地址,如下图

获取到所有的Tool之后,测试验证一下我们刚完成的天气接口是否生效。

至此,验证工作完成,说明我们的MCP Server是可以正常工作的,接下来就是接入实际的业务系统,来调用这个Server提供的能力了。

在业务系统创建MCP Client

注入SK服务

这部分略过,在前面的系列文章里已经写过了,或者大家也可以直接查看微软的官方文档,这里不再赘述。

编写插件

这里呢,因为我在之前的项目里,已经开始使用SK框架了,并且完成了部分的Agent功能,都是以Plugin的方式注入到系统里的,这里的演示也就暂时以这种方式来接入,后续再根据实际情况调整。

插件的代码如下

[KernelFunction("call_weather_api")]
[Description("通过地理编码,获取天气信息")]
[return: Description("如果运行正常,返回编号所属地址的天气详情")]
public async Task<string> CallWeatherApi(string adcode)
{Logger.Debug("--------天气插件正确执行---------------");var defaultOptions = new McpClientOptions{ClientInfo = new() { Name = "SK", Version = "1.0.0" }};var defaultConfig = new SseClientTransportOptions{Endpoint = new Uri($"http://localhost:5001/sse"),Name = "Magic.Services.MCPServer",};await using var client = await McpClientFactory.CreateAsync(new SseClientTransport(defaultConfig),defaultOptions);var result = await client.CallToolAsync("GetWeather", new Dictionary<string, object?>{{ "adcode",adcode}});return JsonHelper.JsonSerialize(result);
}

调用

我这里是在Web系统里进行的演示,所以以接口形式来调用插件,代码如下

public async Task<IActionResult> CallLocalServer(string adcode)
{_kernel.Plugins.AddFromType<LocalServer>("LocalServer", _serviceProvider);// 获取聊天完成服务var chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>();// 启用自动函数调用OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new(){ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,//FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()};PromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };ChatHistory chatHistory = [];chatHistory.AddSystemMessage($"你是一个天气预报员,本函数的入参会给你一个中国地理位置编码,你要调用合适的MCP Server来完成天气预报。");chatHistory.AddUserMessage($"查询地理编码为【{adcode}】的地点天气");var chatResult = await chatCompletionService.GetChatMessageContentAsync(chatHistory,openAIPromptExecutionSettings,_kernel);Console.Write($"\nAssistant : {chatResult}\n");return Json(chatResult);
}

验证

为了方便验证,可以先把接口的访问等级降低,直接通过URL地址访问,效果如下

控制台打印的信息如下

至此,我们已经在本地创建了一个MCPServer,并通过MCP Inspector进行了验证,同时又在原有使用SK的系统里,通过SK成功调用了这个Server提供的tool,接入复杂度可以接受,效果也非常不错。

接下来,再试试SK能不能成功调用高德地图的MCP Server

调用高德MCP Server

前置工作

注意,如果前面的天气接口你是使用的高德的服务,那么相信你已经注册了高德的key,如果没有,这里需要去注册一下。

编写服务

由于调用的是第三方的MCP,我们这里可以直接在接口或者服务类里编写调用代码

public async Task<IActionResult> CallGaodeServer(string msg)
{// 第一步:创建 mcp 客户端var defaultOptions = new McpClientOptions{ClientInfo = new() { Name = "地图规划", Version = "1.0.0" }};var defaultConfig = new SseClientTransportOptions{Endpoint = new Uri(ConfigurationHelper.GetSectionValue("GaodeMCP")),Name = "Magic.Services.MCPServer",};await using var client = await McpClientFactory.CreateAsync(new SseClientTransport(defaultConfig),defaultOptions);var tools = await client.ListToolsAsync();foreach (var tool in tools){Logger.Debug($"秀一下高德的能力之--- {tool.Name}");}#pragma warning disable SKEXP0001_kernel.Plugins.AddFromFunctions("gaodemap", tools.Select(aiFunction => aiFunction.AsKernelFunction()));#pragma warning restore SKEXP0001// 获取聊天完成服务var chatCompletionService = _kernel.GetRequiredService<IChatCompletionService>();// 启用自动函数调用OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new(){ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,};PromptExecutionSettings settings = new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };ChatHistory chatHistory = [];chatHistory.AddUserMessage(msg);var chatResult = await chatCompletionService.GetChatMessageContentAsync(chatHistory,openAIPromptExecutionSettings,_kernel);Console.Write($"\nAssistant : {chatResult}\n");return Json(chatResult);
}

验证

验证工作,还是和前面一样,暂时在浏览器里直接访问即可。

控制台打印的信息更友好一点

结束语

好了,至此,我们成功在原有的系统上,使用SK框架充当MCP Client的角色,完成了本地MCP Server的调用和第三方MCP Server的调用目标。

最后,再推荐几个介绍MCP的参考站点

  • 中文官网:https://mcp-docs.cn/
  • MCP.so:https://mcp.so/zh
  • 痴者工良的博客:https://www.cnblogs.com/whuanle/p/18837493
  • Semantic Kernel的学习文档:https://learn.microsoft.com/zh-cn/semantic-kernel/
  • 本人 之前写的两篇介绍SK的博客,本文的代码部分缺失很多初始化的代码,均在这两篇当中https://juejin.cn/post/7460393309552164902,https://juejin.cn/post/7463301527991762955

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/77112.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

识别图片内容OCR并重命名文件

在工作场景中&#xff0c;经常出现通过拍摄设备获取图片后&#xff0c;未及时进行有效命名的情况。这些图片中往往包含关键信息&#xff08;如合同编号、产品型号、日期等&#xff09;&#xff0c;需要人工识别并命名&#xff0c;存在以下痛点&#xff1a; 效率低下&#xff1…

【防火墙 pfsense】3 portal

&#xff08;1&#xff09;应该考虑的问题&#xff1a; ->HTTPS 连接的干扰问题&#xff1a;HTTPS 是一种旨在防止恶意第三方截取和篡改流量的协议。但强制门户的工作原理是截取并改变终端用户与网络之间的连接。这对于 HTTP 流量来说不是问题&#xff0c;但使用 HTTPS 加密…

银发科技:AI健康小屋如何破解老龄化困局

随着全球人口老龄化程度的不断加深&#xff0c;如何保障老年人的健康、提升他们的生活质量&#xff0c;成为了社会各界关注的焦点。 在这场应对老龄化挑战的战役中&#xff0c;智绅科技顺势而生&#xff0c;七彩喜智慧养老系统构筑居家养老安全网。 而AI健康小屋作为一项创新…

TCP协议理解

文章目录 TCP协议理解理论基础TCP首部结构图示字段逐项解析 TCP是面向连接&#xff08;Connection-Oriented&#xff09;面向连接的核心表现TCP 面向连接的核心特性TCP 与UDP对比 TCP是一个可靠的(reliable)序号与确认机制&#xff08;Sequencing & Acknowledgment&#xf…

什么是机器视觉3D碰撞检测?机器视觉3D碰撞检测是机器视觉3D智能系统中安全运行的核心技术之一

机器视觉3D碰撞检测是一种结合计算机视觉和三维空间分析的技术,旨在检测三维场景中物体之间是否发生碰撞(即物理接触或交叠)。它通过分析物体的形状、位置、运动轨迹等信息,预测或实时判断物体间的碰撞可能性。以下是其核心要点: 基本原理 三维感知:利用深度相机(如RGB-…

nacos设置权重进行负载均衡不生效

nacos设置权重进行负载均衡不生效&#xff0c;必须在启动类下加上这个bean Beanpublic IRule nacosRule(){return new NacosRule();}如下图所示

创建 Node.js Playwright 项目:从零开始搭建自动化测试环境

一、环境准备 在开始创建 Playwright 项目之前&#xff0c;确保你的电脑上已经安装了以下工具&#xff1a; Node.js&#xff1a;Playwright 依赖于 Node.js 环境&#xff0c;确保你已经安装了最新版本的 Node.js。可以通过以下命令检查是否安装成功&#xff1a; node -v npm -…

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(11): てあります。

日语学习-日语知识点小记-构建基础-JLPT-N4阶段&#xff08;11&#xff09;&#xff1a; てあります。 1、前言&#xff08;1&#xff09;情况说明&#xff08;2&#xff09;工程师的信仰 2、知识点&#xff08;1&#xff09;てあります。&#xff08;&#xff12;&#xff09;…

【金仓数据库征文】- 深耕国产数据库优化,筑牢用户体验新高度

目录 引言 一、性能优化&#xff1a;突破数据处理极限&#xff0c;提升运行效率 1.1 智能查询优化器&#xff1a;精准优化数据检索路径 1.2 并行处理技术&#xff1a;充分释放多核计算潜力 1.3 智能缓存机制&#xff1a;加速数据访问速度 二、稳定性提升&#xff1a;筑牢…

Java代理讲解

代理 代理模式是一种结构型设计模式&#xff0c;它允许我们通过添加一个代理对象来控制对另一个对象的访问。代理对象和实际对象具有相同的接口&#xff0c;使得客户端在不知情的情况下可以使用代理对象进行操作。代理对象在与客户端进行交互时&#xff0c;可以控制对实际对象…

利用deepseek快速生成甘特图

一、什么是甘特图 甘特图&#xff08;Gantt Chart&#xff09;是一种直观的项目管理工具&#xff0c;广泛应用于多个领域&#xff0c;主要用于​​时间规划、任务分配和进度跟踪​​。 直观性​​&#xff1a;时间轴清晰展示任务重叠或延迟。 ​​灵活性​​&#xff1a;支持…

从零开始学习SLAM|技术路线

概念 视觉SLAM&#xff08;Simultaneous Localization and Mapping&#xff09;系统中&#xff0c;整个过程通常分为 前端 和 后端 两个主要部分。前端处理的是从传感器数据&#xff08;如相机图像、激光雷达等&#xff09;中提取和处理信息&#xff0c;用于实时定位和建图&am…

LeetCode 解题思路 44(Hot 100)

解题思路&#xff1a; dp 数组的含义&#xff1a; 以 nums[i] 为结尾的最长递增子序列的长度。递推公式&#xff1a; dp[i] Math.max(dp[i], dp[j] 1)。dp 数组初始化&#xff1a; dp[i] 1。遍历顺序&#xff1a; 从小到大去遍历&#xff0c;从 i 1 开始&#xff0c;直到 …

精益数据分析(22/126):解锁创业增长密码与长漏斗分析

精益数据分析&#xff08;22/126&#xff09;&#xff1a;解锁创业增长密码与长漏斗分析 在创业与数据分析的探索旅程中&#xff0c;我们都在不断寻求新的知识和方法&#xff0c;以提升创业的成功率。我一直期望能和大家共同学习、共同进步&#xff0c;今天就让我们继续深入研…

大模型应用开发之LLM入门

一、大模型概述 1、大模型概念 LLM是指用有大量参数的大型预训练语言模型&#xff0c;在解决各种自然语言处理任务方面表现出强大的能力&#xff0c;甚至可以展现出一些小规模语言模型所不具备的特殊能力 2、语言模型language model 语言建模旨在对词序列的生成概率进行建模…

Vue 计算属性 VS 侦听器:从原理到性能的深度对比

在 Vue 开发中&#xff0c;computed&#xff08;计算属性&#xff09;和watch&#xff08;侦听器&#xff09;是响应式系统的两大核心工具。 它们看似都能处理数据变化&#xff0c;实则设计理念和应用场景大相径庭。 一、核心区别&#xff1a;数据驱动的两种范式 1. 触发机制…

特斯拉宣布启动自动驾驶网约车测试,无人出租车服务进入最后准备阶段

特斯拉公司于4月24日正式宣布&#xff0c;已在美国得克萨斯州奥斯汀和加利福尼亚州旧金山湾区启动自动驾驶网约车服务的员工内部测试。这项测试将为今年夏季计划推出的完全无人驾驶出租车服务进行最后的验证和准备。 此次测试使用约200辆经过特殊改装的Model 3车型&#xff0c;…

基于springboot的在线教育系统

一、系统架构 前端&#xff1a;vue | element-ui | html | jquery | css | ajax 后端&#xff1a;springboot | mybatis 环境&#xff1a;jdk1.8 | mysql | maven | nodejs | idea 二、代码及数据 三、功能介绍 01. web端-首页1 02. web端-首页2 03. w…

文档编辑:reStructuredText全面使用指南 — 第四部分 高级主题

文档编辑&#xff1a;reStructuredText全面使用指南 — 第四部分 高级主题 reStructuredText&#xff08;简称RST或ReST&#xff09;是一种轻量级的标记语言&#xff0c;用于编写结构化的文档。它由Python社区开发&#xff0c;并广泛应用于技术文档、书籍、博客文章等。reStruc…

git Http改用户下载

用原先别人账号,无权下更新 http方式设置自己账号 例如 git fetch --all 提示没有权限从 http://192.168.1.2/gitlab/项目路径.git下载 git remote set-url origin http://your-username192.168.1.2/gitlab/项目路径.git your-username修改成自己的git账号 需要输入一个Tok…