diff --git a/README.md b/README.md index 972dbfca9..3e2da652a 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,15 @@ 关于麦麦的开发和部署相关的讨论群(不建议发布无关消息)这里不会有麦麦发言! +## 开发计划TODO:LIST + +- 兼容gif的解析和保存 +- 小程序转发链接解析 +- 对思考链长度限制 +- 修复已知bug +- 完善文档 + +
@@ -62,6 +71,92 @@ NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose restart - 把env.example改成.env,并填上你的apikey(硅基流动或deepseekapi) - 把bot_config_toml改名为bot_config.toml,并填写相关内容,不然无法正常运行 + #### .env 文件配置说明 + ```ini + # 环境配置 + ENVIRONMENT=dev # 开发环境设置 + HOST=127.0.0.1 # 主机地址 + PORT=8080 # 端口号 + + # 命令前缀设置 + COMMAND_START=["/"] # 命令起始符 + + # 插件配置 + PLUGINS=["src2.plugins.chat"] # 启用的插件列表 + + # MongoDB配置 + MONGODB_HOST=127.0.0.1 # MongoDB主机地址 + MONGODB_PORT=27017 # MongoDB端口 + DATABASE_NAME=MegBot # 数据库名称 + MONGODB_USERNAME="" # MongoDB用户名(可选) + MONGODB_PASSWORD="" # MongoDB密码(可选) + MONGODB_AUTH_SOURCE="" # MongoDB认证源(可选) + + # API密钥配置 + CHAT_ANY_WHERE_KEY= # ChatAnyWhere API密钥 + SILICONFLOW_KEY= # 硅基流动 API密钥(必填) + DEEP_SEEK_KEY= # DeepSeek API密钥(必填) + + # API地址配置 + CHAT_ANY_WHERE_BASE_URL=https://api.chatanywhere.tech/v1 + SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1/ + DEEP_SEEK_BASE_URL=https://api.deepseek.com/v1 + ``` + + #### bot_config.toml 文件配置说明 + ```toml + # 数据库设置 + [database] + host = "127.0.0.1" # MongoDB主机地址 + port = 27017 # MongoDB端口 + name = "MegBot" # 数据库名称 + + # 机器人基本设置 + [bot] + qq = # 你的机器人QQ号(必填) + nickname = "麦麦" # 机器人昵称 + + # 消息处理设置 + [message] + min_text_length = 2 # 最小响应文本长度 + max_context_size = 15 # 上下文最大保存数量 + emoji_chance = 0.2 # 表情包使用概率 + + # 表情包功能设置 + [emoji] + check_interval = 120 # 表情检查间隔(秒) + register_interval = 10 # 表情注册间隔(秒) + + # CQ码设置 + [cq_code] + enable_pic_translate = false # 是否启用图片转换(无效) + + # 响应设置 + [response] + api_using = "siliconflow" # 回复使用的API(siliconflow/deepseek) + model_r1_probability = 0.8 # R1模型使用概率 + model_v3_probability = 0.1 # V3模型使用概率 + model_r1_distill_probability = 0.1 # R1蒸馏模型使用概率(对deepseek api 无效) + + # 其他设置 + [others] + enable_advance_output = false # 是否启用详细日志输出 + + # 群组设置 + [groups] + talk_allowed = [ # 允许回复的群号列表 + # 在这里添加群号,逗号隔开 + ] + + talk_frequency_down = [ # 降低回复频率的群号列表 + # 在这里添加群号,逗号隔开 + ] + + ban_user_id = [ # 禁止回复的用户QQ号列表 + # 在这里添加QQ号,逗号隔开 + ] + ``` + 5. **运行麦麦** ```bash conda activate 你的环境 diff --git a/requirements.txt b/requirements.txt index d94f30ecd..7ddb691bc 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/src/gui/reasoning_gui.py b/src/gui/reasoning_gui.py index 79e274d6d..356be3bd1 100644 --- a/src/gui/reasoning_gui.py +++ b/src/gui/reasoning_gui.py @@ -73,7 +73,7 @@ class ReasoningGUI: self.frame.pack(pady=20, padx=20, fill="both", expand=True) # 添加标题 - self.title = ctk.CTkLabel(self.frame, text="AI推理监控系统", font=("Arial", 24)) + self.title = ctk.CTkLabel(self.frame, text="麦麦的脑内所想", font=("Arial", 24)) self.title.pack(pady=10, padx=10) # 创建左右分栏 @@ -87,8 +87,14 @@ class ReasoningGUI: self.group_label = ctk.CTkLabel(self.left_frame, text="群组列表", font=("Arial", 16)) self.group_label.pack(pady=5) - self.group_listbox = ctk.CTkTextbox(self.left_frame, width=180, height=400) - self.group_listbox.pack(pady=5, padx=5) + # 创建可滚动框架来容纳群组按钮 + self.group_scroll_frame = ctk.CTkScrollableFrame(self.left_frame, width=180, height=400) + self.group_scroll_frame.pack(pady=5, padx=5, fill="both", expand=True) + + # 存储群组按钮的字典 + self.group_buttons: Dict[str, ctk.CTkButton] = {} + # 当前选中的群组ID + self.selected_group_id: Optional[str] = None # 右侧内容显示 self.right_frame = ctk.CTkFrame(self.paned) @@ -106,6 +112,7 @@ class ReasoningGUI: self.content_text.tag_config("user", foreground="#4CAF50") # 用户名使用绿色 self.content_text.tag_config("message", foreground="#2196F3") # 消息使用蓝色 self.content_text.tag_config("model", foreground="#9C27B0") # 模型名称使用紫色 + self.content_text.tag_config("prompt", foreground="#FF9800") # prompt内容使用橙色 self.content_text.tag_config("reasoning", foreground="#FF9800") # 推理过程使用橙色 self.content_text.tag_config("response", foreground="#E91E63") # 回复使用粉色 self.content_text.tag_config("separator", foreground="#666666") # 分隔符使用深灰色 @@ -122,9 +129,6 @@ class ReasoningGUI: ) self.clear_button.pack(side="left", padx=5) - # 添加群组点击事件 - self.group_listbox.bind('', self._on_group_click) - # 启动自动更新线程 self.update_thread = threading.Thread(target=self._auto_update, daemon=True) self.update_thread.start() @@ -154,9 +158,45 @@ class ReasoningGUI: def _update_group_list_gui(self): """在主线程中更新群组列表""" - self.group_listbox.delete("1.0", "end") + # 清除现有按钮 + for button in self.group_buttons.values(): + button.destroy() + self.group_buttons.clear() + + # 创建新的群组按钮 for group_id in self.group_data.keys(): - self.group_listbox.insert("end", f"群号: {group_id}\n") + button = ctk.CTkButton( + self.group_scroll_frame, + text=f"群号: {group_id}", + width=160, + height=30, + corner_radius=8, + command=lambda gid=group_id: self._on_group_select(gid) + ) + button.pack(pady=2, padx=5) + self.group_buttons[group_id] = button + + # 如果有选中的群组,保持其高亮状态 + if self.selected_group_id and self.selected_group_id in self.group_buttons: + self._highlight_selected_group(self.selected_group_id) + + def _on_group_select(self, group_id: str): + """处理群组选择事件""" + self._highlight_selected_group(group_id) + self._update_display_gui(group_id) + + def _highlight_selected_group(self, group_id: str): + """高亮显示选中的群组按钮""" + # 重置所有按钮的颜色 + for gid, button in self.group_buttons.items(): + if gid == group_id: + # 设置选中按钮的颜色 + button.configure(fg_color="#1E88E5", hover_color="#1976D2") + else: + # 恢复其他按钮的默认颜色 + button.configure(fg_color="#2B2B2B", hover_color="#404040") + + self.selected_group_id = group_id def _update_display_gui(self, group_id: str): """在主线程中更新显示内容""" @@ -179,15 +219,27 @@ class ReasoningGUI: self.content_text.insert("end", "模型: ", "timestamp") self.content_text.insert("end", f"{item.get('model', '')}\n", "model") + # Prompt内容 + self.content_text.insert("end", "Prompt内容:\n", "timestamp") + prompt_text = item.get('prompt', '') + if prompt_text and prompt_text.lower() != 'none': + lines = prompt_text.split('\n') + for line in lines: + if line.strip(): + self.content_text.insert("end", " " + line + "\n", "prompt") + else: + self.content_text.insert("end", " 无Prompt内容\n", "prompt") + # 推理过程 self.content_text.insert("end", "推理过程:\n", "timestamp") reasoning_text = item.get('reasoning', '') - # 处理推理过程中的Markdown格式 - lines = reasoning_text.split('\n') - for line in lines: - if line.strip(): - # 添加缩进 - self.content_text.insert("end", " " + line + "\n", "reasoning") + if reasoning_text and reasoning_text.lower() != 'none': + lines = reasoning_text.split('\n') + for line in lines: + if line.strip(): + self.content_text.insert("end", " " + line + "\n", "reasoning") + else: + self.content_text.insert("end", " 无推理过程\n", "reasoning") # 回复内容 self.content_text.insert("end", "回复: ", "timestamp") @@ -237,11 +289,12 @@ class ReasoningGUI: new_data[group_id].append({ 'time': time_obj, - 'user': item.get('user_nickname', item.get('user_id', '未知')), + 'user': item.get('user', '未知'), 'message': item.get('message', ''), 'model': item.get('model', '未知'), 'reasoning': item.get('reasoning', ''), 'response': item.get('response', ''), + 'prompt': item.get('prompt', '') # 添加prompt字段 }) print(f"从数据库加载了 {total_count} 条记录,分布在 {len(new_data)} 个群组中") @@ -253,10 +306,12 @@ class ReasoningGUI: # 将更新任务添加到队列 self.update_queue.put({'type': 'update_group_list'}) if self.group_data: - latest_group = next(iter(self.group_data)) + # 如果没有选中的群组,选择最新的群组 + if not self.selected_group_id or self.selected_group_id not in self.group_data: + self.selected_group_id = next(iter(self.group_data)) self.update_queue.put({ 'type': 'update_display', - 'group_id': latest_group + 'group_id': self.selected_group_id }) except Exception as e: print(f"自动更新出错: {e}") @@ -264,59 +319,6 @@ class ReasoningGUI: # 每5秒更新一次 time.sleep(5) - def _on_group_click(self, event): - """处理群组点击事件""" - try: - # 获取点击位置的文本行 - index = self.group_listbox.index(f"@{event.x},{event.y}") - line = self.group_listbox.get(f"{index} linestart", f"{index} lineend") - if line.startswith("群号: "): - group_id = line.replace("群号: ", "").strip() - self.update_display(group_id) - except Exception as e: - print(f"处理群组点击事件出错: {e}") - - def update_display(self, group_id: str): - """更新显示指定群组的内容""" - if group_id in self.group_data: - self.content_text.delete("1.0", "end") - for item in self.group_data[group_id]: - # 时间戳 - time_str = item['time'].strftime("%Y-%m-%d %H:%M:%S") - self.content_text.insert("end", f"[{time_str}]\n", "timestamp") - - # 用户信息 - self.content_text.insert("end", "用户: ", "timestamp") - self.content_text.insert("end", f"** {item.get('user', '未知')} **\n", "user") - - # 消息内容 - self.content_text.insert("end", "消息: ", "timestamp") - self.content_text.insert("end", f"{item.get('message', '')}\n", "message") - - # 模型信息 - self.content_text.insert("end", "模型: ", "timestamp") - self.content_text.insert("end", f"{item.get('model', '')}\n", "model") - - # 推理过程 - self.content_text.insert("end", "推理过程:\n", "timestamp") - reasoning_text = item.get('reasoning', '') - # 处理推理过程中的Markdown格式 - lines = reasoning_text.split('\n') - for line in lines: - if line.strip(): - # 添加缩进 - self.content_text.insert("end", " " + line + "\n", "reasoning") - - # 回复内容 - self.content_text.insert("end", "回复: ", "timestamp") - self.content_text.insert("end", f"{item.get('response', '')}\n", "response") - - # 分隔符 - self.content_text.insert("end", f"\n{'='*50}\n\n", "separator") - - # 滚动到顶部 - self.content_text.see("1.0") - def clear_display(self): """清除显示内容""" self.content_text.delete("1.0", "end") diff --git a/src/plugins/chat/bot_config_toml b/src/plugins/chat/bot_config_toml index f51a7a4d8..fe6b702d8 100644 --- a/src/plugins/chat/bot_config_toml +++ b/src/plugins/chat/bot_config_toml @@ -20,7 +20,7 @@ check_interval = 120 register_interval = 10 [cq_code] -enable_pic_translate = true +enable_pic_translate = false [response] @@ -30,7 +30,7 @@ model_v3_probability = 0.1 # 麦麦回答时选择V3模型的概率 model_r1_distill_probability = 0.1 # 麦麦回答时选择R1蒸馏模型的概率 [others] -enable_advance_output = false # 开启后输出更多日志,false关闭true开启 +enable_advance_output = true # 开启后输出更多日志,false关闭true开启 [groups] diff --git a/src/plugins/chat/llm_generator.py b/src/plugins/chat/llm_generator.py index 98494d795..bb68d3618 100644 --- a/src/plugins/chat/llm_generator.py +++ b/src/plugins/chat/llm_generator.py @@ -129,7 +129,7 @@ class LLMResponseGenerator: content = response.choices[0].message.content # 获取推理内容 - reasoning_content = "模型思考过程:\n" + prompt + reasoning_content = "" if hasattr(response.choices[0].message, "reasoning"): reasoning_content = response.choices[0].message.reasoning or reasoning_content elif hasattr(response.choices[0].message, "reasoning_content"):