1. 为什么 Spring AI 2.0.0 项目要单独配一个 settings.json
Spring AI 2.0.0 是 Spring 生态里用来对接大模型的抽象层,它把 ChatClient、EmbeddingModel、Tool Calling、MCP 这些能力统一成一套 Java API,让你不用为每家模型写一套调用代码。适合谁?正在用 Spring Boot 3.x 写后端、又想把模型对话或向量检索塞进现有服务的开发者。它本身不绑定任何一家厂商,真正决定“请求发到哪”的,是底层 OpenAI 兼容客户端的 base-url 和 api-key。
问题就出在这。很多同学在application.yml里写了spring.ai.openai.api-key,本地跑得好好的,一换环境就 401 或超时,排查半天发现是配置散落在 yml、环境变量、IDE 运行配置三处,谁覆盖谁说不清。我试过把模型通道参数收敛到一个独立的settings.json,由 Spring 启动时加载成配置源,好处是:Key、base-url、模型名、超时全部集中,换通道只改一个文件,代码零改动。
这篇就按这个思路走:用 TaoToken 作为统一 Key/API 通道,给出一份可复制的settings.json骨架,配好 Spring AI 2.0.0 的依赖坐标,写一个最小 ChatClient 调用,最后用启动日志和单次请求确认连通性。全程可跟做,不需要你先懂 MCP 或 RAG。
TaoToken 在这里的角色是统一入口:你拿一个 Key,就能通过 OpenAI 兼容协议访问多家模型,Spring AI 侧只需要认base-url和api-key两个值。官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 根地址是 https://taotoken.net/api ,注意这个地址后面不加任何查询参数。
2. 前置准备:依赖坐标与 settings.json 骨架
2.1 Maven 依赖怎么选
Spring AI 2.0.0 的 starter 命名沿用了spring-ai-starter-model-*的规则。走 OpenAI 兼容通道,用spring-ai-starter-model-openai即可,它内部就是标准 OpenAI 客户端,TaoToken 的兼容接口能直接吃。BOM 记得锁版本,否则子模块版本会飘。
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>2.0.0</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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>如果你用的是 Gradle,把 BOM 换成platform("org.springframework.ai:spring-ai-bom:2.0.0"),其余坐标一致。注意 Spring AI 2.0.0 要求 Spring Boot 3.4+ 和 JDK 17 起步,JDK 21 更稳。
2.2 settings.json 配置骨架
把下面这份放到src/main/resources/settings.json。字段名我按“通道 + 模型 + 超时”三块组织,方便你一眼定位。
{ "spring": { "ai": { "openai": { "base-url": "https://taotoken.net/api", "api-key": "sk-你的TaoToken密钥", "chat": { "options": { "model": "gpt-4o-mini", "temperature": 0.7 } }, "embedding": { "options": { "model": "text-embedding-3-small" } } } } }, "taotoken": { "connect-timeout-ms": 10000, "read-timeout-ms": 60000, "log-request": true } }几个关键点说清楚。base-url写https://taotoken.net/api,不要带尾部斜杠,也不要拼/v1,Spring AI 的 OpenAI 客户端会自己补路径。api-key先占位,真实值走环境变量注入,别提交到 Git。model填你账号下可用的模型名,不确定就先填一个通用对话模型,后面验证阶段会打印实际返回。
注意:
settings.json不是 Spring Boot 默认加载的文件名。要么在启动类里手动读成PropertySource,要么用spring.config.import引入。下一节给具体做法。
2.3 让 Spring 认识这个文件
最省事的做法是在application.yml里加一行导入,Spring Boot 3.x 支持 JSON 作为配置源:
spring: config: import: classpath:settings.json ai: openai: api-key: ${TAOTOKEN_API_KEY}这样settings.json提供骨架,application.yml用环境变量覆盖敏感字段,优先级清晰:环境变量 > yml > json。启动前设置TAOTOKEN_API_KEY,Windows 用set,macOS/Linux 用export,别写死在代码里。
3. 可复制配置:最小 ChatClient 调用示例
3.1 注入与调用
Spring AI 2.0.0 推荐用ChatClient的流式构建方式。下面这个 Controller 可以直接复制,路径/ai/chat,传参msg。
@RestController @RequestMapping("/ai") public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient = builder .defaultSystem("你是一个简洁的 Java 技术助手") .build(); } @GetMapping("/chat") public String chat(@RequestParam String msg) { return chatClient.prompt() .user(msg) .call() .content(); } }ChatClient.Builder由 starter 自动装配,它会读取spring.ai.openai.*下的配置。你不需要手动 new 任何 OpenAI 客户端,这是 Spring AI 2.0.0 相比 1.x 更顺手的地方。
3.2 超时参数怎么落到客户端
settings.json里的taotoken.connect-timeout-ms和read-timeout-ms是自定义字段,Spring AI 不认。要让它生效,得自己建一个RestClient.Builder定制 Bean,或者用 starter 暴露的定制点。简单做法是加一个配置类:
@Configuration public class HttpClientConfig { @Value("${taotoken.connect-timeout-ms:10000}") private int connectTimeout; @Value("${taotoken.read-timeout-ms:60000}") private int readTimeout; @Bean public RestClientCustomizer restClientCustomizer() { return builder -> builder.requestFactory( new JdkClientHttpRequestFactory( HttpClient.newBuilder() .connectTimeout(Duration.ofMillis(connectTimeout)) .build() ) ); } }这样连接超时和读取超时就绑到了底层 HTTP 客户端,模型响应慢的时候不会卡死线程。实测下来,读取超时给 60 秒对大多数对话模型够用,流式场景可以再放宽。
3.3 模型名与通道的对应关系
TaoToken 是统一通道,模型名按你实际开通的填。下面这张表帮你对照配置字段和实际含义:
| 配置项 | 示例值 | 作用 |
|---|---|---|
| base-url | https://taotoken.net/api | 请求根地址,不带 /v1 |
| api-key | sk-xxx | 身份凭证,走环境变量 |
| chat.options.model | gpt-4o-mini | 对话模型名 |
| embedding.options.model | text-embedding-3-small | 向量模型名 |
| temperature | 0.7 | 采样温度,0 更确定 |
模型名写错是最常见的 404 来源,验证阶段会专门看返回体里的报错信息。
4. 连通性验证:启动日志与单次请求
4.1 启动日志看什么
项目起来后,控制台会打印自动装配的 Bean 列表。搜OpenAiChatModel和OpenAiApi两个关键字,能看到 base-url 被正确注入。如果日志里出现baseUrl=https://taotoken.net/api,说明配置源生效了。如果还是默认的api.openai.com,说明settings.json没被加载,回去检查spring.config.import那行。
再确认端口和上下文路径,默认 8080。启动完成后用 curl 打一发:
curl "http://localhost:8080/ai/chat?msg=用一句话说明什么是Spring%20AI"4.2 成功返回长什么样
正常返回是一段纯文本,类似“Spring AI 是 Spring 生态中用于集成大模型的抽象层”。同时控制台会打印请求耗时。如果返回 401,是 Key 没注入或写错;返回 404,多半是模型名不对或 base-url 多了/v1;返回超时,看第 3.2 节的超时配置。
想更直观地确认模型通道,可以打开模型对话页面手动发一条同样的消息,对比返回风格是否一致。模型对话入口在 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite ,用它交叉验证能快速判断是代码问题还是通道问题。
4.3 用日志确认请求真的发出去了
把taotoken.log-request设为 true 后,可以在自定义拦截器里打印请求路径和状态码。简单加一个ClientHttpRequestInterceptor:
@Bean public ClientHttpRequestInterceptor loggingInterceptor() { return (request, body, execution) -> { ClientHttpResponse response = execution.execute(request, body); System.out.println("[TaoToken] " + request.getMethod() + " " + request.getURI() + " -> " + response.getStatusCode()); return response; }; }挂到RestClient.Builder上,每次调用都会打一行。看到POST https://taotoken.net/api/chat/completions -> 200,连通性就算彻底确认了。
5. 本篇常见错排查
5.1 401 Unauthorized
九成是 Key 没生效。检查顺序:环境变量名是否和 yml 里的${TAOTOKEN_API_KEY}一致;settings.json里的占位值是否被 yml 覆盖;IDE 运行配置里有没有单独设过旧 Key。Spring 的配置优先级里,环境变量高于配置文件,但 IDE 的运行配置可能又高于环境变量,这点最容易踩。
5.2 404 Not Found 或 model not found
先看 base-url 有没有多写/v1。Spring AI 的 OpenAI 客户端默认会拼/v1/chat/completions,你写https://taotoken.net/api就够了,写成https://taotoken.net/api/v1会变成/api/v1/v1/...。再看模型名是否在你账号下可用,换一个通用模型试。
5.3 启动报 settings.json 找不到
spring.config.import: classpath:settings.json要求文件在src/main/resources根目录。如果你放在子目录,路径要写全,比如classpath:config/settings.json。另外 JSON 里不能有注释,尾逗号也会导致解析失败,用 IDE 的 JSON 校验先过一遍。
5.4 请求卡住不返回
读取超时没配或配太大。按第 3.2 节把read-timeout-ms设成 60000,连接超时 10000。如果还是卡,检查本机网络是否能正常访问taotoken.net,用curl -I https://taotoken.net/api看响应头。
5.5 流式返回乱码
流式场景要设置Accept: text/event-stream,Spring AI 的stream()方法会处理。如果你自己拼 HTTP 请求,注意编码用 UTF-8。中文乱码多半是响应体没按 UTF-8 解,检查RestClient的defaultCharset。
6. 下一步:把 Key 管起来,把编码交给 Coding Plan
配置跑通只是第一步。真实项目里 Key 要轮换、要分环境、要审计调用量,这些在控制台里管理最省心。API Keys 管理入口在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到字段含义不清直接查文档比翻源码快。
如果你接下来要把 Spring AI 接进长期编码或 Agent 工作流,比如让模型持续读写代码、跑多轮工具调用,单次对话的额度模式就不太合适了。Coding Plan 入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite ,适合这种高频、长会话的场景。Claude Code 相关的接入说明在 https://taotoken.net/claude-code?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite ,需要的话可以对照配置。
最后留一个实用习惯:把settings.json里的api-key永远写成占位符,真实值只走环境变量,提交前用git diff扫一眼。这个动作能挡掉绝大多数密钥泄露事故,比任何加密方案都直接。