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 项目,添加以下依赖:

xml 复制代码
<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 密钥:

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

在 Spring AI 中,工具是一个可以被大模型调用的功能单元。工具通常对应一个具体的函数或方法,用于执行特定的任务,如查询数据库、调用外部 API、执行计算等。

工具的核心特点:

  • 有明确的功能和输入输出
  • 可以被大模型理解和调用
  • 可以组合使用,形成复杂的工作流
四、开发自定义工具

下面通过一个完整的例子,展示如何开发自定义工具。我们将创建一个简单的智能助手,支持数学计算、天气查询和翻译功能。
先给出整体流程图:

1. 数学计算工具
java 复制代码
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. 天气查询工具
java 复制代码
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. 翻译工具
java 复制代码
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 {
            // 这里使用简单的模拟,实际应用中可以调用翻译API
            if ("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 注解的工具类,无需手动逐个注册。

java 复制代码
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 {

    @Bean
    public ToolCallbackProvider toolCallbackProvider(Object[] toolBeans) {
        // 自动注册所有扫描到的工具
        return MethodToolCallbackProvider.builder()
                .toolObjects(toolBeans)
                .build();
    }
}
六、配置AI客户端与动态工具描述

动态生成系统提示,自动包含所有已注册工具的信息:

java 复制代码
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 {

    @Bean
    public 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();
    }
}
七、智能工具调用处理器

创建一个服务来处理模型生成的工具调用:

java 复制代码
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 {
                // 解析工具调用JSON
                Map<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;
    }
}
八、创建控制器处理用户请求
java 复制代码
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;
    }

    @PostMapping
    public 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);
    }
}
九、模型类
java 复制代码
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. 数学计算测试
json 复制代码
{
    "message": "3乘以5等于多少?"
}

预期模型可能会生成工具调用:

复制代码
[TOOL_CALL]
{
    "name": "calculator",
    "parameters": {
        "expression": "3*5"
    }
}
[/TOOL_CALL]
2. 天气查询测试
json 复制代码
{
    "message": "今天北京的天气如何?"
}

预期模型可能会生成工具调用:

复制代码
[TOOL_CALL]
{
    "name": "weather",
    "parameters": {
        "city": "北京"
    }
}
[/TOOL_CALL]
3. 翻译测试
json 复制代码
{
    "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 开发,让你能够轻松开发自己的智能体工具!

相关推荐
大明者省1 小时前
AI 在课程思政的 10 大应用:从资源挖掘到效果升华
前端·人工智能·easyui
liliangcsdn3 小时前
金融领域LLM开源测试集
人工智能·金融
卓豪终端管理3 小时前
如何实现补丁管理自动化?
运维·人工智能·安全·网络安全·自动化·补丁管理·补丁自动化
sg_knight4 小时前
Spring Cloud LoadBalancer深度解析:官方负载均衡方案迁移指南与避坑实践
java·spring boot·spring·spring cloud·微服务·负载均衡
_何同学4 小时前
Ollama 安装 DeepSeek 与 Spring Boot 集成指南
java·spring boot·后端·ai
xyzso1z5 小时前
飞书 MCP:AI 编码工具与飞书文档的桥梁
人工智能·飞书·mcp
Honeysea_705 小时前
目标检测相关【清晰易懂】
人工智能·计算机视觉·目标跟踪
Eric.Lee20216 小时前
数据集-目标检测系列- 杯子 数据集 bottle >> DataBall
人工智能·目标检测·计算机视觉·杯子检测·bottle detect
mall_09056 小时前
Spring Cloud使用Eureka调用接口,超时设置(三)
spring·spring cloud·eureka