简述MCP的原理-AI时代的USB接口

1 简介

随着AI的不断发展,RAG(检索增强生成)和function calling等技术的出现,使得大语言模型的对话生成能力得到了增强。然而,function calling的实现逻辑比较复杂,一个简单的工具调用和实现方式需要针对不同的系统和大模型单独编写适配接口,十分复杂。

在此背景下,mcp应运而生,为当前业内AI高效可靠地调用外部工具实现了标准化。下面,我将带大家一起认识下mcp的基本原理和实现方式。

2 执行流程

在我们开始今天的正题之前,需要先了解下通常用户与大模型进行一次交互的执行流程:↓

图片

当用户问“北京今天天气怎么样”时,我们的程序会将用户的问题、以及预先识别到的工具列表包装成提示词发送给大模型。熟悉function calling原理的小伙伴们都知道,这时候大模型会基于预训练的function calling技术识别到想要调用的工具是什么,并将其结构化输出。我们的程序识别后再去调用对应的工具,最后将得到的结果和之前的上下文再次发送给大模型,得到最终的结果返回给用户。当然,如何让大模型选择要调用的工具不是本期的重点,这里不再赘述。

我们需要关注重点在于工具调用的这部分逻辑,在mcp没有诞生之前是这样子调用的:

图片

每次新增一个系统,都需要开发者单独做适配,即使tool的功能很简单,也会有极大的重复开发量。 在mcp出现后,调用方式发生了变化:

图片

系统与工具的调用方式实现了解耦,调用逻辑统一封装到了mcp client和 mcp server之间,这一步的交互方式由官方提供了不同开发语言的sdk,不再需要我们开发者处理了。

3 mcp架构

3.1 mcp架构设计

接下来让我们详细看下mcp的架构设计,mcp实现采用了标准的C/S架构模式。

图片

host:用于承载接受用户请求,与大模型交互,调用工具的一段程序。广义上我们可以将其看作是一个AI Agent。

client: 基于mcp规则实现的客户端,负责与mcp服务端进行通信。

server: 基于mcp规则实现的服务端,实现了工具内部的逻辑操作,并将执行结果返回给mcp客户端。

3.2 mcp基本功能

当下主流的与大模型交互的三要素无非是:工具、资源、提示词,而mcp针对这三类均做了标准化处理。 以下是几个重要的功能:

  • Resource:类似文件的数据,可以被客户端读取,如数据库数据或文件内容。

  • Tools:可以被大模型调用的函数。

  • prompt:预先编写的模板,帮助用户完成特定任务。

  • sampling:允许server主动通过client调用大模型获取数据进行采样。

4 mcp通信原理

4.1 JSON-RPC

MCP采用JSON-RPC作为底层的通信协议。JSON-RPC是一种基于JSON的轻量级远程调用协议,相较于HTTP来说它更加简洁、高效、容易处理。

请求结构体

{jsonrpc: "2.0",id: number | string,method: string,params?: object
}

响应结构体

{jsonrpc: "2.0",id: number | string,result?: object,error?: {code: number,message: string,data?: unknown}
}

在发起通信的源码中我们也可以看到确实使用到了json-rpc

 @Override
public <T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef) {String requestId = this.generateRequestId();return Mono.<McpSchema.JSONRPCResponse>create(sink -> {this.pendingResponses.put(requestId, sink);// 构建json-rpc请求McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, method,requestId, requestParams);// 发送请求this.transport.sendMessage(jsonrpcRequest).subscribe(v -> {}, error -> {this.pendingResponses.remove(requestId);sink.error(error);});}).timeout(this.requestTimeout).handle((jsonRpcResponse, sink) -> {// 省略异常处理});}

json-rpc与http的对比

属性

HTTP

JSON-RPC

本质

应用层协议(Web核心协议)

轻量级RPC协议(基于JSON格式)

数据格式

支持JSON/XML/二进制等多种格式

强制JSON格式,结构更简洁

协议功能

包含缓存/认证/状态码等完整功能

仅定义RPC调用规范(无底层逻辑)

通信模式

无状态,支持GET/POST等多方法

无状态,基于method字段调用

适用场景

Web API、浏览器交互、复杂业务

微服务内部调用、物联网等轻量场景

典型应用

RESTful接口、网页加载

服务间函数调用、嵌入式设备通信

4.2 通信方式

mcp基于以上通信协议,实现了以下通信方式:

STDIO

采用STDIO的方式,server端会在client端启动时,作为client端的子进程一起启动。这种方式适用于client和server在同一台机器上通信的场景,通常用于工具调试。 它的实现原理是client和server两个进程间通过stdin和stdout进行双向通信。

优点:

  • 无外部依赖

  • 进程间通信极快

  • 脱机可用

缺点

  • 并发能力差,是同步阻塞模型

  • 不支持多进程通信

SSE

全名是server send event,是一种基于服务端到客户端的流式传输方式,同时客户端向服务端通信采用http的方式进行传输。一般用于client在本地,server在远程服务器的场景。

图片

具体执行流程如下:

  • 客户端会向服务端的/sse端点发送http请求,服务端会返回sessionID等信息建立sse连接。

  • 初始化连接完成后,客户端会向服务端请求tools/list接口获取所有的tool列表,用于之后发送给大模型。

  • 在工具调用时,客户端会将调用信息如methodargs通过post请求调用tools/call接口发送给服务端处理,服务端通过sse连接通知客户端结果。

从本质上看,sse是一种异步非阻塞的通信模型,极大的提高了agent的吞吐能力,但其服务器和客户端需要做长连接容易连接中断,会丢失上下文。而官方在今年又推出了一项通信方式的更新,使用streamable http替代sse解决了以上的问题。

5 生命周期

以下是mcp的生命周期:

图片

在mcp client和mcp server建立连接后,client会立即向server请求获取可用的工具列表,这里也体现了mcp工具的动态可插拔性。 接下来我将用Spring AI带大家一起了解下mcp client的调用流程。

我们需要引入Spring AI的maven依赖,以及对spring AI对Mcp的依赖。

5.1 环境搭建

我们需要在server端向外暴露一个工具。

    /** 构建根据城市获取天气的tool* @param city 城市名称* @return 天气信息*/@Tool(name = "getWeather", description = "根据城市获取天气")public String getWeather(String city) {return new String((city).getBytes(), StandardCharsets.UTF_8) + " 天气为晴天 25℃";}

SpringAi会将标有@Tool注解的方法自动注入到ToolCallbackProvider中。 在client端,我们需要配置下mcp server的地址。

spring:ai:mcp:client:sse:connections:server1: # sse服务端url: http://127.0.0.1:8080

写一个demo来模拟用户询问大模型的流程。

    @Beanpublic CommandLineRunner callToolByLLM(ChatClient.Builder chatClientBuilder,ToolCallbackProvider toolCallbackProvider,ConfigurableApplicationContext context) {return args -> {System.out.println("基于spring-ai,llm调用方法------");Gson gson = new Gson();// 模拟用户输入的信息,并把工具列表传给LLMString userInput = "获取北京的天气";System.out.println("用户问: " + userInput);var chatClient = chatClientBuilder.defaultUser("获取北京的天气").defaultTools(toolCallbackProvider).build();// 包装请求LLMString content = chatClient.prompt(userInput).call().content();System.out.println("AI回答: " + gson.toJson(content));// 结束会话context.close();};}

5.2 建立连接获取可用工具列表

当程序启动后,spring会自动注入McpClient和ToolCallbackProvider,此时会向server端发送请求获取所有可用的工具列表。

public class SyncMcpToolCallbackProvider implements ToolCallbackProvider {@Overridepublic ToolCallback[] getToolCallbacks() {var toolCallbacks = new ArrayList<>();this.mcpClients.stream().forEach(mcpClient -> {// mcpClient.listTools()toolCallbacks.addAll(mcpClient.listTools().tools().stream().filter(tool -> toolFilter.test(mcpClient, tool)).map(tool -> new SyncMcpToolCallback(mcpClient, tool)).toList());});var array = toolCallbacks.toArray(new ToolCallback[0]);validateToolCallbacks(array);return array;}
}

mcpClient会用json-rpc的格式调用tools/list方法,获取当前server下所有可用的工具列表。

public Mono<McpSchema.ListToolsResult> listTools(String cursor) {return this.withInitializationCheck("listing tools", initializedResult -> {if (this.serverCapabilities.tools() == null) {return Mono.error(new McpError("Server does not provide tools capability"));}return this.mcpSession.sendRequest(McpSchema.METHOD_TOOLS_LIST, new McpSchema.PaginatedRequest(cursor),LIST_TOOLS_RESULT_TYPE_REF);});
}

5.3 调用工具

当用户询问"北京今天天气怎么样"时,程序会将上述获取到的所有工具和用户的信息生成提示词告诉大模型,大模型选择一个合适的工具告诉程序去调用工具。

    public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatResponse) {// 构建提示词、工具ChatCompletionRequest request = createRequest(prompt, false);// 构建要调用的大模型信息ChatModelObservationContext observationContext = ChatModelObservationContext.builder().prompt(prompt).provider(OpenAiApiConstants.PROVIDER_NAME).requestOptions(prompt.getOptions()).build();ChatResponse response = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,this.observationRegistry).observe(() -> {// post请求大模型ApiResponseEntity<ChatCompletion> completionEntity = this.retryTemplate.execute(ctx -> this.openAiApi.chatCompletionEntity(request, getAdditionalHttpHeaders(prompt)));// 解析结果省略步骤 ...return chatResponse;});// 判断是否是工具调用if (toolExecutionEligibilityPredicate.isToolExecutionRequired(prompt.getOptions(), response)) {var toolExecutionResult = this.toolCallingManager.executeToolCalls(prompt, response);// 判断是否返回结果if (toolExecutionResult.returnDirect()) {// Return tool execution result directly to the client.return ChatResponse.builder().from(response).generations(ToolExecutionResult.buildGenerations(toolExecutionResult)).build();}else {// 带着工具结果直接调用returnthis.internalCall(new Prompt(toolExecutionResult.conversationHistory(), prompt.getOptions()),response);}}return response;}

这里我们对大模型返回的结果进行抓包,可以看到大模型想要调用的方法信息

[{"assistantMessage": {"toolCalls": [{"id": "call_b4a9cb0f04a3495d941b71","type": "function","name": "spring_ai_mcp_client_server1_getWeather","arguments": "{\"city\": \"北京\"}"}],// 中间内容省略..."chatGenerationMetadata": {"metadata": {},"finishReason": "TOOL_CALLS","contentFilters": []}}
]

mcpClient执行调用逻辑。

 public Mono<McpSchema.CallToolResult> callTool(McpSchema.CallToolRequest callToolRequest) {return this.withInitializationCheck("calling tools", initializedResult -> {if (this.serverCapabilities.tools() == null) {return Mono.error(new McpError("Server does not provide tools capability"));}return this.mcpSession.sendRequest(McpSchema.METHOD_TOOLS_CALL, callToolRequest, CALL_TOOL_RESULT_TYPE_REF);});}

执行完成后,程序会携带结果和上下文再次请求大模型获取结果,直到大模型认为可以结束了,会将最终的结果返回给用户。 此次请求的执行结果如下:

图片

6 总结

本文介绍了mcp的基本底层原理,mcp作为AI大模型时代的标准化交互协议,具备显著的优势。对于开发者来说mcp的出现降低了功能集成的成本,有更大的发展前景。但mcp当下也有很多不可回避的缺点,比如频繁与大模型交互,为了保证消息连贯上下文内容剧增,token消耗大,使用成本变高。另外在安全性方面不够健全,对于提示词注入等手段没有成熟的解决方案。

尽管mcp当前不是那么的完美无缺,但他的出现给AI的发展提供了一种全新的交互模式和更多的可能。

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

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

相关文章

CISSP知识点汇总-资产安全

CISSP知识点汇总 域1---安全与风险管理域2---资产安全域3---安全工程域4---通信与网络安全域5---访问控制域6---安全评估与测试域7---安全运营域8---应用安全开发域2 资产安全 一、资产识别和分类 1、信息分级(Classification): 按照敏感程度(机密性被破坏) 按照重要程度…

Spring Boot 3.x 整合 Swagger(springdoc-openapi)实现接口文档

本文介绍 Spring Boot 3.x 如何使用 springdoc-openapi 实现 Swagger 接口文档&#xff0c;包括版本兼容表、最简单的配置示例和常见错误解决方案。1. Spring Boot 3.x 和 springdoc-openapi 版本对应表Spring Boot 版本Spring Framework 版本推荐的 springdoc-openapi 版本3.0…

Redis内存队列Stream

本文为个人学习笔记整理&#xff0c;仅供交流参考&#xff0c;非专业教学资料&#xff0c;内容请自行甄别 文章目录概述一、生产者端操作二、消费者端操作三、消费组操作四、状态查询操作五、确认消息六、消息队列的选择概述 Stream是Redis5.0推出的支持多播的可持久化的消息队…

Minio安装配置,桶权限设置,nginx代理 https minio

**起因&#xff1a;因为用到ruoyi-vue-plus框架中遇到生产环境是https&#xff0c;但是http的minio上传的文件不能在后台系统中访问**安装配置minio1. 下载安装2. 赋文件执行权限3.创建配置文件4.创建minio.service新版minio创建桶需要配置桶权限1.下载客户端2.设置访问权限3.连…

数论基础知识和模板

质数筛 用于快速处理 1&#xff5e;n 中所有素数的算法 因为依次遍历判断每一个数是否质数太慢&#xff0c;所以把一些明显不能质数的筛出来 普通筛法&#xff0c;对于每个整数&#xff0c;删除掉其倍数。 bool vis[N];//0表示是质数 int pri[N],o; //质数表 void get(int n…

Ubuntu20.04.6桌面版系统盘制作与安装

概述 本教程讲述Ubuntu20.04.6桌面版的系统U盘制作与安装&#xff0c;所需工具为一台电脑、大于4G的U盘、一个需要安装Ubuntu系统的主机。 步骤1&#xff1a;下载系统镜像与rufus 在ubuntu官网下载 ubuntu-20.04.6-desktop-amd64.iso&#xff0c;如图 下载rufus工具&#xf…

【C++复习3】类和对象

1.3.1.简述一下什么是面向对象回答&#xff1a;1. 面向对象是一种编程思想&#xff0c;把一切东西看成是一个个对象&#xff0c;比如人、耳机、鼠标、水杯等&#xff0c;他们各 自都有属性&#xff0c;比如&#xff1a;耳机是白色的&#xff0c;鼠标是黑色的&#xff0c;水杯是…

数据结构之二叉平衡树

系列文章目录 数据结构之ArrayList_arraylist o(1) o(n)-CSDN博客 数据结构之LinkedList-CSDN博客 数据结构之栈_栈有什么方法-CSDN博客 数据结构之队列-CSDN博客 数据结构之二叉树-CSDN博客 数据结构之优先级队列-CSDN博客 常见的排序方法-CSDN博客 数据结构之Map和Se…

Maven引入第三方JAR包实战指南

要将第三方提供的 JAR 包引入本地 Maven 仓库&#xff0c;可通过以下步骤实现&#xff08;以 Oracle JDBC 驱动为例&#xff09;&#xff1a;&#x1f527; 方法 1&#xff1a;使用 install:install-file 命令&#xff08;推荐&#xff09;定位 JAR 文件 将第三方 JAR 包&#…

JavaSE -- 泛型详细介绍

泛型 简介 集合存储数据底层是利用 Object 来接收的&#xff0c;意思是说如果不对类型加以限制&#xff0c;所有数据类型柔和在一起&#xff0c;这时如何保证数据的安全性呢&#xff08;如果不限制存入的数据类型&#xff0c;任何数据都能存入&#xff0c;当我们取出数据进行强…

使用 Python 实现 ETL 流程:从文本文件提取到数据处理的全面指南

文章大纲&#xff1a; 引言&#xff1a;什么是 ETL 以及其重要性 ETL&#xff08;提取-转换-加载&#xff09;是数据处理领域中的核心概念&#xff0c;代表了从源数据到目标系统的三个关键步骤&#xff1a;**提取&#xff08;Extract&#xff09;**数据、**转换&#xff08;Tra…

selenium基础知识 和 模拟登录selenium版本

前言 selenium框架是Python用于控制浏览器的技术,在Python爬虫获取页面源代码的时候,是最重要的技术之一,通过控制浏览器,更加灵活便捷的获取浏览器中网页的源代码。 还没有安装启动selenium的同志请先看我的上一篇文章进行配置启动 和 XPath基础 对selenium进行浏览器和驱动…

JS 网页全自动翻译v3.17发布,全面接入 GiteeAI 大模型翻译及自动部署

两行 js 实现 html 全自动翻译。 无需改动页面、无语言配置文件、无 API Key、对 SEO 友好&#xff01; 升级说明 translate.service 深度绑定 GiteeAI 作为公有云翻译大模型算力支持translate.service 增加shell一键部署后通过访问自助完成GiteeAI的开通及整个接入流程。增加…

数据结构:数组:插入操作(Insert)与删除操作(Delete)

目录 插入操作&#xff08;Inserting in an Array&#xff09; 在纸上模拟你会怎么做&#xff1f; 代码实现 复杂度分析 删除操作&#xff08;Deleting from an Array&#xff09; 在纸上模拟一下怎么做&#xff1f; 代码实现 复杂度分析 插入操作&#xff08;Inserti…

Qt之修改纯色图片的颜色

这里以修改QMenu图标颜色为例,效果如下: MyMenu.h #ifndef MYMENU_H #define MYMENU_H#include <QMenu>class MyMenu : public QMenu { public:explicit MyMenu(QWidget *parent = nullptr);protected:void mouseMoveEvent(QMouseEvent *event) override; };#endif /…

uni-app实现单选,多选也能搜索,勾选,选择,回显

前往插件市场安装插件下拉搜索选择框 - DCloud 插件市场&#xff0c;该插件示例代码有vue2和vue3代码 是支持微信小程序和app的 示例代码&#xff1a; <template><view><!-- 基础用法 --><cuihai-select-search:options"options"v-model&quo…

【机器学习深度学习】 微调的十种形式全解析

目录 一、为什么要微调&#xff1f; 二、微调的 10 种主流方式 ✅ 1. 全参数微调&#xff08;Full Fine-tuning&#xff09; ✅ 2. 冻结部分层微调&#xff08;Partial Fine-tuning&#xff09; ✅ 3. 参数高效微调&#xff08;PEFT&#xff09; &#x1f538; 3.1 LoRA&…

信刻光盘安全隔离与文件单向导入/导出系统

北京英特信网络科技有限公司成立于2005年&#xff0c;是专业的数据光盘摆渡、刻录分发及光盘存储备份领域的科技企业&#xff0c;专注为军队、军工、司法、保密等行业提供数据光盘安全摆渡、跨网交换、档案归档检测等专业解决方案。 公司立足信创产业&#xff0c;产品国产安全可…

Python-标准库-os

1 需求 2 接口 3 示例 4 参考资料 在 Python 中&#xff0c;os&#xff08;Operating System&#xff09;模块是一个非常重要的内置标准库&#xff0c;提供了许多与操作系统进行交互的函数和方法&#xff0c;允许开发者在 Python 程序中执行常见的操作系统任务&#xff0c;像文…

OpenCV CUDA模块设备层-----在 GPU 上执行类似于 std::copy 的操作函数warpCopy()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 OpenCV 的 CUDA 模块&#xff08;cudev&#xff09; 中的一个设备端内联模板函数&#xff0c;用于在 GPU 上执行类似于 std::copy 的操作&#xff…