diff --git a/src/config/config.py b/src/config/config.py index 4ca8aa30..cc2ebe60 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -668,8 +668,12 @@ class BotConfig: def idle_conversation(parent: dict): idle_conversation_config = parent["idle_conversation"] if config.INNER_VERSION in SpecifierSet(">=1.6.2"): - config.enable_idle_conversation = idle_conversation_config.get("enable_idle_conversation", config.enable_idle_conversation) - config.idle_check_interval = idle_conversation_config.get("idle_check_interval", config.idle_check_interval) + config.enable_idle_conversation = idle_conversation_config.get( + "enable_idle_conversation", config.enable_idle_conversation + ) + config.idle_check_interval = idle_conversation_config.get( + "idle_check_interval", config.idle_check_interval + ) config.min_idle_time = idle_conversation_config.get("min_idle_time", config.min_idle_time) config.max_idle_time = idle_conversation_config.get("max_idle_time", config.max_idle_time) diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index ae80b91f..270ec451 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -138,7 +138,6 @@ class ActionPlanner: # 获取 ChatObserver 实例 (单例模式) self.chat_observer = ChatObserver.get_instance(stream_id, private_name) - async def plan( self, observation_info: ObservationInfo, @@ -242,7 +241,7 @@ class ActionPlanner: except Exception as end_dec_err: logger.error(f"[私聊][{self.private_name}] 处理结束对话决策时出错: {end_dec_err}") logger.warning(f"[私聊][{self.private_name}] 结束决策出错,将按原计划执行 end_conversation") - final_action = "end_conversation" # 保持原计划 + final_action = "end_conversation" # 保持原计划 final_reason = initial_reason # --- [移除] 不再需要在这里检查 wait 动作的约束 --- @@ -253,25 +252,30 @@ class ActionPlanner: # --- 5. 验证最终行动类型 --- valid_actions = [ - "direct_reply", "send_new_message", "wait", "listening", - "rethink_goal", "end_conversation", "block_and_ignore", "say_goodbye" + "direct_reply", + "send_new_message", + "wait", + "listening", + "rethink_goal", + "end_conversation", + "block_and_ignore", + "say_goodbye", ] if final_action not in valid_actions: logger.warning(f"[私聊][{self.private_name}] LLM 返回了未知的行动类型: '{final_action}',强制改为 wait") final_reason = f"(原始行动'{final_action}'无效,已强制改为wait) {final_reason}" - final_action = "wait" # 遇到无效动作,默认等待 + final_action = "wait" # 遇到无效动作,默认等待 plan_duration = time.time() - plan_start_time logger.success(f"[私聊][{self.private_name}] 最终规划行动: {final_action} (总耗时: {plan_duration:.3f} 秒)") logger.info(f"[私聊][{self.private_name}] 行动原因: {final_reason}") return final_action, final_reason - # --- Helper methods for preparing prompt inputs --- def _get_bot_last_speak_time_info(self, observation_info: ObservationInfo) -> str: """获取机器人上次发言时间提示""" - + time_info = "" try: if not observation_info or not observation_info.bot_id: @@ -297,7 +301,7 @@ class ActionPlanner: def _get_timeout_context(self, conversation_info: ConversationInfo) -> str: """获取超时提示信息""" - + timeout_context = "" try: if hasattr(conversation_info, "goal_list") and conversation_info.goal_list: @@ -307,8 +311,12 @@ class ActionPlanner: last_goal_text = last_goal_item.get("goal", "") elif isinstance(last_goal_item, str): last_goal_text = last_goal_item - if isinstance(last_goal_text, str) and "分钟," in last_goal_text and "思考接下来要做什么" in last_goal_text: - wait_time_str = last_goal_text.split("分钟,")[0].replace("你等待了","").strip() + if ( + isinstance(last_goal_text, str) + and "分钟," in last_goal_text + and "思考接下来要做什么" in last_goal_text + ): + wait_time_str = last_goal_text.split("分钟,")[0].replace("你等待了", "").strip() timeout_context = f"重要提示:对方已经长时间(约 {wait_time_str} 分钟)没有回复你的消息了,请基于此情况规划下一步。\n" logger.debug(f"[私聊][{self.private_name}] 检测到超时目标: {last_goal_text}") except AttributeError as e: @@ -319,7 +327,7 @@ class ActionPlanner: def _build_goals_string(self, conversation_info: ConversationInfo) -> str: """构建对话目标字符串""" - + goals_str = "" try: if hasattr(conversation_info, "goal_list") and conversation_info.goal_list: @@ -349,25 +357,37 @@ class ActionPlanner: async def _build_chat_history_text(self, observation_info: ObservationInfo) -> str: """构建聊天历史记录文本 (包含未处理消息)""" - + chat_history_text = "" try: if hasattr(observation_info, "chat_history_str") and observation_info.chat_history_str: chat_history_text = observation_info.chat_history_str elif hasattr(observation_info, "chat_history") and observation_info.chat_history: history_slice = observation_info.chat_history[-20:] - chat_history_text = await build_readable_messages(history_slice, replace_bot_name=True, merge_messages=False, timestamp_mode="relative", read_mark=0.0) + chat_history_text = await build_readable_messages( + history_slice, replace_bot_name=True, merge_messages=False, timestamp_mode="relative", read_mark=0.0 + ) else: chat_history_text = "还没有聊天记录。\n" - unread_count = getattr(observation_info, 'new_messages_count', 0) - unread_messages = getattr(observation_info, 'unprocessed_messages', []) + unread_count = getattr(observation_info, "new_messages_count", 0) + unread_messages = getattr(observation_info, "unprocessed_messages", []) if unread_count > 0 and unread_messages: bot_qq_str = str(global_config.BOT_QQ) - other_unread_messages = [msg for msg in unread_messages if msg.get("user_info", {}).get("user_id") != bot_qq_str] + other_unread_messages = [ + msg for msg in unread_messages if msg.get("user_info", {}).get("user_id") != bot_qq_str + ] other_unread_count = len(other_unread_messages) if other_unread_count > 0: - new_messages_str = await build_readable_messages(other_unread_messages, replace_bot_name=True, merge_messages=False, timestamp_mode="relative", read_mark=0.0) - chat_history_text += f"\n--- 以下是 {other_unread_count} 条你需要处理的新消息 ---\n{new_messages_str}\n------\n" + new_messages_str = await build_readable_messages( + other_unread_messages, + replace_bot_name=True, + merge_messages=False, + timestamp_mode="relative", + read_mark=0.0, + ) + chat_history_text += ( + f"\n--- 以下是 {other_unread_count} 条你需要处理的新消息 ---\n{new_messages_str}\n------\n" + ) logger.debug(f"[私聊][{self.private_name}] 向 LLM 追加了 {other_unread_count} 条未读消息。") except AttributeError as e: logger.warning(f"[私聊][{self.private_name}] 构建聊天记录文本时属性错误: {e}") @@ -377,10 +397,9 @@ class ActionPlanner: chat_history_text = "[处理聊天记录时出错]\n" return chat_history_text - def _build_action_history_context(self, conversation_info: ConversationInfo) -> Tuple[str, str]: """构建行动历史概要和上一次行动详细情况""" - + action_history_summary = "你最近执行的行动历史:\n" last_action_context = "关于你【上一次尝试】的行动:\n" action_history_list: List[Dict[str, Any]] = [] @@ -437,7 +456,14 @@ class ActionPlanner: end_content, _ = await self.llm.generate_response_async(end_decision_prompt) llm_duration = time.time() - llm_start_time logger.debug(f"[私聊][{self.private_name}] LLM (结束决策) 耗时: {llm_duration:.3f} 秒, 原始返回: {end_content}") - end_success, end_result = get_items_from_json(end_content, self.private_name, "say_bye", "reason", default_values={"say_bye": "no", "reason": "结束决策LLM返回格式错误,默认不告别"}, required_types={"say_bye": str, "reason": str}) + end_success, end_result = get_items_from_json( + end_content, + self.private_name, + "say_bye", + "reason", + default_values={"say_bye": "no", "reason": "结束决策LLM返回格式错误,默认不告别"}, + required_types={"say_bye": str, "reason": str}, + ) say_bye_decision = end_result.get("say_bye", "no").lower() end_decision_reason = end_result.get("reason", "未提供原因") if end_success and say_bye_decision == "yes": diff --git a/src/plugins/PFC/idle_conversation_starter.py b/src/plugins/PFC/idle_conversation_starter.py index 480d66ce..b161edd0 100644 --- a/src/plugins/PFC/idle_conversation_starter.py +++ b/src/plugins/PFC/idle_conversation_starter.py @@ -99,7 +99,8 @@ class IdleConversationStarter: # 重新随机化下一次触发的时间阈值 self.actual_idle_threshold = random.randint(global_config.min_idle_time, global_config.max_idle_time) logger.debug( - f"[私聊][{self.private_name}]更新最后消息时间: {self.last_message_time},新阈值: {self.actual_idle_threshold}秒") + f"[私聊][{self.private_name}]更新最后消息时间: {self.last_message_time},新阈值: {self.actual_idle_threshold}秒" + ) def reload_config(self) -> None: """重新加载配置 @@ -108,13 +109,15 @@ class IdleConversationStarter: """ try: logger.debug( - f"[私聊][{self.private_name}]重新加载主动对话配置: 启用={global_config.enable_idle_conversation}, 检查间隔={global_config.idle_check_interval}秒, 最短间隔={global_config.min_idle_time}秒, 最长间隔={global_config.max_idle_time}秒") + f"[私聊][{self.private_name}]重新加载主动对话配置: 启用={global_config.enable_idle_conversation}, 检查间隔={global_config.idle_check_interval}秒, 最短间隔={global_config.min_idle_time}秒, 最长间隔={global_config.max_idle_time}秒" + ) # 重新计算实际阈值 async def update_threshold(): async with self._lock: - self.actual_idle_threshold = random.randint(global_config.min_idle_time, - global_config.max_idle_time) + self.actual_idle_threshold = random.randint( + global_config.min_idle_time, global_config.max_idle_time + ) logger.debug(f"[私聊][{self.private_name}]更新空闲检测阈值为: {self.actual_idle_threshold}秒") # 创建一个任务来异步更新阈值 @@ -202,7 +205,7 @@ class IdleConversationStarter: try: content, _ = await asyncio.wait_for( self.llm.generate_response_async(prompt), - timeout=30 # 30秒超时 + timeout=30, # 30秒超时 ) except asyncio.TimeoutError: logger.error(f"[私聊][{self.private_name}]生成主动对话内容超时") @@ -213,7 +216,7 @@ class IdleConversationStarter: # 清理结果 content = content.strip() - content = content.strip('"\'') + content = content.strip("\"'") if not content: logger.error(f"[私聊][{self.private_name}]生成的主动对话内容为空") @@ -254,17 +257,13 @@ class IdleConversationStarter: # 发送消息 try: - await self.message_sender.send_message( - chat_stream=chat_stream, - content=content, - reply_to_message=None - ) + await self.message_sender.send_message(chat_stream=chat_stream, content=content, reply_to_message=None) # 更新空闲会话启动器的最后消息时间 await self.update_last_message_time() # 如果新对话实例有一个聊天观察者,请触发更新 - if new_conversation and hasattr(new_conversation, 'chat_observer'): + if new_conversation and hasattr(new_conversation, "chat_observer"): logger.info(f"[私聊][{self.private_name}]触发聊天观察者更新") try: new_conversation.chat_observer.trigger_update() @@ -279,7 +278,7 @@ class IdleConversationStarter: # 顶级异常处理,确保任何未捕获的异常都不会导致整个进程崩溃 logger.error(f"[私聊][{self.private_name}]主动发起对话过程中发生未预期的错误: {str(e)}") - async def _get_chat_stream(self, conversation: Optional['Conversation'] = None) -> Optional[ChatStream]: + async def _get_chat_stream(self, conversation: Optional["Conversation"] = None) -> Optional[ChatStream]: """获取可用的聊天流 尝试多种方式获取聊天流: @@ -296,7 +295,7 @@ class IdleConversationStarter: chat_stream = None # 1. 尝试从对话实例获取 - if conversation and hasattr(conversation, 'should_continue'): + if conversation and hasattr(conversation, "should_continue"): # 等待一小段时间,确保初始化完成 retry_count = 0 max_retries = 10 @@ -309,12 +308,13 @@ class IdleConversationStarter: logger.warning(f"[私聊][{self.private_name}]新对话实例初始化可能未完成,但仍将尝试获取聊天流") # 尝试使用对话实例的聊天流 - if hasattr(conversation, 'chat_stream') and conversation.chat_stream: + if hasattr(conversation, "chat_stream") and conversation.chat_stream: logger.info(f"[私聊][{self.private_name}]使用新对话实例的聊天流") return conversation.chat_stream # 2. 尝试从聊天管理器获取 from src.plugins.chat.chat_stream import chat_manager + try: logger.info(f"[私聊][{self.private_name}]尝试从chat_manager获取聊天流") chat_stream = chat_manager.get_stream(self.stream_id) @@ -327,13 +327,9 @@ class IdleConversationStarter: try: logger.warning(f"[私聊][{self.private_name}]无法获取现有聊天流,创建新的聊天流") # 创建用户信息对象 - user_info = UserInfo( - user_id=global_config.BOT_QQ, - user_nickname=global_config.BOT_NICKNAME, - platform="qq" - ) + user_info = UserInfo(user_id=global_config.BOT_QQ, user_nickname=global_config.BOT_NICKNAME, platform="qq") # 创建聊天流 return ChatStream(self.stream_id, "qq", user_info) except Exception as e: logger.error(f"[私聊][{self.private_name}]创建新聊天流失败: {str(e)}") - return None \ No newline at end of file + return None diff --git a/src/plugins/PFC/observation_info.py b/src/plugins/PFC/observation_info.py index 520a5306..73ff4103 100644 --- a/src/plugins/PFC/observation_info.py +++ b/src/plugins/PFC/observation_info.py @@ -25,15 +25,15 @@ class ObservationInfoHandler(NotificationHandler): """处理来自 ChatObserver 的通知""" notification_type = notification.type data = notification.data - timestamp = notification.timestamp # 获取通知时间戳 + timestamp = notification.timestamp # 获取通知时间戳 try: if notification_type == NotificationType.NEW_MESSAGE: # 处理新消息通知 - message_dict = data # data 本身就是消息字典 + message_dict = data # data 本身就是消息字典 if not isinstance(message_dict, dict): - logger.warning(f"[私聊][{self.private_name}] 收到的 NEW_MESSAGE 数据不是字典: {data}") - return + logger.warning(f"[私聊][{self.private_name}] 收到的 NEW_MESSAGE 数据不是字典: {data}") + return # 解析 UserInfo user_info_dict = message_dict.get("user_info") @@ -42,9 +42,13 @@ class ObservationInfoHandler(NotificationHandler): try: user_info = UserInfo.from_dict(user_info_dict) except Exception as e: - logger.error(f"[私聊][{self.private_name}] 从字典创建 UserInfo 时出错: {e}, dict: {user_info_dict}") + logger.error( + f"[私聊][{self.private_name}] 从字典创建 UserInfo 时出错: {e}, dict: {user_info_dict}" + ) elif user_info_dict is not None: - logger.warning(f"[私聊][{self.private_name}] 收到的 user_info 不是预期的字典类型: {type(user_info_dict)}") + logger.warning( + f"[私聊][{self.private_name}] 收到的 user_info 不是预期的字典类型: {type(user_info_dict)}" + ) # 更新 ObservationInfo await self.observation_info.update_from_message(message_dict, user_info) @@ -52,7 +56,7 @@ class ObservationInfoHandler(NotificationHandler): elif notification_type == NotificationType.COLD_CHAT: # 处理冷场通知 is_cold = data.get("is_cold", False) - await self.observation_info.update_cold_chat_status(is_cold, timestamp) # 使用通知时间戳 + await self.observation_info.update_cold_chat_status(is_cold, timestamp) # 使用通知时间戳 elif notification_type == NotificationType.MESSAGE_DELETED: # 处理消息删除通知 @@ -71,12 +75,12 @@ class ObservationInfoHandler(NotificationHandler): elif notification_type == NotificationType.BOT_SPEAKING: # 机器人开始说话 (例如,如果需要显示"正在输入...") # self.observation_info.is_typing = True - pass # 暂时不处理 + pass # 暂时不处理 elif notification_type == NotificationType.USER_SPEAKING: # 用户开始说话 # self.observation_info.is_typing = True - pass # 暂时不处理 + pass # 暂时不处理 elif notification_type == NotificationType.USER_JOINED: user_id = data.get("user_id") @@ -108,32 +112,32 @@ class ObservationInfo: self.private_name: str = private_name # 聊天记录相关 - self.chat_history: List[Dict[str, Any]] = [] # 存储已处理的消息历史 - self.chat_history_str: str = "还没有聊天记录。" # 用于生成 Prompt 的历史记录字符串 + self.chat_history: List[Dict[str, Any]] = [] # 存储已处理的消息历史 + self.chat_history_str: str = "还没有聊天记录。" # 用于生成 Prompt 的历史记录字符串 self.chat_history_count: int = 0 # 未处理消息相关 (核心修改点) - self.unprocessed_messages: List[Dict[str, Any]] = [] # 存储尚未被机器人回复的消息 - self.new_messages_count: int = 0 # unprocessed_messages 的数量 + self.unprocessed_messages: List[Dict[str, Any]] = [] # 存储尚未被机器人回复的消息 + self.new_messages_count: int = 0 # unprocessed_messages 的数量 # 状态信息 - self.active_users: Set[str] = set() # 当前活跃用户 (私聊场景可能只有对方) + self.active_users: Set[str] = set() # 当前活跃用户 (私聊场景可能只有对方) self.last_bot_speak_time: Optional[float] = None - self.last_user_speak_time: Optional[float] = None # 指对方用户的发言时间 - self.last_message_time: Optional[float] = None # 指所有消息(包括自己)的最新时间 + self.last_user_speak_time: Optional[float] = None # 指对方用户的发言时间 + self.last_message_time: Optional[float] = None # 指所有消息(包括自己)的最新时间 self.last_message_id: Optional[str] = None self.last_message_content: str = "" - self.last_message_sender: Optional[str] = None # user_id of the last message sender - self.bot_id: Optional[str] = None # 机器人自己的 ID + self.last_message_sender: Optional[str] = None # user_id of the last message sender + self.bot_id: Optional[str] = None # 机器人自己的 ID # 冷场状态 self.cold_chat_start_time: Optional[float] = None self.cold_chat_duration: float = 0.0 - self.is_cold_chat: bool = False # 当前是否处于冷场状态 + self.is_cold_chat: bool = False # 当前是否处于冷场状态 # 其他状态 - self.is_typing: bool = False # 是否正在输入 (未来可能用到) - self.changed: bool = False # 状态是否有变化 (用于优化) + self.is_typing: bool = False # 是否正在输入 (未来可能用到) + self.changed: bool = False # 状态是否有变化 (用于优化) # 关联对象 self.chat_observer: Optional[ChatObserver] = None @@ -142,14 +146,14 @@ class ObservationInfo: # 初始化 bot_id try: from ...config.config import global_config + self.bot_id = str(global_config.BOT_QQ) if global_config.BOT_QQ else None if not self.bot_id: - logger.error(f"[私聊][{self.private_name}] 未能从配置中获取 BOT_QQ ID!") + logger.error(f"[私聊][{self.private_name}] 未能从配置中获取 BOT_QQ ID!") except ImportError: - logger.error(f"[私聊][{self.private_name}] 无法导入 global_config 获取 BOT_QQ ID!") + logger.error(f"[私聊][{self.private_name}] 无法导入 global_config 获取 BOT_QQ ID!") except Exception as e: - logger.error(f"[私聊][{self.private_name}] 获取 BOT_QQ ID 时出错: {e}") - + logger.error(f"[私聊][{self.private_name}] 获取 BOT_QQ ID 时出错: {e}") def bind_to_chat_observer(self, chat_observer: ChatObserver): """绑定到指定的 ChatObserver 并注册通知处理器""" @@ -157,8 +161,8 @@ class ObservationInfo: logger.warning(f"[私聊][{self.private_name}] 尝试重复绑定 ChatObserver") return if not self.handler: - logger.error(f"[私聊][{self.private_name}] ObservationInfoHandler 未初始化,无法绑定!") - return + logger.error(f"[私聊][{self.private_name}] ObservationInfoHandler 未初始化,无法绑定!") + return self.chat_observer = chat_observer try: @@ -175,12 +179,11 @@ class ObservationInfo: logger.info(f"[私聊][{self.private_name}] ObservationInfo 成功绑定到 ChatObserver") except AttributeError: - logger.error(f"[私聊][{self.private_name}] 绑定的 ChatObserver 对象缺少 notification_manager 属性!") - self.chat_observer = None # 绑定失败 + logger.error(f"[私聊][{self.private_name}] 绑定的 ChatObserver 对象缺少 notification_manager 属性!") + self.chat_observer = None # 绑定失败 except Exception as e: logger.error(f"[私聊][{self.private_name}] 绑定到 ChatObserver 时出错: {e}") - self.chat_observer = None # 绑定失败 - + self.chat_observer = None # 绑定失败 def unbind_from_chat_observer(self): """解除与 ChatObserver 的绑定""" @@ -189,18 +192,19 @@ class ObservationInfo: notification_manager = self.chat_observer.notification_manager notification_manager.unregister_handler("observation_info", NotificationType.NEW_MESSAGE, self.handler) notification_manager.unregister_handler("observation_info", NotificationType.COLD_CHAT, self.handler) - notification_manager.unregister_handler("observation_info", NotificationType.MESSAGE_DELETED, self.handler) + notification_manager.unregister_handler( + "observation_info", NotificationType.MESSAGE_DELETED, self.handler + ) # ... 注销其他已注册的类型 ... logger.info(f"[私聊][{self.private_name}] ObservationInfo 成功从 ChatObserver 解绑") except Exception as e: logger.error(f"[私聊][{self.private_name}] 从 ChatObserver 解绑时出错: {e}") finally: - self.chat_observer = None # 无论成功与否都清除引用 + self.chat_observer = None # 无论成功与否都清除引用 else: logger.warning(f"[私聊][{self.private_name}] 尝试解绑时 ChatObserver 无效或 handler 未设置") - async def update_from_message(self, message: Dict[str, Any], user_info: Optional[UserInfo]): """根据收到的新消息更新 ObservationInfo 的状态""" message_time = message.get("time") @@ -209,8 +213,8 @@ class ObservationInfo: sender_id_str: Optional[str] = str(user_info.user_id) if user_info else None if not message_time or not message_id: - logger.warning(f"[私聊][{self.private_name}] 收到的消息缺少 time 或 message_id: {message}") - return + logger.warning(f"[私聊][{self.private_name}] 收到的消息缺少 time 或 message_id: {message}") + return # 更新最后消息时间(所有消息) if message_time > (self.last_message_time or 0): @@ -225,7 +229,7 @@ class ObservationInfo: self.last_bot_speak_time = message_time else: self.last_user_speak_time = message_time - self.active_users.add(sender_id_str) # 添加到活跃用户 + self.active_users.add(sender_id_str) # 添加到活跃用户 else: logger.warning(f"[私聊][{self.private_name}] 处理消息更新时缺少有效的 UserInfo, message_id: {message_id}") @@ -237,14 +241,15 @@ class ObservationInfo: # --- [核心修改] 将新消息添加到未处理列表 --- # 检查消息是否已存在于未处理列表中,避免重复添加 if not any(msg.get("message_id") == message_id for msg in self.unprocessed_messages): - # 创建消息的副本以避免修改原始数据(如果需要) - self.unprocessed_messages.append(message.copy()) - self.new_messages_count = len(self.unprocessed_messages) - logger.debug(f"[私聊][{self.private_name}] 添加新未处理消息 ID: {message_id}, 发送者: {sender_id_str}, 当前未处理数: {self.new_messages_count}") - self.update_changed() + # 创建消息的副本以避免修改原始数据(如果需要) + self.unprocessed_messages.append(message.copy()) + self.new_messages_count = len(self.unprocessed_messages) + logger.debug( + f"[私聊][{self.private_name}] 添加新未处理消息 ID: {message_id}, 发送者: {sender_id_str}, 当前未处理数: {self.new_messages_count}" + ) + self.update_changed() else: - logger.warning(f"[私聊][{self.private_name}] 尝试重复添加未处理消息 ID: {message_id}") - + logger.warning(f"[私聊][{self.private_name}] 尝试重复添加未处理消息 ID: {message_id}") async def remove_unprocessed_message(self, message_id_to_delete: str): """从 unprocessed_messages 列表中移除指定 ID 的消息""" @@ -256,11 +261,12 @@ class ObservationInfo: if new_count < original_count: self.new_messages_count = new_count - logger.info(f"[私聊][{self.private_name}] 移除了未处理的消息 (ID: {message_id_to_delete}), 当前未处理数: {self.new_messages_count}") + logger.info( + f"[私聊][{self.private_name}] 移除了未处理的消息 (ID: {message_id_to_delete}), 当前未处理数: {self.new_messages_count}" + ) self.update_changed() else: - logger.warning(f"[私聊][{self.private_name}] 尝试移除不存在的未处理消息 ID: {message_id_to_delete}") - + logger.warning(f"[私聊][{self.private_name}] 尝试移除不存在的未处理消息 ID: {message_id_to_delete}") async def update_cold_chat_status(self, is_cold: bool, current_time: float): """更新冷场状态""" @@ -274,20 +280,18 @@ class ObservationInfo: if self.cold_chat_start_time: self.cold_chat_duration = current_time - self.cold_chat_start_time logger.info(f"[私聊][{self.private_name}] 结束冷场状态,持续时间: {self.cold_chat_duration:.2f} 秒") - self.cold_chat_start_time = None # 结束冷场,重置开始时间 + self.cold_chat_start_time = None # 结束冷场,重置开始时间 self.update_changed() # 持续更新冷场时长 if self.is_cold_chat and self.cold_chat_start_time: self.cold_chat_duration = current_time - self.cold_chat_start_time - def update_changed(self): """标记状态已改变""" self.changed = True # 这个标记通常在处理完改变后由外部逻辑重置为 False - # --- [修改点 15] 重命名并修改 clear_unprocessed_messages --- async def clear_processed_messages(self, message_ids_to_clear: Set[str]): """将指定 ID 的未处理消息移入历史记录,并更新相关状态""" @@ -309,7 +313,9 @@ class ObservationInfo: remaining_messages.append(msg) if not messages_to_move: - logger.debug(f"[私聊][{self.private_name}] 未找到与 ID 列表 {message_ids_to_clear} 匹配的未处理消息进行清理。") + logger.debug( + f"[私聊][{self.private_name}] 未找到与 ID 列表 {message_ids_to_clear} 匹配的未处理消息进行清理。" + ) return logger.debug(f"[私聊][{self.private_name}] 准备清理 {cleared_count} 条已处理消息...") @@ -319,19 +325,19 @@ class ObservationInfo: self.chat_history.extend(messages_to_move) # 限制历史记录长度 (可选) - max_history_len = 100 # 例如保留最近 100 条 + max_history_len = 100 # 例如保留最近 100 条 if len(self.chat_history) > max_history_len: self.chat_history = self.chat_history[-max_history_len:] # 更新历史记录字符串 (仅使用最近一部分生成,提高效率) - history_slice_for_str = self.chat_history[-20:] # 例如最近 20 条 + history_slice_for_str = self.chat_history[-20:] # 例如最近 20 条 try: self.chat_history_str = await build_readable_messages( history_slice_for_str, replace_bot_name=True, merge_messages=False, timestamp_mode="relative", - read_mark=0.0, # read_mark 可能需要调整或移除 + read_mark=0.0, # read_mark 可能需要调整或移除 ) except Exception as e: logger.error(f"[私聊][{self.private_name}] 构建聊天记录字符串时出错: {e}") @@ -342,16 +348,17 @@ class ObservationInfo: self.new_messages_count = len(self.unprocessed_messages) self.chat_history_count = len(self.chat_history) - logger.info(f"[私聊][{self.private_name}] 已清理 {cleared_count} 条消息 (IDs: {message_ids_to_clear}),剩余未处理 {self.new_messages_count} 条,当前历史记录 {self.chat_history_count} 条。") - - self.update_changed() # 状态改变 + logger.info( + f"[私聊][{self.private_name}] 已清理 {cleared_count} 条消息 (IDs: {message_ids_to_clear}),剩余未处理 {self.new_messages_count} 条,当前历史记录 {self.chat_history_count} 条。" + ) + self.update_changed() # 状态改变 # --- Helper methods (可以根据需要添加) --- def get_active_duration(self) -> float: """获取当前活跃时长(距离最后一条消息的时间)""" if not self.last_message_time: - return float('inf') # 或返回 0.0,取决于定义 + return float("inf") # 或返回 0.0,取决于定义 return time.time() - self.last_message_time def get_user_response_time(self) -> Optional[float]: @@ -365,4 +372,3 @@ class ObservationInfo: if not self.last_bot_speak_time: return None return time.time() - self.last_bot_speak_time - diff --git a/src/plugins/PFC/pfc_manager.py b/src/plugins/PFC/pfc_manager.py index d008bab3..dadf31f9 100644 --- a/src/plugins/PFC/pfc_manager.py +++ b/src/plugins/PFC/pfc_manager.py @@ -1,5 +1,5 @@ import time -import asyncio # 引入 asyncio +import asyncio # 引入 asyncio import traceback from typing import Dict, Optional @@ -17,7 +17,7 @@ class PFCManager: # 会话实例管理 _instances: Dict[str, Conversation] = {} - _initializing: Dict[str, bool] = {} # 用于防止并发初始化同一个 stream_id + _initializing: Dict[str, bool] = {} # 用于防止并发初始化同一个 stream_id @classmethod def get_instance(cls) -> "PFCManager": @@ -33,24 +33,26 @@ class PFCManager: if self._initializing.get(stream_id, False): logger.debug(f"[私聊][{private_name}] 会话实例正在初始化中,请稍候: {stream_id}") # 可以选择等待一小段时间或直接返回 None - await asyncio.sleep(0.5) # 短暂等待,让初始化有机会完成 + await asyncio.sleep(0.5) # 短暂等待,让初始化有机会完成 # 再次检查实例是否存在 if stream_id in self._instances and self._instances[stream_id]._initialized: logger.debug(f"[私聊][{private_name}] 初始化已完成,返回现有实例: {stream_id}") return self._instances[stream_id] else: logger.warning(f"[私聊][{private_name}] 等待后实例仍未初始化完成或不存在。") - return None # 避免返回未完成的实例 + return None # 避免返回未完成的实例 # 检查是否已有活动实例 if stream_id in self._instances: instance = self._instances[stream_id] # 检查忽略状态 - if (hasattr(instance, "ignore_until_timestamp") and - instance.ignore_until_timestamp and - time.time() < instance.ignore_until_timestamp): + if ( + hasattr(instance, "ignore_until_timestamp") + and instance.ignore_until_timestamp + and time.time() < instance.ignore_until_timestamp + ): logger.debug(f"[私聊][{private_name}] 会话实例当前处于忽略状态: {stream_id}") - return None # 处于忽略状态,不返回实例 + return None # 处于忽略状态,不返回实例 # 检查是否已初始化且应继续运行 if instance._initialized and instance.should_continue: @@ -66,16 +68,15 @@ class PFCManager: if stream_id in self._initializing: del self._initializing[stream_id] - # --- 创建并初始化新实例 --- conversation_instance: Optional[Conversation] = None try: logger.info(f"[私聊][{private_name}] 创建新的对话实例: {stream_id}") - self._initializing[stream_id] = True # 标记开始初始化 + self._initializing[stream_id] = True # 标记开始初始化 # 创建实例 conversation_instance = Conversation(stream_id, private_name) - self._instances[stream_id] = conversation_instance # 立即存入字典 + self._instances[stream_id] = conversation_instance # 立即存入字典 # **启动实例初始化** # _initialize_conversation 会调用 conversation._initialize() @@ -84,7 +85,7 @@ class PFCManager: # --- 关键修复:在初始化成功后调用 start() --- if conversation_instance._initialized and conversation_instance.should_continue: logger.info(f"[私聊][{private_name}] 初始化成功,调用 conversation.start() 启动主循环...") - await conversation_instance.start() # 确保调用 start 方法 + await conversation_instance.start() # 确保调用 start 方法 else: # 如果 _initialize_conversation 内部初始化失败 logger.error(f"[私聊][{private_name}] 初始化未成功完成,无法启动实例 {stream_id}。") @@ -92,7 +93,7 @@ class PFCManager: await self._cleanup_conversation(conversation_instance) if stream_id in self._instances: del self._instances[stream_id] - conversation_instance = None # 返回 None 表示失败 + conversation_instance = None # 返回 None 表示失败 except Exception as e: logger.error(f"[私聊][{private_name}] 创建或启动会话实例时发生严重错误: {stream_id}, 错误: {e}") @@ -102,7 +103,7 @@ class PFCManager: await self._cleanup_conversation(conversation_instance) if stream_id in self._instances: del self._instances[stream_id] - conversation_instance = None # 返回 None + conversation_instance = None # 返回 None finally: # 确保初始化标记被清除 @@ -117,22 +118,27 @@ class PFCManager: private_name = conversation.private_name try: logger.info(f"[私聊][{private_name}] 管理器开始调用 conversation._initialize(): {stream_id}") - await conversation._initialize() # 调用实例自身的初始化方法 + await conversation._initialize() # 调用实例自身的初始化方法 # 注意:初始化成功与否由 conversation._initialized 和 conversation.should_continue 标志决定 if conversation._initialized: - logger.info(f"[私聊][{private_name}] conversation._initialize() 调用完成,实例标记为已初始化: {stream_id}") + logger.info( + f"[私聊][{private_name}] conversation._initialize() 调用完成,实例标记为已初始化: {stream_id}" + ) else: - logger.warning(f"[私聊][{private_name}] conversation._initialize() 调用完成,但实例未成功标记为已初始化: {stream_id}") + logger.warning( + f"[私聊][{private_name}] conversation._initialize() 调用完成,但实例未成功标记为已初始化: {stream_id}" + ) except Exception as e: # _initialize 内部应该处理自己的异常,但这里也捕获以防万一 - logger.error(f"[私聊][{private_name}] 调用 conversation._initialize() 时发生未捕获错误: {stream_id}, 错误: {e}") + logger.error( + f"[私聊][{private_name}] 调用 conversation._initialize() 时发生未捕获错误: {stream_id}, 错误: {e}" + ) logger.error(traceback.format_exc()) # 确保实例状态反映失败 conversation._initialized = False conversation.should_continue = False - async def _cleanup_conversation(self, conversation: Conversation): """清理会话实例的资源""" if not conversation: @@ -142,14 +148,14 @@ class PFCManager: logger.info(f"[私聊][{private_name}] 开始清理会话实例资源: {stream_id}") try: # 调用 conversation 的 stop 方法来停止其内部组件 - if hasattr(conversation, 'stop') and callable(conversation.stop): - await conversation.stop() # stop 方法应处理内部组件的停止 + if hasattr(conversation, "stop") and callable(conversation.stop): + await conversation.stop() # stop 方法应处理内部组件的停止 else: logger.warning(f"[私聊][{private_name}] Conversation 对象缺少 stop 方法,可能无法完全清理资源。") # 尝试手动停止已知组件 (作为后备) - if hasattr(conversation, 'idle_conversation_starter') and conversation.idle_conversation_starter: + if hasattr(conversation, "idle_conversation_starter") and conversation.idle_conversation_starter: conversation.idle_conversation_starter.stop() - if hasattr(conversation, 'observation_info') and conversation.observation_info: + if hasattr(conversation, "observation_info") and conversation.observation_info: conversation.observation_info.unbind_from_chat_observer() # ChatObserver 是单例,不在此处停止 @@ -163,12 +169,14 @@ class PFCManager: instance = self._instances.get(stream_id) if instance and instance._initialized and instance.should_continue: # 检查忽略状态 - if (hasattr(instance, "ignore_until_timestamp") and - instance.ignore_until_timestamp and - time.time() < instance.ignore_until_timestamp): - return None # 忽略期间不返回 + if ( + hasattr(instance, "ignore_until_timestamp") + and instance.ignore_until_timestamp + and time.time() < instance.ignore_until_timestamp + ): + return None # 忽略期间不返回 return instance - return None # 不存在或无效则返回 None + return None # 不存在或无效则返回 None async def remove_conversation(self, stream_id: str): """移除并清理会话实例""" @@ -188,4 +196,3 @@ class PFCManager: logger.error(traceback.format_exc()) else: logger.warning(f"[管理器] 尝试移除不存在的会话实例: {stream_id}") - diff --git a/src/plugins/PFC/pfc_types.py b/src/plugins/PFC/pfc_types.py index dcdc7247..16495134 100644 --- a/src/plugins/PFC/pfc_types.py +++ b/src/plugins/PFC/pfc_types.py @@ -18,8 +18,7 @@ class ConversationState(Enum): ENDED = "结束" JUDGING = "判断" IGNORED = "屏蔽" - ERROR = "错误" # <--- 添加 ERROR 状态 + ERROR = "错误" # <--- 添加 ERROR 状态 ActionType = Literal["direct_reply", "fetch_knowledge", "wait"] -