030-Spring AI Alibaba OpenAI Chat 功能完整案例

本案例将引导您一步步构建一个 Spring Boot 应用,演示如何利用 Spring AI Alibaba 集成 OpenAI Chat 模型,实现多种对话交互方式,包括简单对话、流式对话、自定义参数对话和 JSON 模式对话。
1. 案例目标
我们将创建一个包含多种对话交互方式的 Web 应用:
- 简单对话 (
/openai/chat-model/simple/chat):最基本的 OpenAI 对话调用方式,没有任何额外参数配置。 - 流式对话 (
/openai/chat-model/stream/chat):实现打字机效果的流式输出,提升用户体验。 - 自定义参数对话 (
/openai/chat-model/custom/chat):通过编程方式自定义大模型参数,如温度、最大令牌数等。 - JSON 模式对话 (
/openai/chat-model/custom/chat/json-mode):使大模型返回标准 JSON 格式数据,便于程序处理。 - ChatClient 简单对话 (
/openai/chat-client/simple/chat):使用 Spring AI 的高级 ChatClient API 进行对话。 - ChatClient 流式对话 (
/openai/chat-client/stream/chat):使用 ChatClient API 实现流式输出。 - ChatClient 流式响应 (
/openai/chat-client/stream/response):使用 Server-Sent Events (SSE) 格式返回流式响应。
2. 技术栈与核心依赖
- Spring Boot 3.x
- Spring AI (用于对接 OpenAI 模型)
- Maven (项目构建工具)
在 pom.xml 中,你需要引入以下核心依赖:
<dependencies><!-- Spring Web 用于构建 RESTful API --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring AI OpenAI 启动器 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId></dependency>
</dependencies><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository>
</repositories>3. 项目配置
在 src/main/resources/application.yml 文件中,配置你的 OpenAI API Key 和基础 URL。
server:port: 10003spring:application:name: spring-ai-alibaba-openai-chat-model-exampleai:openai:api-key: ${OPENAI_API_KEY}base-url: https://api.openai-hk.com# 兼容其他OpenAI格式的大模型配置示例
# 以下为字节火山引擎·方舟大模型(Ark LLM)配置示例
# ai:
# openai:
# # API密钥配置
# api-key: ${OPENAI_API_KEY}
# # 方舟大模型API地址
# base-url: https://ark.cn-beijing.volces.com/api/
# chat:
# options:
# # 模型ID,需要替换为实际的接入点ID
# model: ${OPENAI_MODEL_ID}
# # Chat接口路径,与OpenAI接口保持一致
# completions-path: /v3/chat/completions重要提示:请将 OPENAI_API_KEY 环境变量设置为你从 OpenAI 获取的有效 API Key。你也可以直接将其写在配置文件中,但这不推荐用于生产环境。
4. 编写 Java 代码
4.1 OpenAiChatModelApplication.java
Spring Boot 主程序类。
package com.alibaba.cloud.ai.example.chat.openai;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class OpenAiChatModelApplication {public static void main(String[] args) {SpringApplication.run(OpenAiChatModelApplication.class, args);}}4.2 OpenAiChatModelController.java
实现基于 ChatModel 的对话功能。
package com.alibaba.cloud.ai.example.chat.openai.controller;import jakarta.servlet.http.HttpServletResponse;
import reactor.core.publisher.Flux;import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.ResponseFormat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/openai/chat-model")
public class OpenAiChatModelController {private static final String DEFAULT_PROMPT = "你好,介绍下你自己吧。";private static final String JSON_OUTPUT_PROMPT = "how can I solve 8x + 7 = -23";private final ChatModel openAiChatModel;public OpenAiChatModelController(ChatModel chatModel) {this.openAiChatModel = chatModel;}/*** 最简单的使用方式,没有任何 LLMs 参数注入。** @return String types.*/@GetMapping("/simple/chat")public String simpleChat() {return openAiChatModel.call(new Prompt(DEFAULT_PROMPT)).getResult().getOutput().getText();}/*** Stream 流式调用。可以使大模型的输出信息实现打字机效果。** @return Flux<String> types.*/@GetMapping("/stream/chat")public Flux<String> streamChat(HttpServletResponse response) {// 避免返回乱码response.setCharacterEncoding("UTF-8");Flux<ChatResponse> chatResponseFlux = openAiChatModel.stream(new Prompt(DEFAULT_PROMPT));return chatResponseFlux.map(resp -> resp.getResult().getOutput().getText());}/*** 使用编程方式自定义 LLMs ChatOptions 参数, {@link org.springframework.ai.openai.OpenAiChatOptions}* 优先级高于在 application.yml 中配置的 LLMs 参数!*/@GetMapping("/custom/chat")public String customChat() {OpenAiChatOptions customOptions = OpenAiChatOptions.builder().topP(0.7).model("gpt-4o").maxTokens(1000).temperature(0.8).build();return openAiChatModel.call(new Prompt(DEFAULT_PROMPT, customOptions)).getResult().getOutput().getText();}/*** JSON mode:通过设置 response_format 参数为 JSON 类型,使大模型返回标准的 JSON 格式数据。** @return JSON String.*/@GetMapping("/custom/chat/json-mode")public String jsonChat() {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}""";OpenAiChatOptions customOptions = OpenAiChatOptions.builder().topP(0.7).model("gpt-4o").temperature(0.4).maxTokens(4096).responseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, jsonSchema)).build();return openAiChatModel.call(new Prompt(JSON_OUTPUT_PROMPT, customOptions)).getResult().getOutput().getText();}}4.3 OpenAiChatClientController.java
实现基于 ChatClient 的对话功能。
package com.alibaba.cloud.ai.example.chat.openai.controller;import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.RequestParam;
import reactor.core.publisher.Flux;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/openai/chat-client")
public class OpenAiChatClientController {private static final String DEFAULT_PROMPT = "你好,介绍下你自己!";private final ChatClient openAiChatClient;private final ChatModel chatModel;public OpenAiChatClientController(ChatModel chatModel) {this.chatModel = chatModel;// 构造时,可以设置 ChatClient 的参数// {@link org.springframework.ai.chat.client.ChatClient};this.openAiChatClient = ChatClient.builder(chatModel)// 实现 Chat Memory 的 Advisor// 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。.defaultAdvisors(MessageChatMemoryAdvisor.builder(MessageWindowChatMemory.builder().build()).build())// 实现 Logger 的 Advisor.defaultAdvisors(new SimpleLoggerAdvisor())// 设置 ChatClient 中 ChatModel 的 Options 参数.defaultOptions(OpenAiChatOptions.builder().topP(0.7).build()).build();}// 也可以使用如下的方式注入 ChatClient// public OpenAIChatClientController(ChatClient.Builder chatClientBuilder) {//// this.openAiChatClient = chatClientBuilder.build();// }/*** ChatClient 简单调用*/@GetMapping("/simple/chat")public String simpleChat() {return openAiChatClient.prompt(DEFAULT_PROMPT).call().content();}/*** ChatClient 流式调用*/@GetMapping("/stream/chat")public Flux<String> streamChat(HttpServletResponse response) {response.setCharacterEncoding("UTF-8");return openAiChatClient.prompt(DEFAULT_PROMPT).stream().content();}/*** ChatClient 流式响应*/@GetMapping(value = "/stream/response", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<ServerSentEvent<String>> simpleChat(@RequestParam String message) {return openAiChatClient.prompt().user(message).stream().content().map(content -> ServerSentEvent.<String>builder().data(content).build());}}5. 运行与测试
- 启动应用:运行你的 Spring Boot 主程序。
- 使用浏览器或 API 工具(如 Postman, curl)进行测试。
测试 1:简单对话
访问以下 URL,进行最简单的对话测试。
http://localhost:10003/openai/chat-model/simple/chat预期响应:
你好!我是一个由人工智能驱动的语言模型,旨在提供信息、回答问题和帮助解决各种问题。我的能力包括回答问题、提供解释、协助学习和创作内容等。如果你有任何问题或需要帮助,请随时告诉我!
测试 2:流式对话
访问以下 URL,体验流式输出的打字机效果。
http://localhost:10003/openai/chat-model/stream/chat预期响应:流式文本输出,逐字显示响应内容。
测试 3:自定义参数对话
访问以下 URL,测试自定义参数的对话效果。
http://localhost:10003/openai/chat-model/custom/chat预期响应:使用自定义参数(温度 0.8、topP 0.7、最大令牌数 1000、gpt-4o 模型)生成的响应。
测试 4:JSON 模式对话
访问以下 URL,测试 JSON 模式输出。
http://localhost:10003/openai/chat-model/custom/chat/json-mode预期响应:
{"steps": [{"explanation": "首先,我们需要解方程 8x + 7 = -23。第一步是将常数项移到等式右边。","output": "8x = -23 - 7"},{"explanation": "计算右边的值:-23 - 7 = -30。","output": "8x = -30"},{"explanation": "接下来,我们需要解出 x 的值。将等式两边同时除以 8。","output": "x = -30 / 8"},{"explanation": "计算 -30 除以 8 的结果。","output": "x = -3.75"}],"final_answer": "方程 8x + 7 = -23 的解是 x = -3.75"
}测试 5:ChatClient 简单对话
访问以下 URL,测试基于 ChatClient 的简单对话。
http://localhost:10003/openai/chat-client/simple/chat预期响应:使用 ChatClient API 生成的响应,与简单对话类似但内部实现不同。
测试 6:ChatClient 流式对话
访问以下 URL,测试基于 ChatClient 的流式对话。
http://localhost:10003/openai/chat-client/stream/chat预期响应:使用 ChatClient API 实现的流式文本输出。
测试 7:ChatClient 流式响应
访问以下 URL,测试基于 ChatClient 的 SSE 格式流式响应。
http://localhost:10003/openai/chat-client/stream/response?message=你好,请介绍一下Spring AI预期响应:以 SSE 格式返回的流式响应,适合前端直接使用。
6. 实现思路与扩展建议
实现思路
本案例展示了两种不同的 OpenAI 对话实现方式:
- ChatModel 方式:直接使用 Spring AI 的底层模型接口,提供更细粒度的控制,适合需要精确控制模型参数的场景。
- ChatClient 方式:使用 Spring AI 的高级客户端 API,提供了更简洁的编程模型和内置功能(如聊天记忆、日志记录等),适合大多数应用场景。
两种方式都支持:
- 简单对话:一次性返回完整响应。
- 流式对话:逐步返回响应内容,实现打字机效果。
- 自定义参数:通过编程方式设置模型参数,如温度、topP、最大令牌数等。
- 特殊模式:如 JSON 模式,强制模型返回结构化数据。
扩展建议
- 对话记忆增强:可以扩展 ChatClient 的记忆功能,实现更持久化的对话历史存储,如使用数据库或 Redis 存储对话历史。
- 多模型支持:通过配置不同的模型参数,实现同一应用中支持多种模型(如 GPT-3.5、GPT-4、Claude 等)的切换。
- 异步处理:对于耗时较长的对话请求,可以实现异步处理机制,避免阻塞主线程。
- 错误处理与重试:增加完善的错误处理和自动重试机制,提高应用稳定性。
- API 密钥管理:实现更安全的 API 密钥管理机制,如动态加载、轮换等。
- 请求限流:实现请求限流机制,防止 API 调用过于频繁导致超出配额。
- 成本监控:添加 API 调用成本监控功能,帮助控制使用成本。
- 内容过滤:实现内容安全过滤,防止不当内容的生成和传播。
