Spring AI实战指南:从零构建Java大模型对话应用,解锁异步与流式响应
在人工智能浪潮席卷的当下,Java开发者如何快速集成大语言模型能力?Spring AI应运而生,它作为Spring生态中的新星,为Java应用接入AI功能提供了标准化的、声明式的解决方案。本文将手把手带你搭建一个功能完整的Java大模型对话Demo,涵盖从环境配置、基础同步调用,到进阶的异步处理、流式响应(SSE)以及核心参数调优的全过程。无论你是刚接触AIGC的Java新手,还是希望将AI能力融入现有系统的资深开发者,这篇实战指南都能让你快速上手,高效开发。
一、项目初始化与环境配置
万事开头难,一个稳定的开发环境是成功的第一步。与配置Python的LangChain或JavaScript/TypeScript的LangChain.js类似,Spring AI项目也需要明确的基础依赖。
- JDK 17+:Spring AI对Java版本有要求,JDK 17是目前最稳定且推荐的选择。
- 构建工具:Maven 3.8+ 或 Gradle 7.5+,本文以Maven为例。
- IDE:IntelliJ IDEA(对Spring Boot支持极佳)或VS Code(需安装相应Java插件)。
- API密钥:准备一个可用的OpenAI API Key(或国内大模型如百度文心、阿里通义的密钥)。
首先,在项目的pom.xml文件中引入Spring AI的核心依赖。Spring AI通过Starter的方式,极大简化了集成复杂度,你无需像在C++项目中手动链接库那样繁琐。
<!-- Spring Boot父工程 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Web:用于开发接口 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI OpenAI Starter:核心依赖,封装大模型调用 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0-M1</version>
</dependency>
<!-- Swagger:接口文档,方便调试 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
</dependencies>
依赖添加完成后,接下来在application.yml配置文件中设置连接参数。将密钥等信息放在配置文件中,而非硬编码在Java代码里,是遵循Spring Boot最佳实践的做法,也便于不同环境(开发、测试、生产)的切换。
spring:
ai:
openai:
api-key: 你的OpenAI API密钥
chat:
model: gpt-3.5-turbo # 模型名称,可替换为gpt-4(需开通权限)
temperature: 0.7 # 默认温度,后续会讲解调优技巧
top-p: 0.9 # 默认top_p参数,控制输出的多样性
# Swagger配置(可选,方便接口调试)
springdoc:
api-docs:
path: /api-docs
swagger-ui:
path: /swagger-ui.html
operationsSorter: method
二、实现基础同步对话接口
让我们从最简单的同步调用开始。这种方式逻辑直观,适合回答简短、实时性要求不高的场景。其原理类似于通过HTTP客户端(如Java的OkHttp或Python的requests库)发起一个阻塞式请求并等待响应。
创建一个ChatController,并注入Spring AI自动为我们配置好的ChatClient Bean(这里以OpenAI为例)。核心代码如下:
import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
* 大模型对话接口控制器
* 关注我,获取更多Spring AI实战技巧~
*/
@RestController
public class ChatController {
// 注入Spring AI自动配置的OpenAI对话客户端
private final OpenAiChatClient openAiChatClient;
// 构造方法注入(Spring Boot自动装配,无需手动new)
public ChatController(OpenAiChatClient openAiChatClient) {
this.openAiChatClient = openAiChatClient;
}
/**
* 同步对话接口:接收用户提问,同步返回大模型回答
* @param request 用户提问内容(封装为实体类,更规范)
* @return 大模型回答结果
*/
@PostMapping("/api/chat/sync")
public String syncChat(@RequestBody ChatRequest request) {
// 直接调用客户端的generate方法,传入提问内容,返回回答
return openAiChatClient.generate(request.getQuestion());
}
// 静态内部类:接收前端请求参数
public static class ChatRequest {
private String question; // 用户提问内容
// getter/setter
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
}
}
启动应用后,使用Postman或curl工具测试该接口。发送一个包含问题的JSON请求体,例如询问“用Java实现单例模式”,后端会调用大模型并一次性返回完整的代码答案。这种方式简单直接,但缺点也很明显:如果模型生成内容较长,用户需要等待全部生成完毕才能看到结果,体验上不如ChatGPT那样的“打字机”效果。
[AFFILIATE_SLOT_1]三、进阶能力:异步、流式响应与参数调优
要打造媲美ChatGPT的流畅体验,必须超越同步调用。本节将实现异步非阻塞调用和流式服务器发送事件(SSE)响应,并探讨如何通过参数控制模型输出风格。
1. 异步对话接口
异步处理能避免长时间任务阻塞Web服务器线程,提升系统吞吐量。这在处理高并发请求时尤为重要。我们通过Spring的@Async注解和CompletableFuture来实现。
首先,创建一个Service层方法处理异步调用:
import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
/**
* 大模型对话服务层(封装业务逻辑,便于复用)
*/
@Service
public class ChatService {
private final OpenAiChatClient openAiChatClient;
public ChatService(OpenAiChatClient openAiChatClient) {
this.openAiChatClient = openAiChatClient;
}
/**
* 异步对话调用
* @param question 用户提问
* @return 异步结果(CompletableFuture)
*/
@Async
public CompletableFuture<String> asyncChat(String question) {
// 调用大模型,返回CompletableFuture
return CompletableFuture.supplyAsync(() -> openAiChatClient.generate(question));
}
}
随后,在Controller中调用这个异步服务:
// 注入对话服务
private final ChatService chatService;
// 构造方法添加ChatService注入
public ChatController(OpenAiChatClient openAiChatClient, ChatService chatService) {
this.openAiChatClient = openAiChatClient;
this.chatService = chatService;
}
/**
* 异步对话接口:不阻塞主线程,提高接口吞吐量
* @param request 用户提问
* @return 异步结果
*/
@PostMapping("/api/chat/async")
public CompletableFuture<String> asyncChat(@RequestBody ChatRequest request) {
return chatService.asyncChat(request.getQuestion());
}
最后,别忘了在Spring Boot主应用类上添加@EnableAsync注解以启用异步功能:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync // 开启异步支持
public class SpringAiChatDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiChatDemoApplication.class, args);
}
}
2. 流式SSE输出(实时打字效果)
流式响应是提升用户体验的关键。它允许服务器将大模型的生成结果分块(chunk)实时推送给前端,实现“逐字输出”的效果。Spring AI内置了流式支持,配合Spring MVC的SseEmitter可以轻松实现。
以下是流式对话接口的核心实现:
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
/**
* 流式SSE对话接口:实时返回大模型回答(类似ChatGPT打字效果)
* @param request 用户提问
* @return SseEmitter 流式响应
*/
@PostMapping(value = "/api/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter streamChat(@RequestBody ChatRequest request) {
// 创建SseEmitter,设置超时时间(30秒,避免连接断开)
SseEmitter emitter = new SseEmitter(30000L);
// 调用Spring AI的流式生成方法,分批次接收结果
openAiChatClient.stream(request.getQuestion())
.doOnNext(chunk -> {
try {
// 每收到一个分片,发送到前端
emitter.send(chunk.getContent());
} catch (Exception e) {
emitter.completeWithError(e);
}
})
.doOnComplete(() -> {
// 生成完成,关闭连接
emitter.complete();
})
.doOnError(error -> {
// 出现错误,返回错误信息并关闭连接
emitter.completeWithError(error);
})
.subscribe();
return emitter;
}
测试此接口时,你会看到响应数据像流水一样分批次抵达,前端可以据此实现动态更新的对话界面。
3. 对话参数调优实战
大模型的输出并非一成不变,通过调整参数可以精确控制其“性格”。两个最核心的参数是:
- temperature(温度):控制输出的随机性。值越低(如0.2),输出越确定、保守;值越高(如1.0),输出越有创意、多样。代码生成场景通常用较低温度。
- top_p(核采样):影响输出词汇的多样性。通常与temperature二选一使用。
为了让接口更灵活,我们可以改造请求对象,支持动态传递这些参数:
public static class ChatRequest {
private String question;
private Float temperature; // 可选,不传递则使用配置文件默认值
private Float topP; // 可选,不传递则使用配置文件默认值
// getter/setter 省略
}
然后,在调用模型时应用这些参数:
@PostMapping("/api/chat/sync")
public String syncChat(@RequestBody ChatRequest request) {
// 构建对话参数,优先使用请求中的参数,无则使用默认值
OpenAiChatOptions options = OpenAiChatOptions.builder()
.temperature(request.getTemperature() != null ? request.getTemperature() : 0.7f)
.topP(request.getTopP() != null ? request.getTopP() : 0.9f)
.build();
// 调用大模型,传入参数
return openAiChatClient.generate(request.getQuestion(), options);
}
[AFFILIATE_SLOT_2]
四、接口测试与验证
开发完成后, thorough 的测试至关重要。我们使用Postman来测试上述三种接口。
对于同步和异步接口,使用普通的POST请求即可。对于流式接口,需要确保客户端能够处理SSE流。以下是测试同步接口的一个请求体示例:
{
"question": "请用Java代码实现冒泡排序",
"temperature": 0.2,
"topP": 0.8
}
此外,你也可以集成Swagger或Spring Doc OpenAPI,通过访问 http://localhost:8080/swagger-ui.html 来获得一个交互式的API文档页面,方便在浏览器内快速调试。
五、Spring AI核心机制浅析
了解底层原理有助于更好地使用和排查问题。Spring AI的设计遵循了Spring一贯的“约定大于配置”哲学。
- 自动装配:根据你的依赖(如
spring-ai-openai-spring-boot-starter)和配置,Spring Boot会自动创建OpenAiChatClient等Bean。 - 统一抽象:
ChatClient接口定义了call和stream等方法,提供了对不同模型供应商(OpenAI, Azure, 百度等)的一致访问方式。 - 请求/响应转换:Spring AI内部负责将我们的
Prompt对象和配置参数,转换为对应AI平台API所需的特定格式(如OpenAI的ChatCompletionRequest),并解析返回结果。
这使得开发者从繁琐的HTTP通信、JSON序列化/反序列化、错误处理和重试逻辑中解放出来,只需关注业务提示词(Prompt)和结果处理。
六、总结与展望
通过本文的实践,我们成功使用Spring AI构建了一个具备同步、异步、流式响应和参数调优能力的大模型对话应用。Spring AI极大地降低了Java开发者进入AI应用开发的门槛,其设计理念与Spring生态无缝融合,让集成AI能力变得像集成一个数据库那样简单。
关键收获:始终从简单可用的同步接口开始,然后根据需求逐步引入异步和流式来优化性能和体验。合理使用temperature等参数是控制AI输出质量的重要手段。未来,随着Spring AI功能的不断丰富,如对向量数据库、智能体(Agent)框架的支持,Java在AIGC领域将扮演越来越重要的角色。
浙公网安备 33010602011771号