Fay数字人助理版提交
+ 打出Fay数字人助理版作为主分支(带货版移到分支[`fay-sales-edition`]); + 添加Fay助理的文字沟通窗口(文字与语音同步); + 添加沟通记录本地保存功能; + 升级ChatGLM-6B的应用逻辑,长文本与语音回复分享;
This commit is contained in:
parent
83229a75cd
commit
d90d699b58
177
README.md
177
README.md
@ -2,33 +2,33 @@
|
||||
<br>
|
||||
<img src="images/icon.png" alt="Fay">
|
||||
<h1>FAY</h1>
|
||||
<h3>数 字 人 Fay 控 制 器(这是元宇宙吗?)</h3>
|
||||
<h3>Fay数字人助理</h3>
|
||||
</div>
|
||||
|
||||
|
||||
Fay是一个完整的开源项目,包含Fay控制器及数字人模型,可灵活组合出不同的应用场景:虚拟主播、现场推销货、商品导购、语音助理、远程语音助理、数字人互动、数字人面试官及心理测评、贾维斯、Her。开发人员可以利用该项目简单地构建各种类型的数字人或数字助理。该项目各模块之间耦合度非常低,包括声音来源、语音识别、情绪分析、NLP处理、情绪语音合成、语音输出和表情动作输出等模块。每个模块都可以轻松地更换。
|
||||
Fay数字人助理版是fay开源项目的重要分支,专注于构建智能数字助理的开源解决方案。它提供了灵活的模块化设计,使开发人员能够定制和组合各种功能模块,包括情绪分析、NLP处理、语音合成和语音输出等。Fay数字人助理版为开发人员提供了强大的工具和资源,用于构建智能、个性化和多功能的数字助理应用。通过该版本,开发人员可以轻松创建适用于各种场景和领域的数字人助理,为用户提供智能化的语音交互和个性化服务。
|
||||
|
||||
## **推荐集成的开源仓库**
|
||||
## **推荐集成**
|
||||
|
||||
消费级pc大模型:https://github.com/THUDM/ChatGLM-6B
|
||||
|
||||
全平台抖音抓包:https://github.com/wwengg/douyin
|
||||
消费级pc大模型(ChatGLM-6B的基础上前置Rasa会话管理):https://m.bilibili.com/video/BV1D14y1f7pr
|
||||
|
||||
UE5工程:https://github.com/xszyou/fay-ue5
|
||||
|
||||
实时照片驱动集成:https://github.com/waityousea/xuniren
|
||||
视频三维重建(真人2D驱动):https://github.com/waityousea/xuniren
|
||||
|
||||
|
||||
|
||||
## **一、Fay控制器用途**
|
||||
## **Fay数字人助理版**
|
||||
|
||||
注:带货版移到分支[`fay-sales-edition`](https://github.com/TheRamU/Fay/tree/fay-sales-edition)
|
||||
|
||||
![](images/controller.png)
|
||||
|
||||
|
||||
![](images/kzq.jpg)
|
||||
|
||||
### **PC远程助理** [`PC demo`](https://github.com/TheRamU/Fay/tree/main/python_connector_demo)
|
||||
|
||||
### **远程语音助理** [`PC demo`](https://github.com/TheRamU/Fay/tree/main/python_connector_demo)
|
||||
|
||||
### **远程语音助理** [`android demo`](https://github.com/TheRamU/Fay/tree/main/android_connector_demo)
|
||||
### **手机远程助理** [`android demo`](https://github.com/TheRamU/Fay/tree/main/android_connector_demo)
|
||||
|
||||
|
||||
|
||||
@ -48,8 +48,6 @@ UE5工程:https://github.com/xszyou/fay-ue5
|
||||
|
||||
工程及运行包:https://github.com/xszyou/fay-ue5
|
||||
|
||||
**发您的Fay运行效果视频至公众号领取最新的UE5模型哦**
|
||||
|
||||
通讯地址: [`ws://127.0.0.1:10002`](ws://127.0.0.1:10002)(已接通)
|
||||
|
||||
消息格式: 查看 [WebSocket.md](https://github.com/TheRamU/Fay/blob/main/WebSocket.md)
|
||||
@ -76,16 +74,7 @@ UE5工程:https://github.com/xszyou/fay-ue5
|
||||
|
||||
**注:**
|
||||
|
||||
1、去API及会话管理功能将在下一版本发布;
|
||||
|
||||
2、以上每个模块可轻易替换成自家核心产品。
|
||||
|
||||
3、本地nlp(rasa+chatglm)的替换方法(https://m.bilibili.com/video/BV1D14y1f7pr?wxfid=o7omF0Vs6RIQFUGAzB6LXOBHa6Yg):
|
||||
1、安装启动chatglm(github)
|
||||
2、安装rasa 包:rasa、rasa-sdk
|
||||
3、进入test/rasa目录启动actions:rasa run actions
|
||||
4、启动rasa api server:rasa run --enable-api -p 5006
|
||||
5、fay_core.py 引入nlp_rasa.py
|
||||
以上每个模块可轻易替换成自家核心产品。
|
||||
|
||||
|
||||
### **目录结构**
|
||||
@ -102,7 +91,8 @@ UE5工程:https://github.com/xszyou/fay-ue5
|
||||
│ ├── xf_aiui.py # 讯飞 人机交互-自然语言处理
|
||||
│ ├── chatgpt.py # gpt3.5对接
|
||||
│ ├── yuan_1_0.py # 浪潮.源大模型对接
|
||||
│ └── xf_ltp.py # 讯飞 性感分析
|
||||
│ ├── nlp_rasa.py # ChatGLM-6B的基础上前置Rasa会话管理(强烈推荐)
|
||||
│ └── xf_ltp.py # 讯飞 情感分析
|
||||
├── bin # 可执行文件目录
|
||||
├── core # 数字人核心
|
||||
│ ├── fay_core.py # 数字人核心模块
|
||||
@ -126,68 +116,20 @@ UE5工程:https://github.com/xszyou/fay-ue5
|
||||
|
||||
## **三、升级日志**
|
||||
|
||||
**2023.04:**
|
||||
|
||||
+ 抖音直播互动数据对接更换成系统代理抓包pd解码的方式(运行直播伴侣即可);
|
||||
+ 提供本地nlp的对接代码(rasa+chatglm);
|
||||
+ 修复若干逻辑及说明错误;
|
||||
+ 提高抖音字幕监听的稳定性及包兼容性;
|
||||
+ 更新gpt接口:局部接入代理、prompt上补充角色模拟及简化回复内容(感谢 江湖墨明);
|
||||
+ 修复控制台输入测试消息的bug;
|
||||
+ 补充推荐两个优秀仓库:chatglm、全平台的抖音抓包。
|
||||
|
||||
|
||||
|
||||
|
||||
**2023.03:**
|
||||
|
||||
+ 增加edge-tts语音合成(免费)可替换azure-tts(支持情绪化语音);
|
||||
+ 替换flask发行版运行方式;
|
||||
+ web socket接口增加数字人文字内容同步,以便数人字可以远程运行;
|
||||
+ 优化数字人数据web socket同步逻辑;
|
||||
+ 更改gpt 3.5对接方式。
|
||||
|
||||
**2023.02:**
|
||||
|
||||
+ 提供chatgpt及yuan1.0作为选择。
|
||||
|
||||
**2023.01:**
|
||||
|
||||
+ 控制器pc内网穿透,音频输入输出设备远程直连;
|
||||
+ 提供android 音频输入输出工程示例代码;
|
||||
+ 提供python音频输入输出工程示例代码(远程PC、树莓派等可用);
|
||||
+ 补传1.0语音指令音乐播放模块(暂不支持远程播放);
|
||||
+ 重构及补充若干工具模块:websocket、多线程、缓冲器、音频流录制器等;
|
||||
+ 修复1.x版本的多个bug;
|
||||
+ 集成看板娘;
|
||||
|
||||
**2022.12:**
|
||||
|
||||
+ 上传bin目录(用于直播互动);
|
||||
|
||||
**2022.11:**
|
||||
|
||||
+ 更新抖音直播获取粉丝互动数据的xpath;
|
||||
|
||||
**2022.10.27:**
|
||||
|
||||
+ 更新mac上的麦克风参数;
|
||||
+ 解决mac上无法重启问题;
|
||||
+ 上传brew安装脚本。
|
||||
|
||||
**2022.10.17:**
|
||||
+ 更新语音指令;
|
||||
+ 补充人设语法;
|
||||
|
||||
**2023.05.12:**
|
||||
|
||||
+ 打出Fay数字人助理版作为主分支(带货版移到分支[`fay-sales-edition`](https://github.com/TheRamU/Fay/tree/fay-sales-edition));
|
||||
+ 添加Fay助理的文字沟通窗口(文字与语音同步);
|
||||
+ 添加沟通记录本地保存功能;
|
||||
+ 升级ChatGLM-6B的应用逻辑,长文本与语音回复分享;
|
||||
|
||||
|
||||
## **四、安装说明**
|
||||
|
||||
|
||||
### **环境**
|
||||
- Python 3.8.0 +
|
||||
- Chrome 浏览器 (若不开启直播功能,可跳过)
|
||||
- Python 3.8、3.9、3.10
|
||||
- Windows、macos、linux
|
||||
|
||||
### **安装依赖**
|
||||
|
||||
@ -215,9 +157,10 @@ python main.py
|
||||
| ./ai_module/ms_tts_sdk.py | 微软 文本转情绪语音(可选) | https://azure.microsoft.com/zh-cn/services/cognitive-services/text-to-speech/ |
|
||||
| ./ai_module/xf_ltp.py | 讯飞 情感分析 | https://www.xfyun.cn/service/emotion-analysis |
|
||||
| ./utils/ngrok_util.py | ngrok.cc 外网穿透(可选) | http://ngrok.cc |
|
||||
| ./ai_module/yuan_1_0.py | 浪潮源大模型(NLP 3选1) | https://air.inspur.com/ |
|
||||
| ./ai_module/chatgpt.py | ChatGPT(NLP 3选1) | ******* |
|
||||
| ./ai_module/xf_aiui.py | 讯飞自然语言处理(NLP 3选1) | https://aiui.xfyun.cn/solution/webapi |
|
||||
| ./ai_module/yuan_1_0.py | 浪潮源大模型(NLP 4选1) | https://air.inspur.com/ |
|
||||
| ./ai_module/chatgpt.py | ChatGPT(NLP 4选1) | ******* |
|
||||
| ./ai_module/xf_aiui.py | 讯飞自然语言处理(NLP 4选1) | https://aiui.xfyun.cn/solution/webapi |
|
||||
| ./ai_module/nlp_rasa.py | ChatGLM-6B的基础上前置Rasa会话管理(NLP 4选1) | https://m.bilibili.com/video/BV1D14y1f7pr |
|
||||
|
||||
|
||||
|
||||
@ -226,47 +169,21 @@ python main.py
|
||||
|
||||
### **使用说明**
|
||||
|
||||
+ 抖音虚拟主播:启动bin/Release_2.85/2.85.exe + fay控制器(抖音输入源开启、展板播放关闭)+ 数字人 + 抖音伴侣(测试时直接通过浏览器打开别人的直播间);
|
||||
+ 现场推销货:fay控制器(展板播放关闭、填写商品信息)+ 数字人;
|
||||
+ 商品导购:fay控制器(麦克风输入源开启、展板播放关闭、填写商品信息、填写商品Q&A)+ 数字人;
|
||||
+ 语音助理:fay控制器(麦克风输入源开启、展板播放开启);
|
||||
+ 远程语音助理:fay控制器(展板播放关闭)+ 远程设备接入;
|
||||
+ 数字人互动:fay控制器(麦克风输入源开启、展板播放关闭、填写性格Q&A)+ 数字人;
|
||||
+ 数字人面试官及心理测评:联系免费领取;
|
||||
+ 语音助理:fay控制器(麦克风输入源开启、面板播放开启);
|
||||
+ 远程语音助理:fay控制器(面板播放关闭)+ 远程设备接入;
|
||||
+ 数字人互动:fay控制器(麦克风输入源开启、面板播放关闭、填写性格Q&A)+ 数字人;
|
||||
+ 贾维斯、Her:加入我们一起完成。
|
||||
|
||||
|
||||
### **语音指令**
|
||||
|
||||
- **关闭核心**
|
||||
关闭
|
||||
再见
|
||||
你走吧
|
||||
- **静音**
|
||||
静音
|
||||
闭嘴
|
||||
我想静静
|
||||
- **取消静音**
|
||||
取消静音
|
||||
你在哪呢?
|
||||
你可以说话了
|
||||
- **播放歌曲**(网易音乐库不可用,寻找替代中)
|
||||
播放歌曲
|
||||
播放音乐
|
||||
唱首歌
|
||||
放首歌
|
||||
听音乐
|
||||
你会唱歌吗?
|
||||
- **暂停播放**
|
||||
暂停播放
|
||||
别唱了
|
||||
我不想听了
|
||||
|
||||
|
||||
### **图形界面**
|
||||
|
||||
![](images/controller.png)
|
||||
| 关闭核心 | 静音 | 取消静音 |
|
||||
| ------------------------- | -------------------------- | ------------------------------------------------------------ |
|
||||
| 关闭、再见、你走吧 | 静音、闭嘴、我想静静 | 取消静音、你在哪呢、你可以说话了 |
|
||||
|
||||
| 播放歌曲(音乐库暂不可用) | 暂停播放 | 更多 |
|
||||
| ------------------------- | -------------------------- | ------------------------------------------------------------ |
|
||||
| 播放歌曲、播放音乐、唱首歌、放首歌、听音乐、你会唱歌吗 | 暂停播放、别唱了、我不想听了 | 没有了... |
|
||||
|
||||
### **人设**
|
||||
数字人属性,与用户交互中能做出相应的响应。
|
||||
@ -276,9 +193,9 @@ python main.py
|
||||
|
||||
### **接收来源**
|
||||
|
||||
#### 抖音
|
||||
#### 文本输入
|
||||
|
||||
填入直播间地址,实现与直播间粉丝交互
|
||||
通过沟通窗口与助理文本沟通
|
||||
|
||||
#### 麦克风
|
||||
|
||||
@ -289,21 +206,11 @@ python main.py
|
||||
可以接入远程音频输入,远程音频输出
|
||||
|
||||
|
||||
#### 商品栏
|
||||
|
||||
填入商品介绍,数字人将自动讲解商品。
|
||||
|
||||
当用户对商品有疑问时,数字人可自动跳转至对应商品并解答问题。
|
||||
|
||||
配合抖音接收来源,实现直播间自动带货。
|
||||
|
||||
|
||||
|
||||
### 相关文章:
|
||||
1、集成消费级pc大模型(ChatGLM-6B的基础上前置Rasa会话管理):https://m.bilibili.com/video/BV1D14y1f7pr
|
||||
|
||||
1、[(34条消息) 非常全面的数字人解决方案_郭泽斌之心的博客-CSDN博客_数字人算法](https://blog.csdn.net/aa84758481/article/details/124758727)
|
||||
|
||||
2、[(34条消息) Fay数字人开源项目在mac 上的安装办法_郭泽斌之心的博客-CSDN博客](https://blog.csdn.net/aa84758481/article/details/127551258)
|
||||
2、[(34条消息) 非常全面的数字人解决方案_郭泽斌之心的博客-CSDN博客_数字人算法](https://blog.csdn.net/aa84758481/article/details/124758727)
|
||||
|
||||
3、【开源项目:数字人FAY——Fay新架构使用讲解】 https://www.bilibili.com/video/BV1NM411B7Ab/?share_source=copy_web&vd_source=64cd9062f5046acba398177b62bea9ad
|
||||
|
||||
@ -311,12 +218,14 @@ python main.py
|
||||
|
||||
5、m1机器安装办法(Gason提供):https://www.zhihu.com/question/437075754
|
||||
|
||||
6、bilbil主页:[xszyou的个人空间_哔哩哔哩_bilibili](https://space.bilibili.com/2111554564)
|
||||
|
||||
|
||||
|
||||
二次开发指导联系QQ 467665317
|
||||
商务联系QQ 467665317,我们提供:开发顾问、数字人模型定制及高校教学资源实施服务
|
||||
http://yafrm.com/forum.php?mod=viewthread&tid=302
|
||||
|
||||
关注公众号获取最新微信技术交流群二维码(请先star本仓库)
|
||||
关注公众号获取最新微信技术交流群二维码(**请先star本仓库**)
|
||||
|
||||
![](images/gzh.jpg)
|
||||
|
||||
|
@ -135,7 +135,6 @@ class ALiNls:
|
||||
self.finalResults = ""
|
||||
self.done = False
|
||||
self.__frames.clear()
|
||||
websocket.enableTrace(False)
|
||||
self.__ws = websocket.WebSocketApp(self.__URL + '?token=' + _token, on_message=self.on_message)
|
||||
self.__ws.on_open = self.on_open
|
||||
self.__ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
|
||||
|
11
ai_module/nlp_rasa.py
Normal file
11
ai_module/nlp_rasa.py
Normal file
@ -0,0 +1,11 @@
|
||||
import json
|
||||
import requests
|
||||
|
||||
def question(cont):
|
||||
url="http://localhost:5005/webhooks/rest/webhook"
|
||||
req = json.dumps({"sender": "user", "message": cont})
|
||||
headers = {'content-type': 'application/json'}
|
||||
r = requests.post(url, headers=headers, data=req)
|
||||
lists = json.loads(r.text)
|
||||
|
||||
return lists
|
79
core/content_db.py
Normal file
79
core/content_db.py
Normal file
@ -0,0 +1,79 @@
|
||||
import sqlite3
|
||||
import time
|
||||
import threading
|
||||
import functools
|
||||
def synchronized(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
with self.lock:
|
||||
return func(self, *args, **kwargs)
|
||||
return wrapper
|
||||
class Content_Db:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.lock = threading.Lock()
|
||||
|
||||
|
||||
|
||||
#初始化
|
||||
def init_db(self):
|
||||
conn = sqlite3.connect('fay.db')
|
||||
c = conn.cursor()
|
||||
c.execute('''CREATE TABLE T_Msg
|
||||
(id INTEGER PRIMARY KEY autoincrement,
|
||||
type char(10),
|
||||
way char(10),
|
||||
content TEXT NOT NULL,
|
||||
createtime Int);''')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
|
||||
|
||||
#添加对话
|
||||
@synchronized
|
||||
def add_content(self,type,way,content):
|
||||
conn = sqlite3.connect("fay.db")
|
||||
cur = conn.cursor()
|
||||
cur.execute("insert into T_Msg (type,way,content,createtime) values (?,?,?,?)",(type,way,content,int(time.time())))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return cur.lastrowid
|
||||
|
||||
|
||||
|
||||
#获取对话内容
|
||||
@synchronized
|
||||
def get_list(self,way,order,limit):
|
||||
conn = sqlite3.connect("fay.db")
|
||||
cur = conn.cursor()
|
||||
if(way == 'all'):
|
||||
cur.execute("select type,way,content,createtime,datetime(createtime, 'unixepoch', 'localtime') as timetext from T_Msg order by createtime "+order+" limit ?",(limit,))
|
||||
elif(way == 'notappended'):
|
||||
cur.execute("select type,way,content,createtime,datetime(createtime, 'unixepoch', 'localtime') as timetext from T_Msg where way != 'appended' order by createtime "+order+" limit ?",(limit,))
|
||||
else:
|
||||
cur.execute("select type,way,content,createtime,datetime(createtime, 'unixepoch', 'localtime') as timetext from T_Msg where way = ? order by createtime "+order+" limit ?",(way,limit,))
|
||||
|
||||
list = cur.fetchall()
|
||||
conn.close()
|
||||
return list
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# a = Content_Db()
|
||||
# s = a.get_list('all','desc',10)
|
||||
# print(s)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -25,8 +25,9 @@ from ai_module import yuan_1_0
|
||||
from ai_module import chatgpt
|
||||
import pygame
|
||||
from utils import config_util as cfg
|
||||
|
||||
|
||||
from core.content_db import Content_Db
|
||||
from datetime import datetime
|
||||
from ai_module import nlp_rasa
|
||||
class FeiFei:
|
||||
def __init__(self):
|
||||
pygame.mixer.init()
|
||||
@ -240,6 +241,7 @@ class FeiFei:
|
||||
if self.muting:
|
||||
continue
|
||||
text = ''
|
||||
textlist = []
|
||||
if answer is None:
|
||||
try:
|
||||
wsa_server.get_web_instance().add_cmd({"panelMsg": "思考中..."})
|
||||
@ -252,6 +254,9 @@ class FeiFei:
|
||||
text = yuan_1_0.question(self.q_msg)
|
||||
elif cfg.key_chat_module == 'chatgpt':
|
||||
text = chatgpt.question(self.q_msg)
|
||||
elif cfg.key_chat_module == 'rasa':
|
||||
textlist = nlp_rasa.question(self.q_msg)
|
||||
text = textlist[0]['text']
|
||||
else:
|
||||
raise RuntimeError('讯飞key、yuan key、chatgpt key都没有配置!')
|
||||
util.log(1, '自然语言处理完成. 耗时: {} ms'.format(math.floor((time.time() - tm) * 1000)))
|
||||
@ -271,6 +276,16 @@ class FeiFei:
|
||||
self.a_msg = text
|
||||
else:
|
||||
self.a_msg = user_name + ',' + text
|
||||
contentdb = Content_Db()
|
||||
contentdb.add_content('member','speak',self.q_msg)
|
||||
contentdb.add_content('fay','speak',self.a_msg)
|
||||
wsa_server.get_web_instance().add_cmd({"panelReply": {"type":"fay","content":self.a_msg}})
|
||||
if len(textlist) > 1:
|
||||
i = 1
|
||||
while i < len(textlist):
|
||||
contentdb.add_content('fay','speak',textlist[i]['text'])
|
||||
wsa_server.get_web_instance().add_cmd({"panelReply": {"type":"fay","content":textlist[i]['text']}})
|
||||
i+= 1
|
||||
|
||||
elif index == 2:
|
||||
self.a_msg = ['我们的直播间越来越多人咯', '感谢{}的到来'.format(user_name), '欢印{}来到我们的直播间'.format(user_name)][
|
||||
@ -573,6 +588,60 @@ class FeiFei:
|
||||
self.last_interact_time = time.time()
|
||||
self.speaking = False
|
||||
|
||||
def send_for_answer(self,msg):
|
||||
contentdb = Content_Db()
|
||||
contentdb.add_content('member','send',msg)
|
||||
answer = self.__get_answer('send', msg)
|
||||
|
||||
text = ''
|
||||
textlist = []
|
||||
if answer is None:
|
||||
try:
|
||||
#wsa_server.get_web_instance().add_cmd({"panelMsg": "思考中..."})
|
||||
util.log(1, '自然语言处理...')
|
||||
tm = time.time()
|
||||
cfg.load_config()
|
||||
if cfg.key_chat_module == 'xfaiui':
|
||||
text = xf_aiui.question(msg)
|
||||
elif cfg.key_chat_module == 'yuan':
|
||||
text = yuan_1_0.question(msg)
|
||||
elif cfg.key_chat_module == 'chatgpt':
|
||||
text = chatgpt.question(msg)
|
||||
elif cfg.key_chat_module == 'rasa':
|
||||
textlist = nlp_rasa.question(msg)
|
||||
text = textlist[0]['text']
|
||||
|
||||
|
||||
else:
|
||||
raise RuntimeError('讯飞key、yuan key、chatgpt key都没有配置!')
|
||||
util.log(1, '自然语言处理完成. 耗时: {} ms'.format(math.floor((time.time() - tm) * 1000)))
|
||||
if text == '哎呀,你这么说我也不懂,详细点呗' or text == '':
|
||||
util.log(1, '[!] 自然语言无语了!')
|
||||
text = '哎呀,你这么说我也不懂,详细点呗'
|
||||
# wsa_server.get_web_instance().add_cmd({"panelMsg": ""})
|
||||
|
||||
except BaseException as e:
|
||||
print(e)
|
||||
util.log(1, '自然语言处理错误!')
|
||||
text = '哎呀,你这么说我也不懂,详细点呗'
|
||||
# wsa_server.get_web_instance().add_cmd({"panelMsg": ""})
|
||||
|
||||
elif answer != 'NO_ANSWER':
|
||||
text = answer
|
||||
now = datetime.now()
|
||||
timetext = str(now.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
contentdb.add_content('fay','send',text)
|
||||
wsa_server.get_web_instance().add_cmd({"panelReply": {"type":"fay","content":text}})
|
||||
if len(textlist) > 1:
|
||||
i = 1
|
||||
while i < len(textlist):
|
||||
contentdb.add_content('fay','send',textlist[i]['text'])
|
||||
wsa_server.get_web_instance().add_cmd({"panelReply": {"type":"fay","content":textlist[i]['text']}})
|
||||
i+= 1
|
||||
return text
|
||||
|
||||
|
||||
|
||||
# 冷场情绪更新
|
||||
def __update_mood_runnable(self):
|
||||
while self.__running:
|
||||
|
@ -12,6 +12,8 @@ from gevent import pywsgi
|
||||
from scheduler.thread_manager import MyThread
|
||||
from utils import config_util
|
||||
from core import wsa_server
|
||||
from core.fay_core import FeiFei
|
||||
from core.content_db import Content_Db
|
||||
|
||||
__app = Flask(__name__)
|
||||
CORS(__app, supports_credentials=True)
|
||||
@ -70,6 +72,26 @@ def api_stop_live():
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 0})
|
||||
return '{"result":"successful"}'
|
||||
|
||||
@__app.route('/api/send', methods=['post'])
|
||||
def api_send():
|
||||
data = request.values.get('data')
|
||||
print(data)
|
||||
info = json.loads(data)
|
||||
feiFei = FeiFei()
|
||||
text = feiFei.send_for_answer(info['msg'])
|
||||
return '{"result":"successful","msg":"'+text+'"}'
|
||||
|
||||
@__app.route('/api/get-msg', methods=['post'])
|
||||
def api_get_Msg():
|
||||
contentdb = Content_Db()
|
||||
list = contentdb.get_list('all','desc',1000)
|
||||
relist = []
|
||||
i = len(list)-1
|
||||
while i >= 0:
|
||||
relist.append(dict(type=list[i][0],way=list[i][1],content=list[i][2],createtime=list[i][3],timetext=list[i][4]))
|
||||
i -= 1
|
||||
|
||||
return json.dumps({'list': relist})
|
||||
|
||||
@__app.route('/', methods=['get'])
|
||||
def home_get():
|
||||
|
@ -176,6 +176,7 @@ ul {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.left_box .source ul .but .el-button {
|
||||
|
BIN
gui/static/from.jpg
Normal file
BIN
gui/static/from.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.3 KiB |
@ -1,5 +1,6 @@
|
||||
new Vue({
|
||||
el: '#app',
|
||||
delimiters: ["[[", "]]"],
|
||||
data() {
|
||||
return {
|
||||
testlist: [
|
||||
@ -43,6 +44,7 @@ new Vue({
|
||||
items_data: [],
|
||||
live_state: 0,
|
||||
device_list: [],
|
||||
send_msg:"",
|
||||
// device_list: [
|
||||
// {
|
||||
// value: '选项1',
|
||||
@ -70,10 +72,20 @@ new Vue({
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}],
|
||||
msg_list:[]
|
||||
|
||||
}
|
||||
},
|
||||
created() {
|
||||
window.addEventListener('keydown', this.handkeyCode, true)//开启监听键盘按下事件
|
||||
},
|
||||
methods: {
|
||||
// 回车和空格键提交右侧信息
|
||||
handkeyCode(e) {
|
||||
if(e.keyCode === 13){
|
||||
this.send()
|
||||
}
|
||||
},
|
||||
handleTabsEdit(targetName, action) {
|
||||
if (action === 'add') {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
@ -137,12 +149,12 @@ new Vue({
|
||||
},
|
||||
connectWS() {
|
||||
let _this = this;
|
||||
let socket = new WebSocket('ws://localhost:10003')
|
||||
socket = new WebSocket('ws://localhost:10003')
|
||||
socket.onopen = function () {
|
||||
// console.log('客户端连接上了服务器');
|
||||
}
|
||||
socket.onmessage = function (e) {
|
||||
// console.log(" --> " + e.data)
|
||||
console.log(" --> " + e.data)
|
||||
let data = JSON.parse(e.data)
|
||||
_this.live_broadcast = (data.time % 2) === 0
|
||||
let liveState = data.liveState
|
||||
@ -193,7 +205,11 @@ new Vue({
|
||||
tips.classList.remove("waifu-tips-active");
|
||||
}, 7000);
|
||||
}
|
||||
|
||||
//_this.getMsgList()
|
||||
}
|
||||
let panelReply = data.panelReply;
|
||||
if(panelReply != undefined){
|
||||
_this.addMsg(panelReply)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -231,11 +247,11 @@ new Vue({
|
||||
_this.attribute_hobby = attribute["hobby"]
|
||||
_this.attribute_contact = attribute["contact"]
|
||||
_this.attribute_voice = attribute["voice"]
|
||||
_this.interact_perception_gift = parseInt(perception["gift"])
|
||||
_this.interact_perception_gift = parseInt(perception["follow"])
|
||||
_this.interact_perception_follow = perception["follow"]
|
||||
_this.interact_perception_join = perception["join"]
|
||||
_this.interact_perception_chat = perception["chat"]
|
||||
_this.interact_perception_indifferent = perception["indifferent"]
|
||||
_this.interact_perception_join = perception["follow"]
|
||||
_this.interact_perception_chat = perception["follow"]
|
||||
_this.interact_perception_indifferent = perception["follow"]
|
||||
_this.interact_maxInteractTime = interact["maxInteractTime"]
|
||||
_this.interact_QnA = interact["QnA"]
|
||||
let item_data_list = []
|
||||
@ -302,11 +318,11 @@ new Vue({
|
||||
"QnA": this.interact_QnA,
|
||||
"maxInteractTime": this.interact_maxInteractTime,
|
||||
"perception": {
|
||||
"gift": this.interact_perception_gift,
|
||||
"gift": this.interact_perception_follow,
|
||||
"follow": this.interact_perception_follow,
|
||||
"join": this.interact_perception_join,
|
||||
"chat": this.interact_perception_chat,
|
||||
"indifferent": this.interact_perception_indifferent
|
||||
"join": this.interact_perception_follow,
|
||||
"chat": this.interact_perception_follow,
|
||||
"indifferent": this.interact_perception_follow
|
||||
}
|
||||
},
|
||||
"items": [],
|
||||
@ -436,11 +452,123 @@ new Vue({
|
||||
type: 'success'
|
||||
});
|
||||
},
|
||||
send() {
|
||||
let _this = this;
|
||||
let text = _this.send_msg;
|
||||
if (!text) {
|
||||
alert('请输入内容');
|
||||
return;
|
||||
}
|
||||
let info = {
|
||||
'content' : text ,
|
||||
'timetext' : _this.getCurrentTime() ,
|
||||
'type' : 'member' ,
|
||||
'way' : 'send'
|
||||
}
|
||||
_this.msg_list.push(info);
|
||||
this.timer = setTimeout(()=>{ //设置延迟执行
|
||||
//滚动条置底
|
||||
let height = document.querySelector('.content').scrollHeight;
|
||||
document.querySelector(".content").scrollTop = height;
|
||||
},1000)
|
||||
_this.send_msg = ''
|
||||
let url = "http://127.0.0.1:5000/api/send";
|
||||
let send_data = {
|
||||
"msg": text
|
||||
};
|
||||
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("post", url)
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||
xhr.send('data=' + JSON.stringify(send_data))
|
||||
let executed = false
|
||||
xhr.onreadystatechange = async function () {
|
||||
if (!executed && xhr.status === 200) {
|
||||
// _this.getMsgList()
|
||||
// document.querySelector('#textarea').value = '';
|
||||
// document.querySelector('#textarea').focus();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// // text = text.replace(/\s/g, "<br/>");
|
||||
// text = text.replace(/\n/g, "<br/>");
|
||||
// text = text.replace(/\r\n/g, "<br/>");
|
||||
// let item = document.createElement('div');
|
||||
// item.className = 'item item-right';
|
||||
// item.innerHTML = `<div class="bubble bubble-right">${text}</div><div class="avatar"><img src="static/from.jpg" /></div>`;
|
||||
// document.querySelector('.content').appendChild(item);
|
||||
// document.querySelector('#textarea').value = '';
|
||||
// document.querySelector('#textarea').focus();
|
||||
// //滚动条置底
|
||||
// let height = document.querySelector('.content').scrollHeight;
|
||||
// document.querySelector(".content").scrollTop = height;
|
||||
},
|
||||
getMsgList(){
|
||||
let _this = this;
|
||||
|
||||
let url = "http://127.0.0.1:5000/api/get-msg";
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("post", url)
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||
xhr.send()
|
||||
let executed = false
|
||||
xhr.onreadystatechange = async function () {
|
||||
if (!executed && xhr.status === 200) {
|
||||
try {
|
||||
if (xhr.responseText.length > 0) {
|
||||
let data = await eval('(' + xhr.responseText + ')')
|
||||
_this.msg_list = data['list'];
|
||||
|
||||
//滚动条置底
|
||||
let height = document.querySelector('.content').scrollHeight;
|
||||
document.querySelector(".content").scrollTop = height;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
addMsg(data){
|
||||
let _this = this;
|
||||
let info = {
|
||||
'content' : data['content'] ,
|
||||
'timetext' : _this.getCurrentTime() ,
|
||||
'type' : data['type'] ,
|
||||
'way' : 'send'
|
||||
}
|
||||
_this.msg_list.push(info);
|
||||
|
||||
this.timer = setTimeout(()=>{ //设置延迟执行
|
||||
//滚动条置底
|
||||
let height = document.querySelector('.content').scrollHeight;
|
||||
document.querySelector(".content").scrollTop = height;
|
||||
},1000)
|
||||
|
||||
|
||||
},
|
||||
getCurrentTime() {
|
||||
//获取当前时间并打印
|
||||
var _this = this;
|
||||
let yy = new Date().getFullYear();
|
||||
let mm = new Date().getMonth()+1<10 ? '0'+parseInt(new Date().getMonth()+1) : new Date().getMonth()+1;
|
||||
let dd = new Date().getDate()<10 ? '0'+new Date().getDate() : new Date().getDate();
|
||||
let hh = new Date().getHours()<10 ? '0'+new Date().getHours() : new Date().getHours();
|
||||
let mf = new Date().getMinutes()<10 ? '0'+new Date().getMinutes() : new Date().getMinutes();
|
||||
let ss = new Date().getSeconds()<10 ? '0'+new Date().getSeconds() : new Date().getSeconds();
|
||||
let gettime = yy+'-'+mm+'-'+dd+' '+hh+':'+mf+':'+ss;
|
||||
return gettime;
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
let _this = this;
|
||||
_this.getData();
|
||||
_this.connectWS()
|
||||
_this.getMsgList();
|
||||
_this.connectWS();
|
||||
|
||||
// _this.runnnable()
|
||||
// _this.items_data.push({});
|
||||
},
|
||||
|
BIN
gui/static/to.jpg
Normal file
BIN
gui/static/to.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
@ -7,228 +7,362 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- index.css -->
|
||||
<link rel="stylesheet" href="{{ url_for('static',filename='css/index.css') }}"></link>
|
||||
<link rel="stylesheet" href="{{ url_for('static',filename='css/index.css') }}">
|
||||
</link>
|
||||
<!-- <link rel="stylesheet" href="./css/index.css"> -->
|
||||
<!-- 引入element-ui样式 -->
|
||||
<!-- <link rel="stylesheet" href="./css/element.css"> -->
|
||||
<link rel="stylesheet" href="{{ url_for('static',filename='css/element.css') }}"></link>
|
||||
<link rel="stylesheet" href="{{ url_for('static',filename='css/element.css') }}">
|
||||
</link>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
|
||||
<script src="/static/live2d/autoload.js"></script>
|
||||
|
||||
<title>自动商品介绍控制器</title>
|
||||
<title>Fay</title>
|
||||
<style>
|
||||
.container {
|
||||
max-height: calc(100vh - 55px);
|
||||
min-height: 953px;
|
||||
width: 913px;
|
||||
border-radius: 4px;
|
||||
border: 0.5px solid #e0e0e0;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: calc(100% - 20px);
|
||||
padding: 20px;
|
||||
overflow-y: scroll;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.content:hover::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.bubble {
|
||||
/* max-width: 400px; */
|
||||
max-width: 60%;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
color: #000;
|
||||
word-wrap: break-word;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.item-left .bubble {
|
||||
margin-left: 15px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.item-left .bubble:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-top: 10px solid transparent;
|
||||
border-right: 10px solid #fff;
|
||||
border-bottom: 10px solid transparent;
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
.item-right .bubble {
|
||||
margin-right: 15px;
|
||||
background-color: #63f5a1;
|
||||
}
|
||||
|
||||
.item-right .bubble:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid #63f5a1;
|
||||
border-top: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-bottom: 10px solid transparent;
|
||||
right: -20px;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.item.item-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.item.item-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.item.item-center span {
|
||||
font-size: 12px;
|
||||
padding: 2px 4px;
|
||||
color: #fff;
|
||||
background-color: #dadada;
|
||||
border-radius: 3px;
|
||||
-moz-user-select: none;
|
||||
/*火狐*/
|
||||
-webkit-user-select: none;
|
||||
/*webkit浏览器*/
|
||||
-ms-user-select: none;
|
||||
/*IE10*/
|
||||
-khtml-user-select: none;
|
||||
/*早期浏览器*/
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.avatar img {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.input-area {
|
||||
border-top: 0.5px solid #e0e0e0;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
textarea {
|
||||
flex: 1;
|
||||
padding: 5px;
|
||||
font-size: 14px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
outline: none;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.button-area {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
margin-right: 10px;
|
||||
line-height: 40px;
|
||||
padding: 5px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.button-area button {
|
||||
width: 80px;
|
||||
border: none;
|
||||
outline: none;
|
||||
border-radius: 4px;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 设置滚动条的样式 */
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
/* 滚动槽 */
|
||||
::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.3);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* 滚动条滑块 */
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.5);
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="main">
|
||||
<div class="title">
|
||||
<h2>Fay控制器2.0</h2>
|
||||
</div>
|
||||
<div class="main_box">
|
||||
<div class="left">
|
||||
<div class="left_top">
|
||||
<p class="left_top_p">人设:</p>
|
||||
<div class="character">
|
||||
<div class="character_top">
|
||||
<div class="character_left">
|
||||
<ul>
|
||||
<li>
|
||||
<p>姓名:</p>
|
||||
<el-input v-model="attribute_name" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>性别:</p>
|
||||
<el-input v-model="attribute_gender" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>年龄:</p>
|
||||
<el-input v-model="attribute_age" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>出生地:</p>
|
||||
<el-input v-model="attribute_birth" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>生肖:</p>
|
||||
<el-input v-model="attribute_zodiac" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>星座:</p>
|
||||
<el-input v-model="attribute_constellation" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>职业:</p>
|
||||
<el-input v-model="attribute_job" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>喜好:</p>
|
||||
<el-input v-model="attribute_hobby" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>联系方式:</p>
|
||||
<el-input v-model="attribute_contact" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="character_right">
|
||||
<ul>
|
||||
<li>
|
||||
<p>送礼敏感度:</p>
|
||||
|
||||
<el-slider v-model="interact_perception_gift"></el-slider>
|
||||
|
||||
</li>
|
||||
<li>
|
||||
<p>关注敏感度:</p>
|
||||
<el-slider v-model="interact_perception_follow"></el-slider>
|
||||
</li>
|
||||
<li>
|
||||
<p>进入敏感度:</p>
|
||||
<el-slider v-model="interact_perception_join"></el-slider>
|
||||
</li>
|
||||
<li>
|
||||
<p>留言敏感度:</p>
|
||||
<el-slider v-model="interact_perception_chat"></el-slider>
|
||||
</li>
|
||||
<li>
|
||||
<p>冷场敏感度:</p>
|
||||
<el-slider v-model="interact_perception_indifferent"></el-slider>
|
||||
</li>
|
||||
<li>
|
||||
<p>单次互动时长:</p>
|
||||
<el-input-number v-model="interact_maxInteractTime" @change="handleChange" :min="1" :max="60" label="描述文字"></el-input-number>
|
||||
<span style=" padding-left: 10px;
|
||||
line-height: 40px;">秒</span>
|
||||
</li>
|
||||
<li>
|
||||
<p>声音选择:{{attribute_voice}}</p>
|
||||
<el-select v-model="attribute_voice" placeholder="请选择">
|
||||
<el-option v-for="item in voice_list" :key="item.value" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</li>
|
||||
<br>
|
||||
<li>
|
||||
<p>使用面板播放:</p>
|
||||
<el-switch v-model="play_sound_enabled" active-color="#13ce66" inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="character_box">
|
||||
<p>Q&A文件:</p>
|
||||
<el-input v-model="interact_QnA" placeholder="请输入内容"></el-input>
|
||||
<!-- <el-upload class="upload-demo" action="http://127.0.0.1:5000/"-->
|
||||
<!-- :on-success="handlePreview">-->
|
||||
<!-- <el-input v-model="interact_QnA" placeholder="请输入内容"></el-input>-->
|
||||
<!-- </el-upload>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="left_box">
|
||||
<p> </p>
|
||||
<div class="source">
|
||||
<ul>
|
||||
<li class="url">
|
||||
<el-switch v-model="source_liveRoom_enabled" active-color="#13ce66" inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
<p>抖 音</p>
|
||||
<el-input v-model="source_liveRoom_url" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li class="url">
|
||||
<el-switch v-model="source_record_enabled" active-color="#13ce66" inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
<p>麦克风</p>
|
||||
<el-select v-model="source_record_device" placeholder="请选择">
|
||||
<el-option v-for="item in device_list" :key="item.value" :label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</li>
|
||||
<li class="url">
|
||||
|
||||
<p style="margin-left: 40px">消 息</p>
|
||||
<el-input v-model="panel_msg" :disabled="true"></el-input>
|
||||
</li>
|
||||
<li class="but">
|
||||
<el-button v-if="live_state == 1" type="success" class="btn_close" style="width:200px" @click=postStopLive()>关闭(运行中)</el-button>
|
||||
<el-button v-else-if="live_state == 2" type="primary" plain disabled style="width:200px">正在开启...</el-button>
|
||||
<el-button v-else-if="live_state == 3" type="success" plain disabled style="width:200px">正在关闭...</el-button>
|
||||
<el-button v-else type="primary" style="width:200px" class="btn_open" @click=postStartLive()>开启</el-button>
|
||||
<el-button type="button" style="width:200px" @click=postData()>保存配置</el-button>
|
||||
</li>
|
||||
<li class="p_red">
|
||||
<p>注:启动后请选中场景客户端,让其前端运行,否则可能会卡顿,或者无声音。</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="app">
|
||||
<div class="main">
|
||||
<div class="title">
|
||||
<h2>Fay数字人助理版</h2>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="right_main">
|
||||
<el-tabs v-model="activeName" type="card" @tab-click="handleClick" editable @edit="handleTabsEdit">
|
||||
<el-tab-pane :label="'商品'+(index+1)" :name="itme.tab_name" v-for="(itme, index) in items_data" :key="index">
|
||||
<div class="main_box">
|
||||
<div class="left">
|
||||
<div class="left_top">
|
||||
<p class="left_top_p">人设:</p>
|
||||
<div class="character">
|
||||
<div class="character_top">
|
||||
<div class="character_left">
|
||||
<ul>
|
||||
<li>
|
||||
<p>姓名:</p>
|
||||
<el-input v-model="attribute_name" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>性别:</p>
|
||||
<el-input v-model="attribute_gender" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>年龄:</p>
|
||||
<el-input v-model="attribute_age" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>出生地:</p>
|
||||
<el-input v-model="attribute_birth" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>生肖:</p>
|
||||
<el-input v-model="attribute_zodiac" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>星座:</p>
|
||||
<el-input v-model="attribute_constellation" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>职业:</p>
|
||||
<el-input v-model="attribute_job" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>喜好:</p>
|
||||
<el-input v-model="attribute_hobby" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>联系方式:</p>
|
||||
<el-input v-model="attribute_contact" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="character_right">
|
||||
<ul>
|
||||
|
||||
<li>
|
||||
<p>敏感度:</p>
|
||||
<el-slider v-model="interact_perception_follow"></el-slider>
|
||||
</li>
|
||||
|
||||
<br>
|
||||
<li>
|
||||
<p>使用面板播放:</p>
|
||||
<el-switch v-model="play_sound_enabled" active-color="#13ce66"
|
||||
inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="character_box">
|
||||
<p>Q&A文件:</p>
|
||||
<el-input v-model="interact_QnA" placeholder="请输入内容"></el-input>
|
||||
<!-- <el-upload class="upload-demo" action="http://127.0.0.1:5000/"-->
|
||||
<!-- :on-success="handlePreview">-->
|
||||
<!-- <el-input v-model="interact_QnA" placeholder="请输入内容"></el-input>-->
|
||||
<!-- </el-upload>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="left_box">
|
||||
<p> </p>
|
||||
<div class="source">
|
||||
<ul>
|
||||
<li>
|
||||
<p>名称:</p>
|
||||
<el-input v-model='itme.name' placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>商品简介:</p>
|
||||
<el-input type="textarea" :rows="3" placeholder="请输入内容" v-model='itme.explain.intro'>
|
||||
</el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>使用场景:</p>
|
||||
<el-input type="textarea" :rows="3" placeholder="请输入内容" v-model='itme.explain.usage'>
|
||||
</el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>售价说明:</p>
|
||||
<el-input type="textarea" :rows="3" placeholder="请输入内容" v-model='itme.explain.price'>
|
||||
</el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>促销:</p>
|
||||
<el-input type="textarea" :rows="3" placeholder="请输入内容" v-model='itme.explain.discount'>
|
||||
</el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>主播担保:</p>
|
||||
<el-input type="textarea" :rows="3" placeholder="请输入内容" v-model='itme.explain.promise'>
|
||||
</el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>商品特点:</p>
|
||||
<el-input type="textarea" :rows="3" placeholder="请输入内容" v-model='itme.explain.character'>
|
||||
</el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>展示视频:</p>
|
||||
<el-input v-model="itme.demoVideo" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>Q&A文件:</p>
|
||||
<el-input v-model="itme.QnA" placeholder="请输入内容"></el-input>
|
||||
</li>
|
||||
<li>
|
||||
<p>是否启用:</p>
|
||||
<el-switch v-model='itme.enabled' active-color="#13ce66" inactive-color="#ff4949">
|
||||
|
||||
<li class="url">
|
||||
<el-switch v-model="source_record_enabled" active-color="#13ce66"
|
||||
inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
<p>麦克风</p>
|
||||
<el-select v-model="source_record_device" placeholder="请选择">
|
||||
<el-option v-for="item in device_list" :key="item.value" :label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</li>
|
||||
<li class="url">
|
||||
|
||||
<p style="margin-left: 40px">消 息</p>
|
||||
<el-input v-model="panel_msg" :disabled="true"></el-input>
|
||||
</li>
|
||||
<li class="but">
|
||||
<el-button v-if="live_state == 1" type="success" class="btn_close"
|
||||
style="width:200px" @click=postStopLive()>关闭(运行中)</el-button>
|
||||
<el-button v-else-if="live_state == 2" type="primary" plain disabled
|
||||
style="width:200px">正在开启...</el-button>
|
||||
<el-button v-else-if="live_state == 3" type="success" plain disabled
|
||||
style="width:200px">正在关闭...</el-button>
|
||||
<el-button v-else type="primary" style="width:200px" class="btn_open"
|
||||
@click=postStartLive()>开启</el-button>
|
||||
<el-button type="button" style="width:200px" @click=postData()>保存配置</el-button>
|
||||
</li>
|
||||
<li class="p_red">
|
||||
<p>注:启动后请选中场景客户端,让其前端运行,否则可能会卡顿,或者无声音。</p>
|
||||
</li>
|
||||
</ul>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<br v-if="items_data.length==0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="right_main">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
|
||||
<div v-for="item in msg_list">
|
||||
<div class="item item-center"><span>[[item.timetext]]</span></div>
|
||||
|
||||
<div class="item item-left" v-if="item.type == 'fay'">
|
||||
<div class="avatar"><img src="{{ url_for('static',filename='to.jpg') }}" />
|
||||
</div>
|
||||
<div class="bubble bubble-left">[[item.content]]
|
||||
</div>
|
||||
</div>
|
||||
<div class="item item-right" v-else>
|
||||
<div class="bubble bubble-right">[[item.content]]</div>
|
||||
<div class="avatar"><img src="{{ url_for('static',filename='from.jpg') }}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="input-area">
|
||||
<textarea v-model="send_msg" name="text" id="textarea" placeholder="发送些内容给Fay..."></textarea>
|
||||
<div class="button-area">
|
||||
<button id="send-btn" @click="send()">发 送</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// function send() {
|
||||
// let text = document.querySelector('#textarea').value;
|
||||
// if (!text) {
|
||||
// alert('请输入内容');
|
||||
// return;
|
||||
// }
|
||||
// // text = text.replace(/\s/g, "<br/>");
|
||||
// text = text.replace(/\n/g, "<br/>");
|
||||
// text = text.replace(/\r\n/g, "<br/>");
|
||||
// let item = document.createElement('div');
|
||||
// item.className = 'item item-right';
|
||||
// item.innerHTML = `<div class="bubble bubble-right">${text}</div><div class="avatar"><img src="{{ url_for('static',filename='from.jpg') }}" /></div>`;
|
||||
// document.querySelector('.content').appendChild(item);
|
||||
// document.querySelector('#textarea').value = '';
|
||||
// document.querySelector('#textarea').focus();
|
||||
// //滚动条置底
|
||||
// let height = document.querySelector('.content').scrollHeight;
|
||||
// document.querySelector(".content").scrollTop = height;
|
||||
// }
|
||||
</script>
|
||||
</body>
|
||||
<!-- 开发环境vue.js -->
|
||||
<script src="{{ url_for('static',filename='js/vue.js') }}"></script>
|
||||
|
@ -78,4 +78,4 @@ class TDevWindow(QDialog):
|
||||
self.mainLayout.setSpacing(5)
|
||||
self.mainLayout.addWidget(self.pWebGroup)
|
||||
self.setLayout(self.mainLayout)
|
||||
self.setMinimumSize(800, 800)
|
||||
self.setMinimumSize(800, 800)
|
Binary file not shown.
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 106 KiB |
BIN
images/luoji.png
BIN
images/luoji.png
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 112 KiB |
7
main.py
7
main.py
@ -11,7 +11,7 @@ from gui import flask_server
|
||||
from gui.window import MainWindow
|
||||
from utils import config_util
|
||||
from scheduler.thread_manager import MyThread
|
||||
|
||||
from core.content_db import Content_Db
|
||||
|
||||
def __clear_samples():
|
||||
if not os.path.exists("./samples"):
|
||||
@ -35,7 +35,10 @@ if __name__ == '__main__':
|
||||
__clear_samples()
|
||||
__clear_songs()
|
||||
config_util.load_config()
|
||||
|
||||
dbstatus = os.path.exists("fay.db")
|
||||
if(dbstatus == False):
|
||||
contentdb = Content_Db()
|
||||
contentdb.init_db()
|
||||
ws_server = wsa_server.new_instance(port=10002)
|
||||
ws_server.start_server()
|
||||
web_ws_server = wsa_server.new_web_instance(port=10003)
|
||||
|
@ -25,17 +25,17 @@ def receive_audio(client):
|
||||
while True:
|
||||
data = client.recv(9)
|
||||
filedata = b''
|
||||
if b"\x00\x01\x02\x03\x04\x05\x06\x07\x08" == data: #wav文件开始传输标志
|
||||
if b"\x00\x01\x02\x03\x04\x05\x06\x07\x08" == data: #mp3文件开始传输标志
|
||||
while True:
|
||||
data = client.recv(1024)
|
||||
filedata += data
|
||||
filedata = filedata.replace(b'\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8', b"") #去除心跳信息
|
||||
if b"\x08\x07\x06\x05\x04\x03\x02\x01\x00" == filedata[-9:]:#wav文件结束传输标志
|
||||
if b"\x08\x07\x06\x05\x04\x03\x02\x01\x00" == filedata[-9:]:#mp3文件结束传输标志
|
||||
filedata = filedata[:-9]
|
||||
break
|
||||
print("receive audio end:{}".format(len(filedata)), end="")
|
||||
|
||||
filename = "sample/recv_{}.wav".format(time.time())
|
||||
filename = "sample/recv_{}.mp3".format(time.time())
|
||||
with open(filename, "wb") as f:
|
||||
f.write(filedata)
|
||||
f.close()
|
||||
|
10
system.conf
10
system.conf
@ -13,18 +13,18 @@ ms_tts_region=
|
||||
xf_ltp_app_id=
|
||||
xf_ltp_api_key=
|
||||
|
||||
#NLP三选一:xfaiui、yuan、chatgpt
|
||||
chat_module=xfaiui
|
||||
#NLP四选一:xfaiui、yuan、chatgpt、rasa(需启动chatglm及rasa,https://m.bilibili.com/video/BV1D14y1f7pr)
|
||||
chat_module=
|
||||
|
||||
# 讯飞 自然语言处理 服务密钥(NLP3选1) https://aiui.xfyun.cn/solution/webapi/
|
||||
# 讯飞 自然语言处理 服务密钥(NLP4选1) https://aiui.xfyun.cn/solution/webapi/
|
||||
xf_aiui_app_id=
|
||||
xf_aiui_api_key=
|
||||
|
||||
#浪.潮源大模型 服务密钥(NLP3选1) https://air.inspur.com/
|
||||
#浪.潮源大模型 服务密钥(NLP4选1) https://air.inspur.com/
|
||||
yuan_1_0_account=
|
||||
yuan_1_0_phone=
|
||||
|
||||
#gpt 服务密钥(NLP3选1) https://openai.com/
|
||||
#gpt 服务密钥(NLP4选1) https://openai.com/
|
||||
chatgpt_api_key=
|
||||
|
||||
#ngrok内网穿透id,远程设备可以通过互联网连接Fay(非必须)http://ngrok.cc
|
||||
|
@ -109,3 +109,40 @@ def parse_datetime(datetime_str):
|
||||
return datetime_obj
|
||||
except ValueError:
|
||||
print("无法解析日期和时间")
|
||||
|
||||
class ActionAskProblem(Action):
|
||||
def name(self) -> str:
|
||||
return "action_ask_problem"
|
||||
|
||||
async def run(self, dispatcher: CollectingDispatcher, tracker, domain):
|
||||
history = []
|
||||
user_messages = []
|
||||
bot_messages = []
|
||||
|
||||
# Separate user messages and bot messages
|
||||
for event in tracker.events:
|
||||
if event.get("event") == "user":
|
||||
user_messages.append(event.get("text"))
|
||||
elif event.get("event") == "bot":
|
||||
bot_messages.append(event.get("text"))
|
||||
|
||||
# Combine user and bot messages
|
||||
for user, bot in zip(user_messages, bot_messages):
|
||||
history.append([user, bot])
|
||||
|
||||
print("*******************************")
|
||||
print(history)
|
||||
print("*******************************")
|
||||
|
||||
url = "http://127.0.0.1:8000"
|
||||
req = json.dumps({
|
||||
"prompt": tracker.latest_message.get("text"),
|
||||
"history": history})
|
||||
headers = {'content-type': 'application/json'}
|
||||
r = requests.post(url, headers=headers, data=req)
|
||||
a = json.loads(r.text).get('response')
|
||||
history = json.loads(r.text).get('history')
|
||||
|
||||
dispatcher.utter_message(a)
|
||||
|
||||
return []
|
||||
|
@ -2,38 +2,13 @@ recipe: default.v1
|
||||
assistant_id: 20230416-203150-proud-texture
|
||||
language: zh
|
||||
pipeline:
|
||||
# # No configuration for the NLU pipeline was provided. The following default pipeline was used to train your model.
|
||||
# # If you'd like to customize it, uncomment and adjust the pipeline.
|
||||
# # See https://rasa.com/docs/rasa/tuning-your-model for more information.
|
||||
- name: JiebaTokenizer
|
||||
- name: RegexFeaturizer
|
||||
- name: LexicalSyntacticFeaturizer
|
||||
- name: CountVectorsFeaturizer
|
||||
- name: CountVectorsFeaturizer
|
||||
analyzer: char_wb
|
||||
min_ngram: 1
|
||||
max_ngram: 4
|
||||
- name: DIETClassifier
|
||||
epochs: 100
|
||||
constrain_similarities: true
|
||||
- name: EntitySynonymMapper
|
||||
- name: ResponseSelector
|
||||
epochs: 100
|
||||
constrain_similarities: true
|
||||
- name: FallbackClassifier
|
||||
threshold: 0.3
|
||||
ambiguity_threshold: 0.1
|
||||
policies:
|
||||
# # No configuration for policies was provided. The following default policies were used to train your model.
|
||||
# # If you'd like to customize them, uncomment and adjust the policies.
|
||||
# # See https://rasa.com/docs/rasa/policies for more information.
|
||||
- name: MemoizationPolicy
|
||||
- name: RulePolicy
|
||||
- name: UnexpecTEDIntentPolicy
|
||||
max_history: 5
|
||||
epochs: 100
|
||||
- name: TEDPolicy
|
||||
max_history: 5
|
||||
epochs: 100
|
||||
constrain_similarities: true
|
||||
|
||||
- name: JiebaTokenizer # 分词器,用于处理中文文本
|
||||
- name: RegexFeaturizer # 基于正则表达式提取特征
|
||||
- name: LexicalSyntacticFeaturizer # 用于提取词汇和句法特征
|
||||
- name: CountVectorsFeaturizer # 根据词频创建词向量
|
||||
- name: DIETClassifier # 支持中文的意图分类器
|
||||
epochs: 100 # 训练轮数,可以根据需要进行调整
|
||||
constrain_similarities: True
|
||||
policies:
|
||||
- name: MemoizationPolicy # 记忆策略,用于记住先前的对话状态
|
||||
- name: RulePolicy # 规则策略,用于处理基于规则的对话逻辑
|
@ -14,17 +14,33 @@ nlu:
|
||||
- 回头说
|
||||
- 先这样
|
||||
|
||||
- intent: ask_time
|
||||
- intent: ask_problem
|
||||
examples: |
|
||||
- 现在几点钟了?
|
||||
- 请告诉我现在的时间
|
||||
- 你知道现在是几点吗?
|
||||
- 请帮我写一篇文章
|
||||
- 请问一下这个问题为什么这样?
|
||||
- 我想写一篇文章
|
||||
- 我想咨询一下这个问题
|
||||
- 我想问问这里是什么错误
|
||||
- 这个错误是怎么导致的
|
||||
- 可以详细说说吗
|
||||
- 详细一点说
|
||||
- 详细
|
||||
- 文章
|
||||
- 继续
|
||||
- 请教
|
||||
- 请问
|
||||
|
||||
- intent: ask_date
|
||||
examples: |
|
||||
- 今天是几号?
|
||||
- 今天星期几?
|
||||
|
||||
- intent: ask_time
|
||||
examples: |
|
||||
- 现在几点钟了?
|
||||
- 请告诉我现在的时间
|
||||
- 你知道现在是几点吗?
|
||||
|
||||
- intent: out_of_scope
|
||||
examples: |
|
||||
- 今天天气真好
|
||||
|
@ -27,4 +27,9 @@ rules:
|
||||
- intent: ask_time
|
||||
- action: action_ask_time
|
||||
|
||||
- rule: 用户咨询
|
||||
steps:
|
||||
- intent: ask_problem
|
||||
- action: action_ask_problem
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@ intents:
|
||||
- ask_time
|
||||
- ask_date
|
||||
- out_of_scope
|
||||
- ask_problem
|
||||
|
||||
responses:
|
||||
utter_greet:
|
||||
@ -23,3 +24,5 @@ actions:
|
||||
- action_ask_time
|
||||
- action_ask_date
|
||||
- action_gpt_response
|
||||
- action_ask_problem
|
||||
|
||||
|
BIN
test/rasa/models/20230504-183830-electrical-grass.tar.gz
Normal file
BIN
test/rasa/models/20230504-183830-electrical-grass.tar.gz
Normal file
Binary file not shown.
@ -2,4 +2,4 @@
|
||||
2、安装rasa 包:rasa、rasa-sdk、jieba
|
||||
3、进入test/rasa目录启动actions:rasa run actions
|
||||
4、启动rasa api server:rasa run --enable-api
|
||||
5、fay_core.py 引入nlp_rasa.py
|
||||
5、fay_core.py 引入nlp_rasa.py
|
Loading…
Reference in New Issue
Block a user