C#实现MCP Client 与 LLM 连接,抓取网页内容功能!

笔记哥 / 05-26 / 23点赞 / 0评论 / 182阅读
# 一、添加依赖库 目前来说,绝大部分的大模型的API,都是遵循OpenAI的接口规范。 Microsoft.Extensions.AI 是微软官方提供的一套 统一的 AI 抽象层 ,大大简化 AI 模型在 .NET 应用中的集成。 添加依赖库:Microsoft.Extensions.AI.OpenAI,版本为:最新预发行版 9.4.4-preview.1.25259.16**,添加的时候记得勾选:包括预发行版。** ![图片](https://cdn.res.knowhub.vip/c/2505/28/d9f1a1e3.png?G1EAAGQ9PS9a1Zb6Tju6JYaEZoAhiaBSzXq99%2b7TAL7fCYnjM%2ftYfj783sdysFyqASEJCkImTUjMolaCVjbTnOKeDg%3d%3d) 添加依赖库:Microsoft.Extensions.AI,版本为:9.4.4-preview.1.25259.16。 ![图片](https://cdn.res.knowhub.vip/c/2505/28/035804fe.png?G1IAAMTsdJxI8pKi26hD2jvFHc0AQxJBpZr1es9Z%2byb6fgdD4zNan74%2f%2fN76dKopl0pgGBtCxyUMVYPkULRaYgHiGg4%3d) # 二、OpenAI 客户端实现 新增文件:**ChatAIClient** ![图片](https://cdn.res.knowhub.vip/c/2505/28/3472c864.png?G1IAAMTsdJxIEq%2bh26hD2jvFHc0AQxJBpZr1es9Z%2byb6fldWxGe0Pn1%2f%2bL316VSTlUrKmjkjdL2EFYAVDgDAJlLiGg4%3d) ## 2.1 初始化OpenAI客户端 * * * 初始化OpenAI客户端,并使用UseFunctionInvocation 来增强客户端, 这里启用函数调用。 **备注:以下代码涉及的秘钥,记得替换为自己的。** ```csharp using Microsoft.Extensions.AI; using OpenAI; using System.ClientModel; namespace MCPClient {     ///      /// 表示一个用于与 AI 聊天模型交互的客户端封装类。     /// 负责初始化聊天客户端并维护对话上下文。     ///      public class ChatAIClient     {         ///          /// 封装后的 AI 聊天客户端接口,支持函数调用等功能。         ///          private IChatClient ChatClient;         ///          /// 存储当前会话中的所有聊天消息记录。         ///          private IList Messages;         ///          /// API 访问密钥,用于身份认证。【记得替换为自己的】         ///          private const string _apiKey = "6092598c-ce00-48fd-a5be-0d758088c888";         ///          /// AI 服务的基础请求地址。【记得替换为自己的】         ///          private const string _baseURL = "https://api-inference.modelscope.cn/v1/";         ///          /// 使用的 AI 模型标识符。【记得替换为自己的】         ///          private const string _modelID = "Qwen/Qwen2.5-72B-Instruct";         ///          /// 初始化一个新的  实例。         /// 构造函数中自动完成聊天客户端的初始化配置。         ///          public ChatAIClient()         {             InitIChatClient();         }         ///          /// 初始化内部使用的 AI 聊天客户端实例。         /// 配置 API 凭证、服务端点,并构建具备函数调用能力的客户端。         /// 同时初始化系统消息作为对话起点。         ///          private void InitIChatClient()         {             // 创建 API 密钥凭证             ApiKeyCredential apiKeyCredential = new ApiKeyCredential(_apiKey);             // 设置 OpenAI 客户端选项,如自定义服务端点             OpenAIClientOptions openAIClientOptions = new OpenAIClientOptions();             openAIClientOptions.Endpoint = new Uri(_baseURL);             // 创建 OpenAI 客户端并获取指定模型的聊天接口             var openaiClient = new OpenAIClient(apiKeyCredential, openAIClientOptions)                 .GetChatClient(_modelID)                 .AsIChatClient();             // 构建增强功能的聊天客户端(例如启用函数调用)             ChatClient = new ChatClientBuilder(openaiClient)                 .UseFunctionInvocation()                 .Build();             // 初始化对话历史,包含一条系统提示信息             Messages =             [                 // 添加系统角色消息                 new(ChatRole.System, "您是一位乐于助人的助手,帮助我们测试MCP服务器功能,优先使用中文回答!"),             ];         }     } } ``` ## 2.2 处理用户的自然语言查询 在**ChatAIClient文件,添加如下代码,实现与 AI 模型交互,并传入 MCP 工具。** ```csharp ///  /// 异步处理用户的自然语言查询,并与 AI 模型进行交互,支持 MCP 工具调用。 ///  /// 用户的自然语言查询内容 /// 可用的 MCP 工具列表,用于扩展 AI 的外部能力 /// AI 返回的最终文本响应结果 public async Task ProcessQueryAsync(string query, IList tools) {     // 如果消息历史为空,则初始化系统提示消息     if (Messages.Count == 0)     {         Messages =          [             new(ChatRole.System, "您是一位乐于助人的助手,帮助我们测试MCP服务器功能,优先使用中文回答!")         ];     }     // 添加用户输入的消息到对话历史     Messages.Add(new(ChatRole.User, query));     // 设置请求选项,注入可用工具     var options = new ChatOptions     {         Tools = [.. tools]     };     // 调用 AI 客户端获取响应     var response = await ChatClient.GetResponseAsync(Messages, options);     // 将 AI 响应加入对话历史     Messages.AddMessages(response);     // 输出调用的工具信息     OutputToolUsageInfo(response);     // 返回模型生成的文本响应     return response.Text; } ``` ## 2.3 MCP 工具使用情况日志 * * * 在**ChatAIClient文件,添加如下代码,输出 AI 调用MCP 工具的情况。** ```csharp  ///      /// 辅助方法:输出 AI 在响应中调用的工具信息到控制台。     ///      /// 来自 AI 的完整响应对象     private void OutputToolUsageInfo(ChatResponse response)     {         // 获取所有 Tool 角色的消息         var toolUseMessages = response.Messages.Where(m => m.Role == ChatRole.Tool).ToList();         // 判断是否调用了工具         // 获取响应中所有角色为 Tool 的消息(即 AI 调用了哪些工具)         var toolUseMessage = response.Messages.Where(m => m.Role == ChatRole.Tool);         // 判断第一条消息的内容是否多于一个(通常第一个消息是用户问题,第二个是调用函数)         if (response.Messages[0].Contents.Count > 1)         {             // 尝试从第一条消息的第二个内容项提取出函数调用信息             var functionCall = (FunctionCallContent)response.Messages[0].Contents[1];             // 设置控制台输出颜色为绿色,用于突出显示工具调用信息             Console.ForegroundColor = ConsoleColor.Green;             string arguments = "";             // 如果函数调用包含参数,则拼接参数信息             if (functionCall.Arguments != null)             {                 foreach (var arg in functionCall.Arguments)                 {                     arguments += $"{arg.Key}:{arg.Value};";                 }                 // 输出调用的方法名及参数信息                 Console.WriteLine($"调用方法名:{functionCall.Name};参数信息:{arguments}");                 // 遍历所有 Tool 消息,输出每个工具调用的结果                 foreach (var message in toolUseMessage)                 {                     // 提取工具调用后的执行结果                     var functionResultContent = (FunctionResultContent)message.Contents[0];                     Console.WriteLine($"调用工具结果:{functionResultContent.Result}");                 }                 // 恢复控制台默认颜色(白色)                 Console.ForegroundColor = ConsoleColor.White;             }             else             {                 // 如果没有参数                 Console.WriteLine("工具参数为空");             }         }         else         {             Console.ForegroundColor = ConsoleColor.Green;             Console.WriteLine("本次没有调用工具");             Console.ForegroundColor = ConsoleColor.White;         }     } } ``` # 三、为LLM**添加工具能力** 在前面课程基础之上,在Program.cs添加代码。 ![图片](https://cdn.res.knowhub.vip/c/2505/28/a2053b9a.png?G1IAAMTsdJxI%2bhKk26hD2jvFHc0AQxJBpZr1es9Z%2byb6fgdD4jNan74%2f%2fN76dLJUqhEYyorQkS%2bGiHK1gAxlaElxDQc%3d) **代码说明:**为LLM添加工具能力,并处理客户提交的内容。 ```csharp // 创建聊天客户端实例 ChatAIClient chatAIClient = new ChatAIClient(); // 进入主循环,持续接收用户输入直到输入 "exit" while (true) {     try     {         // 设置控制台文字颜色为黄色,提示用户输入问题         Console.ForegroundColor = ConsoleColor.Yellow;         Console.Write("\n提问: ");         // 读取用户输入并去除前后空格,若为空则赋默认空字符串         string query = Console.ReadLine()?.Trim() ?? string.Empty;         // 判断用户是否输入 "exit" 以退出程序         if (query.ToLower() == "exit")         {             break;         }         // 调用异步方法处理用户查询,并传入预定义的工具列表(listToolsResult)         string response = await chatAIClient.ProcessQueryAsync(query, listToolsResult);         // 设置输出颜色为黄色,显示 AI 的响应内容         Console.ForegroundColor = ConsoleColor.Yellow;         Console.WriteLine($"AI:{response}");         // 恢复控制台默认颜色(白色)         Console.ForegroundColor = ConsoleColor.White;     }     catch (Exception ex)     {         // 捕获所有异常并输出错误信息,防止程序崩溃         Console.WriteLine($"\nError: {ex.Message}");     } } ``` # 四、测试效果 启动项目,并输入以下内容: ```csharp 抓取 https://blog.csdn.net/daremeself/article/details/147166987 的内容,并markdown格式输出 ``` 调用MCP Server的工具的情况日志。 ![图片](https://cdn.res.knowhub.vip/c/2505/28/c2cfb8d5.png?G1MAAMTW3Dgp8CFF22gDdWfqnTYDDGkElWrW6917rpvo%2bwMMzU%2bvbcT68HttI8iP83ICo3BBCjBhqBY3ThA1MxfWPHsA) AI响应的结果: ![图片](https://cdn.res.knowhub.vip/c/2505/28/8252434a.png?G1IAAMTsdJxIfGik26hD2jvFHc0AQxJBpZr1es9Z%2byb6fgcjx2e0Pn1%2f%2bL316WSi1QiMwgWh40qMnIuyhapVTBI0ruE%3d) 好了,今天就分享到这边! 下一个课程:**实现自己的MCP Server。** **文中示例代码:** **https://pan.quark.cn/s/b5b8853200f9**