from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel from PyQt5.QtCore import Qt, QThread, pyqtSignal from qfluentwidgets import TextEdit, LineEdit, PushButton, TitleLabel, SubtitleLabel, FluentIcon, InfoBar, InfoBarPosition import requests import json class AIWorker(QThread): response_signal = pyqtSignal(str) done_signal = pyqtSignal() error_signal = pyqtSignal(str) # 新增 def __init__(self, prompt, parent=None): super().__init__(parent) self.prompt = prompt def run(self): url = "http://154.37.215.220:11434/api/generate" payload = { "model": "qwen3:0.6b", "prompt": self.prompt } try: response = requests.post(url, json=payload, stream=True, timeout=60) got_response = False for line in response.iter_lines(): if line: got_response = True try: data = json.loads(line.decode('utf-8')) self.response_signal.emit(data.get("response", "")) if data.get("done", False): self.done_signal.emit() break except Exception: continue if not got_response: self.error_signal.emit("服务器繁忙,请稍后再试。") self.done_signal.emit() except requests.ConnectionError: self.error_signal.emit("网络连接失败,请检查网络设置。") self.done_signal.emit() except Exception as e: self.error_signal.emit(f"[错误]: {str(e)}") self.done_signal.emit() class AIInterface(QWidget): MAX_HISTORY = 20 # 新增最大对话条数 def __init__(self, parent=None): super().__init__(parent) self.setObjectName("aiPage") self.layout = QVBoxLayout(self) self.layout.setContentsMargins(20, 20, 20, 20) self.layout.setSpacing(10) self.title = SubtitleLabel("MRobot AI小助手", self) self.title.setAlignment(Qt.AlignCenter) self.layout.addWidget(self.title) self.chat_display = TextEdit(self) self.chat_display.setReadOnly(True) self.layout.addWidget(self.chat_display, stretch=1) input_layout = QHBoxLayout() self.input_box = LineEdit(self) self.input_box.setPlaceholderText("请输入你的问题...") input_layout.addWidget(self.input_box, stretch=1) # self.send_btn = PushButton("发送", self) self.send_btn = PushButton("发送", icon=FluentIcon.SEND, parent=self) self.send_btn.setFixedWidth(80) input_layout.addWidget(self.send_btn) self.layout.addLayout(input_layout) self.send_btn.clicked.connect(self.send_message) self.input_box.returnPressed.connect(self.send_message) self.worker = None self.is_waiting = False self.history = [] self.chat_display.setText( "MRobot: 欢迎使用MRobot AI小助手!" ) def send_message(self): if self.is_waiting: return prompt = self.input_box.text().strip() if not prompt: return if len(prompt) > 1000: InfoBar.warning( title='警告', content="每条发送内容不能超过1000字,请精简后再发送。", orient=Qt.Horizontal, isClosable=True, position=InfoBarPosition.BOTTOM, duration=-1, parent=self ) return if len(self.history) >= self.MAX_HISTORY: InfoBar.warning( title='警告', content="对话条数已达上限,请清理历史或重新开始。", orient=Qt.Horizontal, isClosable=True, position=InfoBarPosition.BOTTOM, duration=-1, parent=self ) return self.append_chat("你", prompt) self.input_box.clear() self.append_chat("MRobot", "", new_line=False) self.is_waiting = True # 只在首次对话时加入身份提示 if not self.history: system_prompt = ( "你是MRobot,是QUT青岛理工大学机器人战队的AI机器人。" "请以此身份与用户进行交流。" ) else: system_prompt = "" self.history.append({"role": "user", "content": prompt}) context = system_prompt + "\n" if system_prompt else "" for msg in self.history: if msg["role"] == "user": context += f"你: {msg['content']}\n" else: context += f"AI: {msg['content']}\n" self.worker = AIWorker(context) self.worker.response_signal.connect(self.stream_response) self.worker.done_signal.connect(self.finish_response) self.worker.error_signal.connect(self.show_error) # 新增 self.worker.start() def append_chat(self, sender, message, new_line=True): if new_line: self.chat_display.append(f"{sender}: {message}") else: self.chat_display.append(f"{sender}: ") self.chat_display.moveCursor(self.chat_display.textCursor().End) # 新增:保存AI回复到历史 if sender == "AI" and message: self.history.append({"role": "ai", "content": message}) def stream_response(self, text): cursor = self.chat_display.textCursor() cursor.movePosition(cursor.End) cursor.insertText(text) self.chat_display.setTextCursor(cursor) # 新增:流式保存AI回复 if self.history and self.history[-1]["role"] == "ai": self.history[-1]["content"] += text elif text: self.history.append({"role": "ai", "content": text}) def finish_response(self): self.chat_display.append("") # 换行 self.is_waiting = False def show_error(self, msg): # 新增 InfoBar.error( title='失败', content=msg, orient=Qt.Vertical, isClosable=True, position=InfoBarPosition.TOP, duration=-1, parent=self ) self.is_waiting = False