1. Spring AI 1.0 GA 发布后,MCP 与 RAG 怎么快速跑通
Spring AI 1.0 GA 正式发布之后,很多做 Java 后端的同学第一反应是:终于不用在 LangChain4j 和 Spring AI 之间反复横跳了。但真正动手时,卡点往往不在框架本身,而在模型接入层——ChatClient 要配 OpenAI 的 Key,Embedding 要配另一家的 Key,MCP 工具调用可能又指向第三个 endpoint。配置文件越写越长,环境变量越堆越多,本地跑通、换台机器就报 401。
这篇内容聚焦一个具体场景:用 Spring AI 1.0 GA 的 MCP 工具调用和 RAG 检索增强两条主线,把模型 endpoint 与 API Key 统一指向 TaoToken,让 application.yml 里只维护一份 base-url 和一份 key。适合已经会 Spring Boot、想快速验证 Spring AI 1.0 新特性的后端开发者,也适合正在评估 MCP 协议落地方式的架构同学。
我会给出可复制的 Maven 依赖、application.yml、MCP 服务注册代码、RAG 向量库接入步骤,以及一次完整的对话验证和日志排查动作。全程按「能跟着敲」的标准写,不跳步。
先说清楚 TaoToken 在这个链路里的位置:它是一个兼容 OpenAI 协议的统一模型接入层,提供 https://taotoken.net/api 作为 base-url,你拿一个 Key 就能在 ChatClient、EmbeddingModel、MCP 工具调用之间复用。Spring AI 的 OpenAI Starter 本身支持自定义 base-url,所以接入成本很低。
2. TaoToken 前置准备:一个 Key 覆盖 Chat 与 Embedding
2.1 为什么要在 Spring AI 里做 endpoint 统一
Spring AI 1.0 GA 的 ChatClient 设计目标是「可移植」,官方支持 20+ 模型提供商。可移植的代价是:每个提供商有自己的 starter、自己的配置前缀、自己的鉴权方式。你在 application.yml 里会看到类似这样的结构:
spring: ai: openai: api-key: ${OPENAI_API_KEY} anthropic: api-key: ${ANTHROPIC_API_KEY} zhipuai: api-key: ${ZHIPUAI_API_KEY}一旦 MCP 工具调用需要换模型、RAG 的 Embedding 又想用另一家,配置就会分裂成三份。更麻烦的是团队协作:每个人本地环境变量不一样,CI 里还要再配一套。
TaoToken 的思路是把这些收敛成一份。因为它的 API 兼容 OpenAI 的/v1/chat/completions和/v1/embeddings,Spring AI 的spring-ai-openai-spring-boot-starter只要改base-url就能指向它。这样 ChatClient、EmbeddingModel、甚至 MCP 客户端内部发起的模型请求,都走同一个出口。
2.2 拿 Key 与确认 base-url
操作路径很直接:打开 https://taotoken.net/api-keys ,创建一个 API Key,复制保存。注意这个 Key 只在创建时完整显示一次,后面只能看到前缀。
base-url 用https://taotoken.net/api,不要带末尾斜杠。Spring AI 的 OpenAI 客户端会在后面拼接/v1/chat/completions,所以最终请求地址是https://taotoken.net/api/v1/chat/completions。
注意:不要把 Key 硬编码进 application.yml 提交到 Git。用环境变量
${TAOTOKEN_API_KEY}引用,本地用 IDE 的 EnvFile 或.env加载。
2.3 模型 ID 怎么选
TaoToken 的模型列表在 https://taotoken.net/models 可以查。Spring AI 里spring.ai.openai.chat.options.model填的就是模型 ID。做 MCP 工具调用时,建议选支持 function calling 的模型;做 RAG 的 Embedding 时,选一个 embedding 模型,比如常见的text-embedding-3-small这类。
这里有个容易踩的坑:Chat 模型和 Embedding 模型是两个不同的 model ID,但可以共用同一个 Key 和同一个 base-url。Spring AI 允许你分别配置:
spring: ai: openai: chat: options: model: your-chat-model-id embedding: options: model: your-embedding-model-id这样一份 Key 就同时覆盖了两条链路。
3. 可复制配置:application.yml 与 MCP 服务注册
3.1 Maven 依赖片段
先确认 Spring AI 1.0.0 的 BOM 导入。在pom.xml的dependencyManagement里加:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>1.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>然后在dependencies里加三个 starter:OpenAI 模型、MCP 客户端、以及一个向量库。这里用内存向量库SimpleVectorStore做最小示例,生产环境可以换 PGVector 或 Milvus。
<dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-simple</artifactId> </dependency> </dependencies>注意 1.0 GA 之后 starter 命名有调整,模型相关的 starter 统一带-model-中缀,比如spring-ai-starter-model-openai。如果你从 M 版本升级过来,旧的spring-ai-openai-spring-boot-starter要换掉,否则会报找不到自动配置类。
3.2 application.yml 完整配置
这是本篇最核心的可复制片段,路径和原文一致:
server: port: 8080 spring: application: name: spring-ai-mcp-rag-demo ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: your-chat-model-id temperature: 0.7 embedding: options: model: your-embedding-model-id mcp: client: enabled: true name: demo-mcp-client version: 1.0.0 type: SYNC request-timeout: 30s几个关键点解释一下。base-url指向 TaoToken,api-key用环境变量注入。mcp.client.type: SYNC表示同步客户端,适合最小示例;如果要流式,改成ASYNC。request-timeout设 30 秒,因为 MCP 工具调用可能涉及外部服务,默认值偏短容易超时。
提示:如果你同时要用 MCP Server 暴露自己的工具,再加
spring-ai-starter-mcp-server,并在 yml 里配spring.ai.mcp.server相关项。本篇聚焦客户端调用,Server 端不展开。
3.3 MCP 服务注册代码
Spring AI 1.0 的 MCP 客户端通过McpSyncClient管理连接。最小注册方式是在配置类里声明一个McpSyncClientBean,并指定要连接的 MCP 服务器。这里用 stdio 方式连接一个本地 MCP 服务做演示:
@Configuration public class McpClientConfig { @Bean public McpSyncClient mcpSyncClient() { ServerParameters params = ServerParameters.builder("npx") .args(List.of("-y", "@modelcontextprotocol/server-filesystem", "/tmp/mcp-demo")) .build(); McpClientTransport transport = new StdioClientTransport(params); McpSyncClient client = McpClient.sync(transport) .requestTimeout(Duration.ofSeconds(30)) .build(); client.initialize(); return client; } }这段代码做了三件事:用npx拉起一个文件系统 MCP 服务,通过 stdio 建立传输通道,初始化客户端。client.initialize()会完成协议握手,如果这里抛异常,通常是 MCP 服务没装好或者路径不对。
注册完客户端后,Spring AI 会自动把 MCP 工具暴露成ToolCallback,ChatClient 调用时带上.tools()就能触发。下面是一个把 MCP 工具挂到 ChatClient 的示例:
@RestController public class ChatController { private final ChatClient chatClient; private final McpSyncClient mcpSyncClient; public ChatController(ChatClient.Builder builder, McpSyncClient mcpSyncClient) { this.mcpSyncClient = mcpSyncClient; this.chatClient = builder .defaultSystem("你是一个可以调用文件系统工具的助手") .build(); } @GetMapping("/chat") public String chat(@RequestParam String message) { return chatClient.prompt() .user(message) .tools(new McpToolCallbackProvider(mcpSyncClient)) .call() .content(); } }McpToolCallbackProvider是 1.0 GA 提供的适配器,把 MCP 工具转成 Spring AI 的ToolCallback。这样模型在对话中如果判断需要读文件,就会自动发起工具调用,Spring AI 负责执行并把结果回填给模型。
4. RAG 向量库接入与一次对话验证
4.1 RAG 接入步骤
RAG 的核心是把文档切块、向量化、存进向量库,检索时用问题向量去匹配。Spring AI 1.0 提供了QuestionAnswerAdvisor和RetrievalAugmentationAdvisor两种方式。最小示例用前者。
先注入VectorStore和EmbeddingModel,把一段文档写进去:
@Service public class RagIngestService { private final VectorStore vectorStore; public RagIngestService(VectorStore vectorStore) { this.vectorStore = vectorStore; } public void ingest() { List<Document> docs = List.of( new Document("Spring AI 1.0 GA 于 2025 年 5 月发布,支持 MCP 协议和 RAG。"), new Document("MCP 是 Model Context Protocol 的缩写,用于标准化工具调用。"), new Document("TaoToken 提供兼容 OpenAI 的 API,base-url 是 https://taotoken.net/api。") ); vectorStore.add(docs); } }vectorStore.add()内部会调用EmbeddingModel把文本转成向量。因为 EmbeddingModel 已经指向 TaoToken,所以这一步的请求也走统一出口。
然后在 ChatClient 调用时挂上QuestionAnswerAdvisor:
@GetMapping("/rag") public String rag(@RequestParam String question) { return chatClient.prompt() .user(question) .advisors(new QuestionAnswerAdvisor(vectorStore)) .call() .content(); }QuestionAnswerAdvisor会自动做三件事:把问题向量化、去向量库检索 top-k 相似文档、把检索结果拼进 prompt 上下文。你不需要手写检索逻辑。
4.2 一次对话验证
启动应用后,先调/chat验证 MCP 工具调用:
curl "http://localhost:8080/chat?message=列出/tmp/mcp-demo目录下的文件"预期返回里会包含目录下的文件名。如果模型判断需要调用工具,日志里会看到 MCP 工具调用的记录。
再调/rag验证检索增强:
curl "http://localhost:8080/rag?question=TaoToken的base-url是什么"预期返回会包含https://taotoken.net/api。这说明向量检索命中了第三条文档,并且模型基于检索结果回答了问题。
4.3 日志里该看什么
Spring AI 1.0 的日志分几层。开logging.level.org.springframework.ai=DEBUG后,你能看到:
OpenAiApi发出的 HTTP 请求,确认 base-url 是不是https://taotoken.net/apiSimpleVectorStore的 add 和 similaritySearch 记录- MCP 客户端的 initialize 和 tool call 日志
如果请求发出去了但返回 401,先检查环境变量TAOTOKEN_API_KEY有没有被正确加载。可以在启动类里加一行System.out.println(System.getenv("TAOTOKEN_API_KEY") != null)快速确认。
5. 本篇常见错排查:401、local proxy failed 与 reading choices
5.1 401 Unauthorized
最常见的报错长这样:
org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]原因通常是三个:Key 没配、Key 配错、或者 base-url 写成了https://taotoken.net少了/api。检查顺序是:先确认环境变量存在,再确认 yml 里api-key引用的是${TAOTOKEN_API_KEY}而不是字面量,最后确认base-url是https://taotoken.net/api。
还有一种隐蔽情况:你用了spring.ai.openai.api-key但同时又配了spring.ai.openai.chat.api-key,后者会覆盖前者。统一只配一处。
5.2 local proxy failed
这个报错通常出现在 MCP 客户端初始化阶段:
java.io.IOException: local proxy failed to startMCP 的 stdio 传输依赖本地进程拉起。如果npx不在 PATH 里,或者@modelcontextprotocol/server-filesystem没装,就会报这个。解决方式是先在终端手动跑一遍:
npx -y @modelcontextprotocol/server-filesystem /tmp/mcp-demo能正常启动再回到代码里。另外 Windows 上npx要写成npx.cmd,否则 Java 的 ProcessBuilder 找不到可执行文件。
5.3 reading choices 相关报错
如果你看到类似Error reading choices from response或者反序列化失败:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.util.List<...>` from Object value这通常是模型返回格式和 Spring AI 预期不一致。检查两点:一是base-url是否指向了兼容 OpenAI 的 endpoint,二是模型 ID 是否拼写正确。TaoToken 的模型 ID 在 https://taotoken.net/models 查,不要凭记忆写。
还有一种情况是流式和非流式混用。chatClient.prompt().stream()返回的是Flux,如果你用.call()去接就会报错。确认调用方式和返回类型匹配。
5.4 OAuth 相关报错
MCP 远程服务器如果启用了 OAuth,客户端初始化时会报:
McpError: OAuth authentication requiredSpring AI 1.0 的 MCP 客户端支持 OAuth,但需要在McpClient.sync()时传入OAuth2ClientCredentials或OAuth2AuthorizationCode的 provider。最小示例里用的是本地 stdio 服务,不涉及 OAuth。如果你连的是远程 MCP 服务,参考官方文档的 OAuth 配置章节。
注意:排查时优先看第一个异常,后面的异常往往是连锁反应。比如 401 会导致后续所有请求失败,日志里会刷一堆错,但根因只有一个。
6. 统一 Key 之后,下一步可以做什么
把 endpoint 和 Key 统一到 TaoToken 之后,Spring AI 1.0 的几条主线就串起来了。ChatClient 负责对话,EmbeddingModel 负责向量化,MCP 负责工具调用,RAG 负责检索增强,它们共用一份配置。这意味着你换模型时只改一个 model ID,换环境时只改一个环境变量。
如果你要长期跑编码类 Agent,或者需要更稳定的并发额度,可以看看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。如果只是想先验证模型对话效果,直接开 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 试几句。接入过程中遇到配置问题,文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。
最后留一个实操建议:把application.yml里的base-url和api-key抽成 profile 无关的公共配置,用spring.config.import引入一个taotoken.yml。这样本地、测试、生产三套环境只需要替换taotoken.yml,Spring AI 的业务代码一行不用动。我试过在三个环境之间切换,改一个文件就够了,比在每个 profile 里重复写 base-url 省事得多。