写个AI虚拟主播:看懂弹幕,妙语连珠,悲欢形于色,以一种简单的实现

这不是你期望的介绍深度学习大模型的文章。而是使用人人都能理解、编程、运行的简单技术(包括机器学习和非机器学习技术),通过一系列工程进行整合、组装,从而实现一个 AI 虚拟主播的故事。

muvtuber

让 AI 成为虚拟主播:看懂弹幕,妙语连珠,悲欢形于色,以一种简单的实现

元宇宙终于不再铺天盖地,ChatGPT 又来遮天蔽日。看不下去了,那当然是选择——加入这虚浮的演出,诶嘿~(误)

想法

蹭热度的想法是这样:元宇宙 => 皮套人,ChatGPT => 中之人,放到一起:AI 主播!

咳,实际上没那么假大空啦。真实的故事是这样:

既然我可以让 AI 看懂你的心情,并推荐应景的音乐,以一种简单的实现,那么,我还想再进一步!让 AI 站到唱机背后,聆听你的故事,为你播出此时此刻萦绕心中、却难以言表的那段旋律。曲终意犹未尽当时 ,一段 AI 细语入耳更入心 —— 机器不是冰冷的玩具,她比你更懂你,至少她更懂那个真实的你:她不会说谎,而你每天都在骗自己。

这个想法指向的产品是一个 AI 电台,我把它叫做 muradio。在细化设计的过程中,我不得不考虑电台的传播问题。我当然可以在本地完成一切,让 AI 电台只为你一人播,这样实现起来还简单;但是,电台果然还是要大家一起分享吧。所以,我考虑用一种现代的方式——视频直播。

那么,在实现 muradio 之前,先让 AI 学会直播吧!

设计

我把这个 AI 主播叫做 muvtuber 1

人是如何做的

要设计一个直播的机器,其实很简单。首先考察人是如何完成直播的:

  • 首先需要一个人作为主播:这个人是会动会说话的,心情是会写在脸上的;(只是以普遍理性而论,非必要条件,更非歧视残障人士。)
  • 在开始前有一个预订的主题,也可能没有;
  • 主线:推进主题:打游戏、一起看、点歌 …;
  • 支线:主线被弹幕打断,转而与观众互动:聊天;
  • 最后,需要一个直播推流工具,比如 OBS。

互动是直播的灵魂。直播没有主题仍是直播,而没有了互动就成了电视。简单起见,我们先忽略主题问题,只考虑注入灵魂的互动实现。互动的过程如下:

  • 观察:看到弹幕
  • 过滤:选择性忽略一些没有意义或太有意义的话
  • 思考:对过滤后留下来的话进行思考 => 回应的话
  • 过滤:想到的部分话可能是不适合说出的,也要过滤掉
  • 发声:把回应说出来
  • 动作与表情:伴随观察、思考、表达这个流程的动作控制、表情管理。

用机器模拟人

清楚了直播的处理过程,就容易用机器模拟:

  • 主播:我们可以用 Live2D 模型来作为主播本人。
  • 看弹幕:使用弹幕姬类程序,暴露接口即可:func danmaku(room) chan text
  • 思考:机器文本生成:func chat(question) answer
  • 发声:机器语音合成:func tts(text) audio
  • 过滤:func filter(text) bool
  • 动作表情:Live2D 模型可以具有动作、表情,可以通过某种方式驱动。

Live2D 和弹幕姬是现成的解决方案,而从看弹幕、到思考、到发声的整个流程也是非常模块化、函数式的,直观清晰。难点是如何让 Live2D 模型动起来?

模型何时该摆什么样的表情,何时该做什么样的动作?真人主播解决这个问题靠得是本能,虚拟主播解决这个问题靠得是动捕——归根到底还是人。看来现实不能直接借鉴,那么只好做个抽象了。

我认为,表情是心情的反映,动作是表达的延伸。淦我写不清关于心情 => 表达 => 动作,所以 动作 = f(心情) 的观点😭


(怎么会有人问 ChatGPT “我要如何解释我的观点?”😭)

所以说,我们可以用“心情”来驱动 Live2D,赋予 AI 主播表情与动作。

那么,我们又如何让机器拥有“心情”呢?事实上,在前文提到的 让 AI 看懂你的心情,并推荐应景的音乐,以一种简单的实现 一文的“中文文本情感分析”小节中,我们用一种及其简单的方式,实现了一种 文本 -> 心情 的模型。这里我们就沿用当时的成果,不作额外介绍了,新读者可以先去阅读那篇文章的对应部分。

总之,我们可以用“思考”的输出,即主播要说的话,通过文本情感分析技术,反推出主播的心情,然后根据心情,就可以控制 Live2D 模型做出合适的表情、动作。

整体设计

小结一下上述设计,我们用机器来模拟人类主播,完成与观众的闲聊交互,整个过程如下图所示:

我们把 muvtuber 分成了一个个独立的模块。每个模块使用独立的技术栈,实现自己的工作,并通过某种方式暴露 API,供他人调用。最后,我们会通过一个“驱动程序”muvtuberdriver 来调用各个模块,通过一个顺序流程,实现上图所示的数据流动。

组件实现

本节介绍对上述 muvtuber 设计方案的实现。我会先写各个独立组件,再写如何把他们组装到一起完成工作。喜欢“自顶向下”的读者请自行安排阅读顺序。

这是一个拥有众多组件的项目,装起 numpy 从头撸似乎不够明智。我们会心怀感激地借助一些开源项目。我希望提供一种最简单的实现——面向麻(my)瓜(self)编程——只写必要的、能理解的代码。

先修课程:这是个 Web 前端 + 后端 + 机器学习的综合项目,需要使用到 Vue(TypeScript)、Go、Python 等相关语言和技术。

文短码长,下文只给出必要的设计、核心的代码,忽略错误处理、并发安全等等工程中实际的问题。真实的代码实现,请读者参考源码:

服务说明
xfgryujk/blivechat获取直播间弹幕消息
Live2dView前端:显示 Live2D 模型
Live2dDriver驱动前端 Live2D 模型动作表情
ChatGPTChatbot基于 ChatGPT 的优质聊天机器人
MusharingChatbot基于 ChatterBot 的简单聊天机
Emotext中文文本情感分析
muvtuberdriver组装各模块,驱动整个流程

弹幕姬

技术栈:Node,Python。

为了和其他模块对接,我们需要那种能通过比较底层的方式输出弹幕的工具。我们要给机器看的,而不是一个给人看的黑箱的图形界面。

好在也不必自己从头写。xfgryujk/blivedm 就是这样的一个工具。作者大大还顺别提供了显示给人看的界面 blivechat。

这里我们直接使用封装好的 blivechat。这个东西后端通过 WebSocket 把弹幕、礼物等消息发送给前端的界面。拉取、编译、运行 blivechat(blivechat 的 README 里有详细的安装、使用方法,此处从简。):

# clone repo
git clone --recursive https://github.com/xfgryujk/blivechat.git
cd blivechat# 编译前端
cd frontend
npm install
npm run build
cd ..# 运行服务
pip3 install -r requirements.txt
python3 main.py

虽然没有文档,但稍微抓包,就可以模仿出一个前端,通过 WebSocket 接口获取到弹幕:

// 建立链接
let ws = new WebSocket('ws://localhost:12450/api/chat');// 订阅直播间
ws.send(JSON.stringify({"cmd": 1,"data": { "roomId": 000 }
}));// 接收弹幕消息
ws.onmessage = (e) => {console.log(JSON.parse(e.data))
};// KeepAlive:每 10 秒发一次 heartbeat
setInterval(() => {ws.send(`{"cmd":0,"data":{}}`)
}, 1000 * 10);

除此之外,我们还可以使用它自带的图形界面(有没有一种可能,这才是这个项目的正确用法),这个弹幕框可以由 OBS 采集输出到直播间里:

优质聊天机器人:ChatGPT

技术栈:Python

这应该是最有意思的部分,却也是最难的。幸好,有 ChatGPT。

acheong08/ChatGPT 项目提供了一个很好的 ChatGPT 接口。

安装:

pip3 install revChatGPT

使用:

from revChatGPT.V1 import Chatbotchatbot = Chatbot(config={"access_token": "浏览器访问https://chat.openai.com/api/auth/session获取"
})prompt = "你好"
response = ""for data in chatbot.ask(prompt):response = data["message"]print(response)

我们只要编写一条合适的 prompt,即可让 ChatGPT 化身“虚拟主播”,例如:

请使用女性化的、口语化的、抒情的、感性的、可爱的、调皮的、幽默的、害羞的、态度傲娇的语言风格,扮演一个正在直播的 vtuber,之后我的输入均为用户评论,请用简短的一句话回答它们吧。

大概是这种效果:

我并不擅长写 prompt,上面这段也改编自网络(原版大概是让做猫娘的),如果你有更好的 prompt,欢迎与我分享。

注:prompt 笑话一则:

嘿,知道吗?以后不需要程序员啦!只要描述出需求,ChatGPT 就能写出代码!

——太棒了,那么如何写出简洁清晰无歧义的需求描述呢?

呃,未来或许会有那么一种语言…

——你说的可能是:计算机程序设计语言 😃

我们把这个东西简单封装出一个 HTTP API,方便其他组件调用:

$ curl -X POST localhost:9006/renew -d '{"access_token": "eyJhb***99A"}' -i
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 2ok
$ curl -X POST localhost:9006/ask -d '{"prompt": "你好"}'
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 45你好!有什么我可以帮助你的吗?

基本聊天机器人:ChatterBot

技术栈:Python

ChatGPT 很好用,输出质量非常高,但是:

  • 这不是一个开源的组件,或者廉价的公共服务。不能保证始终可用,也不能确定用它直播是否符合规则。
  • OpenAI 有访问限制:请求太频繁会拒绝服务。在实践中 30 秒/次会触发,60 秒/次 才比较安全。

总之,ChatGPT 硬用下去也不是个办法。我们可以实现一个丐版聊天机器人作为补充。在 ChatGPT 由于网络、限制等原因无法访问时,也提供一点点对话能力。

在很多年前,我曾用过一个开箱即用的傻瓜式聊天机器人库:ChatterBot。训练、推理都非常简单,调用者无需任何机器学习知识:

from chatterbot import ChatBot
from chatterbot.trainers import ListTrainerchatbot = ChatBot('Charlie')# 训练
trainer = ListTrainer(chatbot)
trainer.train(["Hi, can I help you?","Sure, I'd like to book a flight to Iceland.","Your flight has been booked."
])# 推理:对话
response = chatbot.get_response('I would like to book a flight.')
print(response)

可以用列表传入对话进行训练。训练完后,调用 get_response 方法即可进行对话。同样简单封装成 HTTP API:

curl 'http://localhost:9007/chatbot/get_response?chat=文本内容'

它的对话效果非常差。但有趣的是,这个库可以在线学习。在你和他对话的过程中,它会持续学习。在测试时,我给它接上 blivechat,随便进一个直播间,让它旁听,和弹幕虚空交流。经过一上午它已经学会了包括但不限于“妙啊”“有点东西”“对对对”等直播弹幕常(抽)用(象)表达,可以在恰当语境下输出作为回应。(这样让它黑听学习也有弊端,它会顺别学到许多“别的女人”,然后满嘴 Lulu、白菜、社长什么什么的😂)

不能指望这个库做出 ChatGPT 那样的优质、有逻辑的回答。只把它作为兜底,让它快速跟着弹幕瞎附合嚷嚷还是不错的,别冷场就好。

我在直播实测中的对比:

  • ChatGPTChatbot:由 ChatGPT 生成的回答
  • MusharingChatbot:本节介绍的 Chatterbot 库(我最初在一个叫做 musharing 的项目中使用了这个库,故此命名)


注意:ChatterBot 库早已年久失修。但可以找到一些可以用的 fork,例如 RaSan147/ChatterBot_update(from issues #2287):

pip install https://github.com/RaSan147/ChatterBot_update/archive/refs/heads/master.zip  # 直接从 GitHub 而非 PyPI 仓库安装
python -m spacy download en_core_web_sm  # 一个执行不到的依赖,但是不装跑不起来。

语音合成

技术栈:macOS,Go

用 Chatbot 生成了回应观众的文本,(暂时忽略过滤),接下来就要送到 TTS 语音合成模块,让机器把话说出来。

这个要效果好似乎必须上深度学习,PaddleSpeech 似乎是一个不错的选择,不过 i5-8700U + 核显大概跑不动就是了。

可以考虑用一点云服务:

  • Microsoft Speech Studio:要注意网络问题
  • 百度 AI 开方平台 - 短文本在线合成:要注意钱包问题

选择心仪的服务照着文档写就好了,非常容易。但这还是有点麻烦了,如果你使用 macOS,事情还可以更简单:

SAY(1) 命令使用 macOS 的 Speech Synthesis Manager 将输入文本转换为可听语音,并通过在“系统偏好设置”中选择的声音输出设备播放它,或将其存储到 AIFF 文件中。
你可以通过 man say 查看这个命令的详细用法。

打开扬声器或者耳机,调节音量;打开终端,键入命令:

say '你好,这是系统自带的 TTS 语音合成。'

不出意外,你会听到 Siri 或者婷婷声情并茂地大声朗读。

如果你对听到的声音不胜满意,(例如系统默认配置的似乎是低配婷婷,只能听个响),可以到「系统设置 > 辅助功能 > 朗读内容 > 系统声音」按照个人喜好修改(个人觉得简体中文系女声,讲的最好的就是「Siri 2」)。

容易在任何主流语言中调用这个工具,例如我们之后会在 Go 语言中调用它:

package mainimport "os/exec"type Sayer interface {Say(text string) error
}// sayer is a caller to the SAY(1) command on macOS.
type sayer struct {voice       string // voice to be usedrate        string // words per minuteaudioDevice string // audio device to be used to play the audio
}func (s *sayer) Say(text string) error {// say -v voice -r rate -a audioDevice textreturn exec.Command("say","-v", s.voice, "-r", s.rate, "-a", s.audioDevice,text,).Run()
}

文本情感分析

技术栈:Python

在 murecom 项目中,我们实现了一个名为 Emotext 的中文文本情感分析模块。它的作用就是输入文本,输出其中蕴含的情感(Emotext = func (text) -> emotions):

关于这个东西的原理即实现详见让 AI 看懂你的心情,并推荐应景的音乐,以一种简单的实现 一文(说人话的介绍)。下面仅给出核心算法(不说人话的介绍)(某蒟蒻的毕业论文,我先喷:学术辣鸡):

其中词典 E E E 使用大连理工情感本体库 2。大连理工情感本体库是较为成熟的一套中文情感词典方案,提供了对数万中文词汇的情感分类及强度分析,在中文文本情感分析领域被广泛使用。该库的情感划分方案 与 表 3-1 保持一致。该库的情感强度表示为 1 ∼ 9 的数值,数值越大,认为对应情感越强烈。

定义整段输入文本的总情感为所有关键词情感强度的 tfidf 加权总和:

e ( d ) = ∑ t ∈ k e y w o r d s t f i d f ( t , d , D ) ⋅ I t e(d) = \sum_{t \in keywords} tfidf(t,d,D)\cdot I_t e(d)=tkeywordstfidf(t,d,D)It

在有必要的情况下,还可以对 e ( d ) e(d) e(d) 进行 softmax 计算,得到文本在各情感类别上的概率分布。

总之,这东西就是一个很简单的算法,抛开效果不谈(该算法仅作为非深度学习的 baseline),人人都能写、人人都能理解。

此处复用当时的代码,只做简单的 API 重新封装,开放如下 HTTP 接口:

$ curl -X POST localhost:9008/ --data '高兴'
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 149{"emotions": ..., "polarity": ...}

实现的工作示意图和核心类图如下:

Live2D View

技术栈:TypeScriptVue.js,Quasar,Pinia,WebSocket

我从未设想过,所有组件中最麻烦的居然是 Live2D 这个看似除编外人员 OBS 之外最成熟的东西。

因为我们的需求有一点刁钻。正常的 Live2D 使用方式是:

  1. 把模型作为皮套,通过面/动捕进行驱动。但我们是机器作为中之人,机器没头没脸、没法自己动。所以一些封装的很好的、大家都在用的直播皮套 app 不能用。
  2. 网站上的看板娘,把 Live2D 模型加载到网页上,并全自动地吸引你的目光(而她的目光却被你的鼠标吸引😖),同时响应你对她的点(抚)击(摸)等动作。这个东西为了对使用者友好,一般封装地非常严实,基本不暴露什么接口,所有我们也不能直接拿来用。

我们要找那种暴露了底层一点的接口,能通过程序调用控制的方案。事实上,我发现常用的 Live2DViewerEX 就提供了非常好的 ExAPI 功能。但考虑到它的接口还是不够丰富,并且该 app 也不是开源实现,所以,我决定自己写。(绝对不是心疼那 $4.99,只是恰好缺那 $4.99罢了)

Live2D 官方 提供有各种 SDK。例如 Web、Unity 等等。出于能耗考虑,决定使用 Web 平台。但是打开文档,不太能看懂,搞了好久 Demo 都没跑起来。好在我找到了一个对官方 SDK 的封装:

  • guansss/pixi-live2d-display: A PixiJS plugin to display Live2D models of any kind.

这个库提供了简单清晰的接口,可以导入、显示模型,并控制表情、动作。

import * as PIXI from 'pixi.js';
import { Live2DModel } from 'pixi-live2d-display';
window.PIXI = PIXI;// 模型文件
const cubism2Model ="https://cdn.jsdelivr.net/gh/guansss/pixi-live2d-display/test/assets/shizuku/shizuku.model.json";// 初始化 view
const app = new PIXI.Application({view: document.getElementById('canvas'),
});// 读取模型
const model = await Live2DModel.from(cubism2Model);// 显示模型
app.stage.addChild(model);// 设置动作、表情
model.motion('tap_body');
model.expression('smile');

用 Vue 把上面的 Demo 封装成为 component,名曰 Live2DView。辅以一个 Live2DStore,用于控制 View 中 Live2D 的动作和表情。一个通用的、可复用的 Live2D 组件就完成了:

<template><Live2DView />
</template><script setup lang="ts">
import Live2DView from './Live2DView.vue';import { useLive2DStore } from 'stores/live2D-store';
const live2DStore = useLive2DStore();// 通过 store 控制 view 中的 Live2D 皮套
live2DStore.setMotion({ group: 'tap_body' })
live2DStore.setExpression('smile')
</script>

运行一下(背后的调试功能只是简单地暴露 store 的 actions,文中不作介绍):

再写一个 WsStore,用来接收来自 WebSocket 的命令,进而调用 Live2DStore,完成响应的动作、表情控制:

import { defineStore } from 'pinia';
import { computed, ref } from 'vue';
import { useLive2DStore } from './live2D-store';export const DEFAULT_WS_ADDR = 'ws://localhost:9001/live2d';const live2DStore = useLive2DStore();export const useWsStore = defineStore('ws', () => {const ws = ref<WebSocket>();function dialWebSocket(address: string = DEFAULT_WS_ADDR) {ws.value = new WebSocket(address);ws.value.onmessage = (event: MessageEvent) => {let data = JSON.parse(event.data);if (data.model) {  // 更换模型live2DStore.setModel(data.model);}if (data.motion) {  // 设置动作live2DStore.setMotion(data.motion);}if (data.expression) {  // 设置表情live2DStore.setExpression(data.expression);}};}return { dialWebSocket, };
});

这样,我们就可以通过 WebSocket,发送 JSON 命令,从其他程序来控制她了:

{"model": "https://path/to/model.json","motion": "tap_body","expression": "smile"
}

这个用来控制她的程序是 live2ddriver——

Live2D Driver

技术栈:Go,HTTP,WebSocket

live2ddriver 要做的是:接收来自 Chatbot 输出的文本;调用 Emotext 进行情感分析;并根据得到的“心情”,驱动 Live2D View,使做出对应的表情、动作。

  • 输入:文本,Vtuber 要说的话。
    • 接收自 HTTP 请求。
  • 输出:JOSN,符合输入文本情感的表情、动作。
    • 发送到 WebSocket。

这个程序非常简单,不过多介绍,只给出关键代码(已去掉烦人的日志打印、错误处理和并发安全)。

访问 Emotext:进行文本情感分析:

var EmotextServer = "http://localhost:9008/"
var client = &http.Client{}func Query(text string) (EmotionResult, error) {body := strings.NewReader(text)resp, _ := client.Post(EmotextServer, "text/plain", body)defer resp.Body.Close()var result EmotionResultjson.NewDecoder(resp.Body).Decode(&result)return result, nil
}

驱动 Live2D 模型:文本 -> 驱动指令:

type Live2DDriver interface {Drive(textIn string) Live2DRequest
}// DriveLive2DHTTP get text from request body.
// A Live2DRequest will be generated by the driver and sent to chOut
func DriveLive2DHTTP(driver Live2DDriver, addr string) (chOut chan []byte) {chOut = make(chan []byte, BufferSize)go func() {router := gin.Default()router.POST("/driver", func(c *gin.Context) {body := c.Request.Bodydefer body.Close()text, _ := ioutil.ReadAll(body)// 调用 driver 计算该做何中动作res := driver.Drive(string(text))j, _ := json.Marshal(res)chOut <- j})router.Run(addr)}()return chOut
}

我们需要针对每个模型写对应的 Driver 具体实现(情感的转移实际上使用了一个一阶马尔可夫过程,这里简化了):

type shizukuDriver struct {currentExpression shizukuExpressioncurrentMotion     shizukuMotionemotions emotext.Emotions // emotion => motionpolarity emotext.Polarity // polarity => expression
}func (d *shizukuDriver) Drive(textIn string) Live2DRequest {// 请求 EmotextemoResult, err := emotext.Query(text)d.emotions, d.polarity = emoResult.Emotions, emoResult.Polarity// 找得分最大的情感maxEmotion, maxPolarity := keyOfMaxValue(d.emotion), keyOfMaxValue(d.polarity)// 构造驱动命令var req Live2DRequestif d.currentExpression != maxPolarity {d.currentExpression = maxPolarityreq.Expression = d.currentExpression}if d.currentMotion != maxEmotion {d.currentMotion = maxEmotionreq.Motion = d.currentMotion}return req
}

WebSocket 消息转发器:把消息通过 WebSocket 发给前端,就是简单的开几个 goruntine 用 chan 来传数据:

// messageForwarder forwards messages to connected clients, that are, Live2DViews.
type messageForwarder struct {msgChans []chan []byte
}func (f *messageForwarder) ForwardMessageTo(ws *websocket.Conn) {ch := make(chan []byte, BufferSize)// addf.msgChans = append(f.msgChans, ch)// forwardfor msg := range ch {ws.Write(msg)}
}func (f *messageForwarder) ForwardMessageFrom(msgCh <-chan []byte) {for msg := range msgCh {// sendfor _, ch := range f.msgChans {ch <- msg}}
}

main:

// WebSocket 转发器:把东西发到 WebSocket 中
forwarder := wsforwarder.NewMessageForwarder()// 监听 :9004,获取来自 Chatbot 的 text
// 分析情感,生成驱动指令,发给 WebSocket 转发器。
go func() {driver := live2ddriver.NewShizukuDriver()dh := live2ddriver.DriveLive2DHTTP(driver, ":9004")forwarder.ForwardMessageFrom(dh)
}()// WebSocket 服务:转发 Live2D 驱动指令到前端
http.Handle("/live2d", websocket.Handler(func(c *websocket.Conn) {forwarder.ForwardMessageTo(c)
}))
http.ListenAndServe(":9001", nil)

组件小结

端口服务说明
12450Blivechat获取直播间弹幕消息
9000Live2dView前端:显示 Live2D 模型
9001Live2dDriver::ws发动作表情给前端的 WebSocket 服务
9004Live2dDriver::driver发文本给 live2ddriver 以驱动模型动作表情
9006ChatGPTChatbot优质聊天机器人
9007MusharingChatbot基于 ChatterBot 的简单聊天机
9008Emotext中文文本情感分析
-Say文本语音合成:系统自带命令

总装,开播!

技术栈:Go

好了,把前面实现的具体组件放到最开始的设计图中:

我们通过 blivechat,从 B 站的直播间获取弹幕消息;传递给 ChatGPT 或者备用的 ChatterBot 获取对话的回应;然后一方面把回应文本朗读出来,另一方面把回应文本传给 Live2DDriver,用 Emotext 进行情感分析,然后视话中情感,向 Live2DView 发出 WebSocket 命令;前端的 Live2DView 接收到命令后,更新页面显示的模型动作表情。最后把弹幕框、Live2D以及 Sayer 朗读出的音频通过 OBS 采集,放到一个画面里,向 B 站直播间推流。这样一个简单的 AI 直播流程就实现了!同时为了保证直播质量,我们可能还需要过滤掉一些意义不明的弹幕,以及过于大胆的发言,这可以通过额外的 Filter 组件实现(本文不作介绍)。

最后的任务:把上面这段话,或者或上面那张图翻译成实际的代码。我们借助 Go 语言完成这个工作。

第一步,就是对上一节介绍的各个服务分别编写出客户端,这个非常容易,就是一些参数编码、http 请求、解析响应、错误处理这样繁琐但简单的调 API 的标准工作,大(GitHub)家(Copilot)都可以轻松完成。

第二步,也是最后一步,一个 main 函数,串起其所有组件,也就是完成设计图中的那些连线:

func main() {    textInChan := make(chan *TextIn, BufSize)textOutChan := make(chan *TextOut, BufSize)// (blivedm) -> ingo TextInFromDm(*roomid, textInChan)// in -> filter (目前没有) -> intextInFiltered := textInChan// in -> chatbot -> outchatbot := NewPrioritizedChatbot(map[Priority]Chatbot{PriorityLow:  NewMusharingChatbot(),PriorityHigh: NewChatGPTChatbot(),})go TextOutFromChatbot(chatbot, textInFiltered, textOutChan)// out -> filter (目前没有) -> outtextOutFiltered := textOutChan// out -> (live2d) & (say) & (stdout)live2d := NewLive2DDriver()sayer := NewSayer()for textOut := range textOutFiltered {live2d.TextOutToLive2DDriver(textOut)sayer.Say(textOut.Content)fmt.Println(textOut)}
}

归功于 Go 语言的 goruntine 和 chan,一切都非常直观。

最后一步,启动程序,打开 OBS,把输出的音视频采集进去:

  • 弹幕框:localhost:12450/...
  • Live2DView:localhost:9000
  • 音频(say)的输出:你使用的音频设备

在 B 站开始直播,OBS 推流,完成!

(详细的 OBS 使用方法以及如何在 B 站开启直播,大家可以在 B 站搜索相关视频学习,此处不作介绍。)

想看看她直播的效果?这个项目不是什么复杂的深度学习大模型,不需要任何特殊的硬/软件,一般配置的 Mac + Git + Python + Poetry + Node + Pnpm + Go + OBS 即可搭起全套环境,轻松复现。(我也会尽快实现容器化,届时便可直接一键运行。)

总结

大家可能很失望,本文没有去训练某种奇幻的深度学习模型,从而实现一切。这只是因为我不会。

我始终认为,计算机程序就应该是这样的。“Keep it simple, stupid!”。麻瓜模块的快速组合,而不是端到端的数学大魔法。强类型数据流、活动和状态,而不是高维浮点张量乘。。。又扯远了。

是这样的,我希望分享的是一个“从想法到 app”的过程。我如何想到一个点子,又如何把它变成可以程序化的需求,或者说把它变成可行的计算:我要的 = f(现有的),明确输入(弹幕)、输出(直播)和限制(我的机器跑的动)。然后,我又如何设计这个 f,把它拆分成一个个相对独立的小模块,每个模块完成一件事情,用最简单的代码去实现他们。最后我又如何把这些模块拼在一起,让他们协同工作,实现一个有趣的大程序(AI 虚拟主播)。

总之,这个项目实现了一个灵活的框架,你可以任意替换其中的组件,从而不断提升这个 AI 主播的业务能力。请容我再次放出整体的设计图,并在下面添加一些我觉得有意思的 TODO:

满打满算我只用了一周时间设(摸)计(鱼)、一周时间编码来实现了这个项目。所以她是非常粗糙的。换句话说,她有非常大的改进空间。例如我现在已经开始将其中的部分手写的 HTTP 通信迁移到 gRPC,用更少的代码,带来更强的性能、更完善的错误处理。

我把这个项目当作自己的“学期(课外)学习计划”来不断优化:希望在功能上实现上图的各种 todo 的同时,也在实现上不断重构,从现在的幼稚模式,逐步用上 RPC、容器编排、函数计算、devOps 等等现代云原生的方式。

最终这个项目或许会变得非常棒。如果有所进展,我会在下一篇文章“muvtuber:进化!从散装微服务到云原生”中介绍,请大家监督。

我不是一个老练的程序员,我写不出好的设计模式、刷不懂 Leetcode,我只有不断冒出的想法,和越来越长的 todo list。我的备忘录里躺满了 app 等待实现,但说到的总是做不到,美妙的想法都等到了发霉。可也只能一行一行慢慢写,just for fun。我用十年写了满屏烂代码,就算越来越迷茫,也就只好这样继续吧……


最后再次给出全部源码链接,望读者垂阅斧正:

服务说明基于
xfgryujk/blivechat获取直播间弹幕消息-
Live2dView前端:显示 Live2D 模型guansss/pixi-live2d-display
Live2dDriver驱动前端 Live2D 模型动作表情-
ChatGPTChatbot基于 ChatGPT 的优质聊天机器人acheong08/ChatGPT
MusharingChatbot基于 ChatterBot 的简单聊天机RaSan147/ChatterBot_update
musharing-team/chatbot_api
Emotext中文文本情感分析cdfmlr/murecom-verse-1
muvtuberdriver组装各模块,驱动整个流程-

参考资料:

  • Cubism: Live2D Cubism SDK チュートリアル. https://docs.live2d.com/cubism-sdk-tutorials/
  • Gunther Cox: ChatterBot Docs. https://chatterbot.readthedocs.io/en/stable/
  • Pavo Studio: Live2DViewerEX 文档. http://live2d.pavostudio.com/doc/zh-cn/about/
  • CDFMLR: 让 AI 看懂你的心情,并推荐应景的音乐,以一种简单的实现. https://juejin.cn/post/7070819253309407268
  • guansss: pixi-live2d-display docs. https://guansss.github.io/pixi-live2d-display/
  • MDN: The WebSocket API (WebSockets). https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API
  • OBS: Open Broadcaster Software. https://obsproject.com
  • 大连理工大学 - 信息检索研究室: 情感词汇本体-词典. https://ir.dlut.edu.cn/info/1013/1142.htm
  • Bilibili 帮助中心: 成为主播. https://link.bilibili.com/p/help/index#/open-live

本项目、文章能够完成,还要特别感谢 GitHub Copilot 和 ChatGPT 的帮助。


  1. 何谓“mu”:一说是 machine/you 的缩写:这个系列用于探索机器与人、人工智能与艺术。另有一说 mu… 是 make your … 的意思:保持简单,开源共享,人人都能做。(事实上 mu 最初的来源是 music,所以这些探索的切入点是:音乐、分享、感动。) ↩︎

  2. 徐琳宏 , 林鸿飞 , 潘宇 , et al. 情感词汇本体的构造 [J]. 情报学报, 2008, 27(2) : 6. ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/12995.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:Python遍历-一种高效的数据处理技术

Python 遍历 - 一种高效的数据处理技术 对于程序员来说&#xff0c;数据处理是一个常见的任务。而在处理数据时&#xff0c;遍历是最常用的技术之一。Python 是一种强大的编程语言&#xff0c;它提供了很多用于数据遍历的工具和函数。在本文中&#xff0c;我们将介绍 Python 中…

ChatGpt输出结果时,文字中间有一根长长的删除线标记是什么意思?(ChatGpt删除线标记)

如图&#xff0c;这根删除线的含义是&#xff1a; 表示该文字不可信或是不准确的&#xff0c;建议仔细核实其内容。 这是系统为了提醒用户注意文字中可能存在的错误&#xff0c;避免对实际操作产生误导。

Stack Overflow 临时禁用 ChatGPT 生成内容,网友:人类和AI快打起来!

如果有一天我们查询到的「知识」真假难辨&#xff0c;那这就太可怕了。 要问最近 AI 圈哪个模型最火爆&#xff0c;你不得不把 OpenAI 推出的 ChatGPT 排在前面。自从发布以来&#xff0c;这个对话模型可谓是出尽风头&#xff0c;很多人更是对其产生了一百个新玩法&#xff0c;…

chatgpt开发内核定时器模块的完整过程

描述任务需求后&#xff0c;几乎在同一时刻chatgpt给出响应&#xff0c;开始进入代码刷屏模式&#xff0c;coding过程中还不忘给代码添加注释。 要求它顺便将Makefile文件写好提供给我&#xff0c;刷完代码后&#xff0c;进跟着开始刷Makefie文件&#xff1a; 编译生成的代码时…

MyBatisX插件没有出现蓝色鸟

如题&#xff0c;新版1.5.~的MyBatisX插件下载后&#xff0c;只有xml文件前出现红色鸟&#xff0c;对应的mapper接口中没有蓝色鸟。 解决办法&#xff1a;在idea中卸载掉该插件&#xff0c;加入插件页面中给出的qq群号&#xff0c;去群文件里下载1.4版本的压缩包&#xff0c;并…

新概念2-课文名称和知识点

目录 一、新概念2课文特点 二、新概念2知识点 1、新概念2知识点思维导图 2、简单句的核心 3、简单句的扩展 4、复合句 三、新概念2课文明细 一、新概念2课文特点 1&#xff09;总计96篇课文&#xff0c;4个单元&#xff0c;每个单元都是逐步加深知识点 2&#xff09;一…

蓝鸟BIP-1300无线终端解锁未签名程序运行权限

我司有一批BIP-1300&#xff0c;安装WM6系统。因为机器服役时间过长&#xff0c;系统很慢&#xff0c;于是将其中两台机器恢复了出厂设置。&#xff08;F6Reset&#xff09; 但是出现了一个问题&#xff0c;重置后的系统拒绝执行未经签名的应用程序&#xff0c;也拒绝Windows …

ChatGPT 成学生“作弊神器”?教授:“我感到极度恐惧”

上线没到一个月&#xff0c;ChatGPT 已经被网友玩出了“花”&#xff1a;写诗、编剧本、写代码、做计算题……在 ChatGPT 获得颇多赞誉的同时&#xff0c;近来一些学术界教授却发现了它另一种令人担忧的用途&#xff1a;学生正在利用 ChatGPT 代写论文。 为此&#xff0c;弗曼…

ChatGPT已死?AutoGPT太强?

今天聊聊 AutoGPT。 OpenAI 的 Andrej Karpathy 都大力宣传&#xff0c;认为 AutoGPT 是 prompt 工程的下一个前沿。 近日&#xff0c;AI 界貌似出现了一种新的趋势&#xff1a;自主人工智能。 这不是空穴来风&#xff0c;最近一个名为 AutoGPT 的研究开始走进大众视野。特斯拉…

Midjourney|文心一格 Prompt:完整参数列表、风格汇总、文生图词典合集

Midjourney|文心一格 Prompt&#xff1a;完整参数列表、风格汇总、文生图词典合集 1.Midjourney 完整参数列表 参数名称 调用方法使用案例注意事项V5V4V3niji版本在关键词后加空格&#xff0c;然后带上版本参数&#xff1a; --v 或者 —v –version 或者 —versionvibrant cali…

AI本地绘画软件(无需联网)+7Glatest模型+10G数据整合包

今年的AI技术实在太强了......AI对话、AI绘画...后面围绕AI演变出的无数产品正在跃跃欲试&#xff0c;颇有种让人跟不上时代的感觉;如果说ChatGPT是AI对话领域的老大哥&#xff0c;那么Midjourney就是全网最强的AI绘画平台&#xff0c;只要关键词描述得够好&#xff0c;它生成的…

元宇宙走进现实,被黑科技普照的生活日常真的来了

开年热播的《三体》&#xff0c;开启了对科技文明的全新想象。今年ChatGPT更是引发热议&#xff0c;人工智能、科技创新成为高频词汇&#xff0c;“元宇宙看两会”系列更是融合了比特数智人形象生成、AR互动技术、定位跟踪等技术&#xff0c;将科技互动感和沉浸感拉满。当下&am…

2023上海车展,智能汽车发烧友逛展攻略

来了&#xff0c;来了&#xff01;我们来了&#xff01; 2023上海国际汽车工业展览会&#xff0c;上海国家会展中心&#xff0c;一直持续到4月27日&#xff01; 各大汽车先锋企业汇聚于此。诸多全球首发/国内首发新车首次展现锋芒&#xff0c;众多领先汽车科技集聚于此。 什…

巴比特 | 元宇宙每日必读:美国网红利用“AI版的自己”和网友谈恋爱,收费每分钟1美元,GPT时代,数字人会变得更加普及吗?...

摘要&#xff1a;据科技新知报道&#xff0c;国外拥有180万粉丝的Snapchat网红Caryn Marjorie&#xff0c;开发AI版本的自己&#xff0c;结合GPT-4&#xff0c;利用不同版本的Caryn AI和粉丝谈恋爱&#xff0c;按分钟收费。除了视频博主&#xff0c;线上讲师、带货主播、导游、…

想成为顶级 AI 绘画师?先让这 7 个平台帮你成为 Prompt 高手

ChatGPT 的流行使得 AI 成为 2023 最大的科技风口&#xff0c;同时驱动了各种类型的 AI 发展。这些 AI 正在改变甚至颠覆很多行业的业务模式。ChatGPT 推动了 生成式 AI 革命的到来&#xff0c;这正在彻底颠覆内容的生产方式。自PGC、UGC 之后&#xff0c;内容生成终于迎来下一…

用Cursor 来提升你的开发效率

背景 随着chatGPT的兴起引起了整个社会对于智能化的关注度&#xff0c;那么它究竟能做什么&#xff1f;对我们日常工作有哪些影响&#xff1f; 前言 作为团队TLor程序开发or测试开发&#xff0c;chatGPT能帮我们干什么事情 &#xff08;1&#xff09;代码注释不够阅读困难-…

人工神经网络在肺结核诊断中应用的研究进展

人工神经网络在肺结核诊断中应用的研究进展 作者&#xff1a;胡青云 指导老师&#xff1a;杭诚方、李舰 摘要&#xff1a;结核病是一种全世界致死率极高的传染性疾病,转染性极强&#xff0c;可通过空气传播。该病在亚洲和非洲发病率极高&#xff0c;尤其是在大多数低收入国家。…

女子用ChatGPT交1000+男友,聊天按分钟收费,一周收入50万

量子位 | 公众号 QbitAI 注意看&#xff0c;这个女人叫卡琳&#xff0c;靠着GPT-4&#xff0c;她现在同时谈着1000男朋友。 对&#xff0c;我知道事情听上去有些离谱。就连GPT-4自己&#xff0c;都直呼“我一个AI都觉得非常不常见”。 但是先别急&#xff0c;因为更让人挠头的事…

一个10年符号主义学者的深度讨论:如何理性看待ChatGPT?

大厂技术 坚持周更 精选好文 趣谈前端, 让技术, 更有料 【导读】从ChatGPT来看&#xff0c;符号主义和连接主义该如何发展&#xff1f; 近十年&#xff0c;连接主义者在各种深度学习模型加持下&#xff0c;借着大数据、高算力的东风在人工智能赛道上领跑符号主义。 但每次有新…

​​​​​ChatGPT可以被用来帮助检测老年痴呆?

​​​​​可以被用来帮助检测阿尔兹海默病&#xff08;也就是老年痴呆 &#xff09;&#xff0c;这是我在ScienceDaily上看到的一篇文章。 目前的诊断方法是病史回顾和医学评估&#xff0c;包括了身体和神经方面。虽然目前无法治愈&#xff0c;但是早发现的话可以有更多的治…