跳到主要内容
版本:2.0.1

OpenAI 聊天

QWen 3.8 中英对照 OpenAI OpenAI Chat

Spring AI 支持 OpenAI 提供的各种 AI 语言模型。作为 ChatGPT 背后的公司,OpenAI 凭借其打造的行业领先的文本生成模型和嵌入技术,极大地激发了人们对 AI 驱动文本生成的兴趣。

备注

从版本 2.0.0-M5 开始,Spring AI 底层使用官方的 openai-java SDK 来支持所有 OpenAI 模型。此次过渡预期是平滑的,对于现有 OpenAI API 属性和构建器的用户没有破坏性变更。如果您发现任何问题,请通过 Spring AI GitHub Issues 向我们报告。

前提条件​

您需要创建一个 OpenAI API 以访问 ChatGPT 模型。

在 OpenAI 注册页面 创建账户,并在 API Keys 页面 生成 token。

Spring AI 项目定义了一个名为 spring.ai.openai.api-key 的配置属性,你需要将其设置为从 openai.com 获取的 API Key 的值。

你可以在 application.properties 文件中设置此配置属性:

spring.ai.openai.api-key=<your-openai-api-key>

在处理 API 密钥等敏感信息时,为了增强安全性,可以使用 Spring Expression Language (SpEL) 来引用自定义环境变量:

# In application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
# In your environment or .env file
export OPENAI_API_KEY=<your-openai-api-key>

你也可以在应用代码中以编程方式设置此配置:

// Retrieve API key from a secure source or environment variable
String apiKey = System.getenv("OPENAI_API_KEY");

添加仓库和 BOM​

Spring AI 工件已发布到 Maven Central 和 Spring Snapshot 仓库。请参考 Artifact Repositories 部分,将这些仓库添加到你的构建系统中。

为帮助进行依赖项管理,Spring AI 提供了一个 BOM(物料清单),以确保在整个项目中始终使用相同版本的 Spring AI。请参考依赖项管理章节,将 Spring AI BOM 添加到你的构建系统中。

自动配置​

备注

Spring AI 的自动配置和 starter 模块的 artifact 名称已发生重大变更。更多信息请参阅升级说明。

Spring AI 为 OpenAI Chat Client 提供了 Spring Boot 自动配置。要启用该功能,请在项目的 Maven pom.xml 或 Gradle build.gradle 构建文件中添加以下依赖项:

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

请参阅 依赖管理 部分,以在您的构建文件中添加 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要连接的 URLapi.openai.com
spring.ai.openai.api-keyAPI Key-
spring.ai.openai.organization-id可选,指定用于 API 请求的组织。-
spring.ai.openai.project-id可选,指定用于 API 请求的项目。-
spring.ai.openai.timeoutOpenAI 客户端的请求超时时间。60 sec.
spring.ai.openai.max-retriesOpenAI 客户端的最大重试次数。3
spring.ai.openai.proxyOpenAI 客户端的代理设置。-
spring.ai.openai.custom-headers添加到 OpenAI 客户端请求中的自定义 HTTP 头。空
spring.ai.openai.connection-pool-metrics-enabled是否启用底层 OkHttp 客户端的连接池指标。false
提示

如果您属于多个组织(或者正在通过您的旧版用户 API 密钥访问项目),您可以选择性地指定 API 请求所使用的组织和项目。这些 API 请求的用量将计入所指定的组织和项目。

User-Agent 标头​

Spring AI 会自动在所有发往 OpenAI 的请求中发送 User-Agent: spring-ai 请求头。这有助于 OpenAI 识别源自 Spring AI 的请求,以便用于分析和提供支持。该请求头会自动发送,Spring AI 用户无需进行任何配置。

如果您是构建兼容 OpenAI 服务的 API 提供方,可以通过读取服务器上接收到的请求中的 User-Agent HTTP 请求头来跟踪 Spring AI 的使用情况。

配置属性​

备注

启用和禁用聊天自动配置现在通过前缀为 spring.ai.model.chat 的顶层属性进行配置。

启用方式:spring.ai.model.chat=openai(默认已启用)

禁用方式:spring.ai.model.chat=none(或任何与 openai 不匹配的值)

此更改旨在允许配置多个模型。

前缀 spring.ai.openai.chat 是用于配置 OpenAI 聊天模型实现的属性前缀。

Property描述默认
spring.ai.openai.chat.enabled (已移除且不再有效)启用 OpenAI 聊天模型。true
spring.ai.model.chat启用 OpenAI 聊天模型。OpenAI
spring.ai.openai.chat.base-urlspring.ai.openai.base-url 属性的可选覆盖项,用于提供聊天专用 URL。-
spring.ai.openai.chat.api-key用于为聊天提供特定 API Key 的 spring.ai.openai.api-key 的可选覆盖配置。-
spring.ai.openai.chat.organization-id可选地,您可以指定用于 API 请求的组织。-
spring.ai.openai.chat.project-id可选地,您可以指定用于 API 请求的项目。-
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 之间的数值。正值会根据新标记在当前文本中已有的频率对其进行惩罚,从而降低模型逐字重复相同内容的概率。0.0f
spring.ai.openai.chat.logit-bias修改指定 token 在补全中出现的可能性。-
spring.ai.openai.chat.max-tokens聊天补全中生成的最大 token 数量。输入 token 和生成 token 的总长度受模型上下文长度限制。适用于非推理模型(例如,gpt-4o、gpt-3.5-turbo)。不能用于推理模型(例如,o1、o3、o4-mini 系列)。与 maxCompletionTokens 互斥 - 同时设置两者将导致 API 错误。-
spring.ai.openai.chat.max-completion-tokens生成补全内容时,可生成 token 数量的上限,包括可见输出 token 和推理 token。对于推理模型是必需的(例如 o1、o3、o4-mini 系列)。不能与非推理模型一起使用(例如 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用于在聊天补全仪表板中筛选补全的开发人员自定义标签和值空 map
spring.ai.openai.chat.output-modalities指定希望模型在此请求中生成的输出类型。大多数模型能够生成文本,这是默认行为。gpt-audio 模型还可用于生成音频。若希望该模型同时生成文本和音频响应,可以使用:text、audio。不支持流式传输。-
spring.ai.openai.chat.output-audio音频生成的音频参数。当使用 output-modalities: audio 请求音频输出时为必填项。需要使用 gpt-audio 模型,且不支持流式补全。-
spring.ai.openai.chat.presence-penalty介于 -2.0 和 2.0 之间的数值。正值会根据新标记是否已经出现在文本中对其进行惩罚,从而增加模型讨论新话题的可能性。-
spring.ai.openai.chat.response-format.type兼容 GPT-4o、GPT-4o mini、GPT-4 Turbo 以及所有比 gpt-3.5-turbo-1106 更新的 GPT-3.5 Turbo 模型。JSON_OBJECT 类型启用 JSON 模式,确保模型生成的消息是有效的 JSON。JSON_SCHEMA 类型启用 Structured Outputs,确保模型输出符合您提供的 JSON schema。使用 JSON_SCHEMA 类型时,还必须设置 responseFormat.schema 属性。-
spring.ai.openai.chat.response-format.name响应格式模式名称。仅适用于 responseFormat.type=JSON_SCHEMAcustom_schema
spring.ai.openai.chat.response-format.schema响应格式 JSON 模式。仅适用于 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 将停止在这些位置生成进一步的 token。-
spring.ai.openai.chat.top-p采样的一种替代方法,称为核采样(nucleus sampling),模型会考虑概率质量在 top_p 范围内的 token 结果。例如,0.1 表示仅考虑概率质量最高的前 10% 的 token。通常建议只修改此参数或 temperature,而不要同时修改两者。-
spring.ai.openai.chat.tools模型可能调用的工具列表。当前仅支持函数作为工具。使用此功能提供模型可能生成 JSON 输入的函数列表。-
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(仅限流式传输)设置为 true 以添加一个包含整个请求的 token 使用统计的额外块。该块的 choices 字段为空数组,而其他所有块也将包含一个 usage 字段,但其值为 null。false
spring.ai.openai.chat.parallel-tool-calls是否在工具使用过程中启用并行函数调用。true
spring.ai.openai.chat.prompt-cache-keyOpenAI 用于优化相似请求缓存命中率的缓存键。可改善延迟并降低成本。用于取代已弃用的 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 范围的参数(vLLM、Ollama 等)的 OpenAI 兼容服务器。官方 OpenAI API 会以 400 错误拒绝未知参数。详情请参阅 在 OpenAI 兼容服务器中使用额外参数。-
备注

当使用暴露 OpenAI 兼容 API 的本地 Docker Model Runner 时,如果服务器期望该路径,您可能需要在基础 URL 末尾追加 /v1。例如:spring.ai.openai.chat.base-url=http://localhost:12434/engines/v1,而不是 [localhost:12434/engines](http://localhost:12434/engines)。

备注

当使用 GPT-5 系列模型,例如 gpt-5、gpt-5-mini 和 gpt-5-nano 时,不支持 temperature 参数。这些模型经过推理优化,不使用 temperature。指定 temperature 值会导致错误。相比之下,对话型模型如 gpt-5-chat 支持 temperature 参数。

备注

您可以覆盖 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 账户或不同的模型端点来调用不同模型时非常有用。

提示

所有以 spring.ai.openai.chat 为前缀的属性,都可以通过在 Prompt 调用中添加请求特定的运行时选项在运行时进行覆盖。

Token 限制参数:特定模型的使用情况​

OpenAI 提供了两个互斥的参数来控制 token 生成的上限:

参数用例兼容模型
maxTokens非推理模型gpt-4o, gpt-4o-mini, gpt-4-turbo, gpt-3.5-turbo
maxCompletionTokens推理模型o1, o1-mini, o1-preview, o3, o4-mini 系列
important

这些参数互斥。同时设置两者会导致 OpenAI 返回 API 错误。

使用示例​

对于非推理模型(gpt-4o、gpt-3.5-turbo):

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 系列):

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()
));

Builder Pattern Validation: OpenAI ChatOptions 构建器自动采用“后设置优先”(last-set-wins)的方式强制执行互斥性:

// 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 类提供了模型配置,例如使用的 model、temperature、frequency penalty 等。

在启动时,默认选项可以通过 OpenAiChatModel(api, options) 构造函数或 spring.ai.openai.chat.* 属性进行配置。

在运行时,您可以通过向 Prompt 调用添加新的请求特定选项来覆盖默认选项。例如,要为特定请求覆盖默认模型和温度:

ChatResponse response = chatModel.call(
new Prompt(
"Generate the names of 5 famous pirates.",
OpenAiChatOptions.builder()
.model("gpt-4o")
.temperature(0.4)
.build()
));
提示

除了模型特定的 OpenAiChatOptions 之外,你还可以使用通过 ChatOptions#builder() 创建的便携式 ChatOptions 实例。

Tool Calling​

OpenAiChatModel 支持工具调用。模型可以请求执行工具,但不会自行运行工具——Spring AI 负责处理执行。

对于大多数应用,请使用带有自动注册的 ToolCallingAdvisor 的 ChatClient — 请参阅 Tool Calling。若需要低级别控制并自行驱动循环,请参阅 ChatModel Tool Calling。

多模态​

多模态是指模型能够同时理解和处理来自多种来源的信息,包括文本、图像、音频以及其他数据格式。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 模型将用户文本与图像进行融合。

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()));
提示

自 2024 年 6 月 17 日起,GPT_4_VISION_PREVIEW 将仅对现有用户可用。如果您不是现有用户,请使用 GPT_4_O 或 GPT_4_TURBO 模型。更多详情见 此处

或使用 gpt-4o 模型的等效图像 URL:

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()));
提示

也可以同时传入多张图片。

下例展示了一个模型以 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.

Audio​

提供输入音频多模态支持的 OpenAI 模型包括 gpt-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 模型将用户文本与音频文件融合。

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()));
提示

您也可以同时传递多个音频文件。

输出音频​

提供输入音频多模态支持的 OpenAI 模型包括 gpt-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 模型时,用户文本响应及音频字节数组的返回格式:

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 提供了自定义的 Structured Outputs API,确保模型生成的响应严格遵循您提供的 JSON Schema。除了现有的 Spring AI 模型无关的 结构化输出 外,这些 API 提供了更精细的控制和更高的准确性。

备注

目前,OpenAI 支持 JSON Schema 语言的一个子集 格式。

配置​

Spring AI 允许您通过以编程方式使用 OpenAiChatOptions 构建器或通过应用属性来配置响应格式。

使用 Chat Options Builder​

您可以使用 OpenAiChatOptions 构建器以编程方式设置响应格式,如下所示:

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);
备注

请遵循 OpenAI JSON Schema 语言子集的格式。

与 BeanOutputConverter 工具集成​

你可以利用现有的 BeanOutputConverter 工具,从你的领域对象中自动生成 JSON Schema,并在之后将结构化响应转换为特定领域的实例:

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);
备注

虽然这在 JSON Schema 中是可选的,但 OpenAI 要求结构化响应必须包含必填字段才能正确工作。Kotlin 反射用于根据类型的可空性和参数的默认值来推断哪些属性是必填的,因此在大多数使用场景中,@get:JsonProperty(required = true) 并非必需。@get:JsonProperty(value = "custom_name") 可用于自定义属性名称。请确保使用 @get: 语法在相关的 getter 上生成该注解,参见相关文档。

通过应用属性进行配置​

或者,在使用 OpenAI 自动配置时,可以通过以下应用属性来配置所需的响应格式:

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 聊天模型:

spring.ai.openai.api-key=YOUR_API_KEY
spring.ai.openai.chat.model=gpt-4o
spring.ai.openai.chat.temperature=0.7
提示

将 api-key 替换为您的 OpenAI 凭据。

这将创建一个 OpenAiChatModel 实现,你可以将其注入到你的类中。下面是一个简单的 @RestController 类示例,它使用聊天模型进行文本生成。

@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,并使用 [low-level-api] 连接 OpenAI 服务。

将 spring-ai-openai 依赖项添加到项目的 Maven pom.xml 文件中:

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

或添加到你的 Gradle build.gradle 构建文件中。

dependencies {
implementation 'org.springframework.ai:spring-ai-openai'
}
提示

有关如何将 Spring AI BOM 添加到构建文件,请参阅 依赖管理 章节。

接下来,创建一个 OpenAiChatModel,并使用它进行文本生成:

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 Key 管理​

Spring AI 通过 ApiKey 接口及其实现提供了灵活的 API 密钥管理。默认实现 SimpleApiKey 适用于大多数用例,但您也可以创建自定义实现以应对更复杂的场景。

默认配置​

默认情况下,Spring Boot 自动配置会基于 spring.ai.openai.api-key 属性创建一个 API key bean:

spring.ai.openai.api-key=your-api-key-here

自定义 API Key 配置​

你可以使用建造者模式,通过自定义的 ApiKey 实现来创建 OpenAiChatModel 的自定义实例:

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 密钥选择逻辑

可观测性​

Spring AI 在每次 OpenAI 调用中都会在两个层级发出 Micrometer 观察(observations):

  • 聊天模型层(Chat-model layer)。 gen_ai.client.operation 观测(observation)封装对 OpenAiChatModel.call(…​) 或 .stream(…​) 的每次调用,承载请求参数、响应元数据以及 Token 用量。完整的标签和指标集请参阅 可观测性参考。

  • HTTP 层。 okhttp.requests 观测(observation)封装对 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():

OpenAiChatModel chatModel = OpenAiChatModel.builder()
.options(...)
.observationRegistry(observationRegistry)
.build();

连接池指标(需手动启用)​

Spring AI 还可以将 OkHttp 连接池指标(okhttp.pool.connection.count,其中 state=active|idle 且 client.kind=sync|async)绑定到应用的 MeterRegistry。这些是次要遥测数据,适用于容量调优,但对于追踪或单请求延迟并非必需,因此默认处于禁用状态。

要在 Spring Boot 应用程序中启用它们:

spring.ai.openai.chat.connection-pool-metrics-enabled=true

对于非 Boot 应用,直接将 MeterRegistry 传递给 builder;只要提供了 registry,gauge 就会绑定:

OpenAiChatModel chatModel = OpenAiChatModel.builder()
.options(...)
.observationRegistry(observationRegistry)
.meterRegistry(meterRegistry)
.build();

HTTP 分发器执行器(高级)​

默认情况下,Spring AI 管理 OkHttp 调度器的执行器——一个无限制的线程池,以复制 SDK 的默认行为。对于高 HTTP 并发的工作负载,或者为了利用 Java 21+ 虚拟线程,你可以提供自己的 ExecutorService:

OpenAiChatModel chatModel = OpenAiChatModel.builder()
.options(...)
.dispatcherExecutor(Executors.newVirtualThreadPerTaskExecutor())
.build();

同步和异步(流式)客户端由同一个执行器(executor)提供支持。当你自行提供执行器时,你需要管理其生命周期——Spring AI 不会调用其 shutdown() 方法。如果省略,内部执行器将被自动创建并清理。

important

流式 HTTP 观察(Observations)并未嵌套在聊天模型(chat-model)观察之下

对于同步调用,okhttp.requests Span 正确嵌套在 gen_ai.client.operation Span 之下——完整的追踪树(trace tree)按预期形成。

对于流式调用,okhttp.requests Span 会触发并记录在注册表(registry)中,计时器记录在 MeterRegistry 中,并且 traceparent 仍然会在出站请求上进行传播——但该 Span 未嵌套在聊天模型 Span 之下。这是因为 OpenAI SDK 的异步流式实现在调用 Spring AI 的 HTTP 客户端之前切换到了 ForkJoinPool.commonPool(),从而在该边界处丢失了调用线程的观察上下文。Spring AI 无法在其 HTTP 客户端包装器内部恢复该上下文。

如果你需要在追踪 UI 中将流式 HTTP Span 与其父级聊天模型 Span 进行关联,可以按 Span 名称 okhttp.requests 和主机 api.openai.com 进行过滤,并通过追踪 ID(trace ID)或时间戳范围与对应的 gen_ai.client.operation Span 进行关联。流式的内置父级链接正在 OpenAI Java SDK 上游推进中。

自定义 HTTP 客户端​

Spring AI 底层使用官方的 openai-java SDK,并通过由 SpringAiOpenAiHttpClient.Builder 构建的自定义 OkHttp 客户端配置其 HTTP 传输层。你可以在底层 OkHttpClient 创建之前拦截该构建器,方法是暴露一个或多个 OpenAiHttpClientBuilderCustomizer Bean。每个自定义器都会接收到所有 OpenAI 模型(聊天、嵌入、图像、音频、内容审核)共用的同一个构建器,因此该自定义配置会统一生效。

@FunctionalInterface
public interface OpenAiHttpClientBuilderCustomizer {
void customize(SpringAiOpenAiHttpClient.Builder builder);
}

典型的使用场景包括:

  • 注册 OkHttp Interceptor 实例(例如:身份验证、传播请求头、自定义日志记录);
  • 替换分发器 ExecutorService(例如:将异步 I/O 路由至虚拟线程);
  • 配置由构建器暴露的代理、SSL、主机名验证或连接池大小。

当存在多个定制器时,它们将按照 @Order / Ordered 的顺序,在 Spring AI 自身的默认配置之后进行应用,因此用户代码具有更高的优先级。

通过 OpenAi*Model.Builder 手动配置模型时,也可以使用该钩子:

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 客户端时能够利用服务器特定功能。

important

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 下的每个属性都会成为请求中的顶层参数:

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 请求:

{
"model": "meta-llama/Llama-3-8B-Instruct",
"temperature": 0.7,
"top_k": 50,
"repetition_penalty": 1.1,
"messages": [...]
}

使用 Builder 进行运行时配置​

你也可以在运行时通过 options builder 指定额外的参数:

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 服务器​

在运行 vLLM 并使用 Llama 模型时,你可能希望使用 vLLM 特定的采样参数:

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 特有的参数:

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 文档 了解可用的参数。

备注

extraBody 参数接受任意 Map<String, Object>,允许您传递目标服务器支持的任意参数。Spring AI 不会验证这些参数,它们会被直接传递给服务器。这种设计为与多种 OpenAI 兼容实现配合使用提供了最大的灵活性。

来自推理模型的推理内容​

一些支持推理模型(例如 DeepSeek R1、带有推理解析器的 vLLM)的 OpenAI 兼容服务器,会在其 API 响应中通过 reasoning_content 字段暴露模型的内部思维链。该字段包含模型用于得出最终答案的逐步推理过程。

Spring AI 将 JSON 响应中的该字段映射到 AssistantMessage 元数据中的 reasoningContent 键。

important

关于 reasoning_content 可用性的关键区分:

  • OpenAI 兼容服务器(DeepSeek、vLLM):在 Chat Completions API 响应中暴露 reasoning_content ✅

  • 官方 OpenAI 模型(GPT-5、o1、o3):在 Chat Completions API 响应中不暴露推理文本 ❌

当使用 Chat Completions API 时,官方 OpenAI 推理模型会隐藏思维链(chain-of-thought)内容。它们仅在 usage 统计中暴露 reasoning_tokens 数量。要从官方 OpenAI 模型获取实际的推理文本,必须使用 OpenAI 的 Responses API(这是一个独立的端点,当前此客户端尚不支持)。

回退行为: 当服务器未提供 reasoning_content 时(例如官方 OpenAI Chat Completions),reasoningContent 元数据字段将为空字符串。

访问推理内容​

使用兼容服务器时,你可以从响应元数据中访问推理内容。

直接使用 ChatModel:

// 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:

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");

流式推理内容​

在使用流式响应时,推理内容会像常规消息内容一样在多个数据块之间进行累积:

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 是一个展示其内部推理过程的推理模型:

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 在配置了推理解析器(reasoning parser)时,支持推理模型:

vllm serve deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B \
--enable-reasoning \
--reasoning-parser deepseek_r1
spring.ai.openai.base-url=http://localhost:8000
spring.ai.openai.chat.model=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B

请参阅 vLLM 推理输出文档,了解支持的推理模型和解析器。

备注

reasoning_content 的可用性完全取决于你所使用的推理服务器。即使在使用具备推理能力的模型时,并非所有兼容 OpenAI 的服务器都会暴露推理内容。请始终查阅服务器的 API 文档,以了解响应中可用的字段。

OpenAI 兼容提供商的工具调用附加属性​

某些兼容 OpenAI 的服务商会在其响应的工具调用对象中添加额外字段。一个常见示例是 Google Gemini 的 OpenAI 兼容端点,它会返回一个 thought_signature 字段,该字段用于支持多轮工具调用:

{
"tool_calls": [
{
"id": "call_1",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"location\":\"Seoul\"}" },
"extra_content": { "google": { "thought_signature": "..." } }
}
]
}

如果后续请求中未包含这些提供商特定的字段,部分模型会拒绝工具调用历史记录。

Spring AI 会自动捕获提供方响应中工具调用对象上的任何额外字段,并在后续请求中包含助手消息时原样重放这些字段。无需任何配置——该行为在阻塞和流式流程中均对两者透明。

备注

这仅适用于工具调用对象本身的额外字段。嵌套在 function 对象内部的额外字段不会被保留。