1. Spring AI 上生产后我踩过的五个致命错误Spring AI 把 Java 接入大模型的门槛压得很低一个ChatClient加几行配置就能跑通对话。但能跑通 Demo 和能扛住生产流量中间隔着一整条排障链路。我在一个日请求量六位数的 Java 服务里接 Spring AI上线第一周就被五个问题轮番教育Key 硬编码导致轮换困难、超时设置过短让长回答频繁中断、流式响应没有背压把内存顶爆、异常处理一刀切导致限流和超时无法区分、Token 消耗没有监控让账单失控。这五个错误里前四个都能靠配置和代码骨架解决唯独 API Key 与 Token 管理混乱是根子上的问题——它同时牵扯安全、多模型切换、成本核算三件事。本文会先拆这五个坑的现象和修法再给出一套可复制的application.yml与统一 Key 配置骨架最后用启动验证和错误日志排查动作收尾。适合已经在用 Spring AI、准备或刚上生产环境的 Java 开发者。核心检索词就三个Spring AI、Java 生产环境、API Key 与 Token 管理。2. 错误一API Key 硬编码轮换一次改一次代码2.1 现象与风险最常见的写法是把 Key 直接写进application.yml# 危险写法 spring: ai: openai: api-key: sk-xxxxxxxxxxxxxxxxxxxxxxxx这段配置一旦提交进 GitKey 就等于公开了。更麻烦的是轮换Key 泄露要换、额度用尽要换、供应商切换要换每次都得改配置重新打包。生产环境里配置和代码耦合是运维事故的高发区。2.2 正确做法环境变量 统一入口# 安全写法 spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: ${TAOTOKEN_BASE_URL:https://taotoken.net/api}Key 从环境变量注入base-url指向统一入口。这样本地、测试、生产三套环境用同一份配置只换环境变量。进阶方案是接 Spring Cloud Vault 或云厂商的密钥管理服务实现动态拉取。3. 错误二到五超时、背压、异常、Token 监控3.1 超时设置过短Spring AI 默认超时对短问答够用但大模型生成长文本动辄几十秒。默认值下请求会被提前掐断日志里出现Read timed out。建议按类型分开设超时类型推荐值说明connect-timeout10s建立 TCP 连接read-timeout120s等待完整响应流式模式300s流式响应持续输出spring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: ${TAOTOKEN_BASE_URL:https://taotoken.net/api} timeout: 120s3.2 流式响应没有背压控制模型输出速度大于消费速度时缓冲区会持续膨胀最终 OOM。给流式链路加背压策略// 背压控制 return chatClient.prompt() .user(message) .stream() .content() .onBackpressureBuffer(100) .onBackpressureDrop(drop - log.warn(缓冲区溢出丢弃数据));onBackpressureBuffer(100)限制缓冲上限溢出时走onBackpressureDrop记录日志而不是让内存无限增长。3.3 异常处理过于简单一个catch (Exception e)走天下会把限流、超时、Key 失效全归成一类无法针对性重试// 区分异常类型 try { response chatClient.call(prompt); } catch (ApiException e) { // Key 无效、额度耗尽 → 切换备用 Key return fallbackToBackupKey(prompt); } catch (TimeoutException e) { // 超时 → 指数退避重试 return retryWithBackoff(prompt, 3); } catch (RateLimitException e) { // 限流 → 等待后重试 sleep(e.getRetryAfter()); return retry(prompt); }3.4 Token 消耗无监控一个简单请求可能因为返回过长产生高额费用。先设上限再埋监控Bean public ChatModel chatModel() { return OpenAiChatModel.builder() .apiKey(apiKey) .defaultOptions( ChatOptionsBuilder.builder() .withMaxTokens(2000) .build() ) .build(); }Component public class TokenMonitor { Autowired private ChatClient chatClient; public String chat(String prompt) { int inputTokens countTokens(prompt); String response chatClient.prompt().user(prompt).call().content(); int outputTokens countTokens(response); metrics.record(token.input, inputTokens); metrics.record(token.output, outputTokens); metrics.record(cost.total, calculateCost(inputTokens, outputTokens)); return response; } }4. TaoToken 统一 Key 配置骨架4.1 为什么需要统一入口多模型供应商意味着多套 Key、多套 base-url、多套限流策略。每接一个模型就改一次配置运维复杂度线性上升。统一入口的价值在于一个 Key 管多个模型限流和熔断在入口层统一处理费用监控集中在一处。4.2 可复制的 application.ymlspring: ai: openai: api-key: ${TAOTOKEN_API_KEY} base-url: ${TAOTOKEN_BASE_URL:https://taotoken.net/api} timeout: 120s chat: options: model: ${TAOTOKEN_MODEL:gpt-4o-mini} max-tokens: 2000 temperature: 0.7环境变量在部署时注入export TAOTOKEN_API_KEY你的统一Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_MODELgpt-4o-mini4.3 多模型切换骨架Configuration public class ChatModelConfig { Value(${spring.ai.openai.api-key}) private String apiKey; Value(${spring.ai.openai.base-url}) private String baseUrl; Bean public ChatClient chatClient(ChatModel chatModel) { return ChatClient.builder(chatModel).build(); } Bean public ChatModel chatModel() { return OpenAiChatModel.builder() .apiKey(apiKey) .baseUrl(baseUrl) .defaultOptions( ChatOptionsBuilder.builder() .withMaxTokens(2000) .build() ) .build(); } }Key 的申请和查看在控制台的 API Keys 页面完成接入细节参考接入文档。这两处是排障和接入阶段最常回看的地方。5. 启动验证与错误日志排查5.1 启动验证动作配置写完后先做一次最小验证确认 Key 和 base-url 生效SpringBootTest class ChatClientSmokeTest { Autowired private ChatClient chatClient; Test void smokeTest() { String response chatClient.prompt() .user(用一句话说明什么是 Spring AI) .call() .content(); assertNotNull(response); System.out.println(响应: response); } }跑通说明 Key、base-url、模型名三者都对。跑不通就按下面的日志特征定位。5.2 常见错误日志对照日志关键词原因处理动作401 UnauthorizedKey 无效或未注入检查环境变量是否生效404 Not Foundbase-url 或模型名错误核对 base-url 与模型名Read timed out超时过短调大 read-timeout429 Too Many Requests触发限流加退避重试或切换 KeyOutOfMemoryError流式无背压加 onBackpressureBuffer5.3 排查顺序先看启动日志里spring.ai.openai相关配置是否加载成功再确认环境变量在容器内可见最后用 smoke test 打一次真实请求。三步走完九成配置问题能定位。如果验证模型本身是否可用可以直接在模型对话页面手动发一条消息对比结果排除是代码问题还是模型侧问题。6. 长期编码与 Agent 场景的 Key 管理如果你的 Spring AI 服务要长期跑编码辅助或 Agent 任务Key 的消耗会从「偶尔调用」变成「持续高频」。这时候单 Key 的限流和额度管理会成为瓶颈。Coding Plan 这类面向长期编码场景的方案在 Key 管理和额度分配上做了针对性设计适合把 Spring AI 接进日常开发流的团队。回到本文的五个错误本质上都指向同一件事生产环境的 Spring AI 不是把 Demo 的配置复制过去就行。Key 要能轮换、超时要能覆盖长响应、流式要能控内存、异常要能分类、Token 要能监控。这五件事做完服务才算真正上了生产。
