开发喵星球

Java实现ChatGPT流式输出

Java实现ChatGPT流式输出

Java集成ChatGPT在前面的文章中已经讲过了这次带大家实现官网的流式输出效果。首先我们明确的是官网是采用SSE技术来实现的流式输出,SSE(Server-Sent Events):通俗解释起来就是一种基于HTTP的,以流的形式由服务端持续向客户端发送数据的技术。

看到这里可能有人会说这不是WebSocket一样吗?其实不然,SSE比WebSocket更加的轻量化,更加的易用。

那么让GPT帮我们讲解一下SSE的工作流程吧:
image-20240323114935308

image-20240323115124576

后段核心代码

@Controller
@RequestMapping("/chat")
public class ChatController {

    /**
     * 暂存消息(由于前端EventSource对象仅支持Get请求,故消息通过POST发送到后端后进行中转)
     */
    Map<String, String> msgMap = new ConcurrentHashMap<>();

    @Autowired
    ChatService chatService;

    /**
     * 发送消息
     *
     * @param msg 消息
     * @return 消息ID
     */
    @ResponseBody
    @PostMapping("/sendMsg")
    public String sendMsg(String msg) {
        String msgId = IdUtil.simpleUUID();
        msgMap.put(msgId, msg);
        return msgId;
    }

    /**
     * 对话
     *
     * @param msgId 消息ID
     * @return SseEmitter
     */
    @GetMapping(value = "/conversation/{msgId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter conversation(@PathVariable("msgId") String msgId) {
        SseEmitter sseEmitter = new SseEmitter();
        String msg = msgMap.remove(msgId);

        //调用流式会话服务
        chatService.streamChatCompletion(msg, sseEmitter);

        //及时返回SseEmitter对象
        return sseEmitter;
    }
}

代码解析

  1. sendMsg 方法
    • 这是一个HTTP POST请求处理方法,映射到路径 "/chat/sendMsg"。
    • 方法接收一个名为 msg 的参数,表示要发送的消息。
    • 该方法生成一个消息ID(使用了 IdUtil.simpleUUID() 方法生成),然后将消息ID和消息内容存储到 msgMap 中,并返回消息ID。
  2. conversation 方法
    • 这是一个HTTP GET请求处理方法,映射到路径 "/chat/conversation/{msgId}"。
    • 方法使用 @GetMapping 注解进行映射,并指定了返回的内容类型为 MediaType.TEXT_EVENT_STREAM_VALUE,表明这是一个SSE流式事件的请求。
    • 方法接收一个路径参数 msgId,表示消息ID。
    • 方法创建了一个 SseEmitter 对象,用于向客户端发送SSE事件。
    • msgMap 中根据消息ID取出对应的消息内容。
    • 调用 chatServicestreamChatCompletion 方法,传递消息内容和 SseEmitter 对象,以便进行后续的流式处理。
    • 返回创建的 SseEmitter 对象,以便客户端与服务器建立SSE连接并接收事件流。

前断核心代码

var eventSource = new EventSource('http://localhost:8080/chat/conversation/'+data)
             eventSource.addEventListener('open', function(e) {
                console.log("EventSource连接成功");
             });
             eventSource.addEventListener("message", function(evt){
                var data = evt.data;
                var json = JSON.parse(data);
                var content = json.content ? json.content : '';
                content = content.replaceAll('\n','<br/>');
                console.log(json)
                var outputBody = ('#outputTxt');
                outputBody.html(outputBody.html()+content);
                var outputCard =('#outputCard');
                var scrollHeight = outputCard[0].scrollHeight;
                outputCard.scrollTop(scrollHeight);
            });
            eventSource.addEventListener('error', function (e) {
                console.log("EventSource异常:" + e)
            });

代码解析

  1. 创建 EventSource 对象

    • 使用 new EventSource('http://localhost:8080/chat/conversation/'+data) 创建了一个 EventSource 对象,指定了服务器端的 SSE 路径。其中 data 是变量,可能是用于动态构建路径的信息。
  2. 事件监听器

    • 添加了三个事件监听器:
      • open:当连接建立成功时触发。在这里,打印消息 "EventSource连接成功"。
      • message:当接收到新的消息时触发。在这里,将消息内容解析为 JSON 格式,然后将内容展示到页面上。此处使用了 jQuery 来操作 DOM。
      • error:当连接发生错误时触发。在这里,打印错误信息。
  3. 处理消息

    • message 事件监听器中,首先解析收到的消息数据,并将其转换为 JSON 格式。
    • 然后从 JSON 对象中提取内容,并将换行符替换为 <br/>,以便在页面上正确显示换行。
    • 将提取的内容追加到页面上指定的输出区域 ($('#outputTxt')) 中。
    • 然后,将页面滚动到输出区域的底部,以确保始终显示最新的消息。

   
分类:玩技术 作者:荡荡, 浩浩 发表于:2024-03-23 11:56:27 阅读量:341
<<   >>


powered by kaifamiao