首页 / Spring AI 入门教程 / 接入 OpenAI

Spring AI 入门教程

接入 OpenAI

本教程共 45 篇 · 第 11 篇 · 更新于 2026-08-16 · 约 8 分钟阅读

Spring AIOpenAIChatGPTgpt-4oAPI Key流式响应OpenAiChatModel兼容端点

本节目标:完成 OpenAI 的接入全流程:注册账号、配置依赖、写第一个对话接口,并掌握流式输出和运行时参数覆盖。

11.1 准备工作:账号和 API Key

OpenAI 是 ChatGPT 背后的公司,它的 API 是当前事实上的行业标准。接入前需要两样东西:账号和 API Key。

platform.openai.com 注册账号,然后在 API Keys 页面生成一个 Key。Key 只在创建时完整显示一次,记得立刻保存。

Key 是敏感信息,不要写死在代码里。推荐用环境变量,Spring AI 支持 SpEL 引用:

spring.ai.openai.api-key=${OPENAI_API_KEY}
Note

OpenAI 的 Key 是付费使用的,首次注册有少量免费额度。调用失败时先检查 Key 是否有效、账户是否有余额。

11.2 添加依赖

Spring AI 2.0 的依赖命名做了统一调整,厂商 starter 统一叫 spring-ai-starter-model-*。OpenAI 对应 spring-ai-starter-model-openai

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

版本不用写,由 spring-ai-bom 统一管理。BOM 的引入方式见第 2 章。

Note

2.0 起 OpenAI 模块底层换成了官方 openai-java SDK。这是 2.0.0-M5 引入的变化,对外 API 和配置属性完全不变,你不需要改任何代码。

11.3 配置连接属性

OpenAI 的连接配置前缀是 spring.ai.openai

spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.base-url=https://api.openai.com

spring.ai.openai.chat.model=gpt-4o
spring.ai.openai.chat.temperature=0.7
spring.ai.openai.chat.max-tokens=500

几个常用属性说明:

  • base-url:API 地址,默认就是官方地址,一般不用写。
  • chat.model:模型名,默认 gpt-5-mini。常用还有 gpt-4o、gpt-4o-mini、gpt-4-turbo、gpt-3.5-turbo。
  • chat.temperature:采样温度,默认 0.8。越高越随机,越低越确定。
  • chat.max-tokens:最大生成 token 数,控制回复长度和成本。

还有一套重试配置,前缀是 spring.ai.retry。默认最多重试 10 次,指数退避从 2 秒开始,最大间隔 3 分钟。4xx 客户端错误默认不重试。

spring.ai.retry.max-attempts=5
spring.ai.retry.backoff.initial-interval=1s
Tip

2.0 移除了框架层的默认温度。之前所有模型默认 0.7,现在改用各厂商自己的默认值。想保持行为一致,就显式配置 temperature。

11.4 第一个对话接口

配置完成后,Spring Boot 自动配置会生成一个 OpenAiChatModel Bean。直接注入使用:

@RestController
public class ChatController {

    private final OpenAiChatModel chatModel;

    public ChatController(OpenAiChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @GetMapping("/ai/generate")
    public Map<String, String> generate(@RequestParam(defaultValue = "讲个笑话") String message) {
        return Map.of("generation", chatModel.call(message));
    }
}

启动应用,访问 http://localhost:8080/ai/generate,就能拿到模型回复。

日常开发更推荐用 ChatClient,它比直接操作 ChatModel 更顺手,还自带流式、工具调用等能力:

@RestController
public class ChatClientController {

    private final ChatClient chatClient;

    public ChatClientController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @GetMapping("/ai/chat")
    public String chat(@RequestParam(defaultValue = "你好") String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

11.5 流式响应

大模型生成慢,一次返回全部内容要等好几秒。流式响应像打字机一样,生成一点推一点,体验好很多。

ChatClient 把 call() 换成 stream() 就行:

@GetMapping(value = "/ai/chat/stream", produces = "text/html;charset=UTF-8")
public Flux<String> chatStream(@RequestParam(defaultValue = "写一首诗") String message) {
    return chatClient.prompt()
            .user(message)
            .stream()
            .content();
}

返回类型是 Flux<String>,Spring MVC 会把它转成 SSE 流。浏览器直接访问能看到逐字输出。

Note

produces = "text/html;charset=UTF-8" 是为了让浏览器直接预览。如果走 SSE 协议,用 MediaType.TEXT_EVENT_STREAM,前端用 EventSource 或 fetch 流式读取。

11.6 运行时参数覆盖

全局默认值写在属性文件里。单次请求想用不同参数,通过 OpenAiChatOptions 在运行时覆盖:

ChatResponse response = chatModel.call(new Prompt(
        "给我 5 个编程笑话",
        OpenAiChatOptions.builder()
                .model("gpt-4o")
                .temperature(0.4)
                .maxTokens(200)
                .build()));

String text = response.getResult().getOutput().getText();

ChatClient 的写法更简洁:

String content = chatClient.prompt()
        .user("给我 5 个编程笑话")
        .options(OpenAiChatOptions.builder()
                .temperature(0.4)
                .maxTokens(200)
                .build())
        .call()
        .content();
Tip

OpenAI 有两套 token 上限参数。普通模型用 maxTokens;o1、o3 这类推理模型必须用 maxCompletionTokens。两者互斥,同时设置会报错。OpenAI 的 builder 采用”后设生效”:后设置的那个会清掉前一个。

11.7 对接 OpenAI 兼容服务

OpenAI 的接口规范成了事实标准,很多服务直接兼容它。vLLM、Ollama、部分国产模型都提供 OpenAI 兼容端点。接入方法很简单:改 base-url 和模型名。

# 对接本地 vLLM 服务
spring.ai.openai.base-url=http://localhost:8000
spring.ai.openai.chat.model=meta-llama/Llama-3-8B-Instruct

Ollama 也提供 OpenAI 兼容端点,地址是 http://localhost:11434/v1,完整接法见第 14 章。

兼容服务常有一些 OpenAI 没有的采样参数,比如 top_k、repetition_penalty。用 extra-body 传:

spring.ai.openai.chat.extra-body.top_k=50
spring.ai.openai.chat.extra-body.repetition_penalty=1.1

这些参数会原样平铺进请求 JSON 的顶层。注意官方 OpenAI API 不认未知参数,会返回 400,只对兼容服务用。

Note

兼容服务如果返回 reasoning_content 字段(比如 DeepSeek R1),Spring AI 会把它放进响应的 reasoningContent 元数据里。官方 OpenAI 模型不暴露思维链内容,这个字段只对兼容服务有效。DeepSeek 的完整接法见第 13 章。

11.8 多模态:图片输入

OpenAI 支持图片输入。把图片包成 Media 对象,和文字一起放进 UserMessage:

var imageResource = new ClassPathResource("/multimodal.png");

var userMessage = new UserMessage("这张图里有什么?",
        new Media(MimeTypeUtils.IMAGE_PNG, imageResource));

ChatResponse response = chatModel.call(new Prompt(userMessage,
        OpenAiChatOptions.builder().model("gpt-4o").build()));

Media 支持本地资源,也支持 URL:

var userMessage = new UserMessage("这张图里有什么?",
        new Media(MimeTypeUtils.IMAGE_PNG,
                URI.create("https://example.com/image.png")));

gpt-4o 和 gpt-4o-mini 都支持视觉输入。传多张图也允许,一个 Media 列表即可。

11.9 结构化输出

OpenAI 原生支持 JSON Schema 约束输出,保证模型返回的 JSON 符合指定结构。配合 BeanOutputConverter 自动生成 Schema 最省事:

record MathReasoning(
        @JsonProperty(required = true, value = "steps") Steps steps,
        @JsonProperty(required = true, value = "final_answer") String finalAnswer) {
    record Steps(@JsonProperty(required = true) Items[] items) {
        record Items(@JsonProperty(required = true) String explanation,
                     @JsonProperty(required = true) String output) {
        }
    }
}

var converter = new BeanOutputConverter<>(MathReasoning.class);

Prompt prompt = new Prompt("如何解 8x + 7 = -23",
        OpenAiChatOptions.builder()
                .model("gpt-4o-mini")
                .responseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA,
                        converter.getJsonSchema()))
                .build());

ChatResponse response = chatModel.call(prompt);
MathReasoning result = converter.convert(response.getResult().getOutput().getText());

结构化输出的通用方案(StructuredOutputConverter)在第 8 章讲过,这里的 responseFormat 是 OpenAI 的原生增强,约束更严格。

11.10 常见问题排查

接入 OpenAI 报错,九成是下面几个原因。

401 认证失败,检查 api-key。Key 放环境变量时,确认 ${OPENAI_API_KEY} 拼写一致,环境变量没设的话 Spring 启动就会失败。

429 限流,先看账户余额和配额。OpenAI 按用量计费,余额不足会拒绝请求。重试配置只对瞬时错误有效,别指望它解决欠费。

400 Unknown parameter,说明把 extra-body 用在了官方 API 上。官方端点不认未知参数,extra-body 只给兼容服务用。

模型名不存在,查一下官方模型列表。模型会下线,教程里写的名字未必还在,以 platform.openai.com/docs/models 为准。

Tip

怀疑是网络问题(国内直连不稳定),可以给 base-url 配代理地址,或把请求转发到中转服务。注意中转服务有数据安全风险,生产环境慎用。

11.11 配置要点:多环境与成本

开发、测试、生产通常用不同的 Key 和模型。公共配置留在 application.properties,环境差异拆到 application-dev.properties、application-prod.properties 这类文件里,用 spring.profiles.active 切换。比如开发环境用 gpt-4o-mini 省钱,生产环境用 gpt-4o;Key 按环境各配一份,互不影响。同名配置会覆盖主文件,覆盖关系一目了然。

成本控制记住三点:调试用便宜模型,回复长度用 max-tokens 限制,长文本用流式输出。OpenAI 按 token 计费,同一个问题反复调试,费用会一点点累积。每次调用的 token 消耗可以从响应的 usage 里读出来,记进日志,月底对账就有依据。上线前把温度、max-tokens、重试次数都定下来,预算才可控。

11.12 小结

OpenAI 的接入就四步:注册拿 Key、加 starter 依赖、配属性、注入模型。同步调用用 call(),流式用 stream()。对接兼容服务只改 base-url。下一章看 Anthropic,它和 OpenAI 的接入流程几乎一样,但 2.0 的内部实现变化很大。