本案例将引导您一步步构建一个 Spring Boot 应用,演示如何利用 Spring AI Alibaba 集成 OpenAI Chat 模型,实现多种对话交互方式,包括简单对话、流式对话、自定义参数对话和 JSON 模式对话。

1. 案例目标

我们将创建一个包含多种对话交互方式的 Web 应用:

  1. 简单对话 (/openai/chat-model/simple/chat):最基本的 OpenAI 对话调用方式,没有任何额外参数配置。
  2. 流式对话 (/openai/chat-model/stream/chat):实现打字机效果的流式输出,提升用户体验。
  3. 自定义参数对话 (/openai/chat-model/custom/chat):通过编程方式自定义大模型参数,如温度、最大令牌数等。
  4. JSON 模式对话 (/openai/chat-model/custom/chat/json-mode):使大模型返回标准 JSON 格式数据,便于程序处理。
  5. ChatClient 简单对话 (/openai/chat-client/simple/chat):使用 Spring AI 的高级 ChatClient API 进行对话。
  6. ChatClient 流式对话 (/openai/chat-client/stream/chat):使用 ChatClient API 实现流式输出。
  7. 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: 10003

spring:
  application:
    name: spring-ai-alibaba-openai-chat-model-example
  ai:
    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. 运行与测试

  1. 启动应用:运行你的 Spring Boot 主程序。
  2. 使用浏览器或 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 调用成本监控功能,帮助控制使用成本。
  • 内容过滤:实现内容安全过滤,防止不当内容的生成和传播。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐