布景

公司项目期望在客户端开发 AI 方法,支撑 AI 问答,要求完成 ChatGPT 的打字机作用

方针

  1. 了解 ChatGPT 流式呼应背面的技能(打字机)

  2. 调研 AI 服务流式呼应的可行性(技能层面、服务器资源耗费层面)

打字机是怎么完成的

众所周知,ChatGPT API 是一个OpenAI 的谈天机器人接口,它能够根据用户的输入生成智能的回复,为了进步谈天的流畅性和呼应速度,选用丢失输出的呼应方法,相似打字机的出现作用

这其实是选用了 SSE(Sever-sent Events) 服务端推送技能,答应服务器向客户端发送事情,然后完成服务器端推送

webSocket 不同的是,服务端推送是单向的。数据信息被单向从服务端到客户端分发. 当不需求以消息方法将数据从客户端发送到服务器时,这使它们成为绝佳的挑选

SSE 的通讯协议

SSE 通讯协议很简单,本质上就是一个客户端建议的 HTTP GET恳求,服务器在接纳到该恳求后,回来 200 OK状况,并附带以下呼应头:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
  • Content-Type: text/event-stream 表明呼应的内容类型是SSE格局的文本流。
  • Cache-Control: no-cache 表明呼应的内容不应该被缓存,以确保实时性。
  • Connection: keep-alive 表明呼应的衔接应该坚持打开,以便服务器端持续发送数据。 一般,客户端的恳求中会包含特殊的头信息:"Accept: text/event-stream" ,表明客户端体系接纳 SSE 数据

SSE 支撑以下几种字段:

  • event: 表明事情的类型,用于区分不同的事情,默许事情为 message
  • data: 表明事情的数据内容,能够有多行,每行都以data: 开头。
  • id: 表明事情的唯一标识符,用于断线重连和消息追踪。
  • retry: 表明断线重连的时刻距离,单位是毫秒。

SSE 事情流数据示例:

流式输出「够钟下班啦」,并以 event:data 标记事情流完毕

event:message
data:够
event:message
data:钟
event:message
data:下
event:message
data:班
event:message
data:啦
event:end
data:

SSE 接口示例

编写一个支撑 SSE 协议的接口

'use strict';
const Controller = require('egg').Controller;
class SSEController extends Controller {
  async index() {
    // Set SSE header
    const { ctx } = this
    ctx.response.set({
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
    });
    ctx.res.statusCode = 200;
    ctx.res.write(':流式呼应开端\n');
    let count = 0;
    while (count < 6) {
      const cur = count++
      const data = {
        message: `Hello, world ${cur}`,
        time: new Date(),
      };
      await new Promise(resolve => setTimeout(resolve, 1000));
      // Send SSE event
      ctx.res.write(`data: ${JSON.stringify(data)}\n`);
    }
    // event:end 是事情类型,表明完毕事情;客户端识别到服务器现已完毕呼应,然后封闭衔接
    ctx.res.write('event:end\ndata:end\n\n');
    // 监听客户端封闭衔接的事情,然后调用 ctx.res.end() 完毕呼应并封闭衔接。
    ctx.req.on('close', () => {
      ctx.res.end();
    });
  }
}
module.exports = SSEController;

恳求 SSE 接口,流式呼应:

curl -N --location --request GET 'http://127.0.0.1:7001/sse' \
--header 'Accept: text/event-stream'
>>>>
:流式呼应开端
data: {"message":"Hello, world 0","time":"2023-05-19T07:08:51.661Z"}
data: {"message":"Hello, world 1","time":"2023-05-19T07:08:52.662Z"}
data: {"message":"Hello, world 2","time":"2023-05-19T07:08:53.663Z"}
data: {"message":"Hello, world 3","time":"2023-05-19T07:08:54.665Z"}
data: {"message":"Hello, world 4","time":"2023-05-19T07:08:55.666Z"}
data: {"message":"Hello, world 5","time":"2023-05-19T07:08:56.667Z"}
:end
OK

打字机完成

后端代码

const express = require('express');
const router = express.Router();
router.get('/sse', (req, res) => {
 res.set({
  'Content-Type': 'text/event-stream',
  'Cache-Control': 'no-cache',
  Connection: 'keep-alive',
  'Access-Control-Allow-Origin': '*',
 });
 res.statusCode = 200;
 res.write('开端答复:\n');
 const answer = '众所周知,ChatGPT API 是一个OpenAI 的谈天机器人接口,它能够根据用户的输入生成智能的回复,为了进步谈天的流畅性和呼应速度,选用丢失输出的呼应方法,相似打字机的出现作用';
 let i = 0;
 const intervalId = setInterval(() => {
  res.write(`event:message\ndata:${answer[i]}\n\n`);
  i++;
  if (i === answer.length) {
   res.write('event:end\ndata: \n\n');  // event:end 表明事情流完毕
   clearInterval(intervalId);
  }
 }, 100);
 res.end();  // 事情流推送完毕,服务端主动断开衔接
});

前端接入 SSE:

<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<head>
    <title>SSE Example</title>
</head>
<body>
<h1>SSE Example</h1>
<button id="startButton">开端</button>
<div id="output">答复:</div>
<script>
  const startButton = document.getElementById('startButton');
  const outputElement = document.getElementById('output');
  let eventSource;
  startButton.addEventListener('click', function() {
    if (!eventSource) {
      eventSource = new EventSource('http://localhost:7001/sse2');
      eventSource.onmessage = function(event) {
        const message = event.data;
        outputElement.innerHTML += message;
      };
      eventSource.onerror = function(event) {
        console.error('Error: ' + event);
      };
      // 服务器界说了事情流:event:end,因而监听 :end 事情来完毕 eventSource
      eventSource.addEventListener('end', function(event) {
        console.log('SSE 衔接已封闭');
        eventSource.close();
      });
    }
  });
</script>
</body>
</html>

前端呼应示例:

ChatGPT 流式响应背后的技术

安卓接入 SSE:

运用 HttpURLConnection 或 OkHttp 等网络库来树立与服务器的衔接,并通过监听服务器发送的数据流来接纳事情

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class SSEClient {
  public void connectToSSE() {
    try {
      URL url = new URL("http://your-server/sse");
      HttpURLConnection connection = (HttpURLConnection) url.openConnection();
      connection.setRequestMethod("GET");
      connection.setRequestProperty("Accept", "text/event-stream");
      if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
        InputStream inputStream = connection.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String line;
        while ((line = reader.readLine()) != null) {
          // 处理接纳到的事情数据流
        }
        reader.close();
        inputStream.close();
      } else {
        // 处理衔接错误
      }
      connection.disconnect();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

iOS 接入 SSE

运用NSURLSession来树立与服务器的衔接,并通过监听服务器发送的数据流来接纳事情

NSURL *url = [NSURL URLWithString:@"http://your-server/sse"];
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
  if (error) {
    // 处理衔接错误
  } else {
    NSString *eventData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    // 处理接纳到的事情数据流
  }
}];
[task resume];

打字机视频演示作用

gzoffice.mojidict.com:9001/moji-test/%…

服务端推送技能安全性比照

服务端推送技能涉及到客户端和服务器之间的数据传输,因而需求考虑安全性问题。不同的服务端推送技能有不同的安全性特色:

  • Ajax 短轮询和长轮询和根据 iframe 的流都是根据 HTTP 协议的,因而能够运用 HTTPS 协议来加密数据,避免中间人进犯或数据走漏。可是,这些技能都需求频频地发送恳求和呼应,这可能会添加服务器的负载和网络的拥塞,也可能会被一些歹意的恳求或呼应干扰。

  • SSE(Sever-sent Events)也是根据 HTTP 协议的,因而也能够运用 HTTPS 协议来确保数据的安全性。SSE 比较于 Ajax 轮询技能,只需求树立一次衔接,就能够持续地接纳服务器的事情,这样能够削减网络开支和服务器压力。可是,SSE 只支撑单向的通讯,即服务器向客户端发送数据,客户端不能向服务器发送数据。这可能会约束一些交互功用的完成。SSE 多用在例如,谈天运用、股票行情、新闻更新等场景

  • WebSockets 是根据 TCP/IP 协议的,因而能够运用 WSS 协议来加密数据,避免数据被盗取或篡改。WebSockets支撑双向的通讯,客户端和服务器能够随时互相发送数据,这样能够完成更丰厚和灵敏的交互功用。可是,WebSockets 需求额定的端口号和组件来支撑,在一些环境中可能会遇到兼容性或安全性的问题。

综上所述,SSE 技能在 ChatGPT API 中有着重要的运用,它能够进步谈天机器人的呼应速度和用户体会。不同的服务端推送技能有各自的优缺点和安全性特色,需求根据详细的场景和需求来挑选适宜的技能。

SSE 运用在服务端的考虑

问题场景:客户端 > 服务器(调用 openAI),是否要选用 SSE ?

SSE 需求坚持衔接,是否会占用服务器资源?

运用 SSE 时,坚持衔接(keep-alive)会对服务器资源发生一些影响:

  1. 衔接开支:坚持衔接意味着服务器需求坚持与客户端之间的长时刻衔接。这会占用一定的服务器内存和其他资源来处理这些衔接。
  2. 并发衔接:假如有很多客户端一起运用 SSE 与服务器树立衔接,服务器需求一起办理和处理这些并发衔接。这可能会添加服务器的负载和资源耗费。
  3. 带宽占用:坚持衔接需求坚持持续的数据传输,即使是小量的数据也会占用一定的带宽。这可能对服务器的网络带宽和传输能力发生一定的压力。
  4. 状况办理:坚持衔接可能需求服务器维护客户端的衔接状况,以便正确地处理和传输数据。这需求服务器进行额定的状况办理和资源分配。

SSE 相关于其他实时通讯机制(如 WebSocket)来说,它的开支相对较低,因为SSE是根据标准的HTTP协议,运用简单的文本格局进行数据传输,而且不需求双向通讯。SSE 在呼应完成后,能够主动推送 event:end 完毕事情来通知客户端呼应完毕封闭衔接,避免一向坚持衔接

假如不运用 SSE 实时地流式传输,而是让客户端等候服务器完好的呼应后再回来,那么当前恳求的呼应时刻会变长,而且在这期间衔接也会占用服务器的资源。

在传统的同步恳求-呼应方法中,客户端发送恳求后会一向等候服务器生成完好的呼应,期间衔接坚持打开状况,占用服务器的衔接资源。这种等候时刻会添加恳求的呼应时刻,而且服务器需求坚持衔接的状况,耗费一定的资源

比较之下,SSE 答应服务器实时地将部分数据流式传输给客户端,以供给更好的实时性和用户体会。服务器能够在核算过程中逐步发送部分答复,使客户端能够即时获取到部分结果,而无需等候完好的呼应生成。

运用 SSE 的优势在于在核算过程中能够逐步回来结果,削减客户端等候时刻,并下降服务器资源的占用。而在传统的同步恳求-呼应方法中,客户端有必要一向等候完好的呼应,期间衔接会一向坚持打开状况,占用服务器的资源。

SSE 的流量耗费?

流式传输在某些情况下可能会耗费较多的流量,特别是在实时传输很多数据时。流式传输的特色是将数据逐步传输给客户端,而不需求等候完好的呼应生成。这意味着在传输过程中,数据会逐步发送给客户端,而不是一次性发送一切数据。

因而,假如流式传输的数据量很大或许传输速度较快,可能会占用更多的网络带宽和耗费更多的流量。关于大规模的流式传输,特别是关于长时刻的传输,流量耗费可能会变得更显着。

但是,关于一些小规模的流式传输,比如 逐字 或逐段地传输文本数据,相关于一次性传输一切数据,流量耗费的添加可能是能够承受的,而且能够供给更好的用户体会。

openAI 官方关于 stream completion 的阐明

github.com/openai/open…

在官方事例中,选用流式 & 非流式恳求,让 gpt-3.5-turbo 数到100,看看各需求多长时刻:

  • 两个恳求都花了大约 3 秒才完全完成
  • 关于流恳求,咱们在 0.1 秒后收到第一个令牌呼应,而且每隔约 0.01-0.02 秒收到后续令牌

ChatGPT 流式响应背后的技术

在相同的整体呼应时刻内,流式恳求加快了呼应功率,优化了运用体会

总结

关于 AI 问答方法运用场景,能够考虑在服务器调用 openAI 接口时敞开 stream: true

提取 openAI 的 stream completion,而且在业务接口中,选用 SSE,逐字回来调用结果,削减客户端等候时刻,并在呼应完毕后及时封闭衔接

参考资料

How_to_stream_completions.ipynb

了解ChatGPT流式呼应背面的技能,优化数据流处理功率

www.v2ex.com/t/921810