☰
Spring AI系列之Tool Calling实战指南:用TaoToken统一Key打通MCP工具链
2026/10/17 15:05:27 网站建设 项目流程

1. 为什么要在 Spring Boot 里折腾 Tool Calling

如果你正在写一个 Spring Boot 项目,想让大模型不只是“聊天”,而是能真正去查数据库、算价格、调第三方接口,那 Tool Calling(工具调用)就是绕不开的一环。简单说,Tool Calling 就是让模型在回答前先“决定要不要用工具、用哪个工具、传什么参数”,然后由你的 Java 代码真正执行,再把结果回填给模型生成最终回复。它是 AI Agent 的最小可用闭环,也是 MCP(Model Context Protocol)生态里最常被复用的能力单元。

我这次的目标很明确:在一个 Spring Boot 3.x 项目里,用 Spring AI 的@Tool注解定义几个 Java 方法,注册成ToolCallbackProvider,然后通过 TaoToken 的统一 Key 和 API 通道去调用模型,触发工具执行。整条链路要能本地跑通“模型决策 → 调用 Java 方法 → 回填结果”这个闭环,并且给出可复制的application.yml、注册代码和一次 curl 验证。

适合谁看?有 Spring Boot 基础、想快速把 Tool Calling 接进现有项目的后端同学;或者已经在用 Cline、Claude Code 这类工具,想理解 MCP 工具链底层怎么和 Java 服务打通的开发者。你不需要先成为大模型专家,但至少要能跑起一个 Spring Boot 工程、会看 Maven 依赖。

这里有个容易混淆的点先讲清楚:Spring AI 里的 Tool Calling 和 MCP 不是二选一。本地@Tool是“进程内工具”,MCP 是“跨进程/跨应用工具协议”。入门阶段先用本地 Tool Calling 把闭环跑通,理解模型怎么选工具、参数怎么传、结果怎么回填,之后再迁移到 MCP 结构会顺很多。我试过直接上 MCP Server,结果卡在协议调试上,反而本地 Tool 十分钟就跑通了。

所以这篇的路线是:先定义工具类,再注册ToolCallbackProvider,然后配好 TaoToken 的 Base URL 和 Key,最后用 curl 验证模型确实调用了 Java 方法。每一步都给完整代码和配置,你照着改包名就能用。

2. TaoToken 前置准备:统一 Key 与 API 通道

在写代码之前,先把模型通道准备好。Spring AI 的 OpenAI Starter 需要一个base-url和api-key,TaoToken 在这里扮演的就是统一入口:你拿一个 Key,就能通过它的 API 通道调用多种模型,不用在代码里为每个模型厂商维护不同的地址和鉴权方式。对 Tool Calling 来说,这一点很关键,因为工具调用的请求体里会带tools字段,通道必须能正确透传并返回tool_calls。

先做三件事。第一,打开 TaoToken 官网注册并登录,地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。第二,进入控制台创建 API Key,控制台入口在 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,创建后复制那串sk-开头的 Key,后面配置里要用。第三,确认你要用的模型 ID,比如gpt-4o、claude-3-5-sonnet这类支持 function calling 的模型,模型列表可以在模型对话页查看:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。

这里要强调一个细节:Spring AI 的 OpenAI 客户端默认会往{base-url}/v1/chat/completions发请求,所以base-url要填 TaoToken 的 API 根地址,也就是https://taotoken.net/api,不要多加/v1,否则会变成/v1/v1/chat/completions直接 404。API Key 就填你刚复制的那串。模型 ID 填你确认支持工具调用的那个。

如果你还没决定用哪个模型,建议先用一个明确支持 function calling 的,比如gpt-4o或claude-3-5-sonnet。有些小模型虽然便宜,但对tools字段支持不完整,会出现“模型不调用工具、直接编答案”的情况,排查起来很费时间。我踩过的坑就是拿一个不支持工具的模型测了半天,最后发现换模型就好了。

另外,TaoToken 的 API 文档里有完整的请求示例和参数说明,接入前扫一眼能省很多事:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。文档里会告诉你tools字段的格式、tool_choice怎么设、返回的tool_calls结构长什么样,这些和 Spring AI 内部封装是对应的,理解了对排查问题很有帮助。

准备好 Key 和模型 ID 后,就可以进项目了。记住三个值:Base URL =https://taotoken.net/api,API Key = 你的sk-xxx,Model ID = 你选的模型名。后面application.yml里就填这三个。

3. 可复制配置:application.yml 与 ToolCallbackProvider 注册

这一节是核心,直接给可复制的配置和代码。先看 Maven 依赖,Spring AI 用 BOM 管理版本,这里用 1.0.0-M5,OpenAI Starter 负责和 TaoToken 通道通信。

<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>1.0.0-M5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> </dependencies>

然后是application.yml,路径是src/main/resources/application.yml。注意base-url填 TaoToken 的 API 根地址,api-key填你的 Key,model填支持工具调用的模型 ID。

server: port: 8080 spring: ai: openai: api-key: sk-你的TaoToken密钥 base-url: https://taotoken.net/api chat: options: model: gpt-4o temperature: 0.7

接下来定义工具类。用@Tool注解声明式定义,Spring AI 会自动扫描并生成工具描述。先写一个时间工具和一个计算工具,计算工具用 Java 内置的表达式解析,避免引入额外脚本引擎。

package com.example.ai.tool; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.ZoneId; @Component public class TimeTools { @Tool(description = "获取当前日期和时间,用于回答时间相关问题") public String getCurrentTime() { return LocalDateTime.now() .atZone(ZoneId.systemDefault()) .toString(); } }
package com.example.ai.tool; import org.springframework.ai.tool.annotation.Tool; import org.springframework.ai.tool.annotation.ToolParam; import org.springframework.stereotype.Component; @Component public class CalcTools { @Tool(description = "计算两个整数相加,返回结果") public String add( @ToolParam(description = "第一个整数") int a, @ToolParam(description = "第二个整数") int b) { return "计算结果:" + (a + b); } }

工具定义好后,关键是注册ToolCallbackProvider。Spring AI 提供了MethodToolCallbackProvider,可以把带@Tool的 Bean 转成工具回调。在配置类里注册,这样ChatClient就能拿到工具列表。

package com.example.ai.config; import com.example.ai.tool.CalcTools; import com.example.ai.tool.TimeTools; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ToolConfig { @Bean public ToolCallbackProvider toolCallbackProvider(TimeTools timeTools, CalcTools calcTools) { return MethodToolCallbackProvider.builder() .toolObjects(timeTools, calcTools) .build(); } }

最后是 Controller,注入ChatClient.Builder和ToolCallbackProvider,在调用时把工具传进去。注意.tools(toolCallbackProvider)这一步,它让模型知道有哪些工具可用。

package com.example.ai.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ToolCallingController { private final ChatClient chatClient; private final ToolCallbackProvider toolCallbackProvider; public ToolCallingController(ChatClient.Builder builder, ToolCallbackProvider toolCallbackProvider) { this.chatClient = builder.build(); this.toolCallbackProvider = toolCallbackProvider; } @GetMapping("/ai") public String callTool(@RequestParam String msg) { return chatClient.prompt(msg) .tools(toolCallbackProvider) .call() .content(); } }

到这里,配置和代码就齐了。三个关键点再确认一遍:base-url是https://taotoken.net/api,api-key是你的 TaoToken Key,model是支持工具调用的模型 ID。ToolCallbackProvider把@Tool方法注册进去,Controller 调用时通过.tools()传给模型。下一节验证请求。

4. 验证请求:curl 触发模型决策与 Java 方法回填

启动 Spring Boot 应用,看到Tomcat started on port 8080就说明起来了。然后开一个终端,用 curl 发请求。第一个测试问时间,模型应该会决定调用getCurrentTime工具。

curl "http://localhost:8080/ai?msg=现在几点了"

预期返回类似:现在是 2025-01-15T14:30:22.123+08:00。注意这个时间不是模型编的,而是你的 Java 方法LocalDateTime.now()返回的,模型只是把工具结果组织成了自然语言。这就是闭环跑通的标志。

第二个测试问加法,模型应该调用add工具,参数从问题里抽取。

curl "http://localhost:8080/ai?msg=帮我算一下 123 加 456 等于多少"

预期返回:123 加 456 等于 579。这里的 579 是 Java 方法算出来的,不是模型心算的。你可以故意问一个模型容易算错的数,比如98765 加 12345,如果返回111110,说明工具确实被调用了。

第三个测试混合场景,验证模型能在多个工具间选择。

curl "http://localhost:8080/ai?msg=现在几点,另外 10 加 20 等于多少"

预期返回会同时包含时间和加法结果,模型可能调用两个工具,也可能分步调用。如果只返回了时间没算加法,说明模型对多工具编排还不稳定,可以换一个工具调用能力更强的模型再试。

验证成功的核心信号有三个:第一,返回内容里包含只有 Java 方法才能产生的数据(比如精确到毫秒的当前时间);第二,应用日志里能看到工具被调用的记录(可以在@Tool方法里加一行log.info);第三,把api-key改错,请求会报 401,说明请求确实走了 TaoToken 通道。

如果想让验证更直观,可以在TimeTools.getCurrentTime()里加日志:

@Tool(description = "获取当前日期和时间,用于回答时间相关问题") public String getCurrentTime() { System.out.println(">>> 工具被调用: getCurrentTime"); return LocalDateTime.now() .atZone(ZoneId.systemDefault()) .toString(); }

再次 curl,控制台打印>>> 工具被调用: getCurrentTime,就百分百确认模型决策触发了 Java 方法。这个日志技巧在排查“模型到底有没有调工具”时特别有用。

5. 本篇常见错排查:401、local proxy failed 与 choices 解析

跑不通的时候,大部分问题集中在几个固定报错上。下面按真实报错对照排查。

401 Unauthorized。返回体里通常是{"error":{"message":"Invalid API key","type":"invalid_request_error"}}。原因就两个:api-key填错,或者 Key 被禁用/额度用完。检查application.yml里的api-key是不是完整的sk-开头字符串,有没有多余空格。如果确认 Key 没问题,去 TaoToken 控制台看下额度状态。注意不要用base-url去填 Key,两者别搞混。

Connection refused 或 local proxy failed。报错类似java.net.ConnectException: Connection refused或local proxy failed。这通常是base-url写错,比如写成了https://taotoken.net/api/v1,导致请求路径变成/api/v1/v1/chat/completions。正确写法是https://taotoken.net/api,Spring AI 会自动补/v1/chat/completions。另外检查本机网络是否能正常访问该地址,可以用curl https://taotoken.net/api/v1/models -H "Authorization: Bearer sk-xxx"先测通道。

模型不调用工具,直接回答。返回内容看起来正常,但 Java 方法没执行。原因通常是模型不支持 function calling,或者tools没传进去。先确认model是gpt-4o、claude-3-5-sonnet这类支持工具调用的。再检查 Controller 里有没有.tools(toolCallbackProvider),漏了这行模型就不知道有工具。还有一种情况是@Tool的description写得太模糊,模型判断不需要调用,把描述写具体一点。

读取 choices 报错。报错类似Cannot deserialize value of type ... from Object value (token 'JsonToken.START_OBJECT')或Error reading choices。这通常是通道返回的 JSON 结构和 Spring AI 预期不一致,常见于base-url指向了非标准 OpenAI 格式的地址。确认base-url是https://taotoken.net/api,并且模型 ID 拼写正确。如果模型 ID 不存在,有些通道会返回错误结构,也会导致解析失败。

OAuth 或鉴权头冲突。如果你同时配了其他鉴权方式,可能出现OAuth token相关报错。Spring AI 的 OpenAI Starter 默认用Authorization: Bearer {api-key},不要再额外配spring.ai.openai.oauth之类的东西。把application.yml精简到只有api-key、base-url、model三项,排除干扰。

排查顺序建议:先 curl 测通道通不通,再看应用日志里请求有没有发出去,最后看模型返回里有没有tool_calls。把logging.level.org.springframework.ai=DEBUG打开,能看到完整的请求和响应体,定位问题最快。

6. 从本地 Tool 到 MCP:下一步怎么走

本地 Tool Calling 跑通后,你已经掌握了 AI Agent 最核心的闭环:模型决策、工具执行、结果回填。接下来如果要做跨应用、跨语言的工具复用,就可以往 MCP 方向迁移。MCP 把工具定义标准化成 Server,任何支持 MCP 的客户端(包括 Claude Code、Cline 等)都能调用,不再局限于你的 Spring Boot 进程内。

迁移思路是:把现在@Tool方法里的逻辑抽成一个独立的 MCP Server,用 stdio 或 HTTP 暴露工具列表;Spring Boot 这边作为 MCP Client 去连接。这样你的 Java 业务逻辑可以同时被多个 AI 客户端复用。不过在迁移之前,建议先把本地 Tool 的description和参数校验打磨好,因为 MCP 的工具描述质量直接决定模型选工具的准确率。

如果你打算长期做编码类 Agent,或者需要把工具链接到 CI 流程里,可以了解下 Coding Plan 的用法:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。它更适合持续性的编码和 Agent 场景,和单次 API 调用是互补的。

最后给一个实用建议:把ToolCallbackProvider的注册和ChatClient的构建分开,工具注册用配置类,调用用 Controller,这样新增工具时只改配置类,Controller 不用动。工具多了以后,可以按业务域拆成多个ToolCallbackProvider,调用时按需传入,避免一次给模型太多工具导致选择困难。这套结构在本地跑通后,迁移到 MCP 也只是换个 Provider 实现的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询