使用 PyQt5 和 DeepSeek API 构建一个多语言聊天助手
在现代软件开发中,图形用户界面(GUI)和人工智能(AI)的结合变得越来越普遍。本文将介绍如何使用 Python 的 PyQt5 库和 DeepSeek API 构建一个多语言聊天助手。这个助手不仅支持多语言切换,还能保存和加载聊天历史,为用户提供流畅的交互体验。
项目概述
我们的目标是构建一个桌面应用程序,用户可以通过输入 API Key 登录,选择偏好的语言,并与 DeepSeek 的 AI 模型进行交互。应用程序的主要功能包括:
-
登录验证:用户输入 API Key 进行验证。
-
语言选择:用户可以选择英文或中文作为界面语言。
-
聊天功能:用户可以与 AI 进行多轮对话。
-
历史记录:聊天记录可以保存和加载。
-
多标签页:支持多个独立的聊天会话。
技术栈
-
PyQt5:用于构建图形用户界面。
-
DeepSeek API:用于与 AI 模型进行交互。
-
JSON:用于保存和加载聊天历史。
-
多线程:用于处理 API 请求,避免阻塞主线程。
代码解析
1. 登录窗口 (LoginDialog
)
登录窗口负责验证用户的 API Key。用户输入 API Key 后,应用程序会尝试与 DeepSeek API 进行通信,验证 Key 的有效性。
python
复制
class LoginDialog(QDialog): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle(LANGUAGES["en"]["login_title"]) # 默认英文 self.setGeometry(200, 200, 300, 150) layout = QVBoxLayout() # API Key 输入框 self.api_key_input = QLineEdit(self) self.api_key_input.setPlaceholderText(LANGUAGES["en"]["api_key_placeholder"]) layout.addWidget(self.api_key_input) # 登录按钮 self.login_button = QPushButton(LANGUAGES["en"]["login_button"], self) self.login_button.clicked.connect(self.validate_api_key) layout.addWidget(self.login_button) self.setLayout(layout) def validate_api_key(self): api_key = self.api_key_input.text() if not api_key: QMessageBox.warning(self, "Error", LANGUAGES["en"]["error_api_key_empty"]) return # 测试 API Key 是否有效 try: client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com") client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello"}], stream=False, timeout=5 ) self.accept() # 关闭登录窗口 self.api_key = api_key # 保存有效的 API Key except AuthenticationError: QMessageBox.critical(self, "Error", LANGUAGES["en"]["error_invalid_api_key"]) except Exception as e: QMessageBox.critical(self, "Error", LANGUAGES["en"]["error_unknown"].format(str(e)))
2. 语言选择窗口 (LanguageDialog
)
语言选择窗口允许用户选择应用程序的界面语言。用户可以选择英文或中文。
python
复制
class LanguageDialog(QDialog): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle(LANGUAGES["en"]["language_title"]) self.setGeometry(200, 200, 300, 150) layout = QVBoxLayout() # 语言选择提示 self.prompt_label = QLabel(LANGUAGES["en"]["language_prompt"], self) layout.addWidget(self.prompt_label) # 语言选择按钮 self.language_group = QButtonGroup(self) self.language_en = QRadioButton(LANGUAGES["en"]["language_en"], self) self.language_zh = QRadioButton(LANGUAGES["en"]["language_zh"], self) self.language_group.addButton(self.language_en, 1) self.language_group.addButton(self.language_zh, 2) layout.addWidget(self.language_en) layout.addWidget(self.language_zh) # 确认按钮 self.confirm_button = QPushButton("OK", self) self.confirm_button.clicked.connect(self.confirm_language) layout.addWidget(self.confirm_button) self.setLayout(layout) def confirm_language(self): if self.language_group.checkedId() == 1: self.language = "en" elif self.language_group.checkedId() == 2: self.language = "zh" else: QMessageBox.warning(self, "Error", "Please select a language.") return self.accept()
3. 聊天页面 (ChatPage
)
聊天页面是应用程序的核心部分,负责处理用户输入、显示聊天记录、调用 DeepSeek API 并显示 AI 的回复。
python
复制
class ChatPage(QWidget): def __init__(self, client, language, tab_id): super().__init__() self.client = client self.language = language self.tab_id = tab_id self.history_file = os.path.join(HISTORY_DIR, f"chat_history_{self.tab_id}.json") self.messages = self.load_history() # 初始化对话历史 self.init_ui() def init_ui(self): layout = QVBoxLayout() # 聊天记录显示区域 self.chat_display = QTextEdit(self) self.chat_display.setReadOnly(True) # 设置为只读 layout.addWidget(self.chat_display) # 用户输入框 self.user_input = QLineEdit(self) self.user_input.setPlaceholderText(LANGUAGES[self.language]["send_button"]) layout.addWidget(self.user_input) # 发送按钮 self.send_button = QPushButton(LANGUAGES[self.language]["send_button"], self) self.send_button.clicked.connect(self.send_message) layout.addWidget(self.send_button) # 清除对话按钮 self.clear_button = QPushButton(LANGUAGES[self.language]["clear_button"], self) self.clear_button.clicked.connect(self.clear_conversation) layout.addWidget(self.clear_button) self.setLayout(layout) # 初始化时显示历史记录 self.update_chat_display() def send_message(self): user_message = self.user_input.text() if not user_message: return # 如果输入为空,直接返回 # 将用户输入添加到对话历史 self.messages.append({"role": "user", "content": user_message}) # 显示用户输入 self.chat_display.append(f"User: {user_message}") # 清空输入框 self.user_input.clear() # 禁用发送按钮,避免重复点击 self.send_button.setEnabled(False) # 创建工作线程并启动 self.worker = Worker(self.client, self.messages) self.worker.finished.connect(self.handle_reply, Qt.QueuedConnection) self.worker.error.connect(self.handle_error, Qt.QueuedConnection) self.worker.start() def handle_reply(self, reply): # 显示助手回复 self.chat_display.append(f"Assistant: {reply}\n") # 将助手回复添加到对话历史 self.messages.append({"role": "assistant", "content": reply}) # 重新启用发送按钮 self.send_button.setEnabled(True) # 释放工作线程资源 self.worker.quit() self.worker.wait() self.worker = None # 自动保存历史 self.save_history() def handle_error(self, error_message): # 显示错误信息 self.chat_display.append(f"Assistant: {error_message}\n") QMessageBox.critical(self, "Error", error_message) # 重新启用发送按钮 self.send_button.setEnabled(True) # 释放工作线程资源 if self.worker: self.worker.quit() self.worker.wait() self.worker = None def clear_conversation(self): # 清除当前对话历史 self.messages = [{"role": "system", "content": "You are a helpful assistant."}] self.chat_display.clear() QMessageBox.information(self, "Info", LANGUAGES[self.language]["info_conversation_cleared"]) # 自动保存历史 self.save_history() def save_history(self): # 保存对话历史到文件 with open(self.history_file, "w") as f: json.dump(self.messages, f) def load_history(self): # 从文件加载对话历史 try: with open(self.history_file, "r") as f: return json.load(f) except FileNotFoundError: return [{"role": "system", "content": "You are a helpful assistant."}] def update_chat_display(self): # 更新聊天记录显示 self.chat_display.clear() for msg in self.messages: if msg["role"] == "user": self.chat_display.append(f"User: {msg['content']}\n") elif msg["role"] == "assistant": self.chat_display.append(f"Assistant: {msg['content']}\n")
4. 主窗口 (ChatApp
)
主窗口负责管理多个聊天页面,并提供添加新聊天页面的功能。
python
复制
class ChatApp(QWidget): def __init__(self, api_key, language): super().__init__() self.api_key = api_key self.language = language self.client = OpenAI(api_key=self.api_key, base_url="https://api.deepseek.com") self.init_ui() def init_ui(self): self.setWindowTitle(LANGUAGES[self.language]["chat_title"]) self.setGeometry(100, 100, 800, 600) layout = QVBoxLayout() # 选项卡控件 self.tabs = QTabWidget(self) layout.addWidget(self.tabs) # 添加新页面按钮 self.add_tab_button = QPushButton(LANGUAGES[self.language]["new_chat_button"], self) self.add_tab_button.clicked.connect(self.add_tab) layout.addWidget(self.add_tab_button) self.setLayout(layout) # 加载所有历史文件 self.load_all_histories() def add_tab(self): # 添加一个新页面 tab_id = self.tabs.count() + 1 tab = ChatPage(self.client, self.language, tab_id) self.tabs.addTab(tab, f"Chat {tab_id}") def load_all_histories(self): # 加载所有历史文件 history_files = [f for f in os.listdir(HISTORY_DIR) if f.startswith("chat_history_") and f.endswith(".json")] for file in history_files: tab_id = int(file.split("_")[2].split(".")[0]) tab = ChatPage(self.client, self.language, tab_id) self.tabs.addTab(tab, f"Chat {tab_id}")
5. 多线程处理 (Worker
)
为了确保 API 请求不会阻塞主线程,我们使用 QThread
来处理 API 请求。
python
复制
class Worker(QThread): finished = pyqtSignal(str) # 用于发送请求结果的信号 error = pyqtSignal(str) # 用于发送错误信息的信号 def __init__(self, client, messages): super().__init__() self.client = client self.messages = messages def run(self): max_retries = 3 # 最大重试次数 retry_delay = 5 # 每次重试的延迟时间(秒) for attempt in range(max_retries): try: # 调用 DeepSeek API response = self.client.chat.completions.create( model="deepseek-chat", # 使用 DeepSeek 的模型 messages=self.messages, stream=False, timeout=30 # 设置超时时间为 30 秒 ) # 提取助手的回复 assistant_reply = response.choices[0].message.content self.finished.emit(assistant_reply) # 发送成功信号 break # 如果成功,跳出循环 except Exception as e: if attempt < max_retries - 1: time.sleep(retry_delay) else: self.error.emit(f"An error occurred: {str(e)}") break
总结
通过本文的介绍,我们使用 PyQt5 和 DeepSeek API 构建了一个功能丰富的多语言聊天助手。这个应用程序不仅支持多语言切换,还能保存和加载聊天历史,为用户提供了流畅的交互体验。希望这篇文章能帮助你理解如何将 GUI 和 AI 技术结合起来,构建出更强大的应用程序。
如果你对这个项目感兴趣,可以尝试扩展更多功能,比如支持更多的语言、添加语音输入输出、或者集成其他 AI 模型。祝你在编程的旅程中取得更多成就!