当前位置:   article > 正文

ChatGPT流式输出实现原理

chatgpt流式输出

在使用ChatGPT时,模型的回复内容是一个字一个字蹦出来的,而不是整段话直接出现,因为模型需要不断预测接下来要回复什么内容,如果等整段回复生成之后再输出到网页,用户体验就会很差,一直以为这种流式输出效果是用WebSocket实现的,后来接入open ai接口,发现接口是http协议,才了解到SSE技术。

Server-Sent Events (SSE) 是一种基于 HTTP 协议的服务器推送技术,它允许服务器向客户端发送数据和信息。与 WebSocket 不同,SSE 是一种单向通信方式,只有服务器可以向客户端推送消息。SSE 是 HTML5 规范的一部分,使用非常简单,主要由服务端与浏览器端的通讯协议(HTTP协议)和 EventSource 接口来处理 Server-sent events 组成,服务器端的响应的内容类型是“text/event-stream”。

使用postman给open ai接口发个请求,验证一下。

可以看到响应头中的Content-Type为text/event-stream:

再看响应体,可以看到有很多个以data: 开头的片段,每个片段包含一个choices数组,数组元素的delta.content属性的值是一个单词,每个片段中的单词连起来就是ChatGPT回答的内容,最后一个片段内容是[done],表示输出结束。

由此可验证open ai的接口是通过SSE技术来实现的。

接下来我们写一个简单的demo来模拟一下ChatGPT的输出效果,更直观的学习和体验SSE技术。

先看一下效果:

 新建springboot项目,包含一个原生的index.html页面和一个http接口,页面请求接口,接口读取一个txt文件并将文件内容流式输出到页面。点击查看github源码

页面代码:

  1. <body>
  2. <button type="button" onclick="output()">输出文章</button>
  3. <div id="message"></div>
  4. <script>
  5.   function output() {
  6.       let source = new EventSource(
  7.           'http://localhost:8080/article');
  8.       let innerHTML = '';
  9.       source.onmessage = function (e) {
  10.           if (e.data == '[done]') {
  11.               source.close();
  12.           } else {
  13.               innerHTML += e.data;
  14.               document.getElementById("message").innerHTML = innerHTML;
  15.           }
  16.       };
  17.   }
  18. </script>
  19. </body>

服务端代码:

  1. @Controller
  2. public class SseController {
  3.   @Autowired
  4.   private ResourceLoader resourceLoader;
  5.   @RequestMapping("/article")
  6.   public void sendArticle(HttpServletResponse res) throws Exception {
  7.       res.setContentType("text/event-stream;charset=UTF-8");
  8.       Resource resource = resourceLoader.getResource("classpath:1.txt");
  9.       InputStream inputStream = resource.getInputStream();
  10.       BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
  11.       String line;
  12.       while ((line = reader.readLine()) != null) {
  13.           // 这里用空格来将一行内容分为多个单词输出
  14.           String delimiter = " ";
  15.           String[] words = line.split(delimiter);
  16.           // 为了支持将中文句子分为单个汉子输出
  17.           if (words.length == 1) {
  18.               delimiter = "";
  19.               words = line.split(delimiter);
  20.           }
  21.           for (int i = 0; i < words.length; i++) {
  22.               // 每次只输出一个词,每个片段以data: 开头,以\n\n结尾
  23.               res.getWriter().write("data: " + words[i] + delimiter + "\n\n");
  24.               res.getWriter().flush();
  25.               // 睡眠100ms,便于观察效果
  26.               Thread.sleep(100);
  27.           }
  28.           // 由于每次读的是一行数据,因此输出一个换行
  29.           res.getWriter().write("data: </br>\n\n");
  30.       }
  31.       // 也用[done]来标识结束
  32.       res.getWriter().write("data: [done]\n\n");
  33.       res.getWriter().flush();
  34.   }
  35. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/518005
推荐阅读
相关标签
  

闽ICP备14008679号