打造自己的ChatGPT:逐字打印的流式处理
接口的延迟
在调用OpenAI的接口时,不免会有很慢的感觉,抛去地理位置上的网络延迟,大量的延迟往往发生在响应生成的过程中。

因此,如果使用同步接口的话,需要等待响应完全生成之后才能最终显示输出结果,虽然在对接微信或者其他需要通过接口请求的应用服务时没什么特别好的处理方案,但是如果是Web应用的话,就可以通过流式处理实现实时的数据返回,从而提升响应的优先级。
后端的流式处理
绝大部分的SDK都已经对OpenAI API的流式响应做了封装,这里以.NET 的Betalgo.OpenAI.GPT3为例,详细的配置部分不再赘述,可参考其说明文档。
//使用CreateCompletionAsStream获取 completions
var completions = openAIService.Completions.CreateCompletionAsStream(new OpenAI.GPT3.ObjectModels.RequestModels.CompletionCreateRequest
{
Prompt = BuildPrompt(input.Prompt),
MaxTokens = 100,
Temperature = 0.5f,
TopP = 1,
FrequencyPenalty = 0,
PresencePenalty = 0,
StopAsList = new List<string> { "Q:", "A:" }
}, Models.TextDavinciV3);
//将返回内容直接经过UTF编码写入Response
async Task ResponseWrite(string? text) {
if (text == null) return;
await Response.Body.WriteAsync(Encoding.UTF8.GetBytes(text));
await Response.Body.FlushAsync();
}
//逐个获取 completion的返回,并写入到Response中
await foreach (var completionResponse in completions)
{
if (completionResponse.Successful)
{
await ResponseWrite(completionResponse.Choices[0].Text);
}else {
await ResponseWrite("<ERR>");
if(completionResponse.Error != null){
await ResponseWrite(completionResponse.Error.Code);
await ResponseWrite(completionResponse.Error.Message);
}else {
await ResponseWrite("<unknow>");
}
}
}
其中最核心的一点就是 ResponseWrite 部分,提前将内容写入到Response的Body中,并进行Flush。
另外需要注意的就是,Action 不能再有返回值(如果是.NET 7 的话,可以返回 Empty),因为已经在Action结束之前向Response写入了内容,再有返回值的话,就会报错。
前端的流式处理
同样的,后端作为流式发送,前端就需要以流式形式接收。
这里不需要使用到EventSource、WebSocket或是其他的实时通讯框架,仅需要使用普通的fetch即可。
async function send() {
const input = document.getElementById("input").value;
const output = document.getElementById("output");
output.innerText = "";
const url = "/api/stream";
const data = { "Prompt": input };
//直接获取 Fetch 的response, 无法使用 await的话, Promise的方式也是可以的。
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json"
}
})
//获取UTF8的解码
const encode = new TextDecoder("utf-8");
//获取body的reader
const reader = response.body.getReader();
// 循环读取reponse中的内容
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
// 解码内容
const text = encode.decode(value);
// 当获取错误token时,输出错误信息
if (text === "<ERR>") {
output.innerText = "Error";
break;
} else {
// 获取正常信息时,逐字追加输出
output.innerText += text;
}
}
}
fetch获取的response可以,可以通过 response.body.getReader() 获取reader,然后循环获取reader中的响应,根据情况,将响应内容追加到界面上,就实现了逐字打印的效果了。
相关链接
- 以上示例代码,可参见 GPT3_demo
- 想要了解如何理解OpenAI的API,可参考打造自己的ChatGPT:OpenAI 的API接入技巧
- OpenAI 官方对于延迟处理的一些意见improving-latencies
- Dotnet SDK for OpenAI GPT-3 and DALL·E
打造自己的ChatGPT:逐字打印的流式处理的更多相关文章
- android流式布局、待办事项应用、贝塞尔曲线、MVP+Rxjava+Retrofit、艺术图片应用等源码
Android精选源码 android模仿淘宝首页效果源码 一款艺术图片应用,采用T-MVVM打造 Android MVP + RxJava + Retrofit项目 android流式布局实现热门标 ...
- CSS3与页面布局学习笔记(四)——页面布局大全(负边距、双飞翼、多栏、弹性、流式、瀑布流、响应式布局)
一.负边距与浮动布局 1.1.负边距 所谓的负边距就是margin取负值的情况,如margin:-100px,margin:-100%.当一个元素与另一个元素margin取负值时将拉近距离.常见的功能 ...
- HttpURLConnection的流式输出的缺陷和解决方法
转自:http://www.mzone.cc/article/198.html 最近在用applet写文件上传控件的时候发现使用URLConnection来对服务器进行流式输出时的一些问题.我们通常要 ...
- JDFS:一款分布式文件管理系统,第三篇(流式云存储)
一 前言 看了一下,距离上一篇博客的发表已经过去了4个月,时间过得好快啊.本篇博客是JDFS系列的第三篇博客,JDFS的目的是为了实现一个分布式的文件管理系统,前两篇实现了基本的上传.下载功能,但是那 ...
- JDFS:一款分布式文件管理系统,第四篇(流式云存储续篇)
一 前言 本篇博客是JDFS系列博客的第四篇,从最初简单的上传.下载,到后来加入分布式功能,背后经历了大量的调试,尤其当实验的虚拟计算结点数目增加后,一些潜在的隐藏很深的bug就陆续爆发.在此之前笔者 ...
- 【官方文档】Nginx模块Nginx-Rtmp-Module学习笔记(三)流式播放Live HLS视频
源码地址:https://github.com/Tinywan/PHP_Experience HTTP Live Streaming(HLS)是由Apple Inc.实施的非常强大的流视频协议.HLS ...
- Spark Streaming流式处理
Spark Streaming介绍 Spark Streaming概述 Spark Streaming makes it easy to build scalable fault-tolerant s ...
- java JAXB + STAX(是一种针对XML的流式拉分析API)读取xml
JDK1.5需要添加jar包,1.6以后就不需要了<dependency> <groupId>stax</groupId> <artifactId>st ...
- centos 正则,grep,egrep,流式编辑器 sed,awk -F 多个分隔符 通配符 特殊符号. * + ? 总结 问加星 cat -n nl 输出文件内容并加上行号 alias放~/.bash_profile 2015-4-10 第十三节课
centos 正则,grep,egrep,流式编辑器 sed,awk -F 多个分隔符 通配符 特殊符号. * + ? 总结 问加星 cat -n nl 输出文件内容并加上行号 alias放~ ...
- 构建流式应用—RxJS详解[转]
目录 常规方式实现搜索功能 RxJS · 流 Stream RxJS 实现原理简析 观察者模式 迭代器模式 RxJS 的观察者 + 迭代器模式 RxJS 基础实现 Observable Observe ...
随机推荐
- 弹框table
<template> <div> <el-dialog :title="title" :visible.sync="dialogVisibl ...
- 小米盒子TV变装魔法
最近从一位台湾的朋友那里白嫖了一个 v2 节点, 恰好家里有一台家用的 小米盒子, 就寻思着能不能折腾一下, 共享上网 先将小米盒子开启adb调试, 参照这里: https://www.jb51.ne ...
- Realtek 平台一些乱七八糟的编译环境设置
1. 129x 系列之后的就推荐用ubuntu 16.04了 省去一些GCC ,tar,quilt 等问题 Android 平台: sudo apt-get install u-boot-tools ...
- ABP vNext微服务架构详细教程——分布式权限框架
1.简介 ABP vNext框架本身提供了一套权限框架,其功能非常丰富,具体可参考官方文档:https://docs.abp.io/en/abp/latest/Authorization 但是我们使用 ...
- Nginx版本。
Nginx官网提供了三个类型的版本Mainline version:Mainline 是 Nginx 目前主力在做的版本,可以说是开发版Stable version:最新稳定版,生产环境上建议使用的版 ...
- termux搭建服务器方式
pkg install vim apt update 安装debian系统apt install proot-distroproot-distro install debianproot-distro ...
- 使用request对象实现注册示例,请求方式的编码问题
get提交方式: method="get"和地址栏请求方式默认都属于get提交方式 get方式在地址栏显示请求信息﹐(但是地址栏能够容纳的信息有限,4-5KB;如果请求数据存在大文 ...
- 在linux下创建KVM虚拟机
Kernel-based Virtual Machine的简称,是一个开源的系统虚拟化模块,自Linux 2.6.20之后集成在Linux的各个主要发行版本中.它使用Linux自身的调度器进行管理,所 ...
- Ubuntu 22.04 安装 VMWare 16.2.3 后无法启动
异常日志: 2022-06-13T03:49:56.019Z In(05) host-29676 In file included from /tmp/modconfig-XR2GVI/vmmon-o ...
- Asp.Net Core 程序开发技巧汇总
使用Sqlite数据库 创建项目 Asp.Net Core Web应用程序 Web应用程序 ASP.NET Core 2.2 NuGet管理,添加Sqlite数据库支持 Microsoft.Entit ...