Spring Boot 整合 Spring AI 與 MCP 開發智能體工具指南

Spring Boot 整合 Spring AI 與 MCP 開發智能體工具指南

一、引言

隨著大語言模型(LLM)的普及,越來越多的開發者希望將其集成到自己的應用中。Spring AI 作為 Spring 生態下的 AI 集成框架,提供了便捷的方式來對接各種大模型。而 MCP(Model Context Protocol) 則是 Spring AI 中用于擴展模型能力的重要機制,允許我們通過自定義工具(Tool)增強模型的功能。

本文將詳細介紹如何在 Spring Boot 項目中整合 Spring AI 和 MCP,開發自定義工具,并通過一個完整的示例展示整個流程。

二、環境準備

首先創建一個 Spring Boot 項目,添加以下依賴:

<dependencies><!-- Spring Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring AI --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-core</artifactId><version>1.0.0-M6</version></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai</artifactId><version>1.0.0-M6</version></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-server-webmvc</artifactId><version>1.0.0-M6</version></dependency><!-- 工具庫 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

application.properties 中配置 OpenAI API 密鑰:

spring.ai.openai.api-key=your-openai-api-key
spring.ai.openai.model=gpt-3.5-turbo
三、理解工具(Tool)概念

在 Spring AI 中,工具是一個可以被大模型調用的功能單元。工具通常對應一個具體的函數或方法,用于執行特定的任務,如查詢數據庫、調用外部 API、執行計算等。

工具的核心特點:

  • 有明確的功能和輸入輸出
  • 可以被大模型理解和調用
  • 可以組合使用,形成復雜的工作流
四、開發自定義工具

下面通過一個完整的例子,展示如何開發自定義工具。我們將創建一個簡單的智能助手,支持數學計算、天氣查詢和翻譯功能。
先給出整體流程圖:
流程圖

1. 數學計算工具
package com.example.aiagent.tools;import org.springframework.ai.tool.Tool;
import org.springframework.stereotype.Component;@Component
public class CalculatorTool {@Tool(name = "calculator",description = "執行基本的數學計算,支持加(+), 減(-), 乘(*), 除(/)運算",parameters = {@ToolParam(name = "expression", description = "數學表達式,如 '2+3'", type = "string")})public String calculate(String expression) {try {// 簡單的表達式解析if (expression.contains("+")) {String[] parts = expression.split("\\+");double a = Double.parseDouble(parts[0].trim());double b = Double.parseDouble(parts[1].trim());return String.valueOf(a + b);} else if (expression.contains("-")) {String[] parts = expression.split("-");double a = Double.parseDouble(parts[0].trim());double b = Double.parseDouble(parts[1].trim());return String.valueOf(a - b);} else if (expression.contains("*")) {String[] parts = expression.split("\\*");double a = Double.parseDouble(parts[0].trim());double b = Double.parseDouble(parts[1].trim());return String.valueOf(a * b);} else if (expression.contains("/")) {String[] parts = expression.split("/");double a = Double.parseDouble(parts[0].trim());double b = Double.parseDouble(parts[1].trim());return String.valueOf(a / b);} else {return "不支持的表達式: " + expression;}} catch (Exception e) {return "計算錯誤: " + e.getMessage();}}
}
2. 天氣查詢工具
package com.example.aiagent.tools;import org.springframework.ai.tool.Tool;
import org.springframework.stereotype.Component;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;@Component
public class WeatherTool {private static final String API_KEY = "your-weather-api-key";private static final String API_URL = "https://api.weatherapi.com/v1/current.json";@Tool(name = "weather",description = "查詢指定城市的當前天氣",parameters = {@ToolParam(name = "city", description = "城市名稱,如 '北京'", type = "string")})public String getWeather(String city) {try {URL url = new URL(API_URL + "?key=" + API_KEY + "&q=" + city);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET");BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));StringBuilder response = new StringBuilder();String line;while ((line = reader.readLine()) != null) {response.append(line);}reader.close();return response.toString();} catch (Exception e) {return "獲取天氣失敗: " + e.getMessage();}}
}
3. 翻譯工具
package com.example.aiagent.tools;import org.springframework.ai.tool.Tool;
import org.springframework.stereotype.Component;@Component
public class TranslationTool {@Tool(name = "translator",description = "將文本從一種語言翻譯成另一種語言",parameters = {@ToolParam(name = "text", description = "需要翻譯的文本", type = "string"),@ToolParam(name = "targetLanguage", description = "目標語言代碼,如 'en' 表示英語,'zh' 表示中文", type = "string")})public String translate(String text, String targetLanguage) {try {// 這里使用簡單的模擬,實際應用中可以調用翻譯APIif ("en".equalsIgnoreCase(targetLanguage)) {return "This is a simulated translation result for: " + text;} else if ("zh".equalsIgnoreCase(targetLanguage)) {return "這是一個模擬的翻譯結果,原文是: " + text;} else {return "暫不支持的語言: " + targetLanguage;}} catch (Exception e) {return "翻譯失敗: " + e.getMessage();}}
}
五、自動掃描與注冊工具

Spring AI 提供了便捷的方式來自動掃描和注冊所有帶有 @Tool 注解的工具類,無需手動逐個注冊。
注冊工具

package com.example.aiagent.config;import org.springframework.ai.mcp.server.tool.MethodToolCallbackProvider;
import org.springframework.ai.mcp.server.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.ai.tool.Tool;@Configuration
// 自動掃描指定包下所有帶有 @Tool 注解的類
@ComponentScan(basePackages = "com.example.aiagent.tools",includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Tool.class)
)
public class ToolAutoScanConfig {@Beanpublic ToolCallbackProvider toolCallbackProvider(Object[] toolBeans) {// 自動注冊所有掃描到的工具return MethodToolCallbackProvider.builder().toolObjects(toolBeans).build();}
}
六、配置AI客戶端與動態工具描述

動態生成系統提示,自動包含所有已注冊工具的信息:

package com.example.aiagent.config;import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.mcp.server.tool.ToolCallback;
import org.springframework.ai.mcp.server.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AIClientConfig {@Beanpublic ChatClient chatClient(ChatClient.Builder builder, ToolCallbackProvider toolCallbackProvider) {// 動態生成系統提示,包含所有可用工具信息String systemPrompt = generateSystemPrompt(toolCallbackProvider);return builder.defaultSystem(systemPrompt).defaultTools(toolCallbackProvider).build();}private String generateSystemPrompt(ToolCallbackProvider toolCallbackProvider) {StringBuilder sb = new StringBuilder();sb.append("你是一個智能助手,可以使用以下工具來回答用戶問題:\n\n");// 動態添加所有工具描述toolCallbackProvider.getToolCallbacks().forEach(callback -> {sb.append("- ").append(callback.getName()).append(": ").append(callback.getDescription()).append("\n");});sb.append("\n如果你需要使用工具,請按照以下格式返回:\n");sb.append("[TOOL_CALL]\n");sb.append("{\n");sb.append("    \"name\": \"工具名稱\",\n");sb.append("    \"parameters\": {\n");sb.append("        \"參數名\": \"參數值\"\n");sb.append("    }\n");sb.append("}\n");sb.append("[/TOOL_CALL]\n\n");sb.append("如果不需要工具,直接回答用戶問題。");return sb.toString();}
}
七、智能工具調用處理器

創建一個服務來處理模型生成的工具調用:
工具調用處理器

package com.example.aiagent.service;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.mcp.server.tool.ToolCallback;
import org.springframework.ai.mcp.server.tool.ToolCallbackRegistry;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;@Service
public class ToolInvocationService {private static final Pattern TOOL_CALL_PATTERN = Pattern.compile("\\[TOOL_CALL\\](.*?)\\[/TOOL_CALL\\]", Pattern.DOTALL);private final ToolCallbackRegistry toolCallbackRegistry;private final ObjectMapper objectMapper;public ToolInvocationService(ToolCallbackRegistry toolCallbackRegistry, ObjectMapper objectMapper) {this.toolCallbackRegistry = toolCallbackRegistry;this.objectMapper = objectMapper;}/*** 檢查并處理工具調用*/public String processToolCalls(String modelResponse) {Matcher matcher = TOOL_CALL_PATTERN.matcher(modelResponse);while (matcher.find()) {String toolCallJson = matcher.group(1).trim();try {// 解析工具調用JSONMap<String, Object> toolCall = objectMapper.readValue(toolCallJson, HashMap.class);String toolName = (String) toolCall.get("name");Map<String, Object> parameters = (Map<String, Object>) toolCall.get("parameters");// 調用對應的工具ToolCallback callback = toolCallbackRegistry.getToolCallback(toolName);if (callback != null) {Object result = callback.invoke(parameters);// 將工具調用結果替換回響應中modelResponse = modelResponse.replace("[TOOL_CALL]" + toolCallJson + "[/TOOL_CALL]","工具[" + toolName + "]執行結果: " + result);} else {modelResponse = modelResponse.replace("[TOOL_CALL]" + toolCallJson + "[/TOOL_CALL]","錯誤: 未找到工具[" + toolName + "]");}} catch (Exception e) {modelResponse = modelResponse.replace("[TOOL_CALL]" + toolCallJson + "[/TOOL_CALL]","工具調用錯誤: " + e.getMessage());}// 重新匹配,因為內容已被替換matcher = TOOL_CALL_PATTERN.matcher(modelResponse);}return modelResponse;}
}
八、創建控制器處理用戶請求
package com.example.aiagent.controller;import com.example.aiagent.model.ChatRequest;
import com.example.aiagent.model.ChatResponse;
import com.example.aiagent.service.ToolInvocationService;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.PromptTemplate;
import org.springframework.web.bind.annotation.*;import java.util.HashMap;
import java.util.Map;@RestController
@RequestMapping("/api/chat")
public class ChatController {private final ChatClient chatClient;private final ToolInvocationService toolInvocationService;public ChatController(ChatClient chatClient, ToolInvocationService toolInvocationService) {this.chatClient = chatClient;this.toolInvocationService = toolInvocationService;}@PostMappingpublic ChatResponse chat(@RequestBody ChatRequest request) {// 創建用戶提示Map<String, Object> modelParams = new HashMap<>();modelParams.put("userInput", request.getMessage());PromptTemplate promptTemplate = new PromptTemplate("{userInput}", modelParams);Prompt prompt = new Prompt(promptTemplate.create());// 調用AI模型ChatResponse response = chatClient.generate(prompt);String content = response.getGeneration().getContent();// 處理工具調用content = toolInvocationService.processToolCalls(content);return new ChatResponse(content);}
}
九、模型類
package com.example.aiagent.model;import lombok.Data;@Data
public class ChatRequest {private String message;
}@Data
public class ChatResponse {private String content;public ChatResponse(String content) {this.content = content;}
}
十、測試工具功能

完成上述代碼后,你可以通過發送 POST 請求到 /api/chat 測試工具功能。以下是幾個測試示例:

1. 數學計算測試
{"message": "3乘以5等于多少?"
}

預期模型可能會生成工具調用:

[TOOL_CALL]
{"name": "calculator","parameters": {"expression": "3*5"}
}
[/TOOL_CALL]
2. 天氣查詢測試
{"message": "今天北京的天氣如何?"
}

預期模型可能會生成工具調用:

[TOOL_CALL]
{"name": "weather","parameters": {"city": "北京"}
}
[/TOOL_CALL]
3. 翻譯測試
{"message": "請將'Hello World'翻譯成中文"
}

預期模型可能會生成工具調用:

[TOOL_CALL]
{"name": "translator","parameters": {"text": "Hello World","targetLanguage": "zh"}
}
[/TOOL_CALL]
十一、總結

通過以上步驟,我們完成了一個完整的 Spring Boot 項目,整合了 Spring AI 和 MCP 框架,并開發了自定義工具。主要關鍵點包括:

  1. 工具開發:使用 @Tool 注解標記工具方法,明確工具名稱、描述和參數
  2. 自動掃描:通過 @ComponentScanMethodToolCallbackProvider 自動注冊所有工具
  3. 動態系統提示:根據已注冊工具動態生成系統提示,無需手動維護
  4. 工具調用處理:統一處理模型生成的工具調用請求,執行對應工具并返回結果

這里的實現具有以下優勢:

  • 簡化配置:無需手動注冊每個工具類,系統自動發現并注冊
  • 可擴展性:隨時添加新工具,只需在工具包下創建帶 @Tool 注解的類
  • 維護方便:工具描述與工具實現保持在一起,易于理解和修改
  • 靈活性:工具調用處理邏輯集中管理,便于擴展更復雜的工具調用鏈

希望這篇教程能幫助你入門 Spring Boot 與 Spring AI 開發,讓你能夠輕松開發自己的智能體工具!

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

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

相關文章

【開源項目】GraphRAG Agent:可解釋、可推理的下一代智能問答系統

GraphRAG Agent&#xff1a;可解釋、可推理的下一代智能問答系統 ??引言?? 傳統 RAG&#xff08;檢索增強生成&#xff09;系統常因“黑盒推理”和上下文斷裂被詬病。微軟開源的 GraphRAG 框架嘗試用知識圖譜解決這一問題&#xff0c;而??Graph RAG Agent??&#xff0…

【論文筆記】【強化微調】AgentThink:思維鏈推理 + 工具調用

AgentThink: A Unified Framework for Tool-Augmented Chain-of-Thought Reasoning in Vision-Language Models for Autonomous Driving 1. 引述 這是一篇自動駕駛領域的論文。我對這篇論文主要感興趣的點在于其對于工具調用&#xff08;Tool Call&#xff09;的設計。這一點同…

前端頁面Javascript進階DOM與BOM

一、DOM基礎概念 DOM 是文檔對象模型&#xff0c;提供編程接口用于操作 HTML 和 XML 文檔。它將文檔表示為節點樹&#xff0c;每個節點代表文檔的一部分&#xff0c;如元素、文本、屬性等。通過 DOM&#xff0c;開發者可以訪問和修改文檔的結構、樣式與內容。 文檔節點類型 …

AWS CloudFormation深度解析:構建現代云原生應用基礎設施

在現代云原生應用開發中,基礎設施即代碼(Infrastructure as Code, IaC)已成為標準實踐。本文將深入解析一個完整的AWS CloudFormation模板,該模板為GlowChat Connector應用構建了生產級的基礎設施。 模板概述 這個CloudFormation模板是一個兩部分部署架構中的第一部分,專…

Oracle 查看所有表的字段名、數據類型及長度

1.只查看某個特定表的字段名 SELECT column_name, data_type, data_length FROM user_tab_columns WHERE table_name 你的表名 -- 注意大寫 ORDER BY column_id;2.查看當前用戶下所有表的字段名 SELECT table_name, column_name, data_type, data_length FROM user_tab_colu…

創客匠人分享知識付費監管升級下的行業價值重構:從合規挑戰到發展機遇的實踐路徑

引言&#xff1a;監管政策背后的行業邏輯轉向 知識付費領域的監管體系升級&#xff0c;本質上是對行業發展路徑的重新校準。隨著 "內容產品需具備知識沉淀載體" 等新規落地&#xff0c;行業正在經歷從 "流量驅動型增長" 到 "價值驅動型發展" 的…

邊緣計算的認識和應用

邊緣計算 邊緣計算是一種分布式計算范式&#xff0c;它將計算能力和數據存儲放置在離數據源更近的位置&#xff0c;而不是依賴于集中式的數據中心。通過在“邊緣”進行數據處理&#xff0c;邊緣計算可以減少延遲、提高響應速度、節省帶寬&#xff0c;并增強數據隱私和安全性。…

Arduino R4 WIFI橫向滾動顯示16×16LED屏

實現一個從左向右橫向滾動的"吉祥如意"顯示效果。 arduino r4 WiFi滾動顯示16*16led #include <SPI.h>// 引腳定義 const int RowA 2, RowB 3, RowC 4, RowD 5; const int OE 6; const int LATCH 10;// 字模數據 (吉祥如意) const PROGMEM byte characte…

html css js網頁制作成品——HTML+CSS+js力學光學天文網頁設計(4頁)附源碼

目錄 一、&#x1f468;?&#x1f393;網站題目 二、??網站描述 三、&#x1f4da;網站介紹 四、&#x1f310;網站效果 五、&#x1fa93; 代碼實現 &#x1f9f1;HTML 六、&#x1f947; 如何讓學習不再盲目 七、&#x1f381;更多干貨 一、&#x1f468;?&#x1f…

嵌入式開發之freeRTOS移植

FreeRTOS 是一款廣泛應用于嵌入式系統的開源實時操作系統&#xff08;RTOS&#xff09;&#xff0c;其移植過程需要結合具體硬件平臺和編譯器進行適配。以下是 FreeRTOS 移植的詳細步驟和關鍵注意事項&#xff1a; 一、移植前的準備工作 1. 硬件平臺確認 處理器架構&#xf…

【算法 day07】LeetCode 344.反轉字符串 | 541. 反轉字符串II | 卡碼網:54.替換數字

344.反轉字符串 題目鏈接 | 文檔講解 |視頻講解 : 鏈接 1.思路&#xff1a; 采用雙指針&#xff0c;left從0開始移動,right從尾元素進行移動 循環判斷條件&#xff1a;left< right,邊界值使用舉例法&#xff0c;eg: [ h ,e ,l,o ]偶數個不會相遇, [h ,e ,l ,l ,o ]奇數個&…

從檢索到生成:RAG 如何重構大模型的知識邊界?

引言&#xff1a;知識邊界的突破與重構 在人工智能技術快速發展的今天&#xff0c;大型語言模型&#xff08;LLMs&#xff09;已經展現出強大的文本生成和理解能力。然而&#xff0c;這些模型在實際應用中仍面臨著知識時效性、事實準確性和可溯源性等核心挑戰。檢索增強生成&a…

前端基礎知識CSS系列 - 05(BFC的理解)

一、是什么 我們在頁面布局的時候&#xff0c;經常出現以下情況&#xff1a; 這個元素高度怎么沒了&#xff1f;這兩欄布局怎么沒法自適應&#xff1f;這兩個元素的間距怎么有點奇怪的樣子&#xff1f;...... 原因是元素之間相互的影響&#xff0c;導致了意料之外的情況&…

Prompt Engineering 學習指南:從入門到精通的最佳路徑與資源

本 Prompt Engineering 技術報告,旨在提供一個從入門到精通的清晰學習路徑、核心方案,并附上最關鍵的 GitHub 倉庫資源。您可以將此報告作為快速提升 Prompt 能力的“速查手冊”和“成長地圖”。 Prompt Engineering 學習指南:從入門到精通的最佳路徑與資源 技術報告摘要 (…

fastmcp MCPConfig多服務器使用案例;sse、stdio、streamable-http使用

1、sse、stdio、streamable-http使用 參考&#xff1a;https://gofastmcp.com/deployment/running-server#the-run-method stdio本地使用&#xff1b;sse、streamable-http遠程調用&#xff08; Streamable HTTP—New in version: 2.3.0&#xff09; 調用&#xff1a; stdio、…

網站服務器被DDOS攻擊打不開,是要換高防服務器還是加CDN能防護住?

高防云服務器、高防 IP 和高防 CDN 作為常見應對網絡攻擊的重要利器&#xff0c;它們各自有著獨特的特點和應用場景&#xff0c;從技術架構看&#xff0c;高防云服務器是資源型防護&#xff0c;深度整合計算與防御資源&#xff1b;高防IP是流量型防護&#xff0c;以代理模式實現…

深入解析原型模式:從理論到實踐的全方位指南

深入解析原型模式&#xff1a;從理論到實踐的全方位指南 引言&#xff1a;為什么需要原型模式&#xff1f; 在軟件開發過程中&#xff0c;對象創建是一個頻繁且關鍵的操作。傳統方式&#xff08;如直接使用new關鍵字&#xff09;在某些場景下會顯得效率低下且不夠靈活。想象這…

HuggingFace鏡像配置失效問題深度解析:Python模塊導入機制的陷阱

前言 在使用HuggingFace的transformers和datasets庫時&#xff0c;國內用戶經常會遇到網絡連接問題。雖然設置了鏡像源環境變量&#xff0c;但仍然報錯無法連接到huggingface.co。本文將深入分析這個問題的根因&#xff0c;并從Python模塊導入機制的角度解釋為什么環境變量設置…

leetcode146-LRU緩存

leetcode 146 思路 什么是LRU緩存&#xff1f; LRU&#xff08;Least Recently Used&#xff09;緩存是一種常見的緩存淘汰策略&#xff0c;核心思想是&#xff1a;當緩存容量滿時&#xff0c;優先淘汰最久未使用的數據。LeetCode 146 題要求實現一個支持get和put操作的 LR…

MQTT:構建高效物聯網通信的輕量級協議

MQTT – 輕量級物聯網消息推送協議 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是機器對機器(M2M)/物聯網(IoT)連接協議。它被設計為一個極其輕量級的發布/訂閱消息傳輸協議。對于需要較小代碼占用空間和/或網絡帶寬非常寶貴的遠程連接非常有用&#xf…