Spring AI 代码分析(八)–工具和MCP调用

工具和MCP调用

请关注微信公众号:阿呆-bot

1. 工程结构概览

Spring AI 提供了完整的工具调用(Tool Calling)能力,让 AI 模型可以调用外部服务。同时,Spring AI 还支持 MCP(Model Context Protocol),这是一个标准化的工具协议。

spring-ai-model/ ├── tool/                        # 工具调用核心 │   ├── ToolCallback.java        # 工具回调接口 │   ├── ToolDefinition.java      # 工具定义 │   ├── method/                  # 方法工具 │   │   └── MethodToolCallback.java │   ├── function/               # 函数工具 │   │   └── FunctionToolCallback.java │   └── resolution/              # 工具解析 │       ├── ToolCallbackResolver.java │       └── SpringBeanToolCallbackResolver.java │ └── model/tool/                  # 工具调用管理     ├── ToolCallingManager.java     └── DefaultToolCallingManager.java  mcp/                             # MCP 协议支持 ├── common/                      # MCP 核心 │   ├── AsyncMcpToolCallback.java │   ├── SyncMcpToolCallback.java │   ├── AsyncMcpToolCallbackProvider.java │   └── SyncMcpToolCallbackProvider.java └── mcp-annotations-spring/      # MCP 注解支持     └── annotation/spring/ 

2. 技术体系与模块关系

工具调用和 MCP 的关系:

Spring AI 代码分析(八)--工具和MCP调用

3. 关键场景示例代码

3.1 方法工具

使用 @Tool 注解定义工具:

@Service public class WeatherService {     @Tool(name = "get_weather", description = "获取天气信息")     public String getWeather(String city) {         // 调用天气 API         return weatherApi.getWeather(city);     }          @Tool(name = "get_forecast", description = "获取天气预报")     public WeatherForecast getForecast(         String city,          int days,         ToolContext context  // 可选:获取工具上下文     ) {         // 可以从 context 中获取对话历史等         return weatherApi.getForecast(city, days);     } } 

3.2 函数工具

使用函数式接口定义工具:

FunctionToolCallback<String, String> weatherTool =      FunctionToolCallback.builder("get_weather",          (String city) -> weatherApi.getWeather(city))     .description("获取天气信息")     .build(); 

3.3 MCP 工具

MCP 工具通过 MCP 客户端自动发现:

// MCP 工具会自动从 MCP 服务器发现 // 无需手动定义,通过 AsyncMcpToolCallbackProvider 提供 ChatClient chatClient = ChatClient.builder(chatModel)     .defaultAdvisors(         // MCP 工具会自动注册     )     .build(); 

3.4 工具调用流程

工具调用是自动的:

// 1. 定义工具 @Tool(name = "search_docs", description = "搜索文档") public String searchDocs(String query) {     return docService.search(query); }  // 2. 使用 ChatClient(工具会自动发现) ChatClient chatClient = ChatClient.builder(chatModel)     .build();  // 3. 模型会自动调用工具 String response = chatClient.prompt()     .user("帮我搜索 Spring AI 的文档")     .call()     .content(); // 模型会自动调用 search_docs 工具 

4. 核心时序图

4.1 工具调用完整流程

Spring AI 代码分析(八)--工具和MCP调用

4.2 MCP 工具发现流程

Spring AI 代码分析(八)--工具和MCP调用

5. 入口类与关键类关系

Spring AI 代码分析(八)--工具和MCP调用

6. 关键实现逻辑分析

6.1 工具注册机制

Spring AI 支持多种工具注册方式:

方式一:@Tool 注解(推荐)

@Service public class MyService {     @Tool(name = "my_tool", description = "我的工具")     public String myTool(String input) {         return "处理结果: " + input;     } } 

MethodToolCallbackProvider 会自动扫描所有 @Tool 注解的方法:

public class MethodToolCallbackProvider implements ToolCallbackProvider {     @Override     public ToolCallback[] getToolCallbacks() {         return toolObjects.stream()             .flatMap(toolObject ->                  Stream.of(ReflectionUtils.getDeclaredMethods(toolObject.getClass()))                     .filter(this::isToolAnnotatedMethod)                     .map(method -> MethodToolCallback.builder()                         .toolDefinition(ToolDefinitions.from(method))                         .toolMethod(method)                         .toolObject(toolObject)                         .build())             )             .toArray(ToolCallback[]::new);     } } 

方式二:手动注册

ToolCallback tool = FunctionToolCallback.builder("my_tool",      (String input) -> "结果: " + input)     .build();  ToolCallingChatOptions options = ToolCallingChatOptions.builder()     .withToolCallbacks(tool)     .build(); 

方式三:MCP 自动发现
MCP 工具通过 AsyncMcpToolCallbackProvider 自动发现:

public class AsyncMcpToolCallbackProvider implements ToolCallbackProvider {     @Override     public ToolCallback[] getToolCallbacks() {         // 从 MCP 客户端获取工具列表         List<Tool> mcpTools = mcpClient.listTools();                  return mcpTools.stream()             .map(tool -> AsyncMcpToolCallback.builder()                 .mcpClient(mcpClient)                 .tool(tool)                 .build())             .toArray(ToolCallback[]::new);     } } 

6.2 工具发现机制

ToolCallbackResolver 负责解析工具:

public class DelegatingToolCallbackResolver implements ToolCallbackResolver {     private final List<ToolCallbackResolver> resolvers;          @Override     public ToolCallback resolve(String toolName) {         // 按顺序尝试每个解析器         for (ToolCallbackResolver resolver : resolvers) {             ToolCallback tool = resolver.resolve(toolName);             if (tool != null) {                 return tool;             }         }         return null;     } } 

SpringBeanToolCallbackResolver 从 Spring 容器中查找:

public class SpringBeanToolCallbackResolver implements ToolCallbackResolver {     @Override     public ToolCallback resolve(String toolName) {         // 1. 检查缓存         ToolCallback cached = cache.get(toolName);         if (cached != null) {             return cached;         }                  // 2. 从 Spring 容器中查找 Bean         try {             Object bean = applicationContext.getBean(toolName);             ResolvableType toolType = resolveBeanType(bean);                          // 3. 构建 ToolCallback             ToolCallback tool = buildToolCallback(toolName, toolType, bean);                          // 4. 缓存             cache.put(toolName, tool);             return tool;         } catch (Exception e) {             return null;         }     } } 

6.3 工具调用执行

DefaultToolCallingManager 负责执行工具调用:

public class DefaultToolCallingManager implements ToolCallingManager {     @Override     public ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse response) {         // 1. 提取工具调用请求         AssistantMessage assistantMessage = extractToolCalls(response);                  // 2. 构建工具上下文         ToolContext toolContext = buildToolContext(prompt, assistantMessage);                  // 3. 执行每个工具调用         List<ToolResponse> toolResponses = new ArrayList<>();         for (ToolCall toolCall : assistantMessage.getToolCalls()) {             // 3.1 解析工具回调             ToolCallback callback = resolveToolCallback(toolCall.getName());                          // 3.2 执行工具             String result = callback.call(toolCall.getArguments(), toolContext);                          // 3.3 构建响应             toolResponses.add(new ToolResponse(toolCall.getId(), result));         }                  // 4. 构建工具响应消息         ToolResponseMessage toolResponseMessage =              new ToolResponseMessage(toolResponses);                  // 5. 构建对话历史         List<Message> conversationHistory = buildConversationHistory(             prompt.getInstructions(),             assistantMessage,             toolResponseMessage         );                  return ToolExecutionResult.builder()             .conversationHistory(conversationHistory)             .returnDirect(shouldReturnDirect())             .build();     } } 

6.4 MCP 工具适配

MCP 工具通过 AsyncMcpToolCallback 适配到 Spring AI:

public class AsyncMcpToolCallback implements ToolCallback {     @Override     public String call(String toolInput, ToolContext toolContext) {         // 1. 解析工具输入         Map<String, Object> arguments = jsonToMap(toolInput);                  // 2. 转换工具上下文         McpMeta mcpMeta = toolContextToMcpMetaConverter.convert(toolContext);                  // 3. 构建 MCP 请求         CallToolRequest request = CallToolRequest.builder()             .name(tool.name())  // 使用原始工具名             .arguments(arguments)             .meta(mcpMeta)             .build();                  // 4. 调用 MCP 客户端         CallToolResult response = mcpClient.callTool(request)             .onErrorMap(exception ->                  new ToolExecutionException(getToolDefinition(), exception))             .block();                  // 5. 处理错误         if (response.isError()) {             throw new ToolExecutionException(getToolDefinition(),                 new IllegalStateException("Error: " + response.content()));         }                  // 6. 返回结果         return toJsonString(response.content());     } } 

7. MCP 协议集成

7.1 MCP 是什么

MCP(Model Context Protocol)是一个标准化的协议,用于让 AI 模型访问外部工具和数据源。它定义了:

  • 工具发现:服务器可以暴露可用的工具
  • 工具调用:客户端可以调用工具
  • 资源访问:客户端可以访问服务器资源

7.2 MCP 工具提供者

AsyncMcpToolCallbackProvider 负责从 MCP 服务器发现工具:

public class AsyncMcpToolCallbackProvider implements ToolCallbackProvider {     @Override     public ToolCallback[] getToolCallbacks() {         // 1. 从 MCP 客户端获取工具列表         List<Tool> tools = mcpClient.listTools();                  // 2. 转换为 Spring AI ToolCallback         return tools.stream()             .map(tool -> AsyncMcpToolCallback.builder()                 .mcpClient(mcpClient)                 .tool(tool)                 .toolNamePrefixGenerator(prefixGenerator)                 .build())             .toArray(ToolCallback[]::new);     } } 

7.3 MCP 工具动态更新

MCP 支持工具的动态添加和删除:

// MCP 客户端监听工具变更事件 mcpClient.onToolsChanged(event -> {     // 重新获取工具列表     List<Tool> newTools = mcpClient.listTools();          // 更新工具提供者     toolCallbackProvider.updateTools(newTools); }); 

8. 外部依赖

8.1 工具调用

  • Spring Framework:IoC 容器和反射
  • Jackson:JSON 处理(工具 Schema 生成)
  • Swagger Annotations:Schema 生成

8.2 MCP

  • MCP Java SDK:MCP 协议实现
  • Reactor Core:响应式支持(AsyncMcpToolCallback)

9. 工程总结

Spring AI 的工具调用和 MCP 能力设计有几个亮点:

统一的工具抽象。所有工具(方法、函数、MCP)都实现 ToolCallback 接口,这让工具调用变得统一和透明。不管工具来自哪里,调用方式都一样。

灵活的注册机制。支持注解、手动注册、MCP 自动发现等多种方式,适应不同的使用场景。想用注解?加个 @Tool 就行。想手动注册?创建 ToolCallback 就行。

智能的工具发现。通过 ToolCallbackResolver 链,可以按顺序尝试多个解析器,支持 Spring Bean、静态工具、MCP 工具等多种来源。找不到工具?换个解析器试试。

MCP 协议集成。通过 MCP 适配器,Spring AI 可以无缝集成 MCP 服务器提供的工具,支持工具的动态发现和更新。MCP 服务器添加了新工具?Spring AI 会自动同步。

工具上下文传递。工具可以接收 ToolContext,获取对话历史、用户信息等上下文,这让工具可以做出更智能的决策。比如根据用户历史记录,提供个性化服务。

总的来说,Spring AI 的工具调用和 MCP 能力既强大又灵活。统一的抽象让工具调用变得简单,灵活的机制让系统可以适应各种场景。这种设计让 Spring AI 既能支持简单的本地工具,也能支持复杂的 MCP 服务器工具。

发表评论

评论已关闭。

相关文章