之前做個幾個大模型的應用,都是使用Python語言,后來有一個項目使用了Java,并使用了Spring AI框架。隨著Spring AI不斷地完善,最近它發布了1.0正式版,意味著它已經能很好的作為企業級生產環境的使用。對于Java開發者來說真是一個福音,其功能已經能滿足基于大模型開發企業級應用。借著這次機會,給大家分享一下Spring AI框架。
注意:由于框架不同版本改造會有些使用的不同,因此本次系列中使用基本框架是 Spring AI-1.0.0,JDK版本使用的是19,Spring-AI-Alibaba-1.0.0.3-SNAPSHOT。
代碼參考: https://github.com/forever1986/springai-study
目錄
- 1 示例演示
- 1.1 初始化
- 1.2 構建圖和節點
- 1.3 演示效果
上一章演示了Graph框架的并行執行流程,并剖析了其中ParallelNode的實現邏輯。這一章來講一下Graph如何訪問MCP。
1 示例演示
代碼參考lesson26子模塊中的graph-mcp子模塊
示例說明:通過構建一個Graph圖,其中定義一個訪問MCP節點
1.1 初始化
1)本次MCP服務采用前面lesson10子模塊的sse-server子模塊作為MCP Server,啟動該服務
2)在lesson26子模塊下,新建graph-mcp子模塊,其pom引入如下:
<dependencies><!-- 引入智譜的model插件 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-zhipuai</artifactId></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-graph-core</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-client-webflux</artifactId></dependency><!-- 需要引入gson插件 --><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.6</version></dependency>
</dependencies>
3)新建application.properties配置文件
# 聊天模型
spring.ai.zhipuai.api-key=你的智譜API KEY
spring.ai.zhipuai.chat.options.model=GLM-4-Flash-250414
spring.ai.zhipuai.chat.options.temperature=0.7# 指定mcp-servers的URL
spring.ai.mcp.client.type=SYNC
spring.ai.mcp.client.sse.connections.server1.url=http://localhost:8081spring.ai.graph.nodes.node2servers.mcp-node=server1
4)新建McpNodeProperties讀取配置spring.ai.graph.nodes開頭
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.Map;
import java.util.Set;/*** 為了方便解析配置多少個MCP服務*/
@ConfigurationProperties(prefix = McpNodeProperties.PREFIX)
public class McpNodeProperties {public static final String PREFIX = "spring.ai.graph.nodes";private Map<String, Set<String>> node2servers;public Map<String, Set<String>> getNode2servers() {return node2servers;}public void setNode2servers(Map<String, Set<String>> node2servers) {this.node2servers = node2servers;}
}
1.2 構建圖和節點
1)新建McpClientToolCallbackProvider類,用于讀取toolcall
import com.demo.lesson26.mcp.config.McpNodeProperties;
import org.apache.commons.compress.utils.Lists;
import org.glassfish.jersey.internal.guava.Sets;
import org.springframework.ai.mcp.McpToolUtils;
import org.springframework.ai.mcp.client.autoconfigure.properties.McpClientCommonProperties;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.Set;@Service
public class McpClientToolCallbackProvider {private final ToolCallbackProvider toolCallbackProvider;private final McpClientCommonProperties commonProperties;private final McpNodeProperties mcpNodeProperties;public McpClientToolCallbackProvider(ToolCallbackProvider toolCallbackProvider,McpClientCommonProperties commonProperties, McpNodeProperties mcpNodeProperties) {this.toolCallbackProvider = toolCallbackProvider;this.commonProperties = commonProperties;this.mcpNodeProperties = mcpNodeProperties;}/*** 通過配置的spring.ai.graph.nodes的名稱,獲取從Spring AI中獲取到的toolCall*/public Set<ToolCallback> findToolCallbacks(String nodeName) {// 獲取配置文件中spring.ai.graph.nodes開頭的數據Set<ToolCallback> defineCallback = Sets.newHashSet();Set<String> mcpClients = mcpNodeProperties.getNode2servers().get(nodeName);if (mcpClients == null || mcpClients.isEmpty()) {return defineCallback;}List<String> exceptMcpClientNames = Lists.newArrayList();for (String mcpClient : mcpClients) {// my-mcp-clientString name = commonProperties.getName();// mymcpclientserver1String prefixedMcpClientName = McpToolUtils.prefixedToolName(name, mcpClient);exceptMcpClientNames.add(prefixedMcpClientName);}// 從Spring AI的MCP客戶端獲取到的toolCall,放到defineCallback,以方便注冊到MCPNode中的chatClientToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();for (ToolCallback toolCallback : toolCallbacks) {ToolDefinition toolDefinition = toolCallback.getToolDefinition();String name = toolDefinition.name();for (String exceptMcpClientName : exceptMcpClientNames) {if (name.startsWith(exceptMcpClientName)) {defineCallback.add(toolCallback);}}}return defineCallback;}
}
2)構建McpNode節點
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import com.demo.lesson26.mcp.tool.McpClientToolCallbackProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallback;
import reactor.core.publisher.Flux;import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** 自定義MCP節點*/
public class McpNode implements NodeAction {private static final Logger logger = LoggerFactory.getLogger(McpNode.class);private static final String NODENAME = "mcp-node";private final ChatClient chatClient;public McpNode(ChatClient.Builder chatClientBuilder, McpClientToolCallbackProvider mcpClientToolCallbackProvider) {// 獲取mcp-node前綴定義的工具,注冊到chatClient中Set<ToolCallback> toolCallbacks = mcpClientToolCallbackProvider.findToolCallbacks(NODENAME);for (ToolCallback toolCallback : toolCallbacks) {logger.info("Mcp Node load ToolCallback: " + toolCallback.getToolDefinition().name());}this.chatClient = chatClientBuilder.defaultToolCallbacks(toolCallbacks.toArray(ToolCallback[]::new)).build();}@Overridepublic Map<String, Object> apply(OverAllState state) {String query = state.value("query", "");Flux<String> streamResult = chatClient.prompt(query).stream().content();String result = streamResult.reduce("", (acc, item) -> acc + item).block();HashMap<String, Object> resultMap = new HashMap<>();resultMap.put("mcpcontent", result);return resultMap;}
}
3)設置配置類McpGaphConfiguration構建圖:
import com.alibaba.cloud.ai.graph.GraphRepresentation;
import com.alibaba.cloud.ai.graph.KeyStrategy;
import com.alibaba.cloud.ai.graph.KeyStrategyFactory;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.action.AsyncNodeAction;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import com.demo.lesson26.mcp.node.McpNode;
import com.demo.lesson26.mcp.tool.McpClientToolCallbackProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.HashMap;@Configuration
@EnableConfigurationProperties({ McpNodeProperties.class })
public class McpGaphConfiguration {private static final Logger logger = LoggerFactory.getLogger(McpGaphConfiguration.class);@Autowiredprivate McpClientToolCallbackProvider mcpClientToolCallbackProvider;@Beanpublic StateGraph mcpGraph(ChatClient.Builder chatClientBuilder) throws GraphStateException {KeyStrategyFactory keyStrategyFactory = () -> {HashMap<String, KeyStrategy> keyStrategyHashMap = new HashMap<>();// 用戶輸入keyStrategyHashMap.put("query", new ReplaceStrategy());keyStrategyHashMap.put("mcpcontent", new ReplaceStrategy());return keyStrategyHashMap;};// 構建圖StateGraph stateGraph = new StateGraph(keyStrategyFactory).addNode("mcp", AsyncNodeAction.node_async(new McpNode(chatClientBuilder, mcpClientToolCallbackProvider))).addEdge(StateGraph.START, "mcp").addEdge("mcp", StateGraph.END);// 添加 PlantUML 打印GraphRepresentation representation = stateGraph.getGraph(GraphRepresentation.Type.PLANTUML,"mcp flow");logger.info("\n=== mcp UML Flow ===");logger.info(representation.content());logger.info("==================================\n");return stateGraph;}
}
4)新建啟動類:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Lesson26MCPApplication {public static void main(String[] args) {SpringApplication.run(Lesson26MCPApplication.class, args);}}
1.3 演示效果
http://localhost:8080/graph/mcp
結語:本章演示了Graph中如何訪問MCP服務,可見其架構的可擴展性,在Spring AI Alibaba中有一個com.alibaba.cloud.ai.graph.node.McpNode的MCP訪問節點實現,但是該節點只是一個固定MCP訪問,即需要傳入方法和參數,并沒有配置大模型。如果你構建的Graph中只是簡單調用MCP服務,則可以直接使用com.alibaba.cloud.ai.graph.node.McpNode節點。前面通過幾章對Graph框架進行了比較詳細的講解,這是因為在實際應用中,一個應用一般都是一個流程,而非一撮而就,所以使用Graph場景非常多。下一章將講Spring AI Alibaba的nl2sql,這個是一個基于Graph構建的生成SQL的實際案例,你就可以見識到復雜的工作流。
Spring AI系列上一章:《Spring AI 系列之三十四 - Spring AI Alibaba-Graph框架之并行執行》
Spring AI系列下一章:《Spring AI 系列之三十六 - Spring AI Alibaba-nl2sql》