大模型開發 - 基于Spring AI 借助MCP Client 通過STDIO和SSE協議調用MCP Server (上)

文章目錄

  • 概述
  • MCP協議:為AI應用連接外部世界的橋梁
  • MCP Server:上下文與能力的提供者
  • 基于Spring AI 1.0.0的開發之路
    • 1. 使用Spring AI構建MCP客戶端
    • 2. 使用Spring AI構建MCP服務器
  • Mcp Client 實戰
    • 整體架構概覽
    • 技術棧
    • Code
      • pom
      • 配置mcp servers(sse&stdio)
        • amap-maps 服務配置
        • filesystem 服務配置
        • 工作原理
      • ChatClient核心模塊
      • 工具回調機制
    • 典型應用場景代碼示例
      • 基礎對話場景
      • 工具增強對話場景
      • 流式響應場景
      • MCP Server調用
        • 調用高德 MCP Server (stdio)
        • 調用server-filesystem MCP Server (stdio)
        • 調用自己寫的服務(sse)
    • 性能優化建議
      • 緩存策略
      • 異步處理優化
    • 最佳實踐建議
      • 架構設計最佳實踐
      • 性能優化最佳實踐
      • 安全性最佳實踐

在這里插入圖片描述

概述

自2024年11月由Anthropic公司發布以來,模型上下文協議(Model Context Protocol, MCP)作為一項開放標準,正迅速成為人工智能領域,特別是大型語言模型(LLM)應用開發中的一個重要基石。它旨在為LLM應用與外部數據源和工具之間提供一個標準化的“通用接口”,從而打破模型僅能依賴其內部訓練數據的局?pad?,使其能夠獲取和利用實時、動態的外部世界信息。

接下來我們將深入介紹MCP協議的核心概念,重點解析其服務器(MCP Server)的角色與功能,并進一步探討如何利用日趨成熟的Java AI開發框架——Spring AI 1.0.0,來構建支持MCP的應用。


MCP協議:為AI應用連接外部世界的橋梁

MCP協議的核心思想是,在LLM應用(客戶端)和提供上下文信息、工具或數據的服務(服務器)之間建立一個統一的通信規范。這極大地簡化了集成過程,開發者不再需要為每一個數據源或API編寫定制化的連接代碼,而是可以遵循一個通用的協議標準。

MCP協議的主要構成與特點:

  • 開放標準與協作: 由Anthropic發起,并得到了包括OpenAI、Google DeepMind和微軟在內的行業巨頭支持,保證了其廣泛的適用性和未來的發展潛力。
  • 基于成熟技術: MCP在設計上借鑒了語言服務器協議(Language Server Protocol, LSP),并采用JSON-RPC 2.0作為其消息傳輸格式,可通過stdio、HTTP(支持Server-Sent Events)等多種方式進行傳輸。
  • 核心概念:
    • 主機(Host): 指的是LLM應用程序本身,它負責發起與MCP服務器的連接。
    • 客戶端(Client): 位于主機應用內部,是實現MCP通信的具體連接器。
    • 服務器(Server): 負責提供上下文信息,是外部數據和能力的提供者。
  • 安全與隱私: 協議在設計上強調了用戶授權和控制,要求在訪問用戶數據或執行操作前必須獲得明確的用戶同意,并提供了相應的安全指導原則。

MCP Server:上下文與能力的提供者

在MCP生態中,MCP Server扮演著至關重要的角色。它是一個遵循MCP規范的服務,其主要職責是向MCP客戶端(即LLM應用)暴露其擁有的資源和能力。

MCP Server提供的核心能力:

  1. 資源(Resources): 服務器可以提供類似文件的只讀數據。這可以是本地文件系統的文件、數據庫查詢結果、API響應內容等。LLM應用可以通過標準化的請求來讀取這些資源,從而獲得回答問題或執行任務所需的背景信息。

  2. 工具(Tools): 這是MCP Server最強大的功能之一。服務器可以定義一系列可供LLM調用的函數或方法。例如,一個MCP Server可以提供“發送郵件”、“查詢訂單狀態”、“執行代碼”等工具。當LLM在其推理過程中判斷需要使用某個工具時,它會向服務器發起一個工具調用請求,服務器執行相應的功能后將結果返回給LLM。這極大地擴展了LLM的應用場景,使其從一個“對話者”轉變為一個“行動者”。

  3. 提示(Prompts): 服務器還可以提供預定義的、可復用的提示模板。這些模板可以包含參數,允許LLM應用動態地生成更復雜、更具針對性的指令。這有助于提高與模型交互的效率和一致性。

MCP Server的實現與部署:

MCP Server的實現是多樣化的。開發者可以使用官方或社區提供的SDK(目前已有Go, C#, Rust, TypeScript等版本)來構建自己的服務器。根據部署需求,服務器可以:

  • 以子進程(stdio)形式運行: 適用于本地開發或桌面應用集成,服務器與主應用在同一臺機器上運行。
  • 以遠程服務(HTTP/SSE)形式運行: 這是更常見的部署方式,服務器作為一個獨立的Web服務運行,可通過URL被多個客戶端訪問。

微軟的Copilot Studio等產品已經集成了對MCP的支持,允許用戶直接連接到外部的MCP服務器,并將其提供的工具無縫地集成到自己的AI助手中。

基于Spring AI 1.0.0的開發之路

Spring AI作為Spring生態系統的一部分,旨在簡化Java開發者構建AI應用的復雜性。它提供了一套高級API,用于與各種大型語言模型進行交互,并集成了向量數據庫、ETL框架等常用工具。

盡管在當前時間點(2025年8月),Spring AI與MCP協議之間尚未出現官方的、開箱即用的直接集成,但這并不妨礙我們利用Spring AI的強大功能來開發一個符合MCP規范的應用。開發者可以扮演MCP客戶端MCP服務器的角色。

1. 使用Spring AI構建MCP客戶端

當你的Java應用需要連接一個已有的MCP服務器時,你可以利用Spring AI的核心能力來消費MCP服務。

開發步驟概覽:

  1. 理解MCP通信: 首先需要熟悉MCP的JSON-RPC 2.0消息格式和通信流程。你需要知道如何構建初始化請求、工具調用請求等。

  2. HTTP客戶端實現: 使用Spring框架中強大的WebClientRestClient來與遠程的MCP Server(通常是HTTP服務)進行通信。你需要手動構建符合MCP規范的JSON請求體,并解析服務器返回的JSON響應。

  3. 集成Spring AI ChatClient 你的應用核心部分可能仍在使用Spring AI的ChatClient與LLM進行交互。當ChatClient返回的AssistantMessage表明需要調用一個外部工具時,你的代碼需要:

    • 解析出需要調用的工具名稱和參數。
    • 判斷該工具是否由某個已連接的MCP Server提供。
    • 通過前述的HTTP客戶端,向對應的MCP Server發起工具調用請求。
    • 將MCP Server返回的結果包裝成Spring AI的ToolResponseMessage,再次提交給ChatClient,讓LLM根據工具執行結果繼續生成回應。

2. 使用Spring AI構建MCP服務器

當你想將你的Java應用所擁有的數據或能力通過MCP協議暴露出去時,你可以構建一個MCP Server。

開發步驟概覽:

  1. 創建Spring Boot應用: 使用Spring Boot快速搭建一個Web服務,這將是你的MCP Server的基礎。

  2. 定義MCP的API端點: 根據MCP規范,創建處理JSON-RPC請求的Controller。你需要實現如initializetool/run等核心端點。

  3. 利用Spring AI實現工具功能: 這是將Spring AI與MCP Server結合的關鍵。你的MCP Server所暴露的“工具”,其內部邏輯可以完全由Spring AI來驅動。

    • 示例:創建一個“智能文檔問答”工具
      • 工具定義: 在你的MCP Server中,你會向客戶端聲明一個名為askDocument的工具,它接受一個question作為參數。
      • 內部實現: 當收到tool/run請求調用askDocument時,你的Spring Boot應用會:
        1. 使用Spring AI的VectorStore接口,將用戶的question進行向量化,并在你預先加載好的文檔向量庫中進行相似性搜索,檢索出最相關的文檔片段。
        2. 將這些相關的文檔片段作為上下文,連同用戶的原始問題,一起構建成一個更豐富的提示(Prompt)。
        3. 調用Spring AI的ChatClient,將這個豐富的提示發送給LLM(如GPT-4, Claude 3等)。
        4. 將LLM返回的答案作為askDocument工具的執行結果,通過MCP的響應格式返回給客戶端。

通過這種方式,你將Spring AI在文檔檢索、與LLM交互等方面的能力,封裝成了一個符合MCP標準的、可被任何MCP客戶端調用的強大工具。


Mcp Client 實戰

整體架構概覽

MCP-Client框架采用分層架構設計,主要包含以下核心組件:

┌─────────────────────────────────────────────────────────────┐
│                    Web Layer (控制層)                        │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │  ChatController │  │  其他Controller  │                   │
│  └─────────────────┘  └─────────────────┘                   │
└─────────────────────────────────────────────────────────────┘│
┌─────────────────────────────────────────────────────────────┐
│                   Service Layer (服務層)                     │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │   ChatClient    │  │ ToolCallbacks   │                   │
│  └─────────────────┘  └─────────────────┘                   │
└─────────────────────────────────────────────────────────────┘│
┌─────────────────────────────────────────────────────────────┐
│                 Integration Layer (集成層)                   │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │   Spring AI     │  │   MCP Protocol  │                   │
│  └─────────────────┘  └─────────────────┘                   │
└─────────────────────────────────────────────────────────────┘│
┌─────────────────────────────────────────────────────────────┐
│                  Utility Layer (工具層)                      │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │  MarkdownUtil   │  │  其他工具類      │                   │
│  └─────────────────┘  └─────────────────┘                   │
└─────────────────────────────────────────────────────────────┘

技術棧

  • Spring Boot 3.x:提供基礎框架支持和自動配置
  • Spring AI:集成AI模型和工具調用能力
  • MCP Protocol:實現模型上下文協議標準

Code

在這里插入圖片描述

pom

父工程bom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>spring-ai-mcp-deepseek</artifactId><packaging>pom</packaging><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.3</version><relativePath/> <!-- lookup parent from repository --></parent><modules><module>mcp-server</module><module>mcp-client</module><module>mcp-tool-client</module></modules><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><spring.ai.version>1.0.0</spring.ai.version><spring.boot.version>3.4.3</spring.boot.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring.ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies>
</project>

mcp-client 子模塊pom

在這里插入圖片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>spring-ai-mcp-deepseek</artifactId><groupId>com.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>mcp-client</artifactId><dependencies><!-- spring-web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- model openai --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency><!-- 添加 Spring AI MCP client 依賴 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client</artifactId></dependency><dependency><groupId>org.commonmark</groupId><artifactId>commonmark</artifactId><version>0.21.0</version></dependency><dependency><groupId>org.commonmark</groupId><artifactId>commonmark-ext-gfm-tables</artifactId><version>0.21.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

配置mcp servers(sse&stdio)

application.properties

server.port=8082spring.ai.openai.api-key=sk-xxxxxxx
spring.ai.openai.base-url=https://api.deepseek.com
spring.ai.openai.chat.options.model=deepseek-chatspring.ai.mcp.client.enabled=truespring.ai.mcp.client.sse.connections.server1.url=http://localhost:8085spring.ai.mcp.client.stdio.servers-configuration=classpath:mcp-servers-config.jsonlogging.level.org.springframework.ai.model.tool=DEBUG
logging.level.io.modelcontextprotocol=DEBUG

配置項詳解

spring.ai.openai.api-key=sk-xxxxxx

這是Spring AI OpenAI模塊的配置項,用于設置訪問DeepSeek API所需的API密鑰。這個密鑰用于身份驗證,允許應用調用DeepSeek的AI模型服務。

spring.ai.openai.base-url=https://api.deepseek.com

這個配置項指定了OpenAI兼容API的基礎URL。由于項目使用的是DeepSeek API而不是真正的OpenAI API,所以需要將基礎URL設置為DeepSeek的API端點。

spring.ai.openai.chat.options.model=deepseek-chat

此配置項指定要使用的具體AI模型。在這里,使用的是DeepSeek的"deepseek-chat"模型,這是DeepSeek的主要對話模型。

# 啟用 MCP client
spring.ai.mcp.client.enabled=true

這個配置項用于啟用或禁用MCP(Model Context Protocol)客戶端功能。設置為true表示啟用MCP客戶端,允許應用與MCP服務器進行交互。

# 配置 MCP 服務端連接地址
spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8085

這個配置項指定了通過SSE(Server-Sent Events)方式連接的MCP服務器的URL。在這里,MCP客戶端將連接到本地8085端口的MCP服務器。

# 配置 stdio 方式啟動的 MCP 服務
spring.ai.mcp.client.stdio.servers-configuration=classpath:mcp-servers-config.json

這個配置項指定了通過標準輸入輸出(stdio)方式啟動的MCP服務的配置文件位置。該配置文件定義了需要啟動的MCP服務及其參數,例如文件系統服務或高德地圖服務。

# 設置日志級別以便觀察工具調用
logging.level.org.springframework.ai.model.tool=DEBUG
logging.level.io.modelcontextprotocol=DEBUG

這兩個配置項設置了特定包的日志級別為DEBUG,以便更詳細地觀察工具調用過程:

  • logging.level.org.springframework.ai.model.tool=DEBUG:啟用Spring AI工具調用的調試日志
  • logging.level.io.modelcontextprotocol=DEBUG:啟用MCP協議相關組件的調試日志

這些配置項共同構成了MCP客戶端應用的完整配置,使應用能夠連接到DeepSeek AI服務和MCP服務器,并提供詳細的調試信息以觀察工具調用過程。


mcp-servers-config.json

{"mcpServers": {"amap-maps": {"command": "D:\\Program Files\\nodejs\\npx.cmd","args": ["-y","@amap/amap-maps-mcp-server"],"env": {"AMAP_MAPS_API_KEY": "xxxxxx"}},"filesystem": {"command": "D:\\Program Files\\nodejs\\npx.cmd","args": ["-y","@modelcontextprotocol/server-filesystem","D:\\tmp"]}}
}

配置項詳解

amap-maps 服務配置
"amap-maps": {"command": "D:\\Program Files\\nodejs\\npx.cmd","args": ["-y","@amap/amap-maps-mcp-server"],"env": {"AMAP_MAPS_API_KEY": "eb2e8e2d3e2d24188cc28ec46903b331"}
}

高德地圖(AMAP)MCP 服務的配置:

  • "amap-maps": 服務的標識符名稱,用于在系統中唯一標識這個服務
  • command: 指定啟動服務的命令,這里是使用 Node.js 的 npx 命令
  • args: 命令行參數數組,用于傳遞給 command 命令
    • -y: 表示自動確認安裝依賴
    • @amap/amap-maps-mcp-server: 指定要運行的 npm 包
  • env: 環境變量配置
    • AMAP_MAPS_API_KEY: 高德地圖 API 密鑰,用于訪問高德地圖服務

當系統需要使用高德地圖功能時,會通過這個配置啟動相應的 MCP 服務。

KEY申請,請參考: https://lbs.amap.com/api/mcp-server/create-project-and-key
在這里插入圖片描述

filesystem 服務配置
"filesystem": {"command": "D:\\Program Files\\nodejs\\npx.cmd","args": ["-y","@modelcontextprotocol/server-filesystem","D:\\tmp"]
}

這是一個文件系統 MCP 服務的配置:

  • "filesystem": 服務的標識符名稱
  • command : 同樣使用 Node.js 的 npx 命令啟動服務
  • args : 命令行參數數組
    • -y: 自動確認安裝依賴
    • @modelcontextprotocol/server-filesystem: 指定要運行的文件系統服務 npm 包
    • D:\tmp: 指定文件系統服務可以訪問的目錄路徑

這個服務允許 AI 模型訪問本地文件系統,例如讀取、寫入或瀏覽指定目錄中的文件。

工作原理

這些配置定義了可以通過標準輸入輸出(stdio)方式啟動的 MCP 服務。當 AI 模型需要使用特定功能(如訪問地圖信息或文件系統)時,系統會根據這些配置啟動相應的服務進程,并通過標準輸入輸出與之通信。

每個服務都是獨立的進程,通過 MCP 協議與主應用通信,這樣可以實現功能的模塊化和擴展性。當不再需要這些服務時,系統會自動終止相關進程以節省資源。


ChatClient核心模塊

ChatClient是框架的核心組件,負責AI對話的處理和工具調用的協調。

@RestController
public class ChatController {private ChatClient chatClient;public ChatController(ChatClient.Builder chatClientBuilder,ToolCallbackProvider toolCallbackProvider) {this.chatClient = chatClientBuilder.defaultSystem("作為MCP小助手,請根據具體需求智能調用最合適的MCP工具組合來優化回答效果。" +"要求能夠自動識別任務類型,精準匹配工具鏈,并在響應中保持專業性與實用性的平衡。" +"請確保輸出結果既符合技術規范又具備良好的用戶體驗,同時支持多輪交互中的上下文連貫處理。")// 注冊工具方法.defaultToolCallbacks(toolCallbackProvider).build();}}

實現原理分析:

  1. 構建器模式:使用ChatClient.Builder實現靈活的客戶端配置
  2. 系統提示注入:通過defaultSystem()方法設置AI助手的角色定位
  3. 工具回調注冊:defaultToolCallbacks()注冊MCP工具回調處理器
  4. 鏈式調用:支持流暢的API調用鏈

工具回調機制

工具回調機制是MCP-Client的核心特性,實現AI與外部工具的無縫集成:

// 工具調用流程
String content = chatClient.prompt().user(message)           // 用戶輸入.call()                  // 執行AI推理.content();              // 獲取響應內容

執行流程:

用戶輸入 → AI模型分析 → 識別工具需求 → 調用MCP工具 → 整合結果 → 返回響應↓           ↓            ↓            ↓           ↓          ↓message → ChatClient → ToolCallback → MCP Server → Result → content

典型應用場景代碼示例

基礎對話場景

 package com.example.client.controller;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.ResponseEntity;@RestController
public class BasicChatExample {private final ChatClient chatClient;public BasicChatExample(ChatClient.Builder chatClientBuilder) {this.chatClient = chatClientBuilder.build();}@GetMapping("/api/simple-chat")public ResponseEntity<String> simpleChat(@RequestParam String question) {try {String response = chatClient.prompt().user(question).call().content();return ResponseEntity.ok(response);} catch (Exception e) {return ResponseEntity.status(500).body("處理請求時發生錯誤: " + e.getMessage());}}
}

在這里插入圖片描述

工具增強對話場景

package com.example.client.controller;import com.example.client.domain.ChatRequest;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestController
public class ToolEnhancedChatExample {private final ChatClient toolEnabledClient;public ToolEnhancedChatExample(ChatClient.Builder builder,ToolCallbackProvider toolProvider) {this.toolEnabledClient = builder.defaultSystem("作為MCP小助手,請根據具體需求智能調用最合適的MCP工具組合來優化回答效果。" +"要求能夠自動識別任務類型,精準匹配工具鏈,并在響應中保持專業性與實用性的平衡。" +"請確保輸出結果既符合技術規范又具備良好的用戶體驗,同時支持多輪交互中的上下文連貫處理。").defaultToolCallbacks(toolProvider).build();}@PostMapping("/api/tool-chat")public Map<String, Object> toolChat(@RequestBody ChatRequest request) {long startTime = System.currentTimeMillis();String response = toolEnabledClient.prompt().user(request.getMessage()).call().content();long duration = System.currentTimeMillis() - startTime;return Map.of("response", response,"duration", duration,"timestamp", System.currentTimeMillis());}
}

在這里插入圖片描述

日志輸出

2025-08-12T19:43:55.811+08:00 DEBUG 12292 --- [nio-8082-exec-4] o.s.a.m.tool.DefaultToolCallingManager   : Executing tool call: spring_ai_mcp_client_amap_maps_maps_weather
2025-08-12T19:43:56.030+08:00 DEBUG 12292 --- [pool-7-thread-1] io.modelcontextprotocol.spec.McpSchema   : Received JSON message: {"result":{"content":[{"type":"text","text":"{\n  \"city\": \"北京市\",\n  \"forecasts\": [\n    {\n      \"date\": \"2025-08-12\",\n      \"week\": \"2\",\n      \"dayweather\": \"中雨\",\n      \"nightweather\": \"陰\",\n      \"daytemp\": \"30\",\n      \"nighttemp\": \"22\",\n      \"daywind\": \"西南\",\n      \"nightwind\": \"西南\",\n      \"daypower\": \"1-3\",\n      \"nightpower\": \"1-3\",\n      \"daytemp_float\": \"30.0\",\n      \"nighttemp_float\": \"22.0\"\n    },\n    {\n      \"date\": \"2025-08-13\",\n      \"week\": \"3\",\n      \"dayweather\": \"雷陣雨\",\n      \"nightweather\": \"雷陣雨\",\n      \"daytemp\": \"29\",\n      \"nighttemp\": \"22\",\n      \"daywind\": \"東\",\n      \"nightwind\": \"東\",\n      \"daypower\": \"1-3\",\n      \"nightpower\": \"1-3\",\n      \"daytemp_float\": \"29.0\",\n      \"nighttemp_float\": \"22.0\"\n    },\n    {\n      \"date\": \"2025-08-14\",\n      \"week\": \"4\",\n      \"dayweather\": \"陰\",\n      \"nightweather\": \"雷陣雨\",\n      \"daytemp\": \"31\",\n      \"nighttemp\": \"23\",\n      \"daywind\": \"南\",\n      \"nightwind\": \"南\",\n      \"daypower\": \"1-3\",\n      \"nightpower\": \"1-3\",\n      \"daytemp_float\": \"31.0\",\n      \"nighttemp_float\": \"23.0\"\n    },\n    {\n      \"date\": \"2025-08-15\",\n      \"week\": \"5\",\n      \"dayweather\": \"雷陣雨\",\n      \"nightweather\": \"雷陣雨\",\n      \"daytemp\": \"31\",\n      \"nighttemp\": \"24\",\n      \"daywind\": \"南\",\n      \"nightwind\": \"南\",\n      \"daypower\": \"1-3\",\n      \"nightpower\": \"1-3\",\n      \"daytemp_float\": \"31.0\",\n      \"nighttemp_float\": \"24.0\"\n    }\n  ]\n}"}],"isError":false},"jsonrpc":"2.0","id":"2b682735-4"}
2025-08-12T19:43:56.031+08:00 DEBUG 12292 --- [pool-7-thread-1] i.m.spec.McpClientSession                : Received Response: JSONRPCResponse[jsonrpc=2.0, id=2b682735-4, result={content=[{type=text, text={"city": "北京市","forecasts": [{"date": "2025-08-12","week": "2","dayweather": "中雨","nightweather": "陰","daytemp": "30","nighttemp": "22","daywind": "西南","nightwind": "西南","daypower": "1-3","nightpower": "1-3","daytemp_float": "30.0","nighttemp_float": "22.0"},{"date": "2025-08-13","week": "3","dayweather": "雷陣雨","nightweather": "雷陣雨","daytemp": "29","nighttemp": "22","daywind": "東","nightwind": "東","daypower": "1-3","nightpower": "1-3","daytemp_float": "29.0","nighttemp_float": "22.0"},{"date": "2025-08-14","week": "4","dayweather": "陰","nightweather": "雷陣雨","daytemp": "31","nighttemp": "23","daywind": "南","nightwind": "南","daypower": "1-3","nightpower": "1-3","daytemp_float": "31.0","nighttemp_float": "23.0"},{"date": "2025-08-15","week": "5","dayweather": "雷陣雨","nightweather": "雷陣雨","daytemp": "31","nighttemp": "24","daywind": "南","nightwind": "南","daypower": "1-3","nightpower": "1-3","daytemp_float": "31.0","nighttemp_float": "24.0"}]
}}], isError=false}, error=null]

流式響應場景

package com.example.client.controller;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;@RestController
public class StreamingChatExample {private final ChatClient chatClient;public StreamingChatExample(ChatClient.Builder chatClientBuilder) {this.chatClient = chatClientBuilder.build();}@GetMapping(value = "/api/stream-chat",  produces = "text/html;charset=UTF-8")public Flux<String> streamChat(@RequestParam String message) {return chatClient.prompt().user(message).stream().content().map(chunk -> "data: " + chunk + "\n\n");}@GetMapping(value = "/api/stream-chat-2", produces = "text/html;charset=UTF-8")public Flux<String> streamChat() {return chatClient.prompt().user("你是誰").stream().content();}
}

MCP Server調用

package com.example.client.controller;import com.example.client.domain.ChatRequest;
import com.example.client.util.MarkdownUtil;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;import java.util.Map;/*** 聊天控制器,處理AI聊天請求*/
@RestController
public class ChatController {private ChatClient chatClient;public ChatController(ChatClient.Builder chatClientBuilder,ToolCallbackProvider toolCallbackProvider) {this.chatClient = chatClientBuilder.defaultSystem("作為MCP小助手,請根據具體需求智能調用最合適的MCP工具組合來優化回答效果。" +"要求能夠自動識別任務類型,精準匹配工具鏈,并在響應中保持專業性與實用性的平衡。" +"請確保輸出結果既符合技術規范又具備良好的用戶體驗,同時支持多輪交互中的上下文連貫處理。")// 注冊工具方法.defaultToolCallbacks(toolCallbackProvider).build();}@RequestMapping(value = "/chat", produces = MediaType.TEXT_HTML_VALUE)public String chatPage(@RequestParam String message) {System.out.println(">>> Q: " + message);// 記錄開始時間long startTime = System.currentTimeMillis();// 使用API調用聊天String content = chatClient.prompt().user(message).call().content();// 計算AI響應耗時long aiResponseTime = System.currentTimeMillis();long aiDuration = aiResponseTime - startTime;System.out.println(">>> A:" + content);// Markdown轉換耗時統計long markdownStartTime = System.currentTimeMillis();String htmlPage = MarkdownUtil.toHtmlPage(content);long markdownEndTime = System.currentTimeMillis();long markdownDuration = markdownEndTime - markdownStartTime;long totalDuration = markdownEndTime - startTime;System.out.println(">>> 耗時統計 - AI響應: " + aiDuration + "ms, Markdown轉換: " + markdownDuration + "ms, 總計: " + totalDuration + "ms");return htmlPage;}}
調用高德 MCP Server (stdio)

在這里插入圖片描述

在這里插入圖片描述

調用server-filesystem MCP Server (stdio)

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

調用自己寫的服務(sse)
spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8085

在這里插入圖片描述

在這里插入圖片描述

2025-08-12T21:05:32.615+08:00  WARN 56008 --- [nio-8082-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MissingServletRequestParameterException: Required request parameter 'message' for method parameter type String is not present]
>>> Q: 有多少種書
2025-08-12T21:06:15.405+08:00 DEBUG 56008 --- [nio-8082-exec-1] o.s.a.m.tool.DefaultToolCallingManager   : Executing tool call: spring_ai_mcp_client_server1_getBookCategories
2025-08-12T21:06:15.623+08:00 DEBUG 56008 --- [ient-1-Worker-8] io.modelcontextprotocol.spec.McpSchema   : Received JSON message: {"jsonrpc":"2.0","id":"b016e092-6","result":{"content":[{"type":"text","text":"[\"Web服務器\",\"容器技術\",\"數據庫\",\"架構設計\",\"編程\",\"計算機科學\"]"}],"isError":false}}
2025-08-12T21:06:15.630+08:00 DEBUG 56008 --- [ient-1-Worker-8] i.m.spec.McpClientSession                : Received Response: JSONRPCResponse[jsonrpc=2.0, id=b016e092-6, result={content=[{type=text, text=["Web服務器","容器技術","數據庫","架構設計","編程","計算機科學"]}], isError=false}, error=null]
>>> A:目前系統中有以下6種圖書分類:1. Web服務器
2. 容器技術
3. 數據庫
4. 架構設計
5. 編程
6. 計算機科學如果需要查詢某一分類下的圖書,可以告訴我具體分類名稱。
>>> 耗時統計 - AI響應: 12955ms, Markdown轉換: 5ms, 總計: 12960ms

性能優化建議

緩存策略

@Service
@EnableCaching
public class CachedChatService {@Cacheable(value = "chatResponses", key = "#message.hashCode()")public String getCachedResponse(String message) {return chatClient.prompt().user(message).call().content();}@CacheEvict(value = "chatResponses", allEntries = true)@Scheduled(fixedRate = 3600000) // 1小時清理一次public void clearCache() {// 定期清理緩存}
}

異步處理優化

@Service
public class AsyncChatService {@Async("chatExecutor")public CompletableFuture<String> processAsync(String message) {String response = chatClient.prompt().user(message).call().content();return CompletableFuture.completedFuture(response);}@Bean("chatExecutor")public TaskExecutor chatExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(4);executor.setMaxPoolSize(8);executor.setQueueCapacity(100);executor.setThreadNamePrefix("chat-");executor.initialize();return executor;}
}

最佳實踐建議

架構設計最佳實踐

  1. 分層清晰:嚴格按照控制層、服務層、集成層的分層架構設計
  2. 接口抽象:為核心組件定義清晰的接口,便于測試和擴展
  3. 配置外部化:所有可變參數通過配置文件管理
  4. 異常處理:建立統一的異常處理機制

性能優化最佳實踐

  1. 連接復用:使用連接池管理HTTP連接
  2. 緩存策略:對頻繁訪問的數據實施緩存
  3. 異步處理:對耗時操作采用異步處理
  4. 資源管理:及時釋放不再使用的資源

安全性最佳實踐

  1. 輸入驗證:對所有用戶輸入進行嚴格驗證
  2. 輸出轉義:防止XSS攻擊
  3. API密鑰管理:使用環境變量管理敏感信息
  4. 訪問控制:實施適當的訪問控制機制

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/93095.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/93095.shtml
英文地址,請注明出處:http://en.pswp.cn/web/93095.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

分析三個文件--啟動文件、鏈接文件、map文件

目錄 啟動文件 鏈接文件 部分map文件內容 FLASH物理地址(0x08000000開始)的映射關系 0x08000000 之前地址空間 啟動文件 ;******************** (C) COPYRIGHT 2016 STMicroelectronics ******************** ;* File Name : startup_stm32f40_41xxx.s ;* Author…

從零開始學Python之數據結構(字符串以及數字)

一、字符串 1.1 怎么定義字符串 字符串是Python最常用的數據結構之一。在 Python 里是用于處理文本數據的&#xff0c;比如存儲姓名、文章內容等文本信息 。 定義方式&#xff1a; 單引號&#xff1a;用單引號 包裹文本&#xff0c;如 name Alice &#xff0c;單引號內可…

Navicat 全量增量數據庫遷移

在使用 Navicat 進行數據庫遷移時&#xff0c;除了常見的“全量遷移”&#xff08;一次性遷移所有數據和結構&#xff09;&#xff0c;有時還需要支持 增量遷移&#xff08;只遷移新增或修改的數據&#xff09;。下面我將詳細講解如何通過 Navicat 實現&#xff1a;&#x1f50…

css初學者第五天

<1>css的三大特性1.1 層疊性相同選擇器給設置相同的樣式&#xff0c;此時一個樣式就會覆蓋&#xff08;層疊&#xff09;另一份沖突的樣式。層疊式主要解決樣式沖突的問題。層疊性原則&#xff1a;-樣式沖突&#xff0c;遵循的原則是就近原則&#xff0c;哪個樣式離結構近…

從神經網絡語言模型(NNLM)到Word2Vec:自然語言處理中的詞向量學習

語言模型 語言(人說的話)模型(完成某個任務) 任務: 概率評估任務:在兩句話中&#xff0c;判斷哪句話出現的概率大(哪句話在自然語言中更合理)生成任務:預測詞語,我明天要____ 統計語言模型 用統計的方法解決上述的兩個任務 核心思想 給定一個詞序列&#xff0c;計算該序列出現的…

PID學習筆記5-雙環PID

在學習江協科技PID課程時&#xff0c;做一些筆記&#xff0c;對應視頻3-1&#xff0c;對應代碼&#xff1a;1313-雙環PID定速定位置控制-代碼封裝main.c:#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLE…

C#vb.net中Interlocked類實現原子操作加減計算,涵蓋狀態切換、計數控制等常見場景

以下是 C# 中使用 int 類型結合 Interlocked 類實現原子操作的完整示例&#xff0c;涵蓋狀態切換、計數控制等常見場景&#xff1a; 完整代碼示例csharp using System; using System.Threading;/// <summary> /// 基于整數類型的原子操作工具類&#xff08;線程安全&am…

RCL 2025 | LLM采樣機制的新視角:來自處方性偏移的解釋

1. 導讀 大型語言模型&#xff08;Large Language Models, LLMs&#xff09;在自主決策場景中的應用日益廣泛&#xff0c;它們需要在龐大的行動空間中進行響應采樣&#xff08;response sampling&#xff09;。然而&#xff0c;驅動這一采樣過程的啟發式機制仍缺乏深入研究。本…

08 ABP Framework Blazor UI

ABP Framework Blazor UI 架構 overview ABP Blazor UI 系統構建在 Blazorise 組件庫之上&#xff0c;為構建數據驅動應用提供結構化方法&#xff0c;包含 CRUD 操作、主題和本地化的一致模式。 #mermaid-svg-QAvWlELsLhZgYXHu {font-family:"trebuchet ms",verdana,…

JUC學習筆記-----LinkedBlockingQueueConcurrentLinkedQueueCopyOnWriteArrayList

LinkedBlockingQueue基本的入隊出隊初始化public class LinkedBlockingQueue<E> extends AbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable {// 靜態內部類 Node&#xff0c;用于存儲隊列元素及維護節點間關系static class Node<E>…

小杰python高級(six day)——pandas庫

1.數據可視化用于繪制 DataFrame 數據圖形&#xff0c;它允許用戶直接從 DataFrame 創建各種類型的圖表&#xff0c;而不需要使用其他繪圖庫&#xff08;底層實際上使用了 Matplotlib&#xff09;。&#xff08;1&#xff09;plotDataFrame.plot(*args, **kwargs)功能&#xff…

第十六屆藍橋杯青少組C++省賽[2025.8.9]第二部分編程題(1 、慶典隊列)

參考程序&#xff1a;#include <iostream> using namespace std;int main() {int n, A;cin >> n >> A; // 輸入&#xff1a;n 和 A&#xff0c;用空格隔開cout << n / A; // 整數相除&#xff0c;自動向下取整return 0; }

C++進階:智能指針

目錄1. RAII與智能指針2. C庫中的智能指針2.1 智能指針auto_ptr2.2 智能指針unique_ptr2.3 智能指針shared_ptr3. shared_ptr的循環引用4. 智能指針的定值刪除器1. RAII與智能指針 上一篇文章學習了異常相關的知識&#xff0c;其中遺留了一個異常安全相關的問題。那就是異常的拋…

Tkinter 實現按鈕鼠標懸浮提示:兩種方案(繼承Frame與不繼承)

在 Tkinter 桌面應用開發中&#xff0c;為按鈕添加“鼠標懸浮提示”是提升用戶體驗的常用功能——無需點擊&#xff0c;只需將鼠標挪到按鈕上方&#xff0c;就能自動顯示按鈕功能說明。本文將詳細介紹兩種實現方案&#xff1a;不繼承 Frame 類&#xff08;快速簡潔版&#xff0…

20250814 最小生成樹總結

引子 啊啊額&#xff0c;從一張圖里抽出幾條邊&#xff0c;組成一棵樹&#xff0c;無環n?1n-1n?1條邊&#xff0c;就是生成樹。那么邊權和最小的生成樹就叫最小生成樹&#xff0c;最大生成樹同理。 kruskal最小生成樹 要求kruskal最小生成樹&#xff0c;我們首先用結構體數組…

數據大集網:實體店獲客引流的數字化引擎,解鎖精準拓客新密碼?

?在實體店面臨流量焦慮、獲客成本攀升的當下&#xff0c;實體店獲客引流工具的重要性愈發凸顯。如何在激烈的市場競爭中精準觸達目標客戶、構建可持續的客流增長模式&#xff1f;數據大集網憑借其創新的智能獲客體系與全鏈路服務能力&#xff0c;正成為萬千實體店突破增長瓶頸…

nginx --ssl證書生成mkcert

github https://github.com/FiloSottile/mkcert/releases網盤下載地址 https://pan.baidu.com/s/1XI0879pqu7HXZMnmQ9ztaw 提取碼: 1111windows使用示例

守拙以致遠:個人IP的長青之道|創客匠人

2025年被認為是AI應用全面爆發的一年。各種人工智能工具在寫作、制圖、剪輯等領域廣泛使用&#xff0c;大大提升了個人和團隊的工作效率。對于個人IP而言&#xff0c;這類工具的出現確實帶來了新的機會&#xff0c;但也伴隨著一種現象——一些人開始過度依賴甚至神化AI&#xf…

USB 3.0 LTSSM 狀態機

USB2.0在電源供應后&#xff0c;通過Pull Up D-來決定枚舉LS&#xff0c;Pull Up D有一個USB高速握手過程&#xff0c;來決定HS FS。USB3.0則會通過鏈路訓練&#xff08;Link Training&#xff09;&#xff0c;來準備USB3.0通信。每當我們插上USB線的時候&#xff0c;對于3.0的…

MySQL窗口函數與PyMySQL以及SQL注入

MySQL窗口函數與PyMySQL實戰指南&#xff1a;從基礎到安全編程 引言 在數據處理和分析領域&#xff0c;MySQL作為最流行的關系型數據庫之一&#xff0c;其窗口函數功能為數據分析提供了強大的支持。同時&#xff0c;Python作為數據分析的主要語言&#xff0c;通過PyMySQL庫與My…