主题
OpenAI 聊天
Spring AI 支持来自 OpenAI 的各类 AI 语言模型。OpenAI 是 ChatGPT 背后的公司,凭借打造业界领先的文本生成与嵌入模型,点燃了人们对 AI 驱动文本生成的兴趣。
NOTE
从 2.0.0-M5 版本起,Spring AI 在所有 OpenAI 模型底层使用官方的 openai-java SDK。此次迁移预期是无缝的,OpenAI API 属性与构建器的现有用户不会遇到破坏性变更。如发现任何问题,请在 Spring AI GitHub Issues 向我们报告。
前置条件
你需要在 OpenAI 创建一个 API 才能访问 ChatGPT 模型。
在 OpenAI 注册页 创建账户,并在 API Keys 页 生成令牌。
Spring AI 项目定义了一个名为 spring.ai.openai.api-key 的配置属性,你应把它设为从 openai.com 获取的 API Key 的值。
你可以在 application.properties 文件中设置此配置属性:
properties
spring.ai.openai.api-key=<your-openai-api-key>为在 handling 诸如 API 密钥这类敏感信息时获得更强的安全性,你可以使用 Spring 表达式语言(SpEL)引用一个自定义环境变量:
yaml
# In application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}bash
# In your environment or .env file
export OPENAI_API_KEY=<your-openai-api-key>你也可以在应用代码中以编程方式设置此配置属性:
java
// Retrieve API key from a secure source or environment variable
String apiKey = System.getenv("OPENAI_API_KEY");添加仓库与 BOM
Spring AI 的制品发布在 Maven Central 和 Spring Snapshot 仓库中。 参见 制品仓库 一节,把这些仓库添加到你的构建系统。
为便于依赖管理,Spring AI 提供了一个 BOM(物料清单),以确保整个项目中使用一致版本的 Spring AI。参见 依赖管理 一节,把 Spring AI BOM 添加到你的构建系统。
自动配置
NOTE
Spring AI 的自动配置、starter 模块的制品名称发生了重大变更。 更多信息请参阅 升级说明。
Spring AI 为 OpenAI 聊天客户端提供 Spring Boot 自动配置。 要启用它,请将以下依赖添加到你的项目的 Maven pom.xml 或 Gradle build.gradle 构建文件:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>TIP
参见 依赖管理 一节,把 Spring AI BOM 添加到你的构建文件。
聊天属性
重试属性
前缀 spring.ai.retry 用作配置 OpenAI 聊天模型重试机制的属性前缀。
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.retry.max-attempts | 最大重试尝试次数。 | 10 |
| spring.ai.retry.backoff.initial-interval | 指数退避策略的初始等待时长。 | 2 sec. |
| spring.ai.retry.backoff.multiplier | 退避间隔乘数。 | 5 |
| spring.ai.retry.backoff.max-interval | 最大退避时长。 | 3 min. |
| spring.ai.retry.on-client-errors | 若为 false,则抛出 NonTransientAiException,不对 4xx 客户端错误码尝试重试 | false |
| spring.ai.retry.exclude-on-http-codes | 不应触发重试的 HTTP 状态码列表(例如抛出 NonTransientAiException)。 | empty |
| spring.ai.retry.on-http-codes | 应触发重试的 HTTP 状态码列表(例如抛出 TransientAiException)。 | empty |
连接属性
前缀 spring.ai.openai 用作连接 OpenAI 的属性前缀。
| 属性 | 描述 | 默认值 |
|---|---|---|
| spring.ai.openai.base-url | 要连接到的 URL | https://api.openai.com |
| spring.ai.openai.api-key | API 密钥 | - |
| spring.ai.openai.organization-id | 可选地,你可以指定某组织用于 API 请求。 | - |
| spring.ai.openai.project-id | 可选地,你可以指定某项目用于 API 请求。 | - |
| spring.ai.openai.timeout | OpenAI 客户端的请求超时。 | 60 sec. |
| spring.ai.openai.max-retries | OpenAI 客户端的最大重试次数。 | 3 |
| spring.ai.openai.proxy | OpenAI 客户端的代理设置。 | - |
| spring.ai.openai.custom-headers | 要添加到 OpenAI 客户端请求的自定义 HTTP 头。 | empty |
| spring.ai.openai.connection-pool-metrics-enabled | 是否为底层 OkHttp 客户端启用连接池指标。 | false |
TIP
对于属于多个组织(或通过其旧版用户 API 密钥访问其项目)的用户,你可以选择性地指定某组织和项目用于 API 请求。
这些 API 请求的用量将计入所指定组织和项目的用量。
User-Agent 头
Spring AI 会为发往 OpenAI 的所有请求自动发送一个 User-Agent: spring-ai 头。 这有助于 OpenAI 为分析与支持目的识别源自 Spring AI 的请求。 该头会自动发送,无需 Spring AI 用户做任何配置。
如果你是正在构建 OpenAI 兼容服务的 API 提供商,可以通过读取服务器上入站请求的 User-Agent HTTP 头来跟踪 Spring AI 的使用情况。
配置属性
NOTE
聊天自动配置的启用与禁用,现通过以 spring.ai.model.chat 为前缀的顶层属性来配置。
要启用,spring.ai.model.chat=openai(默认启用)
要禁用,spring.ai.model.chat=none(或任何不匹配 openai 的值)
做出此变更是为了支持配置多个模型。
前缀 spring.ai.openai.chat 是为 OpenAI 配置聊天模型实现的属性前缀。
| 配置项 | 说明 | 默认值 |
|---|---|---|
| spring.ai.openai.chat.enabled (已移除,不再有效) | 启用 OpenAI 聊天模型。 | true |
| spring.ai.model.chat | 启用 OpenAI 聊天模型。 | openai |
| spring.ai.openai.chat.base-url | 可选的聊天专用 URL,覆盖 spring.ai.openai.base-url。 | - |
| spring.ai.openai.chat.api-key | 可选的聊天专用 API Key,覆盖 spring.ai.openai.api-key。 | - |
| spring.ai.openai.chat.organization-id | 可选:指定某次 API 请求使用哪个 organization。 | - |
| spring.ai.openai.chat.project-id | 可选:指定某次 API 请求使用哪个 project。 | - |
| spring.ai.openai.chat.model | 要使用的 OpenAI 聊天模型名。 可选模型包括: gpt-5-mini, gpt-4o, gpt-4o-mini, gpt-4-turbo, gpt-3.5-turbo, 等。更多信息见 models 页面。 | gpt-5-mini |
| spring.ai.openai.chat.temperature | 用于控制生成结果创造力的采样温度。 值越大输出越随机,越小越聚焦、越确定。不建议在同一次补全请求中同时改 temperature 与 top_p,二者叠加难以预测。 | 0.8 |
| spring.ai.openai.chat.frequency-penalty | 取值 -2.0 到 2.0。正值会按 token 在已有文本中的出现频率施加惩罚,降低逐字重复的可能。 | 0.0f |
| spring.ai.openai.chat.logit-bias | 调整指定 token 出现在补全结果中的可能性。 | - |
| spring.ai.openai.chat.max-tokens | 聊天补全最多生成的 token 数。 输入与生成 token 的总长度受模型上下文长度限制。 用于非推理模型 (e.g., gpt-4o, gpt-3.5-turbo). 不能用于推理模型 (e.g., o1, o3, o4-mini series). 与 maxCompletionTokens 互斥 - 同时设置会返回 API 错误。 | - |
| spring.ai.openai.chat.max-completion-tokens | 一次补全可生成 token 数的上限,含可见输出 token 与推理 token。 推理模型必须使用 (e.g., o1, o3, o4-mini series). 不能用于非推理模型 (e.g., gpt-4o, gpt-3.5-turbo). 与 maxTokens 互斥 - 同时设置会返回 API 错误。 | - |
| spring.ai.openai.chat.n | 每条输入消息生成多少个聊天补全候选。 注意计费按所有候选生成的总 token 数计算。 建议保持 n 为 1 以降低成本。 | 1 |
| spring.ai.openai.chat.store | 是否保存本次聊天补全的输出以供后续使用 | false |
| spring.ai.openai.chat.metadata | 开发者自定义的标签与取值,用于在聊天补全控制台里筛选 | empty map |
| spring.ai.openai.chat.output-modalities | 本次请求希望模型输出的类型。多数模型只能输出文本,这也是默认值。 The gpt-audio 模型还可用于生成音频。若要求同时输出文本与音频,可使用: text, audio. 流式不支持。 | - | spring.ai.openai.chat.output-audio |
音频生成的音频参数。 当通过 output-modalities: audio. 需要 the gpt-audio 模型,且不支持流式补全。 | - | spring.ai.openai.chat.presence-penalty | 取值 -2.0 到 2.0。正值按 token 是否已出现施加惩罚,提高模型谈论新话题的可能。 |
| - | spring.ai.openai.chat.response-format.type | 兼容 GPT-4o, GPT-4o mini, GPT-4 Turbo 及所有 GPT-3.5 Turbo 系列中不早于 gpt-3.5-turbo-1106. The JSON_OBJECT 类型启用 JSON 模式,保证模型生成的消息是合法 JSON。 The JSON_SCHEMA 类型启用 Structured Outputs ,保证模型输出符合你给出的 JSON schema。使用 JSON_SCHEMA 还需设置 responseFormat.schema 属性。 | - |
| spring.ai.openai.chat.response-format.name | 响应格式 schema 名。 仅适用于 responseFormat.type=JSON_SCHEMA | custom_schema |
| spring.ai.openai.chat.response-format.schema | 响应格式 JSON schema。 仅适用于 responseFormat.type=JSON_SCHEMA | - |
| spring.ai.openai.chat.response-format.strict | 响应格式对 JSON schema 的严格遵循程度。 仅适用于 responseFormat.type=JSON_SCHEMA | - |
| spring.ai.openai.chat.seed | 此特性处于 Beta 阶段。 指定后,系统会尽力做确定性采样:使用相同 seed 与参数的重复请求应返回相同结果。 | - |
| spring.ai.openai.chat.stop | 最多 4 个停止序列,API 遇到就停止生成。 | - |
| spring.ai.openai.chat.top-p | 称为核采样的温度替代方案:模型只考虑累计概率达 top_p 的 token。因此 0.1 表示只看概率前 10% 的 token。通常建议只调这一项或 temperature,不要同时调。 | - |
| spring.ai.openai.chat.tools | 模型可以调用的工具列表。 目前工具只支持 function。 用它提供一组模型可生成 JSON 入参的 function。 | - |
| spring.ai.openai.chat.tool-choice | 控制模型调用哪个(若有)函数。none 表示模型不会调用函数而生成一条消息。auto 表示模型可在生成消息或调用函数之间选择。通过 {"type: "function", "function": {"name": "my_function"}} 指定某个特定函数会强制模型调用该函数。无函数存在时 none 为默认;有函数存在时 auto 为默认。 | - |
| spring.ai.openai.chat.user | 表示你最终用户的唯一标识符,可帮助 OpenAI 监控和检测滥用。 | - |
| spring.ai.openai.chat.stream-usage | (仅流式)设置后会为整个请求附加一个带令牌用量统计的额外块。该块的 choices 字段为空数组,其余所有块也会包含一个 usage 字段但值为 null。 | false |
| spring.ai.openai.chat.parallel-tool-calls | 是否在工具使用期间启用并行函数调用。 | true |
| spring.ai.openai.chat.reasoning-effort | 控制支持该设置的模型的推理强度。该值会被转发给 OpenAI Chat Completions API;仅使用所选模型支持的值。对于启用推理时不支持函数工具的模型,请将其设为 none 或使用兼容的 API/模型组合。 | - |
| spring.ai.openai.chat.prompt-cache-key | OpenAI 用于优化相似请求缓存命中率的一个缓存键。能改善延迟并降低成本。在缓存用途上取代已弃用的 user 字段。了解更多。 | - |
| spring.ai.openai.chat.safety-identifier | 一个稳定的标识符,帮助 OpenAI 检测违反使用政策的用户。应为一个哈希值(例如哈希后的用户名或邮箱)。在安全跟踪用途上取代已弃用的 user 字段。了解更多。 | - |
| spring.ai.openai.chat.http-headers | 要添加到聊天补全请求的可选 HTTP 头。要覆盖 api-key,你需要使用一个 Authorization 头键,并把键值加上 Bearer 前缀。 | - |
| spring.ai.openai.chat.tool-callbacks | 注册到 ChatModel 的工具回调。 | - |
| spring.ai.openai.chat.service-tier | 指定用于处理请求的处理类型。 | - |
| spring.ai.openai.chat.extra-body | 要包含在请求中的额外参数。接受任意键值对,它们会被展平到 JSON 请求的顶层。意在配合支持超出标准 OpenAI API 之外参数的 OpenAI 兼容服务器(vLLM、Ollama 等)使用。官方 OpenAI API 会以 400 错误拒绝未知参数。细节参见 openai-compatible-servers。 | - |
NOTE
当使用暴露 OpenAI 兼容 API 的本地 Docker Model Runner 时,如果服务器期望,你可能需要在基础 URL 后追加 /v1。 例如:用 spring.ai.openai.chat.base-url=http://localhost:12434/engines/v1 而非 http://localhost:12434/engines。
NOTE
当使用 GPT-5 模型(如 gpt-5、gpt-5-mini 和 gpt-5-nano)时,temperature 参数不受支持。 这些模型为推理而优化,不使用温度。 指定温度值会导致错误。 相比之下,像 gpt-5-chat 这样的对话模型确实支持 temperature 参数。
NOTE
你可以为 ChatModel 和 EmbeddingModel 实现覆盖通用的 spring.ai.openai.base-url 和 spring.ai.openai.api-key。
若设置了 spring.ai.openai.chat.base-url 和 spring.ai.openai.chat.api-key 属性,它们优先于通用属性。 当你想为不同模型、不同模型端点使用不同的 OpenAI 账户时,这很有用。
TIP
所有以 spring.ai.openai.chat 为前缀的属性,都可通过在 Prompt 调用中添加请求专用的 chat-options 在运行时被覆盖。
令牌限制参数:按模型的使用
OpenAI 提供两个互斥的参数来控制令牌生成限制:
| 参数 | 用例 | 兼容模型 |
|---|---|---|
maxTokens | 非推理模型 | gpt-4o, gpt-4o-mini, gpt-4-turbo, gpt-3.5-turbo |
maxCompletionTokens | 推理模型 | o1, o1-mini, o1-preview, o3, o4-mini 系列 |
WARNING
这些参数互斥。同时设置两者会导致 OpenAI 返回 API 错误。
用法示例
对于非推理模型(gpt-4o、gpt-3.5-turbo):
java
ChatResponse response = chatModel.call(
new Prompt(
"Explain quantum computing in simple terms.",
OpenAiChatOptions.builder()
.model("gpt-4o")
.maxTokens(150) // Use maxTokens for non-reasoning models
.build()
));对于推理模型(o1、o3 系列):
java
ChatResponse response = chatModel.call(
new Prompt(
"Solve this complex math problem step by step: ...",
OpenAiChatOptions.builder()
.model("o1-preview")
.maxCompletionTokens(1000) // Use maxCompletionTokens for reasoning models
.build()
));构建器模式校验: OpenAI ChatOptions 构建器以“后设者胜”的方式自动强制互斥:
java
// This will automatically clear maxTokens and use maxCompletionTokens
OpenAiChatOptions options = OpenAiChatOptions.builder()
.maxTokens(100) // Set first
.maxCompletionTokens(200) // This clears maxTokens and logs a warning
.build();
// Result: maxTokens = null, maxCompletionTokens = 200运行时选项
OpenAiChatOptions.java 类提供模型配置,例如要使用的模型、温度、频率惩罚等。
在启动时,可通过 OpenAiChatModel(api, options) 构造器或 spring.ai.openai.chat.* 属性配置默认选项。
在运行时,你可以通过向 Prompt 调用添加新的、请求特定的选项来覆盖默认选项。 例如,要为某个特定请求覆盖默认的模型和温度:
java
ChatResponse response = chatModel.call(
new Prompt(
"Generate the names of 5 famous pirates.",
OpenAiChatOptions.builder()
.model("gpt-4o")
.temperature(0.4)
.build()
));TIP
除模型特定的 OpenAiChatOptions 外,你还可以使用一个可移植的 ChatOptions 实例,通过 ChatOptions#builder() 创建。
速率限制元数据
OpenAI 在速率限制响应头中报告你当前的请求与令牌预算。 Spring AI 通过标准的 ChatResponseMetadata#getRateLimit() 访问器暴露它们,它返回一个由这些头填充的 OpenAiRateLimit。
java
ChatResponse response = chatModel.call(prompt);
RateLimit rateLimit = response.getMetadata().getRateLimit();
rateLimit.getRequestsLimit(); // overall request ceiling
rateLimit.getRequestsRemaining(); // requests left in the current window
rateLimit.getRequestsReset(); // time until the request window resets
rateLimit.getTokensLimit(); // overall token ceiling
rateLimit.getTokensRemaining(); // tokens left in the current window
rateLimit.getTokensReset(); // time until the token window resets如果响应不带速率限制头,getRateLimit() 会转而返回一个 EmptyRateLimit 实例。
NOTE
速率限制元数据仅在 call(Prompt) 上被填充。
在 stream(Prompt) 上读取它需要 SDK 的阻塞式流式响应,那会在整个流期间占用一个工作线程,因此 stream(Prompt) 报告一个 EmptyRateLimit。
工具调用
OpenAiChatModel 支持工具调用。模型可以请求执行工具,但它自己并不运行工具——由 Spring AI 处理执行。
对于大多数应用,请搭配自动注册的 ToolCallingAdvisor 使用 ChatClient —— 参见工具调用。若需要由你自己驱动该循环的底层控制,参见 ChatModel 工具调用。
多模态
多模态指的是模型同时理解和处理来自各种来源信息的能力,包括文本、图像、音频及其他数据格式。 OpenAI 支持文本、视觉和音频输入模态。
视觉
提供视觉多模态支持的 OpenAI 模型包括 gpt-4、gpt-4o 和 gpt-4o-mini。 更多信息参见 Vision 指南。
OpenAI User Message API 可以在消息中纳入一个 base64 编码图像或图像 URL 的列表。 Spring AI 的 Message 接口通过引入 Media 类型来便利多模态 AI 模型。 该类型涵盖消息中媒体附件的数据与详情,使用 Spring 的 org.springframework.util.MimeType 和一个 org.springframework.core.io.Resource 表示原始媒体数据。
下面是摘自 OpenAiChatModelIT.java 的一个代码示例,演示用 gpt-4o 模型把用户文本与一张图像融合。
java
var imageResource = new ClassPathResource("/multimodal.test.png");
var userMessage = new UserMessage("Explain what do you see on this picture?",
new Media(MimeTypeUtils.IMAGE_PNG, this.imageResource));
ChatResponse response = chatModel.call(new Prompt(this.userMessage,
OpenAiChatOptions.builder().model("gpt-4o").build()));TIP
GPT4VISIONPREVIEW 自 2024 年 6 月 17 日起将仅对该模型的既有用户继续可用。如果你不是既有用户,请使用 GPT4O 或 GPT4_TURBO 模型。更多细节此处
或用 gpt-4o 模型的图像 URL 等价写法:
java
var userMessage = new UserMessage("Explain what do you see on this picture?",
new Media(MimeTypeUtils.IMAGE_PNG,
URI.create("https://docs.spring.io/spring-ai/reference/_images/multimodal.test.png")));
ChatResponse response = chatModel.call(new Prompt(this.userMessage,
OpenAiChatOptions.builder().model("gpt-4o").build()));TIP
你也可以传入多张图像。
这个示例展示模型以 multimodal.test.png 图像作为输入:
连同文本消息 "Explain what do you see on this picture?",并生成一个像这样的响应:
This is an image of a fruit bowl with a simple design. The bowl is made of metal with curved wire edges that
create an open structure, allowing the fruit to be visible from all angles. Inside the bowl, there are two
yellow bananas resting on top of what appears to be a red apple. The bananas are slightly overripe, as
indicated by the brown spots on their peels. The bowl has a metal ring at the top, likely to serve as a handle
for carrying. The bowl is placed on a flat surface with a neutral-colored background that provides a clear
view of the fruit inside.音频
提供输入音频多模态支持的 OpenAI 模型包括 gpt-audio。 更多信息参见 Audio 指南。
OpenAI User Message API 可以在消息中纳入一个 base64 编码音频文件的列表。 Spring AI 的 Message 接口通过引入 Media 类型来便利多模态 AI 模型。 该类型涵盖消息中媒体附件的数据与详情,使用 Spring 的 org.springframework.util.MimeType 和一个 org.springframework.core.io.Resource 表示原始媒体数据。 目前,OpenAI 只支持以下媒体类型:audio/mp3 和 audio/wav。
下面是摘自 OpenAiChatModelIT.java 的一个代码示例,演示用 gpt-audio 模型把用户文本与一个音频文件融合。
java
var audioResource = new ClassPathResource("speech1.mp3");
var userMessage = new UserMessage("What is this recording about?",
List.of(new Media(MimeTypeUtils.parseMimeType("audio/mp3"), audioResource)));
ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
OpenAiChatOptions.builder().model("gpt-audio").build()));TIP
你也可以传入多个音频文件。
输出音频
提供输入音频多模态支持的 OpenAI 模型包括 gpt-audio。 更多信息参见 Audio 指南。
OpenAI Assistant Message API 可以在消息中包含一个 base64 编码音频文件的列表。 Spring AI 的 Message 接口通过引入 Media 类型来便利多模态 AI 模型。 该类型涵盖消息中媒体附件的数据与详情,使用 Spring 的 org.springframework.util.MimeType 和一个 org.springframework.core.io.Resource 表示原始媒体数据。 目前,OpenAI 只支持以下音频类型:audio/mp3 和 audio/wav。
下面是一个代码示例,演示用 gpt-audio 模型返回用户文本连同音频字节数组的响应:
java
var userMessage = new UserMessage("Tell me joke about Spring Framework");
ChatResponse response = chatModel.call(new Prompt(List.of(userMessage),
OpenAiChatOptions.builder()
.model("gpt-audio")
.outputModalities(List.of("text", "audio"))
.outputAudio(new AudioParameters(Voice.ALLOY, AudioResponseFormat.WAV))
.build()));
String text = response.getResult().getOutput().getText(); // audio transcript
byte[] waveAudio = response.getResult().getOutput().getMedia().get(0).getDataAsByteArray(); // audio data你必须在 OpenAiChatOptions 中指定一个 audio 模态才能生成音频输出。 AudioParameters 类为音频输出提供声音和音频格式。
结构化输出
OpenAI 提供自定义的结构化输出 API,确保模型生成的响应严格符合你提供的 JSON Schema。 除了现有的、Spring AI 与模型无关的结构化输出之外,这些 API 提供了更强的控制与精确度。
NOTE
目前,OpenAI 支持 JSON Schema 语言的一个子集格式。
配置
Spring AI 让你能通过 OpenAiChatOptions 构建器以编程方式,或通过应用属性来配置你的响应格式。
使用聊天选项构建器
你可以像下面这样用 OpenAiChatOptions 构建器以编程方式设置响应格式:
java
String jsonSchema = """
{
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"explanation": { "type": "string" },
"output": { "type": "string" }
},
"required": ["explanation", "output"],
"additionalProperties": false
}
},
"final_answer": { "type": "string" }
},
"required": ["steps", "final_answer"],
"additionalProperties": false
}
""";
Prompt prompt = new Prompt("how can I solve 8x + 7 = -23",
OpenAiChatOptions.builder()
.model(ChatModel.GPT_4_O_MINI)
.responseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, this.jsonSchema))
.build());
ChatResponse response = this.openAiChatModel.call(this.prompt);NOTE
请遵循 OpenAI JSON Schema 语言的子集格式。
与 BeanOutputConverter 工具集成
你可以借助现有的 BeanOutputConverter 工具,自动从你的领域对象生成 JSON Schema,并在稍后把结构化响应转换为领域实例:
java
record MathReasoning(
@JsonProperty(required = true, value = "steps") Steps steps,
@JsonProperty(required = true, value = "final_answer") String finalAnswer) {
record Steps(
@JsonProperty(required = true, value = "items") Items[] items) {
record Items(
@JsonProperty(required = true, value = "explanation") String explanation,
@JsonProperty(required = true, value = "output") String output) {
}
}
}
var outputConverter = new BeanOutputConverter<>(MathReasoning.class);
var jsonSchema = this.outputConverter.getJsonSchema();
Prompt prompt = new Prompt("how can I solve 8x + 7 = -23",
OpenAiChatOptions.builder()
.model(ChatModel.GPT_4_O_MINI)
.responseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, this.jsonSchema))
.build());
ChatResponse response = this.openAiChatModel.call(this.prompt);
String content = this.response.getResult().getOutput().getText();
MathReasoning mathReasoning = this.outputConverter.convert(this.content);NOTE
尽管这对 JSON Schema 是可选的,OpenAI 强制要求字段为必需,结构化响应才能正确工作。Kotlin 反射用于依据类型的可空性或参数的默认值推断哪些属性是必需的,因此对大多数用例 @get:JsonProperty(required = true) 并非必需。@get:JsonProperty(value = "custom_name") 可用于定制属性名。请务必用 @get: 语法在相关 getter 上生成注解,参见相关文档。
通过应用属性配置
或者,在使用 OpenAI 自动配置时,你可以通过以下应用属性配置所需的响应格式:
application.properties
spring.ai.openai.api-key=YOUR_API_KEY
spring.ai.openai.chat.model=gpt-4o-mini
spring.ai.openai.chat.response-format.type=JSON_SCHEMA
spring.ai.openai.chat.response-format.name=MySchemaName
spring.ai.openai.chat.response-format.schema={"type":"object","properties":{"steps":{"type":"array","items":{"type":"object","properties":{"explanation":{"type":"string"},"output":{"type":"string"}},"required":["explanation","output"],"additionalProperties":false}},"final_answer":{"type":"string"}},"required":["steps","final_answer"],"additionalProperties":false}
spring.ai.openai.chat.response-format.strict=true示例控制器
创建 一个新的 Spring Boot 项目,并把 spring-ai-starter-model-openai 添加到你的 pom(或 gradle)依赖。
在 src/main/resources 目录下添加一个 application.properties 文件,以启用并配置 OpenAi 聊天模型:
application.properties
spring.ai.openai.api-key=YOUR_API_KEY
spring.ai.openai.chat.model=gpt-4o
spring.ai.openai.chat.temperature=0.7TIP
用你的 OpenAI 凭据替换 api-key。
这会创建一个可注入到你类中的 OpenAiChatModel 实现。 下面是一个简单 @RestController 类的示例,它使用聊天模型进行文本生成。
java
@RestController
public class ChatController {
private final OpenAiChatModel chatModel;
@Autowired
public ChatController(OpenAiChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/ai/generate")
public Map<String,String> generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return Map.of("generation", this.chatModel.call(message));
}
@GetMapping("/ai/generateStream")
public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return this.chatModel.stream(prompt);
}
}手动配置
OpenAiChatModel 实现了 ChatModel 和 StreamingChatModel,并使用底层 api 连接到 OpenAI 服务。
把 spring-ai-openai 依赖添加到你的项目的 Maven pom.xml 文件:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai</artifactId>
</dependency>或添加到你的 Gradle build.gradle 构建文件。
groovy
dependencies {
implementation 'org.springframework.ai:spring-ai-openai'
}TIP
参见 依赖管理 一节,把 Spring AI BOM 添加到你的构建文件。
接下来,创建一个 OpenAiChatModel 并将其用于文本生成:
java
var chatModel = OpenAiChatModel.builder()
.options(OpenAiChatOptions.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.model("gpt-3.5-turbo")
.temperature(0.4)
.maxTokens(200)
.build())
.build();
ChatResponse response = this.chatModel.call(
new Prompt("Generate the names of 5 famous pirates."));
// Or with streaming responses
Flux<ChatResponse> response = this.chatModel.stream(
new Prompt("Generate the names of 5 famous pirates."));OpenAiChatOptions 为聊天请求提供配置信息。 OpenAiChatOptions.Builder 是用于聊天配置的流式选项构建器。
API 密钥管理
Spring AI 通过 ApiKey 接口及其实现提供灵活的 API 密钥管理。默认实现 SimpleApiKey 适合大多数用例,但你也能创建自定义实现以应对更复杂的场景。
默认配置
默认情况下,Spring Boot 自动配置会用 spring.ai.openai.api-key 属性创建一个 API 密钥 bean:
properties
spring.ai.openai.api-key=your-api-key-here自定义 API 密钥配置
你可以使用构建器模式,配合你自己的 ApiKey 实现创建一个 OpenAiChatModel 的自定义实例:
java
ApiKey customApiKey = new ApiKey() {
@Override
public String getValue() {
// Custom logic to retrieve API key
return "your-api-key-here";
}
};
// Create a chat model with the custom API key
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.options(OpenAiChatOptions.builder()
.apiKey(customApiKey.getValue())
.build())
.build();
// Build the ChatClient using the custom chat model
ChatClient openAiChatClient = ChatClient.builder(chatModel).build();当你需要以下操作时,这很有用:
- 从一个安全密钥库检索 API 密钥
- 动态轮换 API 密钥
- 实现自定义的 API 密钥选择逻辑
可观测性
对每次 OpenAI 调用,Spring AI 会在两层发出 Micrometer 观测:
- 聊天模型层。 一个
gen_ai.client.operation观测包裹着对OpenAiChatModel.call(...)或.stream(...)的每次调用,携带请求参数、响应元数据和令牌用量。标签与指标的完整集合参见可观测性参考。 - HTTP 层。 一个
okhttp.requests观测包裹着对 OpenAI API 的每次 HTTP 尝试,携带 HTTP 方法、URI、状态码和异常标签。每个请求还会在线路上向任何下游服务(AI 网关、OpenAI 兼容推理服务器、代理)传播traceparent。
在一个带 spring-boot-starter-actuator 和追踪桥(micrometer-tracing-bridge-otel 或 micrometer-tracing-bridge-brave)的 Spring Boot 应用中,两层都会被自动装配——无需显式开启。 对于非 Boot 应用,把一个 ObservationRegistry 传给 OpenAiChatModel.builder():
java
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.options(...)
.observationRegistry(observationRegistry)
.build();连接池指标(选择性启用)
Spring AI 还能把 OkHttp 连接池 gauge(带 state=active|idle 和 client.kind=sync|async 的 okhttp.pool.connection.count)绑定到应用的 MeterRegistry。 这些是次级遥测——对容量调优有用,但对追踪或每请求延迟并非必需——因此默认禁用。
要在 Spring Boot 应用中启用它们:
properties
spring.ai.openai.chat.connection-pool-metrics-enabled=true对于非 Boot 应用,把一个 MeterRegistry 直接传给构建器;只要提供了注册表,gauge 就会绑定:
java
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.options(...)
.observationRegistry(observationRegistry)
.meterRegistry(meterRegistry)
.build();HTTP 调度执行器(高级)
默认情况下 Spring AI 管理 OkHttp 调度器的执行器——一个无界的平台线程池,复刻 SDK 的原生行为。 对于高 HTTP 并发的负载,或为了利用 Java 21+ 虚拟线程,你可以提供自己的 ExecutorService:
java
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.options(...)
.dispatcherExecutor(Executors.newVirtualThreadPerTaskExecutor())
.build();同一个执行器同时支撑同步和异步(流式)客户端。 当你提供自己的执行器时,你就拥有它的生命周期——Spring AI 绝不会对它调用 shutdown()。 当省略时,内部执行器会被自动创建与清理。
WARNING
流式 HTTP 观测并不作为聊天模型观测的子级 对于同步调用,okhttp.requests span 正确地嵌套在 gen_ai.client.operation span 之下——完整追踪树如期形成。 对于流式调用,okhttp.requests span 会触发并被记录进注册表,计时器被记录进 MeterRegistry,traceparent 仍在出站请求上传播——但该 span 不会嵌套在聊天模型 span 之下。 这是因为 OpenAI SDK 的异步流式实现在调用 Spring AI 的 HTTP 客户端前会跳到 ForkJoinPool.commonPool(),从而在该边界丢弃调用线程的观测上下文。 Spring AI 无法在其 HTTP 客户端包装器内部恢复该上下文。 如果你需要在追踪 UI 中把流式 HTTP span 与父聊天模型 span 关联,可按 span 名 okhttp.requests 且 host api.openai.com 过滤,并用追踪 ID 或时间戳范围连接到对应的 gen_ai.client.operation span。 流式的内置父级关联正在 OpenAI Java SDK 上游推进。
定制 HTTP 客户端
Spring AI 在底层使用官方的 openai-java SDK,并通过由 SpringAiOpenAiHttpClient.Builder 构建的自定义 OkHttp 客户端来配置其 HTTP 传输。 你可以通过暴露一个或多个 OpenAiHttpClientBuilderCustomizer bean,在底层 OkHttpClient 创建之前拦截那个构建器。 每个定制器接收的都是每个 OpenAI 模型(聊天、嵌入、图像、音频、审核)所使用的同一个构建器,因此该定制会统一生效。
java
@FunctionalInterface
public interface OpenAiHttpClientBuilderCustomizer {
void customize(SpringAiOpenAiHttpClient.Builder builder);
}典型用例包括:
- 注册 OkHttp 的
Interceptor实例(身份认证、传播头、自定义日志); - 更换调度器的
ExecutorService(例如,把异步 I/O 路由到虚拟线程); - 配置代理、SSL、主机名验证,或构建器暴露的连接池大小。
当存在多个定制器时,它们会按 @Order / Ordered 顺序、在 Spring AI 自身默认值之后应用,因此用户代码胜出。
当通过 OpenAi*Model.Builder 手动装配模型时,同样的钩子也可用:
java
var chatModel = OpenAiChatModel.builder()
.options(OpenAiChatOptions.builder().model("gpt-4o").build())
.httpClientBuilderCustomizer(myCustomizer)
.build();与 OpenAI 兼容服务器一起使用额外参数
像 vLLM、Ollama 等 OpenAI 兼容推理服务器,通常支持 OpenAI 标准 API 所定义之外的额外参数。 例如,这些服务器可能接受像 top_k、repetition_penalty 或其他官方 OpenAI API 不识别的采样控制参数。
extraBody 选项让你能向这些服务器传入任意参数。 extraBody 中提供的任何键值对都会被包含在 JSON 请求的顶层,让你在使用 Spring AI 的 OpenAI 客户端时得以利用服务器特定的功能。
WARNING
extraBody 参数意在配合 OpenAI 兼容服务器使用,而非官方 OpenAI API。 官方 OpenAI API 会施加严格校验,若遇到未识别字段会返回一个 HTTP 400 错误("Unknown parameter: 'extra_body'")。 如果你与官方 OpenAI API 通信,你绝不该填充 extraBody 参数。 还需注意,extraBody Map 在序列化期间会被有意展平进 JSON 请求的顶层。所以设置 extraBody(Map.of("custom_flag", true)) 会在 JSON 负载根部产生 {"custom_flag": true},与官方 SDK 的行为一致。
用属性配置
你可以使用 Spring Boot 属性配置额外参数。spring.ai.openai.chat.extra-body 下的每个属性都会成为请求中的一个顶层参数:
properties
spring.ai.openai.base-url=http://localhost:8000
spring.ai.openai.chat.model=meta-llama/Llama-3-8B-Instruct
spring.ai.openai.chat.temperature=0.7
spring.ai.openai.chat.extra-body.top_k=50
spring.ai.openai.chat.extra-body.repetition_penalty=1.1这个配置会产生一个像这样的 JSON 请求:
json
{
"model": "meta-llama/Llama-3-8B-Instruct",
"temperature": 0.7,
"top_k": 50,
"repetition_penalty": 1.1,
"messages": [...]
}用构建器做运行时配置
你也可以在运行时用选项构建器指定额外参数:
java
ChatResponse response = chatModel.call(
new Prompt(
"Tell me a creative story",
OpenAiChatOptions.builder()
.model("meta-llama/Llama-3-8B-Instruct")
.temperature(0.7)
.extraBody(Map.of(
"top_k", 50,
"repetition_penalty", 1.1,
"frequency_penalty", 0.5
))
.build()
));示例:vLLM 服务器
当用 Llama 模型运行 vLLM 时,你可能想使用 vLLM 特定的采样参数:
properties
spring.ai.openai.base-url=http://localhost:8000
spring.ai.openai.chat.model=meta-llama/Llama-3-70B-Instruct
spring.ai.openai.chat.extra-body.top_k=40
spring.ai.openai.chat.extra-body.top_p=0.95
spring.ai.openai.chat.extra-body.repetition_penalty=1.05
spring.ai.openai.chat.extra-body.min_p=0.05受支持采样参数的完整列表参见 vLLM 文档。
示例:Ollama 服务器
当通过 OpenAI 兼容端点使用 Ollama 时,你可以传入 Ollama 特定的参数:
java
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model("llama3.2")
.extraBody(Map.of(
"num_predict", 100,
"top_k", 40,
"repeat_penalty", 1.1
))
.build();
ChatResponse response = chatModel.call(new Prompt("Generate text", options));可用参数参见 Ollama API 文档。
NOTE
extraBody 参数接受任意 Map<String, Object>,让你能传入目标服务器支持的任何参数。 Spring AI 不校验这些参数——它们被直接传给服务器。 这一设计为处理各种 OpenAI 兼容实现提供了最大灵活性。
来自推理模型的推理内容
一些支持推理模型的 OpenAI 兼容服务器(例如 DeepSeek R1、配备推理解析器的 vLLM)会通过在 API 响应中的一个 reasoning_content 字段暴露模型的内部思维链。 该字段包含模型用以得出最终答案的分步推理过程。
Spring AI 把这个字段从 JSON 响应映射到 AssistantMessage 元数据中的 reasoningContent 键。
WARNING
关于 reasoning_content 可用性的重要区别:
- OpenAI 兼容服务器(DeepSeek、vLLM):在 Chat Completions API 响应中暴露
reasoning_content✅ - 官方 OpenAI 模型(GPT-5、o1、o3):不在 Chat Completions API 响应中暴露推理文本 ❌ 官方 OpenAI 推理模型在使用 Chat Completions API 时会隐藏思维链内容。 它们只在用量统计中暴露
reasoning_tokens计数。 要从官方 OpenAI 模型获取推理摘要,请通过OpenAiResponsesChatModel使用 OpenAI 的 Responses API。 那个端点也是唯一在 GPT-5.4 及之后支持推理与工具调用并用的端点。 回退行为: 当服务器未提供reasoning_content时(例如官方 OpenAI Chat Completions),reasoningContent元数据字段会是一个空字符串。
访问推理内容
当使用兼容服务器时,你可以从响应元数据访问推理内容。
直接使用 ChatModel:
java
// Configure to use DeepSeek R1 or vLLM with a reasoning model
ChatResponse response = chatModel.call(
new Prompt("Which number is larger: 9.11 or 9.8?")
);
// Get the assistant message
AssistantMessage message = response.getResult().getOutput();
// Access the reasoning content from metadata
String reasoning = message.getMetadata().get("reasoningContent");
if (reasoning != null && !reasoning.isEmpty()) {
System.out.println("Model's reasoning process:");
System.out.println(reasoning);
}
// The final answer is in the regular content
System.out.println("\nFinal answer:");
System.out.println(message.getContent());使用 ChatClient:
java
ChatClient chatClient = ChatClient.create(chatModel);
String result = chatClient.prompt()
.user("Which number is larger: 9.11 or 9.8?")
.call()
.chatResponse()
.getResult()
.getOutput()
.getContent();
// To access reasoning content with ChatClient, retrieve the full response
ChatResponse response = chatClient.prompt()
.user("Which number is larger: 9.11 or 9.8?")
.call()
.chatResponse();
AssistantMessage message = response.getResult().getOutput();
String reasoning = message.getMetadata().get("reasoningContent");流式推理内容
当使用流式响应时,推理内容会像普通消息内容一样在各块间累积:
java
Flux<ChatResponse> responseFlux = chatModel.stream(
new Prompt("Solve this logic puzzle...")
);
StringBuilder reasoning = new StringBuilder();
StringBuilder answer = new StringBuilder();
responseFlux.subscribe(chunk -> {
AssistantMessage message = chunk.getResult().getOutput();
// Accumulate reasoning if present
String reasoningChunk = message.getMetadata().get("reasoningContent");
if (reasoningChunk != null) {
reasoning.append(reasoningChunk);
}
// Accumulate the final answer
if (message.getContent() != null) {
answer.append(message.getContent());
}
});示例:DeepSeek R1
DeepSeek R1 是一个暴露其内部推理过程的推理模型:
properties
spring.ai.openai.api-key=${DEEPSEEK_API_KEY}
spring.ai.openai.base-url=https://api.deepseek.com
spring.ai.openai.chat.model=deepseek-reasoner当你向 DeepSeek R1 发起请求时,响应会同时包含推理内容(模型的思维过程)和最终答案。
关于推理模型的更多细节参见 DeepSeek API 文档。
示例:配备推理解析器的 vLLM
vLLM 在配置推理解析器时支持推理模型:
bash
vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B \
--enable-reasoning \
--reasoning-parser deepseek_r1properties
spring.ai.openai.base-url=http://localhost:8000
spring.ai.openai.chat.model=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B受支持的推理模型与解析器参见 vLLM 推理输出文档。
NOTE
reasoning_content 的可用性完全取决于你所用的推理服务器。 并非所有 OpenAI 兼容服务器都暴露推理内容,即便使用具备推理能力的模型。 请始终参阅你服务器的 API 文档以了解响应中哪些字段可用。
面向 OpenAI 兼容服务商的工具调用附加属性
一些 OpenAI 兼容服务商会在响应的工具调用对象上附加额外字段。 一个常见例子是 Google Gemini 的 OpenAI 兼容端点,它返回一个多轮工具调用所必需的 thought_signature 字段:
json
{
"tool_calls": [
{
"id": "call_1",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"location\":\"Seoul\"}" },
"extra_content": { "google": { "thought_signature": "..." } }
}
]
}若后续请求中缺少这些服务商特定的字段,某些模型会拒绝工具调用历史。
Spring AI 会自动捕获服务商响应中工具调用对象上存在的任何额外字段,并在助手消息被包含进后续请求时逐字重放它们。 无需配置——该行为对阻塞式和流式流程都是透明的。
NOTE
这只适用于工具调用对象本身的额外字段。 嵌套 function 对象内部的额外字段不会被保留。