diff --git a/README.md b/README.md index 39a8526..38ebb2a 100644 --- a/README.md +++ b/README.md @@ -2,33 +2,33 @@
Fay

FAY

-

数 字 人 Fay 控 制 器(这是元宇宙吗?)

+

Fay数字人助理

-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 demo`](https://github.com/TheRamU/Fay/tree/main/python_connector_demo) +### **PC远程助理** [`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) diff --git a/ai_module/ali_nls.py b/ai_module/ali_nls.py index c8f7abd..9dde18a 100644 --- a/ai_module/ali_nls.py +++ b/ai_module/ali_nls.py @@ -135,7 +135,6 @@ def __connect(self): 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}) diff --git a/ai_module/nlp_rasa.py b/ai_module/nlp_rasa.py new file mode 100644 index 0000000..852fc03 --- /dev/null +++ b/ai_module/nlp_rasa.py @@ -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 diff --git a/core/content_db.py b/core/content_db.py new file mode 100644 index 0000000..360e464 --- /dev/null +++ b/core/content_db.py @@ -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) + + + + + + + + + + diff --git a/core/fay_core.py b/core/fay_core.py index b081a56..4505b60 100644 --- a/core/fay_core.py +++ b/core/fay_core.py @@ -25,8 +25,9 @@ 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 @@ def __auto_speak(self): if self.muting: continue text = '' + textlist = [] if answer is None: try: wsa_server.get_web_instance().add_cmd({"panelMsg": "思考中..."}) @@ -252,6 +254,9 @@ def __auto_speak(self): 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 @@ def __auto_speak(self): 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 @@ def __waiting_speaking(self, file_url): 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: diff --git a/gui/flask_server.py b/gui/flask_server.py index 96b790c..5d74113 100644 --- a/gui/flask_server.py +++ b/gui/flask_server.py @@ -12,6 +12,8 @@ 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(): diff --git a/gui/static/css/index.css b/gui/static/css/index.css index 594b9f6..4e414be 100644 --- a/gui/static/css/index.css +++ b/gui/static/css/index.css @@ -176,6 +176,7 @@ ul { display: flex; justify-content: center; margin: auto; + margin-top: 60px; } .left_box .source ul .but .el-button { diff --git a/gui/static/from.jpg b/gui/static/from.jpg new file mode 100644 index 0000000..603663f Binary files /dev/null and b/gui/static/from.jpg differ diff --git a/gui/static/js/index.js b/gui/static/js/index.js index 05ea01d..d19a645 100644 --- a/gui/static/js/index.js +++ b/gui/static/js/index.js @@ -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, "
"); + // text = text.replace(/\n/g, "
"); + // text = text.replace(/\r\n/g, "
"); + // let item = document.createElement('div'); + // item.className = 'item item-right'; + // item.innerHTML = `
${text}
`; + // 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({}); }, diff --git a/gui/static/to.jpg b/gui/static/to.jpg new file mode 100644 index 0000000..b12016d Binary files /dev/null and b/gui/static/to.jpg differ diff --git a/gui/templates/index.html b/gui/templates/index.html index 272886e..e2e4e29 100644 --- a/gui/templates/index.html +++ b/gui/templates/index.html @@ -7,228 +7,362 @@ - + + - + + - 自动商品介绍控制器 + Fay + -
-
-
-

Fay控制器2.0

-
-
-
-
-

人设:

-
-
-
-
    -
  • -

    姓名:

    - -
  • -
  • -

    性别:

    - -
  • -
  • -

    年龄:

    - -
  • - -
  • -

    出生地:

    - -
  • -
  • -

    生肖:

    - -
  • -
  • -

    星座:

    - -
  • -
  • -

    职业:

    - -
  • -
  • -

    喜好:

    - -
  • - -
  • -

    联系方式:

    - -
  • -
+
+
+
+

Fay数字人助理版

+
+
+
+
+

人设:

+
+
+
+
    +
  • +

    姓名:

    + +
  • +
  • +

    性别:

    + +
  • +
  • +

    年龄:

    + +
  • + +
  • +

    出生地:

    + +
  • +
  • +

    生肖:

    + +
  • +
  • +

    星座:

    + +
  • +
  • +

    职业:

    + +
  • +
  • +

    喜好:

    + +
  • + +
  • +

    联系方式:

    + +
  • +
+
+
+
    + +
  • +

    敏感度:

    + +
  • + +
    +
  • +

    使用面板播放:

    + + +
  • +
+
-
-
    -
  • -

    送礼敏感度:

    - - - -
  • -
  • -

    关注敏感度:

    - -
  • -
  • -

    进入敏感度:

    - -
  • -
  • -

    留言敏感度:

    - -
  • -
  • -

    冷场敏感度:

    - -
  • -
  • -

    单次互动时长:

    - - -
  • -
  • -

    声音选择:{{attribute_voice}}

    - - - - -
  • -
    -
  • -

    使用面板播放:

    - - -
  • -
+
+

Q&A文件:

+ + + + +
-
-

Q&A文件:

- - - - - -
-
-
-

 

-
-
    -
  • - - -

    抖 音

    - -
  • -
  • - - -

    麦克风

    - - - - -
  • -
  • - -

    消 息

    - -
  • -
  • - 关闭(运行中) - 正在开启... - 正在关闭... - 开启 - 保存配置 -
  • -
  • -

    注:启动后请选中场景客户端,让其前端运行,否则可能会卡顿,或者无声音。

    -
  • -
-
-
-
-
-
- - +
+

 

+
    -
  • -

    名称:

    - -
  • -
  • -

    商品简介:

    - - -
  • -
  • -

    使用场景:

    - - -
  • -
  • -

    售价说明:

    - - -
  • -
  • -

    促销:

    - - -
  • -
  • -

    主播担保:

    - - + +
  • + + +

    麦克风

    + + + +
  • -
  • -

    商品特点:

    - - +
  • + +

    消 息

    +
  • -
  • -

    展示视频:

    - +
  • + 关闭(运行中) + 正在开启... + 正在关闭... + 开启 + 保存配置
  • -
  • -

    Q&A文件:

    - -
  • -
  • -

    是否启用:

    - - +
  • +

    注:启动后请选中场景客户端,让其前端运行,否则可能会卡顿,或者无声音。

- - -
+
+
+
+
+
+
+
+ +
+
[[item.timetext]]
+ +
+
+
+
[[item.content]] +
+
+
+
[[item.content]]
+
+
+
+
+ + + + + +
+
+ +
+ +
+
+
+
-
+ diff --git a/gui/window.py b/gui/window.py index f9ca15f..c3805b7 100644 --- a/gui/window.py +++ b/gui/window.py @@ -78,4 +78,4 @@ def init_ui(self): self.mainLayout.setSpacing(5) self.mainLayout.addWidget(self.pWebGroup) self.setLayout(self.mainLayout) - self.setMinimumSize(800, 800) + self.setMinimumSize(800, 800) \ No newline at end of file diff --git a/images/controller.png b/images/controller.png index 2160a47..e24beb5 100644 Binary files a/images/controller.png and b/images/controller.png differ diff --git a/images/luoji.png b/images/luoji.png index 4fe254d..3fd1596 100644 Binary files a/images/luoji.png and b/images/luoji.png differ diff --git a/main.py b/main.py index c7a2c53..c39c1c0 100644 --- a/main.py +++ b/main.py @@ -11,7 +11,7 @@ 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 @@ def __clear_songs(): __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) diff --git a/python_connector_demo/remote_audio.py b/python_connector_demo/remote_audio.py index 038ddd8..af62a79 100644 --- a/python_connector_demo/remote_audio.py +++ b/python_connector_demo/remote_audio.py @@ -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() diff --git a/system.conf b/system.conf index a100e0a..0298c13 100644 --- a/system.conf +++ b/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 diff --git a/test/rasa/actions/actions.py b/test/rasa/actions/actions.py index 3fd5f00..e28e080 100644 --- a/test/rasa/actions/actions.py +++ b/test/rasa/actions/actions.py @@ -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 [] diff --git a/test/rasa/config.yml b/test/rasa/config.yml index 6f0871d..e602b8a 100644 --- a/test/rasa/config.yml +++ b/test/rasa/config.yml @@ -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 # 规则策略,用于处理基于规则的对话逻辑 \ No newline at end of file diff --git a/test/rasa/data/nlu.yml b/test/rasa/data/nlu.yml index fafbea5..4579742 100644 --- a/test/rasa/data/nlu.yml +++ b/test/rasa/data/nlu.yml @@ -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: | - 今天天气真好 diff --git a/test/rasa/data/rules.yml b/test/rasa/data/rules.yml index 2d2b4bf..66fa2a2 100644 --- a/test/rasa/data/rules.yml +++ b/test/rasa/data/rules.yml @@ -27,4 +27,9 @@ rules: - intent: ask_time - action: action_ask_time +- rule: 用户咨询 + steps: + - intent: ask_problem + - action: action_ask_problem + diff --git a/test/rasa/domain.yml b/test/rasa/domain.yml index 0d14b7e..0643a78 100644 --- a/test/rasa/domain.yml +++ b/test/rasa/domain.yml @@ -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 + diff --git a/test/rasa/models/20230504-183830-electrical-grass.tar.gz b/test/rasa/models/20230504-183830-electrical-grass.tar.gz new file mode 100644 index 0000000..cf227dd Binary files /dev/null and b/test/rasa/models/20230504-183830-electrical-grass.tar.gz differ diff --git a/test/rasa/readme.txt b/test/rasa/readme.txt index 66bed12..8fcac5a 100644 --- a/test/rasa/readme.txt +++ b/test/rasa/readme.txt @@ -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 \ No newline at end of file