簡述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
英文地址,請注明出處:http://en.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…