项目需求:
-
Coze中设计一个多agent模式的Bots,用于实现与用户智能对答
-
调用cozeAPI,实现Bots机器人与用户聊天
-
调用百度智能云API,实现识别用户的语音输入及机器人语音回复用户
-
使用SQLite数据库,保存数据到数据库中,提供查看历史记录功能
-
使用Pyside6设计一个简洁干净的Qt界面
设计思路:
-
仿照QQ聊天界面设计一个聊天界面,实现用户输入等基本功能
-
设计一个心理咨询机器人,调用API智能回复用户的问题
-
调用百度智能云API,实现识别用户的语音输入及机器人语音回复用户
-
设计自然语言的提示词(Promots),引导用户如何与机器人交流,制定回复逻辑,确保机器人能够理解用户意图并给出恰当回应。
界面展示:
1.Qt设计界面:
2.表情发送功能:
3.图片发送功能:
4.界面截屏功能:
5.用户交互功能:
6.语音聊天功能:
7.语音聊天功能:
功能代码:
1.用户交互代码:
def sendTextMessage(self):
# 获取当前时间并格式化
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 获取用户输入的消息
message = self.textEdit.toPlainText()
# 如果消息不为空
if message:
# 将消息添加到聊天记录中,并格式化显示发送时间和内容
self.textBrowser.append(f"我:[{now}]\n"
f"{message}\n")
# 调用回复消息函数,传递用户消息作为参数
self.replyTextMessage(message)
# 清空消息输入框
self.textEdit.clear()
# 调用外部API处理消息,这里假设返回值为chat_response
chat_response = self.coze_api(str)
def replyTextMessage(self, message):
# 获取当前时间并格式化
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 调用外部API处理消息,并获取响应结果
res = self.coze_api(message)
# 插入消息和响应结果到数据库或其他存储中
insert_data(message, res)
2.语音对话代码:
def start_recording(self):
# 检查是否已经开始录音,如果没有,则开始录音
if not self.recording:
self.recording = True # 将录音状态设置为True
# 使用上下文管理器来确保线程安全,清除现有的帧数据
with self.frames_lock:
self.frames.clear()
# 更新按钮文本和样式,以指示正在录音
self.pushButton.setText("录音中...")
self.pushButton.setStyleSheet(u"background-color: rgb(255, 0, 0);\n"
"color: rgb(255, 255, 255);\n"
"font-size:20px;\n"
"border-radius:5px")
print("录音中...") # 打印录音开始的消息
# 创建并启动一个新的线程来处理音频录制,以避免阻塞主线程
self.record_audio_thread = threading.Thread(target=self.record_audio)
self.record_audio_thread.start()
def stop_recording(self):
"""
停止录音功能的方法。
本方法通过改变对象的状态来停止录音,同时更新用户界面的按钮文本和样式,
并尝试保存录音文件。确保录音线程安全结束是其核心功能之一。
"""
# 检查是否正在录音,是则停止录音
if self.recording:
self.recording = False
# 更新按钮文本和样式,以反映录音停止的状态
self.pushButton.setText("按住话筒说话")
self.pushButton.setStyleSheet(u"background-color: rgb(85, 170, 0);\n"
"color: rgb(255, 255, 255);\n"
"font-size:20px;\n"
"border-radius:5px")
# 打印停止录音的消息,用于日志记录
print("停止录音...")
# 确保录音线程结束
self.record_audio_thread.join()
# 尝试保存录音文件
success = self.save_audio()
if success:
# 如果文件保存成功,打印确认消息
print("录音文件保存成功!")
self.speech_recognition() # 调用语音识别方法
else:
# 如果文件保存失败,打印错误消息
print("录音文件保存失败!")
def record_audio(self):
"""
录音方法。尝试打开音频流并开始录音,直到停止录音或发生异常。
"""
try:
# 初始化音频流,设置格式、通道、采样率和输入模式
stream = self.audio.open(format=self.FORMAT,
channels=self.CHANNELS,
rate=self.RATE,
input=True,
frames_per_buffer=self.CHUNK)
# 主循环:持续录音直到停止
while self.recording:
# 读取指定数量的音频数据
data = stream.read(self.CHUNK)
# 使用上下文管理器来确保线程安全地添加数据到录音帧中
with self.frames_lock:
self.frames.append(data)
# 停止并关闭音频流
stream.stop_stream()
stream.close()
except Exception as e:
# 打印录音过程中发生的任何异常
print(f"Error during recording: {e}")
def save_audio(self):
try:
# 定义文件名
filename = 'medium/user.wav'
# 打开WAV文件以二进制格式写入
wf = wave.open(filename, 'wb')
# 设置声道数
wf.setnchannels(self.CHANNELS)
# 设置采样宽度
wf.setsampwidth(self.audio.get_sample_size(self.FORMAT))
# 设置帧率
wf.setframerate(self.RATE)
# 使用锁保护frames列表,确保线程安全地写入音频数据
with self.frames_lock:
wf.writeframes(b''.join(self.frames))
# 关闭文件
wf.close()
# 打印保存成功消息
print(f"录音文件已保存为 {filename}")
# 返回成功标志
return True
except Exception as e:
# 打印错误消息
print(f"Error saving audio file: {e}")
# 返回失败标志
return False
def speech_recognition(self):
#语音识别功能
# 初始化BaiduVoice对象,用于后续的语音识别和合成操作
voice = BaiduVoice()
# 识别语音文件并打印识别结果
recognition_result = voice.recognize_speech('medium/user.wav')
print("已识别的文本:", recognition_result)
self.textBrowser.append(f"用户:{recognition_result}--{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
if recognition_result:
#调用cozeapi接口
self.coze_api(recognition_result)
threading.Thread(target=insert_data, args=('用户',recognition_result)).start()
else:
print("识别失败")
def speech_synthesis(self,kword):
# 语音合成功能
# 初始化BaiduVoice对象,用于后续的语音识别和合成操作
voice = BaiduVoice()
# 合成文本并保存为音频文件
voice.synthesize_text(kword, 'medium/bot.mp3')
# self.textBrowser.append(f"机器人:{kword}--{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("已合成的语音文本:", kword)
# 播放合成的音频文件
# 初始化Pygame,这是运行Pygame功能的必要步骤
pygame.init()
# 加载背景音乐文件
pygame.mixer.music.load('medium/bot.mp3')
# 播放背景音乐
pygame.mixer.music.play()
# 等待音乐播放完毕,避免程序提前退出
while pygame.mixer.music.get_busy():
# 控制循环帧率为10帧/秒,以节省资源
pygame.time.Clock().tick(10)
# 音乐播放结束后,释放Pygame资源
pygame.quit()
3.发送表情代码:
def sendEmojis(self, emoji):
"""
向文本框发送表情并进行语音合成。
此函数用于构建一条包含当前时间和表情的消息,并将该消息添加到文本框中。
同时,它还会对消息进行语音合成,以便通过语音输出。
参数:
emoji -- 要发送的表情字符串
返回值:
无
"""
# 获取当前时间并格式化,以便在消息中显示
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 构建包含发送者、时间和表情的消息字符串
message = f"我:[{now}]\n{emoji}\n"
# 将消息添加到文本框中显示
self.textBrowser.append(message)
# 对消息进行语音合成,实现语音输出
self.speech_synthesis(message)
def openEmoji(self):
"""
打开表情选择器的方法。
该方法负责实例化一个Emoji对象,将其发送文本信号连接到sendEmojis方法,然后显示表情选择器。
这种设计模式展示了如何在需要时初始化和交互UI组件,以及如何使用信号和槽机制来处理事件。
"""
# 实例化一个Emoji对象
self.emoji = Emoji()
# 将emoji对象的sendTextSignal连接到sendEmojis方法
self.emoji.sendTextSignal.connect(self.sendEmojis)
# 显示emoji对象
self.emoji.show()
4.图片发送代码:
def sendImage(self):
# 使用QFileDialog打开文件选择对话框
file_path, _ = QFileDialog.getOpenFileName(None ,"选择图片", "",
"图片文件 (*.png *.jpg *.jpeg *.bmp)")
try:
if file_path:
# 更新图片路径到文本行
if file_path:
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
html_image = f"""
<div style="text-align: left; margin-bottom: 10px;">
<span style="font-size: 10px; color: gray;">我:{now}</span>
<br/>
<img src="{file_path}" style="width: 150px; height: 150px; object-fit: cover;"/><br/>
</div>
"""
print(f"Selected file path: {file_path}") # 调试输出
self.textBrowser.append(html_image)
except Exception as e:
print(f"Error: {str(e)}")
5.聊天记录代码:
class History_Window(QMainWindow, Ui_HistoryWindow):
"""
历史记录窗口类,继承自QMainWindow和Ui_HistoryWindow。
该类负责显示历史记录窗口,并提供加载历史记录的功能。
"""
def __init__(self):
"""
初始化方法,用于初始化窗口及其组件。
"""
super().__init__()
self.setupUi(self)
self.setFixedSize(433, 348) # 设置窗口固定大小
def load_history(self):
"""
加载历史记录的方法。
通过调用query_data方法获取历史记录数据,并将其显示在文本浏览器中。
"""
self.textBrowser.setText(query_data()) # 设置文本浏览器内容为查询到的历史数据
def show(self):
"""
重写的显示方法,在显示窗口之前加载最新的历史记录。
"""
self.load_history() # 加载最新历史记录
super().show() # 调用父类的显示方法
6.窗口截屏代码:
def initUI(self):
"""
初始化用户界面,包括窗口设置、标签和布局的创建。
"""
self.setWindowTitle('屏幕截图示例')
self.setGeometry(100, 100, 800, 600)
# 创建一个标签,用于显示截图
self.screenshotLabel = QLabel(self)
self.screenshotLabel.setAlignment(Qt.AlignCenter)
# 创建一个垂直布局管理器
layout = QVBoxLayout()
# 将截图标签添加到布局中
layout.addWidget(self.screenshotLabel)
# 创建一个中心小部件,并设置布局
central_widget = QWidget()
central_widget.setLayout(layout)
# 将中心小部件设置为主窗口的中心小部件
self.setCentralWidget(central_widget)
def takeScreenshot(self):
"""
获取屏幕截图并显示在标签上。
"""
# 获取主屏幕
screen = QApplication.primaryScreen()
if screen is not None:
# 获取整个屏幕的截图
screenshot = screen.grabWindow(0)
# 将截图设置到标签上,并保持纵横比
self.screenshotLabel.setPixmap(
screenshot.scaled(self.screenshotLabel.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
# 显示窗口
self.show()
在设计智能体语音聊天机器人时,可能会遇到以下一些常见问题以及相应的解决方案:
-
理解用户意图的准确性问题
- 问题:智能体可能无法准确理解用户的自然语言输入,导致回答不相关或不准确。
- 解决方案:使用更先进的自然语言处理(NLP)技术,如上下文理解、意图识别和实体抽取。此外,可以通过机器学习不断训练和优化模型。
-
多轮对话管理
- 问题:在多轮对话中,智能体可能会丢失上下文信息,导致回答不连贯。
- 解决方案:实现上下文管理功能,让智能体能够跟踪对话历史,维持对话的连贯性。
-
知识库的局限性
- 问题:智能体的知识库可能不全面,无法回答某些问题。
- 解决方案:定期更新和扩充知识库,或者集成外部知识源,如Wikipedia、专业数据库等。
-
个性化服务的挑战
- 问题:智能体可能无法提供个性化的用户体验。
- 解决方案:通过用户画像和历史交互数据来个性化对话策略,提供定制化服务。
-
处理复杂问题的能力
- 问题:智能体可能难以处理复杂或含糊的问题。
- 解决方案:设计更复杂的对话流程和逻辑,或者集成专家系统来处理特定领域的复杂问题。
-
语音识别和合成的准确性
- 问题:语音识别可能不准确,导致智能体误解用户的指令;语音合成可能不够自然。
- 解决方案:使用高质量的语音识别和合成技术,如深度学习驱动的ASR(自动语音识别)和TTS(文本到语音)系统。