
1. 为什么 Spring AI 项目要统一 OpenAI 兼容 endpoint刚接触 Spring AI 的 Java 开发者最容易卡住的地方不是写代码而是配置。你新建一个 Spring Boot 项目引入spring-ai-openai-spring-boot-starter然后在application.yml里填api-key结果启动就报 401或者请求发出去半天没响应。问题往往出在你用的模型服务商和 Spring AI 默认的 OpenAI 地址对不上。Spring AI 从 1.0 开始把「模型接入」抽象成了ChatModel接口。OpenAI 兼容的 endpoint 意味着只要某个服务商暴露的 HTTP 接口路径、请求体、响应体结构和 OpenAI 的/v1/chat/completions一致Spring AI 的 OpenAI starter 就能直接连。这对 Java 开发者来说是个好消息——你不用为每家模型写一套适配代码改base-url和api-key就行。但实际项目里问题会变复杂。一个后端服务可能同时要调多个模型便宜的模型做分类强的模型做总结还有一个专门做代码补全。如果每个模型都配一套 Key、一套地址application.yml会变成一坨换环境时更容易漏改。更麻烦的是团队里每个人的 Key 不一样CI 环境又得单独配一套。我试过把 Key 硬编码在application.yml里提交到 Git结果被安全扫描拦下来返工了一下午。后来改成环境变量注入但本地调试时又得每次export很烦。再后来我把所有模型的调用通道收敛到一个统一的 OpenAI 兼容 endpoint 上Spring AI 侧只保留一份base-url和一份api-key模型差异通过model参数区分。这样配置清单从「N 个服务商 × M 个 Key」变成「1 个通道 1 个 Key N 个模型 ID」。这篇就是把这个收敛过程拆成可复制的步骤。你会看到依赖坐标怎么选、application.yml里base-url和api-key怎么写、ChatClient怎么发一次请求验证连通、返回结构长什么样、以及 401 和local proxy failed这类报错怎么排查。目标很明确让你本地跑起来一个能对话的 Spring AI 项目并且配置清单是干净的、可迁移的。适合谁看有 Java 基础、用过 Spring Boot、刚想试 Spring AI 但被配置卡住的开发者。不需要你懂大模型原理只需要你会改pom.xml和application.yml。2. TaoToken 作为统一 Key/API 通道的前置准备在动手改配置之前先把「通道」这件事说清楚。Spring AI 的 OpenAI starter 默认指向 OpenAI 官方地址但你可以通过spring.ai.openai.base-url把它指向任何 OpenAI 兼容的服务。TaoToken 提供的就是这样一个兼容 endpointhttps://taotoken.net/api。它的作用是让你用一份 Key、一个地址去调用多个模型而不需要为每个模型单独申请和配置。这里要区分两个概念base-url和完整请求路径。Spring AI 的 OpenAI 客户端会在base-url后面自动拼上/v1/chat/completions具体取决于版本和配置。所以你在application.yml里写的base-url应该是https://taotoken.net/api而不是完整的https://taotoken.net/api/v1/chat/completions。写错了会得到 404这个坑后面排障章节会细说。前置准备分三步。第一步拿到 Key。访问https://taotoken.net/api-keys这个 deep link 带utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite在控制台里创建一个 API Key。创建后立刻复制因为页面刷新后完整 Key 不会再显示。Key 的格式通常是一串以sk-开头的字符串。把它存到环境变量里不要写进代码# macOS / Linux export TAOTOKEN_API_KEYsk-你的key # Windows PowerShell $env:TAOTOKEN_API_KEYsk-你的key第二步确认你要用的模型 ID。TaoToken 的模型列表可以在控制台或文档里查到。常见的对话模型 ID 类似gpt-4o-mini、claude-3-5-sonnet这种命名。Spring AI 里通过spring.ai.openai.chat.options.model指定。注意模型 ID 是大小写敏感的写错了会返回model not found。第三步确认 Spring AI 版本。Spring AI 1.0.x 和 0.8.x 的配置前缀有差异。1.0.x 用的是spring.ai.openai.*0.8.x 有些属性叫spring.ai.openai.chat.*。这篇以 1.0.x 为准。如果你用的是 0.8.x配置项名字要对照官方迁移文档改。版本不匹配是「配置写了但不生效」的常见原因。注意不要把 Key 提交到 Git。用环境变量${TAOTOKEN_API_KEY}占位Spring Boot 启动时会自动解析。如果 CI 里跑把 Key 配到 CI 的 secret 里。到这里你手里应该有三样东西一个 Key、一个模型 ID、一个确定的 Spring AI 版本。接下来就是把这些填进项目。3. 可复制的依赖坐标与 application.yml 配置清单这一节是整篇的核心所有片段都可以直接复制。先看pom.xml。Spring AI 1.0.x 的依赖管理用 BOM这样你不用给每个 starter 写版本号。在dependencyManagement里引入spring-ai-bom然后在dependencies里引入spring-ai-starter-model-openai。注意如果你之前用的是spring-ai-openai-spring-boot-starter那是旧坐标1.0.x 改成了spring-ai-starter-model-openai。坐标写错会导致ClassNotFoundException。properties java.version17/java.version spring-boot.version3.4.0/spring-boot.version spring-ai.version1.0.0/spring-ai.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version${spring-boot.version}/version typepom/type scopeimport/scope /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency /dependenciesJDK 最低 17Spring Boot 要求 3.4.x 或 3.5.x。如果你用 JDK 21 也没问题但java.version要写对。Maven 用 3.9.x 比较稳。接下来是application.yml。这是配置清单里最关键的一段。路径是src/main/resources/application.yml。spring: ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.7逐行解释。base-url写https://taotoken.net/api不要带/v1也不要带/chat/completions。Spring AI 的 OpenAI 客户端会自己拼路径。api-key用${TAOTOKEN_API_KEY}引用环境变量这样 Key 不进代码库。model填你在控制台确认过的模型 ID。temperature是可选参数0 到 2 之间越低越稳定越高越发散。如果你需要同时配多个模型不要复制多份spring.ai.openai而是用 Spring AI 的多模型配置或者自定义OpenAiApiBean。但入门阶段先跑通一个模型再说。配置多了容易互相覆盖。注意base-url结尾不要加斜杠。https://taotoken.net/api/和https://taotoken.net/api在某些 HTTP 客户端里行为不一致可能拼出//v1/chat/completions导致 404。还有一个容易忽略的点Spring AI 1.0.x 默认会尝试自动配置OpenAiChatModel。如果你只引入了 starter 但没配api-key启动时会报api-key must be set。所以环境变量一定要在启动前export好或者在 IDE 的 Run Configuration 里配好。配置写完后启动类不需要额外加EnableAi之类的注解Spring Boot 自动配置会扫描到。你只需要在需要的地方注入ChatClient或OpenAiChatModel。4. 用 ChatClient 发一次请求验证连通与返回结构配置写完下一步是验证。不要急着写业务代码先用一个最小的 Controller 发一次请求确认通道是通的。Spring AI 1.0.x 推荐用ChatClient它比直接注入OpenAiChatModel更灵活。ChatClient可以通过ChatClient.Builder构建Builder 会被自动配置注入。package com.example.demo.controller; import org.springframework.ai.chat.client.ChatClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; RestController RequestMapping(/chat) public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient builder.build(); } PostMapping(/simple) public MapString, Object simple(RequestBody MapString, String body) { String message body.get(message); if (message null || message.isBlank()) { return Map.of(error, message 不能为空); } String content chatClient.prompt() .user(message) .call() .content(); return Map.of(reply, content); } }这段代码做了三件事构造ChatClient、接收用户消息、调用模型并返回文本。chatClient.prompt().user(message).call().content()是 1.0.x 的链式 API。content()返回的是模型回复的纯文本。如果你需要拿到完整的ChatResponse包含 token 用量、finish reason 等把.content()换成.chatResponse()。启动项目mvn spring-boot:run然后用 curl 发一次请求curl -X POST http://localhost:8080/chat/simple \ -H Content-Type: application/json \ -d {message:用一句话解释什么是 Spring AI}如果通道正常你会看到类似这样的返回{ reply: Spring AI 是 Spring 生态中用于简化大模型应用开发的框架它把模型调用抽象成统一的接口。 }返回结构里reply是我们自己包的字段。如果你直接返回ChatResponse结构会包含result、metadata等。metadata里有usage能看到promptTokens和completionTokens。这个对排查「请求发出去了但没返回」很有用——如果usage里 token 数是 0说明请求根本没到模型。实测下来第一次调用可能会慢几秒因为要建立连接。后续调用会快很多。如果你用的是带「深度思考」的模型响应时间会更长入门阶段建议先用轻量模型验证连通性。提示如果返回的是空字符串先检查model参数是否写对。模型 ID 错误时有些服务商会返回空内容而不是报错。到这里连通性验证完成。你有了一个能跑的 Spring AI 项目配置清单只有一份base-url和一份api-key。5. 本篇常见报错排查401、local proxy failed、reading choices这一节按真实报错来。你大概率会遇到下面几个。401 Unauthorized。最常见。原因有三个Key 没配、Key 配错、Key 过期。先确认环境变量是否生效在启动日志里搜api-key看 Spring AI 打印的配置里 Key 是不是${TAOTOKEN_API_KEY}原样说明没解析到。如果是原样说明环境变量没设。在 IDE 里跑的话Run Configuration 的 Environment variables 里加TAOTOKEN_API_KEYsk-xxx。另外Key 前后不要有空格复制时容易带上换行。local proxy failed / Connection refused。这个报错通常出现在你本地配了 HTTP 代理但代理没启动或者代理规则把taotoken.net拦了。检查你的HTTP_PROXY/HTTPS_PROXY环境变量临时清掉再试unset HTTP_PROXY HTTPS_PROXY如果你在用 IDE检查 IDE 的 Proxy 设置。Spring AI 底层用的是 Java 的HttpClient它会读 JVM 的-Dhttp.proxyHost参数。启动命令里如果有这些参数去掉。Error reading choices / Cannot deserialize value of typejava.util.List。这个报错说明响应体结构和 Spring AI 期望的不一致。常见原因是base-url写错了比如写成了https://taotoken.net/api/v1导致实际请求路径变成/api/v1/v1/chat/completions服务端返回了一个 HTML 错误页Jackson 解析 HTML 就报这个错。解决方法是把base-url改回https://taotoken.net/api不带/v1。OAuth / token endpoint 相关报错。如果你看到OAuth字样说明你引入的 starter 不是 OpenAI 兼容的那个而是某个需要 OAuth 认证的 starter。检查pom.xml里是不是误引入了spring-ai-starter-model-azure-openai或类似坐标。Azure 的认证方式和 OpenAI 不同入门阶段先用spring-ai-starter-model-openai。model not found。模型 ID 写错或者你的 Key 没有该模型的权限。去控制台确认模型 ID 拼写注意大小写和连字符。排查顺序建议先看启动日志里base-url和api-key的解析结果再用 curl 直接打https://taotoken.net/api/v1/chat/completions确认通道本身是通的最后才怀疑代码。大部分问题都在配置层不在代码层。6. 把配置清单固化下来下一步怎么走跑通之后把这份配置清单固化到项目里。application.yml里的base-url和model可以提交api-key用环境变量占位。团队协作时在 README 里写清楚需要设置TAOTOKEN_API_KEY新人克隆下来配一下就能跑。如果你要接多个模型不要复制多份spring.ai.openai配置。正确做法是定义一个OpenAiApiBean手动指定base-url和api-key然后基于它构建多个ChatClient每个ChatClient用不同的model参数。这样配置集中在一处改起来不容易漏。下一步可以做的事把ChatClient的调用封装成 Service加上重试和超时用ChatResponse的metadata记录 token 用量把对话历史用MessageChatMemoryAdvisor管起来。这些都在 Spring AI 的文档里有例子。如果你要长期跑编码类任务或者 Agent可以看看 Coding Plan 的接入方式https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite它针对长会话和工具调用做了优化。验证模型本身的能力用模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite直接试更快。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有各语言的完整示例。最后提醒一句base-url和api-key这两项在任何 OpenAI 兼容的客户端里都是成对出现的。换通道时两个一起换不要只换一个。只换 Key 不换地址请求会打到旧服务商只换地址不换 Key会 401。这个坑我踩过排查了半小时才发现是漏改了base-url。