找回密码
 立即注册
首页 业界区 业界 Spring AI 代码分析(八)--工具和MCP调用

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

濮阳雅爱 2025-11-25 23:50:10
工具和MCP调用

请关注微信公众号:阿呆-bot
1. 工程结构概览

Spring AI 提供了完整的工具调用(Tool Calling)能力,让 AI 模型可以调用外部服务。同时,Spring AI 还支持 MCP(Model Context Protocol),这是一个标准化的工具协议。
  1. spring-ai-model/
  2. ├── tool/                        # 工具调用核心
  3. │   ├── ToolCallback.java        # 工具回调接口
  4. │   ├── ToolDefinition.java      # 工具定义
  5. │   ├── method/                  # 方法工具
  6. │   │   └── MethodToolCallback.java
  7. │   ├── function/               # 函数工具
  8. │   │   └── FunctionToolCallback.java
  9. │   └── resolution/              # 工具解析
  10. │       ├── ToolCallbackResolver.java
  11. │       └── SpringBeanToolCallbackResolver.java
  12. └── model/tool/                  # 工具调用管理
  13.     ├── ToolCallingManager.java
  14.     └── DefaultToolCallingManager.java
  15. mcp/                             # MCP 协议支持
  16. ├── common/                      # MCP 核心
  17. │   ├── AsyncMcpToolCallback.java
  18. │   ├── SyncMcpToolCallback.java
  19. │   ├── AsyncMcpToolCallbackProvider.java
  20. │   └── SyncMcpToolCallbackProvider.java
  21. └── mcp-annotations-spring/      # MCP 注解支持
  22.     └── annotation/spring/
复制代码
2. 技术体系与模块关系

工具调用和 MCP 的关系:
1.png

3. 关键场景示例代码

3.1 方法工具

使用 @Tool 注解定义工具:
  1. @Service
  2. public class WeatherService {
  3.     @Tool(name = "get_weather", description = "获取天气信息")
  4.     public String getWeather(String city) {
  5.         // 调用天气 API
  6.         return weatherApi.getWeather(city);
  7.     }
  8.    
  9.     @Tool(name = "get_forecast", description = "获取天气预报")
  10.     public WeatherForecast getForecast(
  11.         String city,
  12.         int days,
  13.         ToolContext context  // 可选:获取工具上下文
  14.     ) {
  15.         // 可以从 context 中获取对话历史等
  16.         return weatherApi.getForecast(city, days);
  17.     }
  18. }
复制代码
3.2 函数工具

使用函数式接口定义工具:
  1. FunctionToolCallback<String, String> weatherTool =
  2.     FunctionToolCallback.builder("get_weather",
  3.         (String city) -> weatherApi.getWeather(city))
  4.     .description("获取天气信息")
  5.     .build();
复制代码
3.3 MCP 工具

MCP 工具通过 MCP 客户端自动发现:
  1. // MCP 工具会自动从 MCP 服务器发现
  2. // 无需手动定义,通过 AsyncMcpToolCallbackProvider 提供
  3. ChatClient chatClient = ChatClient.builder(chatModel)
  4.     .defaultAdvisors(
  5.         // MCP 工具会自动注册
  6.     )
  7.     .build();
复制代码
3.4 工具调用流程

工具调用是自动的:
  1. // 1. 定义工具
  2. @Tool(name = "search_docs", description = "搜索文档")
  3. public String searchDocs(String query) {
  4.     return docService.search(query);
  5. }
  6. // 2. 使用 ChatClient(工具会自动发现)
  7. ChatClient chatClient = ChatClient.builder(chatModel)
  8.     .build();
  9. // 3. 模型会自动调用工具
  10. String response = chatClient.prompt()
  11.     .user("帮我搜索 Spring AI 的文档")
  12.     .call()
  13.     .content();
  14. // 模型会自动调用 search_docs 工具
复制代码
4. 核心时序图

4.1 工具调用完整流程

2.png

4.2 MCP 工具发现流程

3.png

5. 入口类与关键类关系

4.png

6. 关键实现逻辑分析

6.1 工具注册机制

Spring AI 支持多种工具注册方式:
方式一:@Tool 注解(推荐)
  1. @Service
  2. public class MyService {
  3.     @Tool(name = "my_tool", description = "我的工具")
  4.     public String myTool(String input) {
  5.         return "处理结果: " + input;
  6.     }
  7. }
复制代码
MethodToolCallbackProvider 会自动扫描所有 @Tool 注解的方法:
  1. public class MethodToolCallbackProvider implements ToolCallbackProvider {
  2.     @Override
  3.     public ToolCallback[] getToolCallbacks() {
  4.         return toolObjects.stream()
  5.             .flatMap(toolObject ->
  6.                 Stream.of(ReflectionUtils.getDeclaredMethods(toolObject.getClass()))
  7.                     .filter(this::isToolAnnotatedMethod)
  8.                     .map(method -> MethodToolCallback.builder()
  9.                         .toolDefinition(ToolDefinitions.from(method))
  10.                         .toolMethod(method)
  11.                         .toolObject(toolObject)
  12.                         .build())
  13.             )
  14.             .toArray(ToolCallback[]::new);
  15.     }
  16. }
复制代码
方式二:手动注册
  1. ToolCallback tool = FunctionToolCallback.builder("my_tool",
  2.     (String input) -> "结果: " + input)
  3.     .build();
  4. ToolCallingChatOptions options = ToolCallingChatOptions.builder()
  5.     .withToolCallbacks(tool)
  6.     .build();
复制代码
方式三:MCP 自动发现
MCP 工具通过 AsyncMcpToolCallbackProvider 自动发现:
  1. public class AsyncMcpToolCallbackProvider implements ToolCallbackProvider {
  2.     @Override
  3.     public ToolCallback[] getToolCallbacks() {
  4.         // 从 MCP 客户端获取工具列表
  5.         List<Tool> mcpTools = mcpClient.listTools();
  6.         
  7.         return mcpTools.stream()
  8.             .map(tool -> AsyncMcpToolCallback.builder()
  9.                 .mcpClient(mcpClient)
  10.                 .tool(tool)
  11.                 .build())
  12.             .toArray(ToolCallback[]::new);
  13.     }
  14. }
复制代码
6.2 工具发现机制

ToolCallbackResolver 负责解析工具:
  1. public class DelegatingToolCallbackResolver implements ToolCallbackResolver {
  2.     private final List<ToolCallbackResolver> resolvers;
  3.    
  4.     @Override
  5.     public ToolCallback resolve(String toolName) {
  6.         // 按顺序尝试每个解析器
  7.         for (ToolCallbackResolver resolver : resolvers) {
  8.             ToolCallback tool = resolver.resolve(toolName);
  9.             if (tool != null) {
  10.                 return tool;
  11.             }
  12.         }
  13.         return null;
  14.     }
  15. }
复制代码
SpringBeanToolCallbackResolver 从 Spring 容器中查找:
  1. public class SpringBeanToolCallbackResolver implements ToolCallbackResolver {
  2.     @Override
  3.     public ToolCallback resolve(String toolName) {
  4.         // 1. 检查缓存
  5.         ToolCallback cached = cache.get(toolName);
  6.         if (cached != null) {
  7.             return cached;
  8.         }
  9.         
  10.         // 2. 从 Spring 容器中查找 Bean
  11.         try {
  12.             Object bean = applicationContext.getBean(toolName);
  13.             ResolvableType toolType = resolveBeanType(bean);
  14.             
  15.             // 3. 构建 ToolCallback
  16.             ToolCallback tool = buildToolCallback(toolName, toolType, bean);
  17.             
  18.             // 4. 缓存
  19.             cache.put(toolName, tool);
  20.             return tool;
  21.         } catch (Exception e) {
  22.             return null;
  23.         }
  24.     }
  25. }
复制代码
6.3 工具调用执行

DefaultToolCallingManager 负责执行工具调用:
  1. public class DefaultToolCallingManager implements ToolCallingManager {
  2.     @Override
  3.     public ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse response) {
  4.         // 1. 提取工具调用请求
  5.         AssistantMessage assistantMessage = extractToolCalls(response);
  6.         
  7.         // 2. 构建工具上下文
  8.         ToolContext toolContext = buildToolContext(prompt, assistantMessage);
  9.         
  10.         // 3. 执行每个工具调用
  11.         List<ToolResponse> toolResponses = new ArrayList<>();
  12.         for (ToolCall toolCall : assistantMessage.getToolCalls()) {
  13.             // 3.1 解析工具回调
  14.             ToolCallback callback = resolveToolCallback(toolCall.getName());
  15.             
  16.             // 3.2 执行工具
  17.             String result = callback.call(toolCall.getArguments(), toolContext);
  18.             
  19.             // 3.3 构建响应
  20.             toolResponses.add(new ToolResponse(toolCall.getId(), result));
  21.         }
  22.         
  23.         // 4. 构建工具响应消息
  24.         ToolResponseMessage toolResponseMessage =
  25.             new ToolResponseMessage(toolResponses);
  26.         
  27.         // 5. 构建对话历史
  28.         List<Message> conversationHistory = buildConversationHistory(
  29.             prompt.getInstructions(),
  30.             assistantMessage,
  31.             toolResponseMessage
  32.         );
  33.         
  34.         return ToolExecutionResult.builder()
  35.             .conversationHistory(conversationHistory)
  36.             .returnDirect(shouldReturnDirect())
  37.             .build();
  38.     }
  39. }
复制代码
6.4 MCP 工具适配

MCP 工具通过 AsyncMcpToolCallback 适配到 Spring AI:
  1. public class AsyncMcpToolCallback implements ToolCallback {
  2.     @Override
  3.     public String call(String toolInput, ToolContext toolContext) {
  4.         // 1. 解析工具输入
  5.         Map<String, Object> arguments = jsonToMap(toolInput);
  6.         
  7.         // 2. 转换工具上下文
  8.         McpMeta mcpMeta = toolContextToMcpMetaConverter.convert(toolContext);
  9.         
  10.         // 3. 构建 MCP 请求
  11.         CallToolRequest request = CallToolRequest.builder()
  12.             .name(tool.name())  // 使用原始工具名
  13.             .arguments(arguments)
  14.             .meta(mcpMeta)
  15.             .build();
  16.         
  17.         // 4. 调用 MCP 客户端
  18.         CallToolResult response = mcpClient.callTool(request)
  19.             .onErrorMap(exception ->
  20.                 new ToolExecutionException(getToolDefinition(), exception))
  21.             .block();
  22.         
  23.         // 5. 处理错误
  24.         if (response.isError()) {
  25.             throw new ToolExecutionException(getToolDefinition(),
  26.                 new IllegalStateException("Error: " + response.content()));
  27.         }
  28.         
  29.         // 6. 返回结果
  30.         return toJsonString(response.content());
  31.     }
  32. }
复制代码
7. MCP 协议集成

7.1 MCP 是什么

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

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

AsyncMcpToolCallbackProvider 负责从 MCP 服务器发现工具:
  1. public class AsyncMcpToolCallbackProvider implements ToolCallbackProvider {
  2.     @Override
  3.     public ToolCallback[] getToolCallbacks() {
  4.         // 1. 从 MCP 客户端获取工具列表
  5.         List<Tool> tools = mcpClient.listTools();
  6.         
  7.         // 2. 转换为 Spring AI ToolCallback
  8.         return tools.stream()
  9.             .map(tool -> AsyncMcpToolCallback.builder()
  10.                 .mcpClient(mcpClient)
  11.                 .tool(tool)
  12.                 .toolNamePrefixGenerator(prefixGenerator)
  13.                 .build())
  14.             .toArray(ToolCallback[]::new);
  15.     }
  16. }
复制代码
7.3 MCP 工具动态更新

MCP 支持工具的动态添加和删除:
  1. // MCP 客户端监听工具变更事件
  2. mcpClient.onToolsChanged(event -> {
  3.     // 重新获取工具列表
  4.     List<Tool> newTools = mcpClient.listTools();
  5.    
  6.     // 更新工具提供者
  7.     toolCallbackProvider.updateTools(newTools);
  8. });
复制代码
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 服务器工具。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册