Java 项目接入 OpenAI API 完整方案

Spring Boot 项目要接大模型,Spring AI 和官方 openai-java SDK 该选哪个?这篇给出两套可直接落地的配置与代码,讲清 base-url 拼接、流式输出、超时重试和 429 处理,附常见报错对照。

Java 侧接大模型现在有两条成熟路线:Spring AI 提供声明式封装,官方 openai-java SDK 轻量直接。两条路线都能改 base_url 指向兼容网关,但踩坑点不一样——Spring AI 会因为多拼一段 /v1 直接 404,官方 SDK 则是版本升级后类名变了,照着旧博客抄必然编译不过。

这篇给两套能直接跑的最小实现,再补上生产环境必须处理的三件事:超时、重试、流式。

先说结论

  • 已有 Spring Boot 3.x 工程、想要和现有 Bean 体系融合:用 Spring AI,配置写在 application.yml
  • 只想发个 HTTP 请求、不想引入整个 AI 抽象层:用官方 openai-java SDK,依赖更少
  • base-url 的写法两者相反:Spring AI 写到域名(它自己拼 /v1),官方 SDK 要写到 /v1
  • 流式输出必须用 SSE,中间任何一层网关缓冲都会让"流式"变成一次性返回

两条路线怎么选

对比项Spring AIopenai-java SDK
依赖体积较大,带自动配置与 advisor 链小,只含 HTTP 与模型类
配置方式application.yml 声明式代码里 builder 构造
多模型切换换 model 属性即可换 model 字符串
流式支持Flux 直接对接回调式 Stream 处理
适合场景新项目、统一治理老项目、单点调用

两者不冲突,同一工程里也能共存。日常维护角度看,选一个就好,避免两套超时配置互相打架。

路线一:Spring AI

依赖(Spring Boot 3.3+):

<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

配置:

spring:
  ai:
    openai:
      # 注意:这里只写到域名,Spring AI 会自动拼接 /v1/chat/completions
      base-url: https://api.zaimod.com
      api-key: ${ZAIMOD_API_KEY}
      chat:
        options:
          model: gpt-4.1-mini
          temperature: 0.3

调用就是一个普通 Bean:

@RestController
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultSystem("你是严谨的技术助手,回答控制在 200 字内。")
                .build();
    }

    @GetMapping("/ask")
    public String ask(@RequestParam String q) {
        return chatClient.prompt()
                .user(q)
                .call()
                .content();
    }
}

流式版本把 call() 换成 stream(),返回 Flux<String>,Spring WebFlux 会直接按 SSE 推给前端:

@GetMapping(value = "/ask/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream(@RequestParam String q) {
    return chatClient.prompt().user(q).stream().content();
}

路线二:官方 openai-java SDK

<dependency>
  <groupId>com.openai</groupId>
  <artifactId>openai-java</artifactId>
  <version>4.0.0</version>
</dependency>
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;

public class Demo {
    public static void main(String[] args) {
        OpenAIClient client = OpenAIOkHttpClient.builder()
                .apiKey(System.getenv("ZAIMOD_API_KEY"))
                // SDK 不会再补 /v1,必须写全
                .baseUrl("https://api.zaimod.com/v1")
                .build();

        ChatCompletion resp = client.chat().completions().create(
                ChatCompletionCreateParams.builder()
                        .model("gpt-4o-mini")
                        .addUserMessage("用一句话说明什么是 token")
                        .maxCompletionTokens(256)
                        .build());

        resp.choices().forEach(c -> c.message().content()
                .ifPresent(System.out::println));
    }
}

两个细节容易忽略:content() 在 Java SDK 里是 Optional,模型只返回工具调用时它是空的;maxCompletionTokens 在新版本取代了 maxTokens,写旧字段会编译报错。

超时、重试、限流

默认超时对非流式请求太短,长回答会中途断开。建议按调用类型分开配:

场景连接超时读超时重试策略
短问答、分类3s30s2 次,指数退避
长文生成5s180s1 次,仅对 5xx 和超时
流式5s不设总超时不自动重试,由前端续传

429 不要盲目重试。并发超限时立刻重试只会把窗口打得更满,正确做法是退避加抖动:

int attempt = 0;
while (true) {
    try {
        return callApi();
    } catch (RateLimitException e) {
        if (++attempt > 3) throw e;
        long backoff = (long) (Math.pow(2, attempt) * 500) + ThreadLocalRandom.current().nextLong(300);
        Thread.sleep(backoff);
    }
}

完整的限流成因与排查顺序见 大模型 API 429 限流怎么解,错误码全集见 大模型 API 错误码速查。

三个工程实践

不要每次调用都 new 一个 client。 OpenAIClient 内部持有连接池,应当做成单例 Bean,否则高并发下连接数会爆。

日志里绝不能出现完整 Key。 统一在拦截器里脱敏成 sk-****abcd;Key 泄露后的处置流程见 API Key 泄露应急处理。

把模型名放进配置中心而不是常量。 换模型时改配置即可,配合灰度可以先放 5% 流量到新模型,比对延迟和成本再全量。

常见问题

Q:Spring AI 报 404,路径里出现两次 /v1 怎么办?

把 base-url 改成不带 /v1 的域名。若必须带路径前缀(比如走公司网关),用 spring.ai.openai.chat.base-url 单独覆盖,让对话和嵌入各走各的地址。

Q:能用 JDK 8 吗?

Spring AI 要求 Spring Boot 3.x,即 JDK 17 起。JDK 8 项目建议直接用 OkHttp 手写请求,反而比引 SDK 省事;协议本身只是 HTTP POST,见 OpenAI 兼容协议详解。

Q:流式输出在 Nginx 后面变成一次性返回?

关掉响应缓冲:proxy_buffering off;,并确认 X-Accel-Buffering: no 已透传。前端 SSE 的写法参考 SSE 流式输出从 curl 到前端。

Q:一个 Key 能同时跑多个服务吗?

可以,但建议按服务拆 Key,便于定位超量来源和单独吊销。额度与计费口径在 价格速查 里按模型列出。

延伸阅读

#java 调用 openai api#spring boot 接入大模型#spring ai 配置#openai java sdk
免费创建 API Key

相关阅读

Node.js 接入 OpenAI API 完整教程

Node.js 和 TypeScript 项目接大模型,官方 openai SDK 十几行代码就能跑通。这篇覆盖安装配置、baseURL 指向兼容网关、流式输出、超时重试和常见报错排查,附 Next.js 与 Express 两种落地写法。