把 Spring AI 的 OpenAI starter 接到 Router One
Spring AI 为 Spring Boot 应用提供统一的 ChatClient 模型 API。它的 OpenAI starter 发送的是 Chat Completions 请求,所以一把 Router One Key 就能调用目录里在 /v1/chat/completions 上提供服务的所有聊天模型,每次请求都有成本 Trace。不同版本线唯一的差别在 base-url:Spring AI 2.0.x 把这个值交给官方 openai-java SDK,由 SDK 自行拼接 /chat/completions;1.x 则自己拼接 /v1/chat/completions。本指南两条版本线都覆盖,最后用一次普通调用和一次流式输出做验证。
引入 Spring AI BOM 和 OpenAI starter
Spring AI 的正式版发布在 Maven Central,不需要额外配置仓库。导入 spring-ai-bom,再添加 spring-ai-starter-model-openai;这个 starter 会带上 OpenAI 聊天模型实现、自动配置,以及本指南要注入的 ChatClient.Builder。BOM 版本要和你的 Spring Boot 版本线匹配:Spring AI 2.0.x(写作时为 2.0.1)支持 Spring Boot 4.0.x 和 4.1.x,Spring AI 1.1.x(1.1.8)支持 Spring Boot 3.4.x 和 3.5.x。两条版本线的依赖坐标相同,只有版本号不同。
<!-- pom.xml: Spring AI 2.0.x on Spring Boot 4.x; use 1.1.8 with Spring Boot 3.4/3.5 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>2.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>把 Spring AI 配置到 Router One base URL
Spring AI 2.0.x 只需在 src/main/resources/application.yml 里设置三个属性。base-url 填 https://api.router.one/v1,要带 /v1:2.0 会把这个值原样传给 openai-java SDK,由 SDK 自行在后面拼接 /chat/completions(SDK 自己的默认地址本来就以 /v1 结尾,虽然 2.0.1 文档的属性表仍写着旧的纯主机默认值)。api-key 通过 Spring 占位符读取 ROUTER_ONE_API_KEY,这个变量名是本指南定义的,刻意不用 OPENAI_API_KEY——因为属性留空时,2.0 会回退读取环境里的 OPENAI_API_KEY 和 OPENAI_BASE_URL。chat.model 填目录里的精确模型 ID,若 ID 自带 provider 前缀也要保留;2.0 去掉了原来 chat.options.* 里的 options 这一层,旧名字仍能绑定,但已标记为弃用:
# src/main/resources/application.yml: Spring AI 2.0.x (Spring Boot 4.x)
spring:
ai:
openai:
base-url: https://api.router.one/v1
api-key: ${ROUTER_ONE_API_KEY}
chat:
model: <model-id-from-/models>
# Shell (macOS/Linux) before starting the app:
# export ROUTER_ONE_API_KEY=sk-your-router-one-keySpring AI 1.x:主机根地址加 completions-path
Spring AI 1.0.x 和 1.1.x 自己拼接请求地址:base-url 加上 chat.completions-path 属性,后者默认值是 /v1/chat/completions。所以在 1.x 上,base-url 填不带 /v1 的主机根地址 https://api.router.one,模型 ID 写在 chat.options.model 下。如果这里仍然带着 /v1,请求会被发到 /v1/v1/chat/completions。下面的 completions-path 一行就是默认值,写出来只是为了让拼接方式一目了然。另外,1.x 会通过属性默认把 temperature 设为 0.7,而 2.0 只在你设置时才发送 temperature:
# application.yml: Spring AI 1.0.x / 1.1.x (Spring Boot 3.4 / 3.5)
spring:
ai:
openai:
base-url: https://api.router.one
api-key: ${ROUTER_ONE_API_KEY}
chat:
completions-path: /v1/chat/completions
options:
model: <model-id-from-/models>各版本线该填什么
两条版本线都是 Maven Central 上的正式版(写作时为 2.0.1 和 1.1.8),依赖坐标相同;1.0 之前里程碑版本使用的 spring-ai-openai-spring-boot-starter 这个名字止步于 1.0.0-M6。按你运行的 Spring Boot 版本线对照下表:
| Spring AI | Spring Boot | spring.ai.openai.base-url | 模型属性 | 路径由谁拼接 |
|---|---|---|---|---|
| 2.0.x(2.0.1) | 4.0.x / 4.1.x | https://api.router.one/v1 | spring.ai.openai.chat.model | openai-java SDK 拼接 /chat/completions |
| 1.1.x(1.1.8)、1.0.x(1.0.9) | 3.4.x / 3.5.x | https://api.router.one | spring.ai.openai.chat.options.model | spring.ai.openai.chat.completions-path,默认 /v1/chat/completions |
用 ChatClient 各做一次普通调用和流式调用
注入自动配置的 ChatClient.Builder,构建 ChatClient,先做一次普通 call()——模型和端点都来自上面的属性,代码里不用重复。然后调用 stream().content(),它返回 Reactor 的 Flux<String>;Spring AI 的 ChatClient 文档说明流式输出运行在响应式技术栈上。启动应用,确认两段输出都正常,再去加工具调用、对话记忆或结构化输出;这些都留在你的应用里,网关只记录每次模型请求:
package com.example.demo;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
class RouterOneSmokeTest {
@Bean
CommandLineRunner routerOneSmokeTest(ChatClient.Builder builder) {
ChatClient chatClient = builder.build();
return args -> {
// One plain call first; the model comes from spring.ai.openai.chat.model
String reply = chatClient.prompt()
.user("Reply with one short greeting.")
.call()
.content();
System.out.println(reply);
// Then streaming: a Flux<String> from the same ChatClient
chatClient.prompt()
.user("Count from 1 to 5, one number per line.")
.stream()
.content()
.doOnNext(System.out::print)
.blockLast();
};
}
}Spring AI 该填哪个模型 ID?
从 /models 页复制精确的模型 ID,保留大小写、连字符和版本后缀,不要用展示名称代替。打开该模型的详情页,核对支持的 API 端点、上下文窗口和工具调用等能力,再与 Spring AI 当前选择的 provider 和功能对应。模型出现在目录里,不等于当前客户端能调用它的所有功能。建议为每个工具单独建 API Key,并设置 maxSpend 消费上限。
Spring AI 用的是哪种 API 协议?
OpenAI 兼容描述的是接口格式,不能据此推断 Chat Completions(/v1/chat/completions)、Responses(/v1/responses)和 Anthropic Messages(/v1/messages)可以互换。先核对工具当前版本、provider 配置和实际请求路径,再查模型详情与 API 兼容性事实页。一次普通对话成功,也不能证明服务端工具、历史状态或文件编辑功能都受支持。
在 trace 里验证 Spring AI 的调用
先在 Spring AI 发出一次简单文本请求,再到 Dashboard → Logs 按时间、模型和 request_id 核对 trace:tokens、花费、延迟和状态码。成功后再分别验证流式输出、工具调用和多轮历史。失败时保留实际请求路径、完整错误消息及 request_id;没有对应日志时,先查客户端配置和网络,不能仅凭客户端报错认定是网关或上游故障。
常见问题
Spring AI 的 OpenAI starter 用的是 Responses API 吗?
不是。这个 starter 把 Chat Completions 请求发到 /v1/chat/completions;Spring AI 2.0.1 文档明确写着 Responses API 是另一个端点,当前客户端不支持。因此只存在于 /v1/responses 上的功能(托管工具、previous_response_id、对话状态)在这个 starter 里用不到,模型是否支持 Responses 在这里也不重要——要看的是模型详情页是否列出 POST /v1/chat/completions。
从 Spring AI 1.x 升到 2.0 之后请求变成 404,哪里变了?
变了两处。一是 base-url 现在必须带 /v1:2.0 把它直接交给 openai-java SDK,也不再有 completions-path 属性;沿用旧的主机根地址时,请求会发到 /chat/completions,网关返回 404 的 not_found 错误,消息里会直接说明 OpenAI 兼容客户端需要以 /v1 结尾的 base URL。二是 chat.options.* 属性改成了 chat.*,旧名字仍能绑定,但已标记为弃用并将移除。两处都改完后,重新跑一次普通调用。
为什么一次 ChatClient 调用在 Logs 里出现好几条请求?
客户端重试。2.0 里 openai-java SDK 会对 408、409、429、5xx 响应和连接错误重试,次数上限是 spring.ai.openai.max-retries(文档默认 3);1.x 则由 spring.ai.retry 重试模板处理,上限是 spring.ai.retry.max-attempts(文档默认 10),默认不重试 4xx,除非打开 on-client-errors。每一次到达网关的尝试都是独立请求,有各自的 Trace 和费用;而网关在同一次请求内做的同系列故障转移只算一条 Trace。批处理任务建议调低重试次数,并给应用单独配一把设了 maxSpend 的 Key。
调用 Claude 系列模型需要换 Anthropic starter 吗?
本指南的配置不需要。网关的 /v1/chat/completions 服务目录里的所有聊天模型,从 /models 复制的 Claude 系列 ID 和其他模型一样填进 chat.model 即可。Spring AI 的 Anthropic starter 走 /v1/messages 的 Anthropic Messages 格式,用的是自己的 spring.ai.anthropic.* 属性,不在本指南范围内,需要单独验证。
1.x 上第一次调用就报 400,错误里提到 temperature,是网关的问题吗?
通常不是。1.x 默认通过属性发送 temperature 0.7,而 Spring AI 文档说明部分 GPT-5 系列模型不接受 temperature 参数。到 Logs 里看完整错误和 request_id:点名该参数的 400 是模型拒绝了它。2.0 只在你设置时才发送 temperature。针对该模型调整 temperature 设置即可,不要去改 base-url 或模型 ID。
Spring AI 能通过网关用哪些模型?
选用当前目录中同时支持 Spring AI 所用端点和所需功能的模型。精确 ID、当前单价与能力以 /models 及模型详情为准;不要仅按 GPT、Claude 等系列名判断兼容性。客户端能列出模型,只证明模型发现成功,仍需验证实际调用。
能列出模型,但调用报 400 或 404,怎么办?
先记录实际请求路径和错误消息,再核对精确模型 ID。400 可能是参数、工具类型或模型与端点不匹配;404 可能是请求路径或资源不存在,不能直接判定模型下线。若错误提示 must be called via,按它指明的端点调整客户端 provider,或换用支持当前端点的模型。不要在所有工具里统一增删 /v1 或 /chat/completions。
中国大陆能直连吗?
能。网关在大陆可直连、无需 VPN,配置与全球环境完全一致。
报 401/402/403/429 怎么排查?
先到 Dashboard → Logs 核对请求及错误消息。401 查 Key 是否传入和有效;402 查钱包余额与 maxSpend;403 查 Key 权限和访问限制;429 查请求频率、token 限额及上游限流,按错误来源处理。保留 request_id,再按错误码速查页逐项排查。