olivebot/core/qa_service.py
guo zebin 4cfad5ae0f 年翻更新
- 全新ui
- 全面优化websocket逻辑,提高数字人和ui连接的稳定性及资源开销
- 全面优化唤醒逻辑,提供稳定的普通唤醒模式和前置词唤醒模式
- 优化拾音质量,支持多声道麦克风拾音
- 优化自动播放服务器的对接机制,提供稳定和兼容旧版ue工程的对接模式
- 数字人接口输出机器人表情,以适应新fay ui及单片机的数字人表情输出
- 使用更高级的音频时长计算方式,可以更精准控制音频播放完成后的逻辑
- 修复点击关闭按钮会导致程序退出的bug
- 修复没有麦克风的设备开启麦克风会出错的问题
- 为服务器主机地址提供配置项,以方便服务器部署
2024-10-26 11:34:55 +08:00

102 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import csv
import difflib
from utils import config_util as cfg
from scheduler.thread_manager import MyThread
import shlex
import subprocess
import time
from utils import util
class QAService:
def __init__(self):
# 人设提问关键字
self.attribute_keyword = [
[['你叫什么名字', '你的名字是什么'], 'name'],
[['你是男的还是女的', '你是男生还是女生', '你的性别是什么', '你是男生吗', '你是女生吗', '你是男的吗', '你是女的吗', '你是男孩子吗', '你是女孩子吗', ], 'gender', ],
[['你今年多大了', '你多大了', '你今年多少岁', '你几岁了', '你今年几岁了', '你今年几岁了', '你什么时候出生', '你的生日是什么', '你的年龄'], 'age', ],
[['你的家乡在哪', '你的家乡是什么', '你家在哪', '你住在哪', '你出生在哪', '你的出生地在哪', '你的出生地是什么', ], 'birth', ],
[['你的生肖是什么', '你属什么', ], 'zodiac', ],
[['你是什么座', '你是什么星座', '你的星座是什么', ], 'constellation', ],
[['你是做什么的', '你的职业是什么', '你是干什么的', '你的职位是什么', '你的工作是什么', '你是做什么工作的'], 'job', ],
[['你的爱好是什么', '你有爱好吗', '你喜欢什么', '你喜欢做什么'], 'hobby'],
[['联系方式', '联系你们', '怎么联系客服', '有没有客服'], 'contact']
]
self.command_keyword = [
[['关闭', '再见', '你走吧'], 'stop'],
[['静音', '闭嘴', '我想静静'], 'mute'],
[['取消静音', '你在哪呢', '你可以说话了'], 'unmute'],
[['换个性别', '换个声音'], 'changeVoice']
]
def question(self, query_type, text):
if query_type == 'qa':
answer_dict = self.__read_qna(cfg.config['interact']['QnA'])
answer, action = self.__get_keyword(answer_dict, text, query_type)
if action:
MyThread(target=self.__run, args=[action]).start()
return answer
elif query_type == 'Persona':
answer_dict = self.attribute_keyword
answer, action = self.__get_keyword(answer_dict, text, query_type)
elif query_type == 'command':
answer, action = self.__get_keyword(self.command_keyword, text, query_type)
return answer
def __run(self, action):
time.sleep(0.1)
args = shlex.split(action) # 分割命令行参数
subprocess.Popen(args)
def __read_qna(self, filename):
qna = []
try:
with open(filename, 'r', encoding='utf-8') as csvfile:
reader = csv.reader(csvfile)
next(reader) # 跳过表头
for row in reader:
if len(row) >= 2:
qna.append([row[0].split(";"), row[1], row[2] if len(row) >= 3 else None])
except Exception as e:
util.log(1, 'qa文件没有指定不匹配qa')
return qna
def record_qapair(self, question, answer):
if not cfg.config['interact']['QnA'] or cfg.config['interact']['QnA'][-3:] != 'csv':
util.log(1, 'qa文件没有指定不记录大模型回复')
return
log_file = cfg.config['interact']['QnA'] # 指定 CSV 文件的名称或路径
file_exists = os.path.isfile(log_file)
with open(log_file, 'a', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
if not file_exists:
# 写入表头
writer.writerow(['Question', 'Answer'])
writer.writerow([question, answer])
def __get_keyword(self, keyword_dict, text, query_type):
last_similar = 0
last_answer = ''
last_action = ''
for qa in keyword_dict:
for quest in qa[0]:
similar = self.__string_similar(text, quest)
if quest in text:
similar += 0.3
if similar > last_similar:
last_similar = similar
last_answer = qa[1]
if query_type == "qa":
last_action = qa[2]
if last_similar >= 0.6:
return last_answer, last_action
return None, None
def __string_similar(self, s1, s2):
return difflib.SequenceMatcher(None, s1, s2).quick_ratio()