【java】AI内容用SSE流式输出响应给前端,实现打字机效果_java sse流式响应
目录
1、引言
2、包装AI响应的内容
3、控制内容流式响应
(1)直接返回Flux对象,并添加请求头
(2)在Flux对象里面的具体响应数据再用一层ServerSentEvent包裹,不需要再加请求头
(3)专门用于SSE推流的类:SseEmitter(推荐,简单更灵活)
1、引言
随着AI技术的崛起,我们也开始接触开发AI项目了。比如我使用的是springAI来调用大模型,通过一些定制化内容,得到比较有个性化的智能体。
而调用大模型是我们后端的工作,那么如何将AI响应的内容返回给前端呢?我们当然可以直接调用完大模型后,等大模型响应完内容给我们的后端,然后后端再统一把消息返回给前端。这种方法虽然可行,但是在用户体验上就差很多了,如果后端要等大模型完整地返回内容的话,就要阻塞很久,那么用户端是无感知的,反而会觉得这个系统很差,要等这么久才能响应。
所以我们就要达到现有较流行的打字机效果,就是AI响应一点内容,就给用户返回一些内容,要达到这种效果,就要用到SSE流式输出了。
2、包装AI响应的内容
本章节不对如何调用大模型重点讲解,后续会考虑发布相关博客,本章节之针对流式输出做讲解。所以对调用AI大模型的代码如果看不懂不需要太在意,只要知道AI输出的内容也是可以通过stream方法流式输出的即可。
public Flux doChatByStream(String message,String charId) { Flux content = chatClient.prompt() .user(message) //这里的advisors是单词发送执行的拦截器,指定了 .advisors(advisor -> advisor.param(CHAT_MEMORY_CONVERSATION_ID_KEY, charId)//指定会话房间 .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 20))//指定记忆多少轮对话 .stream()//使用流式输出 .content(); return content; }
代码解释:AI响应的内容是String类型的,通过调用一些函数,让Flux包装,这种其实就是响应式编程。
3、控制内容流式响应
在controller层执行,有三种方式:
@RestController@RequestMapping(\"/ai/love_app\")@Slf4jpublic class AiController { @Resource private LoveApp loveApp; @GetMapping(value = \"/doChat/sse\", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux chat(String message, String charId) { Flux stringFlux = loveApp.doChatByStream(message, charId); // 返回响应 return stringFlux; } @GetMapping(\"/doChat/sse2\") public Flux<ServerSentEvent> chatBySse2(String message, String charId) { Flux<ServerSentEvent> serverSentEventFlux = loveApp.doChatByStream(message, charId).map(chunk -> { ServerSentEvent result = ServerSentEvent.builder() .data(chunk) .build(); return result; }); return serverSentEventFlux; } @GetMapping(\"/doChat/sse3\") public SseEmitter chatBySseEmitter(String message, String charId) { SseEmitter sseEmitter = new SseEmitter(180000L);//超时时间 loveApp.doChatByStream(message, charId) .subscribe(chunk -> { // 订阅数据流 try { sseEmitter.send(chunk);// 发送数据到客户端 } catch (Exception e) { sseEmitter.completeWithError(e);// 发送异常时,执行异常完成函数(手动关闭) log.error(\"发送异常:{}\",e); } }, sseEmitter::completeWithError,sseEmitter::complete); return sseEmitter; }}
(1)直接返回Flux对象,并添加请求头
(2)在Flux对象里面的具体响应数据再用一层ServerSentEvent包裹,不需要再加请求头
(3)专门用于SSE推流的类:SseEmitter(推荐,简单更灵活)
调用它的send函数主动向前端推送数据