当前热点-「GPT虚拟直播」实战篇|GPT接入虚拟人实现直播间弹幕回复

2023-05-26 12:15:05 来源:博客园
摘要

ChatGPT和元宇宙都是当前数字化领域中非常热门的技术和应用。结合两者的优势和特点,可以探索出更多的应用场景和商业模式。例如,在元宇宙中使用ChatGPT进行自然语言交互,可以为用户提供更加智能化、个性化的服务和支持;在ChatGPT中使用元宇宙进行虚拟现实体验,可以为用户提供更加真实、丰富、多样化的交互体验。下面我将结合元宇宙和ChatGPT的优势,实战开发一个GPT虚拟直播的Demo并推流到抖音平台,

NodeJS接入ChatGPT与即构ZIM

上一篇文章《人人都能用ChatGPT4.0做Avatar虚拟人直播》,主要介绍了如何使用ChatGPT+即构Avatar做虚拟人直播。由于篇幅原因,对代码具体实现部分描述的不够详细。收到不少读者询问代码相关问题,接下来笔者将代码实现部分拆分2部分来详细描述:


(资料图)

NodeJS接入ChatGPT与即构ZIMChatGPT与即构Avatar虚拟人对接直播

本文主要讲解如何接入ChatGPT并实现后期能与Avatar对接能力。

在开始讲具体流程之前,我们先来回顾一下整个GPT虚拟直播Demo的实现流程图,本文要分享的内容是下图的右边部分的实现逻辑。

1 基本原理

ChatGPT是纯文本互动,那么如何让它跟Avatar虚拟人联系呢?首先我们已知一个先验:

即构Avatar有文本驱动能力,即给Avatar输入一段文本,Avatar根据文本渲染口型+播报语音将观众在直播间发送的弹幕消息抓取后,发送给OpenAI的ChatGPT服务器得到ChatGPT回复后将回复内容通过Avatar语音播报在观众看来,这就是在跟拥有ChatGPT一样智商的虚拟人直播互动了。2 本文使用的工具GPT虚拟直播弹幕:即构ZIM语聊房群聊弹幕GPT4.0:New bingGPT3.5:ChatGPT3 对接ChatGPT

这里主要推荐2个库:

chatgpt-apichatgpt

chatgpt-api封装了基于bing的chatgpt4.0,chatgpt基于openAI官方的chatgpt3.5。具体如何创建bing账号以及如何获取Cookie值以及如何获取apiKey,可以参考我另一篇文章《人人都能用ChatGPT4.0做Avatar虚拟人直播》。

3.1 chatgpt-api

安装:

npm i @waylaidwanderer/chatgpt-api

bing还没有对中国大陆开放chatgpt,因此需要一个代理,因此需要把代理地址也一起封装。代码如下:

import { BingAIClient } from "@waylaidwanderer/chatgpt-api";export class BingGPT {    /*    * http_proxy, apiKey    **/    constructor(http_proxy, userCookie) {        this.api = this.init(http_proxy, userCookie);        this.conversationSignature = "";        this.conversationId = "";        this.clientId = "";        this.invocationId = "";    }    init(http_proxy, userCookie) {       console.log(http_proxy, userCookie)        const options = {             host: "https://www.bing.com",             userToken: userCookie,            // If the above doesn"t work, provide all your cookies as a string instead            cookies: "",            // A proxy string like "http://:"            proxy: http_proxy,            // (Optional) Set to true to enable `console.debug()` logging            debug: false,        };        return new BingAIClient(options);    }    //    //此处省略chat函数......    //} 

上面代码完成了VPN和BingAIClient的封装,还缺少聊天接口,因此添加chat函数完成聊天功能:

//调用chatpgt chat(text, cb) {    var res=""    var that = this;    console.log("正在向bing发送提问", text )     this.api.sendMessage(text, {         toneStyle: "balanced",        onProgress: (token) => {             if(token.length==2 && token.charCodeAt(0)==55357&&token.charCodeAt(1)==56842){                cb(true, res);            }             res+=token;        }    }).then(function(response){         that.conversationSignature = response.conversationSignature;        that.conversationId = response.conversationId;        that.clientId = response.clientId;        that.invocationId = response.invocationId;    }) ;  }

在使用的时候只需如下调用:

var bing = new BingGPT(HTTP_PROXY, BING_USER_COOKIE);bing.chat("这里传入提问内容XXXX?", function(succ, response){    if(succ)        console.log("回复内容:", response)})

需要注意的是,基于bing的chatgpt4.0主要是通过模拟浏览器方式封住。在浏览器端有很多防机器人检测,因此容易被卡断。这里笔者建议仅限自己体验,不适合作为产品接口使用。如果需要封装成产品,建议使用下一节2.2内容。

3.2 chatgpt

安装:

npm install chatgpt

跟上一小节2.1类似,基于openAI的chatgpt3.5依旧需要梯子才能使用。chatgpt库没有内置代理能力,因此我们可以自己安装代理库:

npm install https-proxy-agent node-fetch

接下来将代理和chatgpt库一起集成封装成一个类:

import { ChatGPTAPI } from "chatgpt";import proxy from "https-proxy-agent";import nodeFetch from "node-fetch";export class ChatGPT {      constructor(http_proxy, apiKey) {        this.api = this.init(http_proxy, apiKey);        this.conversationId = null;        this.ParentMessageId = null;    }    init(http_proxy, apiKey) {        console.log(http_proxy, apiKey)        return new ChatGPTAPI({            apiKey: apiKey,            fetch: (url, options = {}) => {                const defaultOptions = {                    agent: proxy(http_proxy),                };                const mergedOptions = {                    ...defaultOptions,                    ...options,                };                return nodeFetch(url, mergedOptions);            },        });    }    //...    //此处省略chat函数    //...} 

完成ChatGPTAPI的封装后,接下来添加聊天接口:

//调用chatpgt chat(text, cb) {    let that = this    console.log("正在向ChatGPT发送提问:", text)    that.api.sendMessage(text, {        conversationId: that.ConversationId,        parentMessageId: that.ParentMessageId    }).then(        function (res) {            that.ConversationId = res.conversationId            that.ParentMessageId = res.id            cb && cb(true, res.text)        }    ).catch(function (err) {        console.log(err)        cb && cb(false, err);    });}

使用时就非常简单:

var chatgpt =  new ChatGPT(HTTP_PROXY, API_KEY);chatgpt.chat("这里传入提问内容XXXX?", function(succ, response){    if(succ)        console.log("回复内容:", response)})

chatgpt库主要基于openAI的官方接口,相对来说比较稳定,推荐这种方式使用。

3.3 两库一起封装

为了更加灵活方便使用,随意切换chatgpt3.5和chatgpt4.0。将以上两个库封装到一个接口中。

首先创建一个文件保存各种配置, KeyCenter.js:

const HTTP_PROXY = "http://127.0.0.1:xxxx";//本地vpn代理端口//openAI的key, const API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxxx";//bing cookieconst BING_USER_COOKIE = "xxxxxxxxxxxxxxxxxxxxxxxx--BA";module.exports = {     HTTP_PROXY: HTTP_PROXY,    API_KEY: API_KEY,    BING_USER_COOKIE:BING_USER_COOKIE}

注意,以上相关配置内容需要读者替换。

接下来封装两个不同版本的chatGPT:

const KEY_CENTER = require("../KeyCenter.js");var ChatGPTObj = null, BingGPTObj = null;//初始化chatgptfunction getChatGPT(onInitedCb) {    if (ChatGPTObj != null) {        onInitedCb(true, ChatGPTObj);        return;    }    (async () => {        let { ChatGPT } = await import("./chatgpt.mjs");        return new ChatGPT(KEY_CENTER.HTTP_PROXY, KEY_CENTER.API_KEY);    })().then(function (obj) {        ChatGPTObj = obj;        onInitedCb(true, obj);    }).catch(function (err) {        onInitedCb(false, err);    });}function getBingGPT(onInitedCb){    if(BingGPTObj!=null) {        onInitedCb(true, BingGPTObj);        return;    }    (async () => {        let { BingGPT } = await import("./binggpt.mjs");        return new BingGPT(KEY_CENTER.HTTP_PROXY, KEY_CENTER.BING_USER_COOKIE);    })().then(function (obj) {        BingGPTObj = obj;        onInitedCb(true, obj);    }).catch(function (err) {        console.log(err)        onInitedCb(false, err);    });}

上面两个函数getBingGPTgetChatGPT分别对应2.1节2.2节封装的版本。在切换版本的时候直接调用对应的函数即可,但笔者认为,还不够优雅!使用起来还是不够舒服,因为需要维护不同的对象。最好能进一步封装,调用的时候一行代码来使用是最好的。那进一步封装,补充以下代码:

//调用chatgpt聊天function chatGPT(text, cb) {    getChatGPT(function (succ, obj) {        if (succ) {            obj.chat(text, cb);        } else {            cb && cb(false, "chatgpt not inited!!!");        }    })}function chatBing(text, cb){    getBingGPT(function (succ, obj) {        if (succ) {            obj.chat(text, cb);        } else {            cb && cb(false, "chatgpt not inited!!!");        }    })}module.exports = {    chatGPT: chatGPT,    chatBing:chatBing} 

加了以上代码后,就舒服多了:想要使用bing的chatgpt4.0,那就调用chatBing函数好了;想要使用openAI官方的chatgpt3.5,那就调用chatGPT函数就好!

4 对接Avatar4.1 基本思路

好了,第2节介绍了对chatgpt的封装,不同的版本只需调用不同函数即可实现与chatgpt对话。接下来怎么将chatGPT的文本对话内容传递给Avatar呢?即构Avatar是即构推出的一款虚拟形象产品,它可以跟即构内的其他产品对接,比如即时通讯ZIM和音视频通话RTC。这就好办了,我们只需利用ZIM或RTC即可。

这里我们主要利用即构ZIM实现,因为即构ZIM非常方便实时文本内容。即构ZIM群聊消息稳定可靠,延迟低,全球任何一个地区都有接入服务的节点保障到达。

尤其是ZIM群聊有弹幕功能,相比发送聊天消息,发送弹幕消息不会被存储,更适合直播间评论功能。

4.2 代码实现

即构官方提供的js版本库主要是基于浏览器,需要使用到浏览器的特性如DOM、localStorage等。而这里我们主要基于NodeJS,没有浏览器环境。因此我们需要安装一些必要的库, 相关库已经在package.json有记录,直接执行如下命令即可:

npm install
4.2.1 创建模拟浏览器环境

首先执行浏览器环境模拟,通过fake-indexeddb、jsdom、node-localstorage库模拟浏览器环境以及本地存储环境。创建WebSocket、XMLHttpRequest等全局对象。

var fs = require("fs");//先清理缓存fs.readdirSync("./local_storage").forEach(function (fileName) {    fs.unlinkSync("./local_storage/" + fileName);});const KEY_CENTER = require("../KeyCenter.js");const APPID = KEY_CENTER.APPID, SERVER_SECRET = KEY_CENTER.SERVER_SECRET;const generateToken04 = require("./TokenUtils.js").generateToken04;var LocalStorage = require("node-localstorage").LocalStorage;localStorage = new LocalStorage("./local_storage");var indexedDB = require("fake-indexeddb/auto").indexedDB;const jsdom = require("jsdom");const { JSDOM } = jsdom;const dom = new JSDOM(``, {    url: "http://localhost/",    referrer: "http://localhost/",    contentType: "text/html",    includeNodeLocations: true,    storageQuota: 10000000});window = dom.window;document = window.document;navigator = window.navigator;location = window.location;WebSocket = window.WebSocket;XMLHttpRequest = window.XMLHttpRequest;
4.2.2 创建ZIM对象

将即构官方下载的index.js引入,获取ZIM类并实例化,这个过程封装到createZIM函数中。需要注意的是登录需要Token,为了安全考虑,Token建议在服务器端生成。接下来把整个初始化过程封装到initZego函数中,包含注册监听接收消息,监控Token过期并重置。

const ZIM = require("./index.js").ZIM; function newToken(userId) {    const token = generateToken04(APPID, userId, SERVER_SECRET, 60 * 60 * 24, "");    return token;}/** * 创建ZIM对象*/function createZIM(onError, onRcvMsg, onTokenWillExpire) {    var zim = ZIM.create(APPID);    zim.on("error", onError);    zim.on("receivePeerMessage", function (zim, msgObj) {        console.log("收到P2P消息")        onRcvMsg(false, zim, msgObj)    });    // 收到群组消息的回调    zim.on("receiveRoomMessage", function (zim, msgObj) {        console.log("收到群组消息")        onRcvMsg(true, zim, msgObj)    });    zim.on("tokenWillExpire", onTokenWillExpire);    return zim;}/**初始化即构ZIM*/function initZego(onError, onRcvMsg, myUID) {    var token = newToken(myUID);    var startTimestamp = new Date().getTime();    function _onError(zim, err) {        onError(err);    }    function _onRcvMsg(isFromGroup, zim, msgObj) {        var msgList = msgObj.messageList;        var fromConversationID = msgObj.fromConversationID;        msgList.forEach(function (msg) {            if (msg.timestamp - startTimestamp >= 0) { //过滤掉离线消息                var out = parseMsg(zim, isFromGroup, msg.message, fromConversationID)                if (out)                    onRcvMsg(out);             }        })    }    function onTokenWillExpire(zim, second) {        token = newToken(userId);        zim.renewToken(token);    }    var zim = createZIM(_onError, _onRcvMsg, onTokenWillExpire);    login(zim, myUID, token, function (succ, data) {        if (succ) {            console.log("登录成功!")        } else {            console.log("登录失败!", data)        }    })    return zim;}
4.2.3 登录、创建房间、加入房间、离开房间

调用zim对象的login函数完成登录,封装到login函数中;调用zim对象的joinRoom完成加入房间,封装到joinRoom函数中;调用zim的leaveRoom函数完成退出房间,封装到leaveRoom函数中。

/** * 登录即构ZIM*/function login(zim, userId, token, cb) {    var userInfo = { userID: userId, userName: userId };    zim.login(userInfo, token)        .then(function () {            cb(true, null);        })        .catch(function (err) {            cb(false, err);        });}/** * 加入房间*/function joinRoom(zim, roomId, cb = null) {    zim.joinRoom(roomId)        .then(function ({ roomInfo }) {            cb && cb(true, roomInfo);        })        .catch(function (err) {            cb && cb(false, err);        });}/** * 离开房间*/function leaveRoom(zim, roomId) {    zim.leaveRoom(roomId)        .then(function ({ roomID }) {            // 操作成功            console.log("已离开房间", roomID)        })        .catch(function (err) {            // 操作失败            console.log("离开房间失败", err)        });}
4.2.4 发送消息、解析消息

发送消息分为一对一发送和发送到房间,这里通过isGroup参数来控制,如下sendMsg函数所示。将接收消息UID和发送内容作为sendMsg参数,最终封装并调用ZIM的sendMessage函数完成消息发送。

接收到消息后,在我们的应用中设置了发送的消息内容是个json对象,因此需要对内容进行解析,具体的json格式可以参考完整源码,这里不做详细讲解。

/** * 发送消息*/function sendMsg(zim, isGroup, msg, toUID, cb) {     var type = isGroup ? 1 : 0; // 会话类型,取值为 单聊:0,房间:1,群组:2    var config = {        priority: 1, // 设置消息优先级,取值为 低:1(默认),中:2,高:3    };     var messageTextObj = { type: 20, message: msg, extendedData: "" };    var notification = {        onMessageAttached: function (message) {             console.log("已发送", message)        }    }     zim.sendMessage(messageTextObj, toUID, type, config, notification)        .then(function ({ message }) {            // 发送成功            cb(true, null);        })        .catch(function (err) {            // 发送失败            cb(false, err)        }); }/** * 解析收到的消息*/function parseMsg(zim, isFromGroup, msg, fromUid) {    //具体实现略}
4.2.5 导出接口

有了以上的实现后,把关键函数导出暴露给其他业务调用:

module.exports = {    initZego: initZego,    sendMsg: sendMsg,    joinRoom: joinRoom}

以上代码主要封装:

即构ZIM初始化发送消息加入房间

至此,我们就具备了将chatgpt消息群发到一个房间的能力、加入房间、接收到房间的弹幕消息能力。

更多关于即构ZIM接口与官方Demo可以点击参考这里,对即构ZIM了解更多可以点击这里

关于Avatar如何播报chatgpt内容,我们在下一篇文章实现。

5 相关代码nodejs接入chatgpt与即构zim

关键词:

X 广告
公司
X 广告

Copyright   2015-2022 北方快递网版权所有  备案号:京ICP备2021034106号-50   联系邮箱: 55 16 53 8@qq.com