一、背景与场景
1.1 为什么需要流式AI问答?
-
传统问答接口痛点:
- 响应延迟高:用户需要等待完整答案生成
- 交互体验差:无法实现「打字机效果」
- 资源浪费:长文本生成时占用大量内存
-
流式传输优势:
- 首包响应时间低至300ms
- 支持实时交互,提升用户体验
- 节省服务器资源,降低内存占用
1.2 技术选型
| 技术栈 | 选型理由 |
|---|---|
| SpringBoot | 快速构建RESTful API,生态丰富 |
| 智谱清言 | 国产大模型,API稳定,支持流式响应 |
| SSE(Server-Sent Events) | 轻量级流式传输协议,兼容性好 |
二、快速开始
1.在pom.xml文件中引入依赖。
<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>oapi-java-sdk</artifactId>
<version>release-V4-2.3.2</version>
</dependency>
2.我们需要在智谱清言开放平台注册个人账号,并获取apikey
# AI 配置
ai:
apiKey: 你的秘钥
三、核心实现
- 配置类封装
@Configuration
@ConfigurationProperties(prefix = "ai")
@Data
public class AiConfig {
private String apiKey;
@Bean
public ClientV4 getClientV4() {
return new ClientV4.Builder(apiKey).build();
}
}
4.AI服务层封装
@Component
public class AiManager {
@Resource
private ClientV4 clientV4;
// 温度参数控制
private static final float STABLE_TEMPERATURE = 0.05f; // 稳定输出
private static final float UNSTABLE_TEMPERATURE = 0.99f; // 创造性输出
/**
* 流式请求(核心方法)
*/
public Flowable<ModelData> doStreamRequest(List<ChatMessage> messages, Float temperature) {
ChatCompletionRequest request = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4Flash)
.stream(Boolean.TRUE)
.temperature(temperature)
.messages(messages)
.build();
try {
return clientV4.invokeModelApi(request).getFlowable();
} catch (Exception e) {
throw new BusinessException("AI服务调用失败", e);
}
}
}
5.流式控制器实现
@RestController
@RequestMapping("/ai")
public class AIController {
@Resource
private AiManager aiManager;
private static final String AI_PROMPT = """
你是一位资深教授,擅长解决学术问题...
<!--技能模块-->
{具体提示词内容}
""";
@GetMapping("/ai_generate/sse")
public SseEmitter aiGenerateQuestionSSE(QuestionAddRequest aiGenerateQuestionRequest, HttpServletRequest request) throws Exception {
ThrowUtils.throwIf(aiGenerateQuestionRequest == null, ErrorCode.PARAMS_ERROR);
// 获取参数
String userMessage = aiGenerateQuestionRequest.getQuestion();
// 封装 Prompt
// 建立 SSE 连接对象,0 表示永不超时
SseEmitter sseEmitter = new SseEmitter(0L);
// AI 生成,SSE 流式返回
Flowable<ModelData> modelDataFlowable = aiManager.doStreamRequest(AI_INTERACTION_MESSAGE, userMessage, null);
// 默认全局线程池
Scheduler scheduler = Schedulers.single();
StringBuilder stringBuilder = new StringBuilder();
modelDataFlowable
.observeOn(scheduler)
.map(modelData -> modelData.getChoices().get(0).getDelta().getContent())
.doOnNext(content -> {
stringBuilder.append(content);
sseEmitter.send(SseEmitter.event().name("message").data(content));
stringBuilder.setLength(0);
})
.doOnError((e) -> log.error("sse error", e))
.doOnComplete(sseEmitter::complete)
.subscribe();
return sseEmitter;
}
四、参考说明
- 智谱清言官方文档:API Reference
- Spring官方文档:SSE支持
- 技术参考:本文部分实现参考了程序员鱼皮的API设计实践,特此致谢