From de761cebea123372e623b8aed397909980581316 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 May 2025 01:15:37 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/config.py | 8 +- src/plugins/PFC/action_planner.py | 44 ++- src/plugins/PFC/actions.py | 417 ++++++++++++-------- src/plugins/PFC/chat_observer.py | 8 +- src/plugins/PFC/conversation.py | 45 ++- src/plugins/PFC/conversation_info.py | 18 +- src/plugins/PFC/conversation_initializer.py | 161 +++++--- src/plugins/PFC/conversation_loop.py | 217 ++++++---- src/plugins/PFC/observation_info.py | 23 +- src/plugins/PFC/pfc.py | 6 +- src/plugins/PFC/pfc_emotion.py | 65 +-- src/plugins/PFC/pfc_manager.py | 18 +- src/plugins/PFC/pfc_processor.py | 14 +- src/plugins/PFC/pfc_relationship.py | 150 ++++--- src/plugins/PFC/pfc_utils.py | 79 ++-- src/plugins/PFC/reply_checker.py | 42 +- src/plugins/PFC/reply_generator.py | 82 ++-- 17 files changed, 857 insertions(+), 540 deletions(-) diff --git a/src/config/config.py b/src/config/config.py index 0242d52a..b0d20ab6 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -273,7 +273,7 @@ class BotConfig: # enable_think_flow: bool = False # 是否启用思考流程 talk_allowed_private = set() enable_pfc_chatting: bool = False # 是否启用PFC聊天 - enable_pfc_reply_checker: bool = True # 是否开启PFC回复检查 + enable_pfc_reply_checker: bool = True # 是否开启PFC回复检查 # idle_conversation enable_idle_conversation: bool = False # 是否启用 pfc 主动发言(未完善) @@ -666,8 +666,10 @@ class BotConfig: config.talk_allowed_private = set(str(user) for user in experimental_config.get("talk_allowed_private", [])) if config.INNER_VERSION in SpecifierSet(">=1.1.0"): config.enable_pfc_chatting = experimental_config.get("pfc_chatting", config.enable_pfc_chatting) - if config.INNER_VERSION in SpecifierSet(">=1.1.0"): - config.enable_pfc_reply_checker = experimental_config.get("enable_pfc_reply_checker", config.enable_pfc_reply_checker) + if config.INNER_VERSION in SpecifierSet(">=1.1.0"): + config.enable_pfc_reply_checker = experimental_config.get( + "enable_pfc_reply_checker", config.enable_pfc_reply_checker + ) logger.info(f"PFC Reply Checker 状态: {'启用' if config.enable_pfc_reply_checker else '关闭'}") def idle_conversation(parent: dict): diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 3b7dcff6..3d7743a7 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -167,6 +167,7 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在 注意:请严格按照JSON格式输出,不要包含任何其他内容。""" + class ActionPlanner: """行动规划器""" @@ -204,7 +205,7 @@ class ActionPlanner: observation_info: ObservationInfo, conversation_info: ConversationInfo, last_successful_reply_action: Optional[str], - use_reflect_prompt: bool = False # 新增参数,用于指示是否使用PROMPT_REFLECT_AND_ACT + use_reflect_prompt: bool = False, # 新增参数,用于指示是否使用PROMPT_REFLECT_AND_ACT ) -> Tuple[str, str]: """ 规划下一步行动。 @@ -227,13 +228,12 @@ class ActionPlanner: goals_str = self._build_goals_string(conversation_info) chat_history_text = await self._build_chat_history_text(observation_info) # 获取 sender_name, relationship_text, current_emotion_text - sender_name_str = getattr(observation_info, 'sender_name', '对方') # 从 observation_info 获取 + sender_name_str = getattr(observation_info, "sender_name", "对方") # 从 observation_info 获取 if not sender_name_str: - sender_name_str = '对方' # 再次确保有默认值 - - relationship_text_str = getattr(conversation_info, 'relationship_text', '你们还不熟悉。') - current_emotion_text_str = getattr(conversation_info, 'current_emotion_text', '心情平静。') + sender_name_str = "对方" # 再次确保有默认值 + relationship_text_str = getattr(conversation_info, "relationship_text", "你们还不熟悉。") + current_emotion_text_str = getattr(conversation_info, "current_emotion_text", "心情平静。") persona_text = f"你的名字是{self.name},{self.personality_info}。" action_history_summary, last_action_context = self._build_action_history_context(conversation_info) @@ -250,14 +250,16 @@ class ActionPlanner: # --- 2. 选择并格式化 Prompt --- try: - if use_reflect_prompt: # 新增的判断 + if use_reflect_prompt: # 新增的判断 prompt_template = PROMPT_REFLECT_AND_ACT log_msg = "使用 PROMPT_REFLECT_AND_ACT (反思决策)" # 对于 PROMPT_REFLECT_AND_ACT,它不包含 send_new_message 选项,所以 spam_warning_message 中的相关提示可以调整或省略 # 但为了保持占位符填充的一致性,我们仍然计算它 spam_warning_message = "" - if conversation_info.my_message_count > 5: # 这里的 my_message_count 仍有意义,表示之前连续发送了多少 - spam_warning_message = f"⚠️【警告】**你之前已连续发送{str(conversation_info.my_message_count)}条消息!请谨慎决策。**" + if conversation_info.my_message_count > 5: # 这里的 my_message_count 仍有意义,表示之前连续发送了多少 + spam_warning_message = ( + f"⚠️【警告】**你之前已连续发送{str(conversation_info.my_message_count)}条消息!请谨慎决策。**" + ) elif conversation_info.my_message_count > 2: spam_warning_message = f"💬【提示】**你之前已连续发送{str(conversation_info.my_message_count)}条消息。请注意保持对话平衡。**" @@ -273,12 +275,12 @@ class ActionPlanner: else: prompt_template = PROMPT_INITIAL_REPLY log_msg = "使用 PROMPT_INITIAL_REPLY (首次/非连续回复决策)" - spam_warning_message = "" # 初始回复时通常不需要刷屏警告 + spam_warning_message = "" # 初始回复时通常不需要刷屏警告 logger.debug(f"[私聊][{self.private_name}] {log_msg}") current_time_value = "获取时间失败" - if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str: + if observation_info and hasattr(observation_info, "current_time_str") and observation_info.current_time_str: current_time_value = observation_info.current_time_str if spam_warning_message: @@ -298,7 +300,7 @@ class ActionPlanner: spam_warning_info=spam_warning_message, sender_name=sender_name_str, relationship_text=relationship_text_str, - current_emotion_text=current_emotion_text_str + current_emotion_text=current_emotion_text_str, ) logger.debug(f"[私聊][{self.private_name}] 发送到LLM的最终提示词:\n------\n{prompt}\n------") except KeyError as fmt_key_err: @@ -338,10 +340,14 @@ class ActionPlanner: if initial_action == "end_conversation": try: time_str_for_end_decision = "获取时间失败" - if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str: + if ( + observation_info + and hasattr(observation_info, "current_time_str") + and observation_info.current_time_str + ): time_str_for_end_decision = observation_info.current_time_str final_action, final_reason = await self._handle_end_conversation_decision( - persona_text, chat_history_text, initial_reason,time_str_for_end_decision + persona_text, chat_history_text, initial_reason, time_str_for_end_decision ) except Exception as end_dec_err: logger.error(f"[私聊][{self.private_name}] 处理结束对话决策时出错: {end_dec_err}") @@ -366,7 +372,7 @@ class ActionPlanner: "block_and_ignore", "say_goodbye", ] - valid_actions_reflect = [ # PROMPT_REFLECT_AND_ACT 的动作 + valid_actions_reflect = [ # PROMPT_REFLECT_AND_ACT 的动作 "wait", "listening", "rethink_goal", @@ -507,9 +513,7 @@ class ActionPlanner: ) logger.debug(f"[私聊][{self.private_name}] 向 LLM 追加了 {other_unread_count} 条未读消息。") else: - chat_history_text += ( - "\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n" - ) + chat_history_text += "\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n" except AttributeError as e: logger.warning(f"[私聊][{self.private_name}] 构建聊天记录文本时属性错误: {e}") chat_history_text = "[获取聊天记录时出错]\n" @@ -571,7 +575,9 @@ class ActionPlanner: ) -> Tuple[str, str]: """处理结束对话前的告别决策""" logger.info(f"[私聊][{self.private_name}] 初步规划结束对话,进入告别决策...") - end_decision_prompt = PROMPT_END_DECISION.format(persona_text=persona_text, chat_history_text=chat_history_text,current_time_str=current_time_str) + end_decision_prompt = PROMPT_END_DECISION.format( + persona_text=persona_text, chat_history_text=chat_history_text, current_time_str=current_time_str + ) logger.debug(f"[私聊][{self.private_name}] 发送到LLM的结束决策提示词:\n------\n{end_decision_prompt}\n------") llm_start_time = time.time() end_content, _ = await self.llm.generate_response_async(end_decision_prompt) diff --git a/src/plugins/PFC/actions.py b/src/plugins/PFC/actions.py index 82ee4536..ea72df82 100644 --- a/src/plugins/PFC/actions.py +++ b/src/plugins/PFC/actions.py @@ -17,7 +17,8 @@ if TYPE_CHECKING: logger = get_logger("pfc_actions") -async def _send_reply_internal(conversation_instance: 'Conversation') -> bool: + +async def _send_reply_internal(conversation_instance: "Conversation") -> bool: """ 内部辅助函数,用于发送 conversation_instance.generated_reply 中的内容。 这之前是 Conversation 类中的 _send_reply 方法。 @@ -43,7 +44,7 @@ async def _send_reply_internal(conversation_instance: 'Conversation') -> bool: reply_to_message=None, # 私聊通常不需要引用回复 ) # 自身发言数量累计 +1 - if conversation_instance.conversation_info: # 确保 conversation_info 存在 + if conversation_instance.conversation_info: # 确保 conversation_info 存在 conversation_instance.conversation_info.my_message_count += 1 # 发送成功后,将状态设置回分析,准备下一轮规划 conversation_instance.state = ConversationState.ANALYZING @@ -55,10 +56,13 @@ async def _send_reply_internal(conversation_instance: 'Conversation') -> bool: conversation_instance.state = ConversationState.ERROR # 发送失败标记错误状态 return False # 返回失败 + async def handle_action( - conversation_instance: 'Conversation', action: str, reason: str, + conversation_instance: "Conversation", + action: str, + reason: str, observation_info: Optional[ObservationInfo], - conversation_info: Optional[ConversationInfo] + conversation_info: Optional[ConversationInfo], ): """ 处理由 ActionPlanner 规划出的具体行动。 @@ -73,19 +77,20 @@ async def handle_action( if not observation_info: logger.error(f"[私聊][{conversation_instance.private_name}] ObservationInfo 为空,无法处理动作 '{action}'。") # 在 conversation_info 和 done_action 存在时更新状态 - if conversation_info and hasattr(conversation_info, 'done_action') and conversation_info.done_action: - conversation_info.done_action[-1].update({ - "status": "error", - "final_reason": "ObservationInfo is None", - }) + if conversation_info and hasattr(conversation_info, "done_action") and conversation_info.done_action: + conversation_info.done_action[-1].update( + { + "status": "error", + "final_reason": "ObservationInfo is None", + } + ) conversation_instance.state = ConversationState.ERROR return - if not conversation_info: # conversation_info 在这里是必需的 + if not conversation_info: # conversation_info 在这里是必需的 logger.error(f"[私聊][{conversation_instance.private_name}] ConversationInfo 为空,无法处理动作 '{action}'。") conversation_instance.state = ConversationState.ERROR return - logger.info(f"[私聊][{conversation_instance.private_name}] 开始处理动作: {action}, 原因: {reason}") action_start_time = time.time() # 记录动作开始时间 @@ -98,7 +103,7 @@ async def handle_action( "final_reason": None, # 最终结果的原因,将在 finally 中设置 } # 安全地添加到历史记录列表 - if not hasattr(conversation_info, "done_action") or conversation_info.done_action is None: # 防御性检查 + if not hasattr(conversation_info, "done_action") or conversation_info.done_action is None: # 防御性检查 conversation_info.done_action = [] conversation_info.done_action.append(current_action_record) # 获取当前记录在列表中的索引,方便后续更新状态 @@ -108,15 +113,14 @@ async def handle_action( action_successful: bool = False # 标记动作是否成功执行 final_status: str = "recall" # 动作最终状态,默认为 recall (表示未成功或需重试) final_reason: str = "动作未成功执行" # 动作最终原因 - + # 在此声明变量以避免 UnboundLocalError is_suitable: bool = False generated_content_for_check_or_send: str = "" check_reason: str = "未进行检查" need_replan_from_checker: bool = False - should_send_reply: bool = True # 默认需要发送 (对于 direct_reply) - is_send_decision_from_rg: bool = False # 标记 send_new_message 的决策是否来自 ReplyGenerator - + should_send_reply: bool = True # 默认需要发送 (对于 direct_reply) + is_send_decision_from_rg: bool = False # 标记 send_new_message 的决策是否来自 ReplyGenerator try: # --- 根据不同的 action 类型执行相应的逻辑 --- @@ -126,7 +130,7 @@ async def handle_action( max_reply_attempts: int = getattr(global_config, "pfc_max_reply_attempts", 3) # 最多尝试次数 (可配置) reply_attempt_count: int = 0 # is_suitable, generated_content_for_check_or_send, check_reason, need_replan_from_checker, should_send_reply, is_send_decision_from_rg 已在外部声明 - + while reply_attempt_count < max_reply_attempts and not is_suitable and not need_replan_from_checker: reply_attempt_count += 1 log_prefix = f"[私聊][{conversation_instance.private_name}] 尝试生成/检查 '{action}' 回复 (第 {reply_attempt_count}/{max_reply_attempts} 次)..." @@ -135,16 +139,16 @@ async def handle_action( conversation_instance.state = ConversationState.GENERATING if not conversation_instance.reply_generator: raise RuntimeError("ReplyGenerator 未初始化") - + raw_llm_output = await conversation_instance.reply_generator.generate( observation_info, conversation_info, action_type=action ) logger.debug(f"{log_prefix} ReplyGenerator.generate 返回: '{raw_llm_output}'") - text_to_process = raw_llm_output # 默认情况下,处理原始输出 + text_to_process = raw_llm_output # 默认情况下,处理原始输出 if action == "send_new_message": - is_send_decision_from_rg = True # 标记这是 send_new_message 的决策过程 + is_send_decision_from_rg = True # 标记这是 send_new_message 的决策过程 parsed_json = None try: # 尝试解析JSON @@ -155,9 +159,9 @@ async def handle_action( conversation_info.last_reply_rejection_reason = "回复生成器未返回有效JSON" conversation_info.last_rejected_reply_content = raw_llm_output should_send_reply = False - text_to_process = "no" # 或者一个特定的错误标记 - - if parsed_json: # 如果成功解析 + text_to_process = "no" # 或者一个特定的错误标记 + + if parsed_json: # 如果成功解析 send_decision = parsed_json.get("send", "no").lower() generated_text_from_json = parsed_json.get("txt", "no") @@ -165,12 +169,12 @@ async def handle_action( should_send_reply = True text_to_process = generated_text_from_json logger.info(f"{log_prefix} ReplyGenerator 决定发送消息。内容: '{text_to_process[:100]}...'") - else: # send_decision is "no" + else: # send_decision is "no" should_send_reply = False - text_to_process = "no" # 保持和 prompt 中一致,txt 为 "no" + text_to_process = "no" # 保持和 prompt 中一致,txt 为 "no" logger.info(f"{log_prefix} ReplyGenerator 决定不发送消息。") # 既然RG决定不发送,就直接跳出重试循环 - break + break # 如果 ReplyGenerator 在 send_new_message 动作中决定不发送,则跳出重试循环 if action == "send_new_message" and not should_send_reply: @@ -179,23 +183,28 @@ async def handle_action( generated_content_for_check_or_send = text_to_process # 检查生成的内容是否有效 - if not generated_content_for_check_or_send or \ - generated_content_for_check_or_send.startswith("抱歉") or \ - generated_content_for_check_or_send.strip() == "" or \ - (action == "send_new_message" and generated_content_for_check_or_send == "no" and should_send_reply): # RG决定发送但文本为"no"或空 - + if ( + not generated_content_for_check_or_send + or generated_content_for_check_or_send.startswith("抱歉") + or generated_content_for_check_or_send.strip() == "" + or ( + action == "send_new_message" + and generated_content_for_check_or_send == "no" + and should_send_reply + ) + ): # RG决定发送但文本为"no"或空 warning_msg = f"{log_prefix} 生成内容无效或为错误提示" - if action == "send_new_message" and generated_content_for_check_or_send == "no": # 特殊情况日志 + if action == "send_new_message" and generated_content_for_check_or_send == "no": # 特殊情况日志 warning_msg += " (ReplyGenerator决定发送但文本为'no')" - + logger.warning(warning_msg + ",将进行下一次尝试 (如果适用)。") - check_reason = "生成内容无效或选择不发送" # 统一原因 + check_reason = "生成内容无效或选择不发送" # 统一原因 conversation_info.last_reply_rejection_reason = check_reason conversation_info.last_rejected_reply_content = generated_content_for_check_or_send - - await asyncio.sleep(0.5) # 暂停一下 - continue # 直接进入下一次循环尝试 - + + await asyncio.sleep(0.5) # 暂停一下 + continue # 直接进入下一次循环尝试 + # --- 内容检查 --- conversation_instance.state = ConversationState.CHECKING if not conversation_instance.reply_checker: @@ -203,132 +212,152 @@ async def handle_action( # 准备检查器所需参数 current_goal_str = "" - if conversation_info.goal_list: # 确保 goal_list 存在且不为空 + if conversation_info.goal_list: # 确保 goal_list 存在且不为空 goal_item = conversation_info.goal_list[-1] if isinstance(goal_item, dict): current_goal_str = goal_item.get("goal", "") elif isinstance(goal_item, str): current_goal_str = goal_item - + chat_history_for_check = getattr(observation_info, "chat_history", []) chat_history_text_for_check = getattr(observation_info, "chat_history_str", "") - current_retry_for_checker = reply_attempt_count - 1 # retry_count 从0开始 + current_retry_for_checker = reply_attempt_count - 1 # retry_count 从0开始 current_time_value_for_check = observation_info.current_time_str or "获取时间失败" # 调用检查器 if global_config.enable_pfc_reply_checker: logger.debug(f"{log_prefix} 调用 ReplyChecker 检查 (配置已启用)...") - is_suitable, check_reason, need_replan_from_checker = await conversation_instance.reply_checker.check( + ( + is_suitable, + check_reason, + need_replan_from_checker, + ) = await conversation_instance.reply_checker.check( reply=generated_content_for_check_or_send, goal=current_goal_str, - chat_history=chat_history_for_check, # 使用完整的历史记录列表 - chat_history_text=chat_history_text_for_check, # 可以是截断的文本 + chat_history=chat_history_for_check, # 使用完整的历史记录列表 + chat_history_text=chat_history_text_for_check, # 可以是截断的文本 current_time_str=current_time_value_for_check, - retry_count=current_retry_for_checker, # 传递当前重试次数 + retry_count=current_retry_for_checker, # 传递当前重试次数 ) logger.info( f"{log_prefix} ReplyChecker 结果: 合适={is_suitable}, 原因='{check_reason}', 需重规划={need_replan_from_checker}" ) - else: # 如果配置关闭 + else: # 如果配置关闭 is_suitable = True check_reason = "ReplyChecker 已通过配置关闭" need_replan_from_checker = False logger.info(f"{log_prefix} [配置关闭] ReplyChecker 已跳过,默认回复为合适。") - + # 处理检查结果 if not is_suitable: conversation_info.last_reply_rejection_reason = check_reason conversation_info.last_rejected_reply_content = generated_content_for_check_or_send - + # 如果是机器人自身复读,且检查器认为不需要重规划 (这是新版 ReplyChecker 的逻辑) if check_reason == "机器人尝试发送重复消息" and not need_replan_from_checker: - logger.warning(f"{log_prefix} 回复因自身重复被拒绝: {check_reason}。将使用相同 Prompt 类型重试。") - if reply_attempt_count < max_reply_attempts: # 还有尝试次数 - await asyncio.sleep(0.5) # 暂停一下 - continue # 进入下一次重试 - else: # 达到最大次数 + logger.warning( + f"{log_prefix} 回复因自身重复被拒绝: {check_reason}。将使用相同 Prompt 类型重试。" + ) + if reply_attempt_count < max_reply_attempts: # 还有尝试次数 + await asyncio.sleep(0.5) # 暂停一下 + continue # 进入下一次重试 + else: # 达到最大次数 logger.warning(f"{log_prefix} 即使是复读,也已达到最大尝试次数。") - break # 结束循环,按失败处理 - elif not need_replan_from_checker and reply_attempt_count < max_reply_attempts: # 其他不合适原因,但无需重规划,且可重试 + break # 结束循环,按失败处理 + elif ( + not need_replan_from_checker and reply_attempt_count < max_reply_attempts + ): # 其他不合适原因,但无需重规划,且可重试 logger.warning(f"{log_prefix} 回复不合适,原因: {check_reason}。将进行下一次尝试。") - await asyncio.sleep(0.5) # 暂停一下 - continue # 进入下一次重试 - else: # 需要重规划,或达到最大次数 + await asyncio.sleep(0.5) # 暂停一下 + continue # 进入下一次重试 + else: # 需要重规划,或达到最大次数 logger.warning(f"{log_prefix} 回复不合适且(需要重规划或已达最大次数)。原因: {check_reason}") - break # 结束循环,将在循环外部处理 - else: # is_suitable is True + break # 结束循环,将在循环外部处理 + else: # is_suitable is True # 找到了合适的回复 - conversation_info.last_reply_rejection_reason = None # 清除之前的拒绝原因 + conversation_info.last_reply_rejection_reason = None # 清除之前的拒绝原因 conversation_info.last_rejected_reply_content = None - break # 成功,跳出循环 - + break # 成功,跳出循环 + # --- 循环结束后处理 --- if action == "send_new_message" and not should_send_reply and is_send_decision_from_rg: # 这是 reply_generator 决定不发送的情况 - logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': ReplyGenerator 决定不发送消息。") - final_status = "done_no_reply" # 一个新的状态,表示动作完成但无回复 + logger.info( + f"[私聊][{conversation_instance.private_name}] 动作 '{action}': ReplyGenerator 决定不发送消息。" + ) + final_status = "done_no_reply" # 一个新的状态,表示动作完成但无回复 final_reason = "回复生成器决定不发送消息" - action_successful = True # 动作本身(决策)是成功的 + action_successful = True # 动作本身(决策)是成功的 # 清除追问状态,因为没有实际发送 conversation_info.last_successful_reply_action = None - conversation_info.my_message_count = 0 # 重置连续发言计数 + conversation_info.my_message_count = 0 # 重置连续发言计数 # 后续的 plan 循环会检测到这个 "done_no_reply" 状态并使用反思 prompt - elif is_suitable: # 适用于 direct_reply 或 (send_new_message 且 RG决定发送并通过检查) + elif is_suitable: # 适用于 direct_reply 或 (send_new_message 且 RG决定发送并通过检查) logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 找到合适的回复,准备发送。") # conversation_info.last_reply_rejection_reason = None # 已在循环内清除 # conversation_info.last_rejected_reply_content = None - conversation_instance.generated_reply = generated_content_for_check_or_send # 使用检查通过的内容 + conversation_instance.generated_reply = generated_content_for_check_or_send # 使用检查通过的内容 timestamp_before_sending = time.time() logger.debug( f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 记录发送前时间戳: {timestamp_before_sending:.2f}" ) conversation_instance.state = ConversationState.SENDING - send_success = await _send_reply_internal(conversation_instance) # 调用重构后的发送函数 - send_end_time = time.time() # 记录发送完成时间 + send_success = await _send_reply_internal(conversation_instance) # 调用重构后的发送函数 + send_end_time = time.time() # 记录发送完成时间 if send_success: action_successful = True - final_status = "done" # 明确设置 final_status + final_status = "done" # 明确设置 final_status final_reason = "成功发送" # 明确设置 final_reason logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 成功发送回复.") - + # --- 新增:将机器人发送的消息添加到 ObservationInfo 的 chat_history --- - if observation_info and conversation_instance.bot_qq_str: # 确保 observation_info 和 bot_qq_str 存在 + if ( + observation_info and conversation_instance.bot_qq_str + ): # 确保 observation_info 和 bot_qq_str 存在 bot_message_dict = { - "message_id": f"bot_sent_{send_end_time}", # 生成一个唯一ID + "message_id": f"bot_sent_{send_end_time}", # 生成一个唯一ID "time": send_end_time, - "user_info": { # 构造机器人的 UserInfo + "user_info": { # 构造机器人的 UserInfo "user_id": conversation_instance.bot_qq_str, - "user_nickname": global_config.BOT_NICKNAME, # 或者 conversation_instance.name - "platform": conversation_instance.chat_stream.platform if conversation_instance.chat_stream else "unknown_platform" + "user_nickname": global_config.BOT_NICKNAME, # 或者 conversation_instance.name + "platform": conversation_instance.chat_stream.platform + if conversation_instance.chat_stream + else "unknown_platform", }, "processed_plain_text": conversation_instance.generated_reply, - "detailed_plain_text": conversation_instance.generated_reply, # 简单处理 + "detailed_plain_text": conversation_instance.generated_reply, # 简单处理 # 根据你的消息字典结构,可能还需要其他字段 } observation_info.chat_history.append(bot_message_dict) observation_info.chat_history_count = len(observation_info.chat_history) - logger.debug(f"[私聊][{conversation_instance.private_name}] 机器人发送的消息已添加到 chat_history。当前历史数: {observation_info.chat_history_count}") - + logger.debug( + f"[私聊][{conversation_instance.private_name}] 机器人发送的消息已添加到 chat_history。当前历史数: {observation_info.chat_history_count}" + ) + # 可选:如果 chat_history 过长,进行修剪 (例如,保留最近N条) - max_history_len = getattr(global_config, 'pfc_max_chat_history_for_checker', 50) # 例如,可配置 + max_history_len = getattr(global_config, "pfc_max_chat_history_for_checker", 50) # 例如,可配置 if len(observation_info.chat_history) > max_history_len: observation_info.chat_history = observation_info.chat_history[-max_history_len:] - observation_info.chat_history_count = len(observation_info.chat_history) # 更新计数 + observation_info.chat_history_count = len(observation_info.chat_history) # 更新计数 # 更新 chat_history_str (如果 ReplyChecker 也依赖这个字符串) # 这个更新可能比较消耗资源,如果 checker 只用列表,可以考虑优化此处 - history_slice_for_str = observation_info.chat_history[-30:] # 例如最近30条 + history_slice_for_str = observation_info.chat_history[-30:] # 例如最近30条 try: observation_info.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 + replace_bot_name=True, + merge_messages=False, + timestamp_mode="relative", + read_mark=0.0, ) except Exception as e_build_hist: - logger.error(f"[私聊][{conversation_instance.private_name}] 更新 chat_history_str 时出错: {e_build_hist}") + logger.error( + f"[私聊][{conversation_instance.private_name}] 更新 chat_history_str 时出错: {e_build_hist}" + ) observation_info.chat_history_str = "[构建聊天记录出错]" # --- 新增结束 --- @@ -342,17 +371,17 @@ async def handle_action( for msg in current_unprocessed_messages: msg_time = msg.get("time") msg_id = msg.get("message_id") - sender_id_info = msg.get("user_info", {}) # 安全获取 user_info - sender_id = str(sender_id_info.get("user_id")) if sender_id_info else None # 安全获取 sender_id - + sender_id_info = msg.get("user_info", {}) # 安全获取 user_info + sender_id = str(sender_id_info.get("user_id")) if sender_id_info else None # 安全获取 sender_id + if ( - msg_id # 确保 msg_id 存在 - and msg_time # 确保 msg_time 存在 - and sender_id != conversation_instance.bot_qq_str # 确保是对方的消息 - and msg_time < timestamp_before_sending # 只清理发送前的 + msg_id # 确保 msg_id 存在 + and msg_time # 确保 msg_time 存在 + and sender_id != conversation_instance.bot_qq_str # 确保是对方的消息 + and msg_time < timestamp_before_sending # 只清理发送前的 ): message_ids_to_clear.add(msg_id) - + if message_ids_to_clear: logger.debug( f"[私聊][{conversation_instance.private_name}] 准备清理 {len(message_ids_to_clear)} 条发送前(他人)消息: {message_ids_to_clear}" @@ -373,59 +402,65 @@ async def handle_action( ) conversation_info.last_successful_reply_action = None # conversation_info.my_message_count 不在此处重置,因为它刚发了一条 - elif action == "direct_reply" or action == "send_new_message": # 成功发送后 + elif action == "direct_reply" or action == "send_new_message": # 成功发送后 logger.info( f"[私聊][{conversation_instance.private_name}] 成功执行 '{action}', 下一轮【允许】使用追问逻辑。" ) conversation_info.last_successful_reply_action = action - + # 更新实例消息计数和关系/情绪 - if conversation_info: # 再次确认 + if conversation_info: # 再次确认 conversation_info.current_instance_message_count += 1 - logger.debug(f"[私聊][{conversation_instance.private_name}] 实例消息计数(机器人发送后)增加到: {conversation_info.current_instance_message_count}") - - if conversation_instance.relationship_updater: # 确保存在 + logger.debug( + f"[私聊][{conversation_instance.private_name}] 实例消息计数(机器人发送后)增加到: {conversation_info.current_instance_message_count}" + ) + + if conversation_instance.relationship_updater: # 确保存在 await conversation_instance.relationship_updater.update_relationship_incremental( conversation_info=conversation_info, observation_info=observation_info, - chat_observer_for_history=conversation_instance.chat_observer # 确保 chat_observer 存在 + chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 ) - - sent_reply_summary = conversation_instance.generated_reply[:50] if conversation_instance.generated_reply else "空回复" + + sent_reply_summary = ( + conversation_instance.generated_reply[:50] + if conversation_instance.generated_reply + else "空回复" + ) event_for_emotion_update = f"你刚刚发送了消息: '{sent_reply_summary}...'" - if conversation_instance.emotion_updater: # 确保存在 + if conversation_instance.emotion_updater: # 确保存在 await conversation_instance.emotion_updater.update_emotion_based_on_context( conversation_info=conversation_info, observation_info=observation_info, - chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 - event_description=event_for_emotion_update + chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 + event_description=event_for_emotion_update, ) - else: # 发送失败 + else: # 发送失败 logger.error(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 发送回复失败。") - final_status = "recall" # 标记为 recall 或 error + final_status = "recall" # 标记为 recall 或 error final_reason = "发送回复时失败" - action_successful = False # 确保 action_successful 为 False + action_successful = False # 确保 action_successful 为 False # 发送失败,重置追问状态和计数 conversation_info.last_successful_reply_action = None - conversation_info.my_message_count = 0 + conversation_info.my_message_count = 0 - elif need_replan_from_checker: # 如果检查器要求重规划 + elif need_replan_from_checker: # 如果检查器要求重规划 logger.warning( f"[私聊][{conversation_instance.private_name}] 动作 '{action}' 因 ReplyChecker 要求而被取消,将重新规划。原因: {check_reason}" ) - final_status = "recall" # 标记为 recall + final_status = "recall" # 标记为 recall final_reason = f"回复检查要求重新规划: {check_reason}" # 重置追问状态,因为没有成功发送 conversation_info.last_successful_reply_action = None # my_message_count 保持不变,因为没有成功发送 - else: # 达到最大尝试次数仍未找到合适回复 (is_suitable is False and not need_replan_from_checker) + else: # 达到最大尝试次数仍未找到合适回复 (is_suitable is False and not need_replan_from_checker) logger.warning( f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 达到最大尝试次数 ({max_reply_attempts}),未能生成/检查通过合适的回复。最终原因: {check_reason}" ) - final_status = "recall" # 标记为 recall + final_status = "recall" # 标记为 recall final_reason = f"尝试{max_reply_attempts}次后失败: {check_reason}" - action_successful = False # 确保 action_successful 为 False + action_successful = False # 确保 action_successful 为 False # 重置追问状态 conversation_info.last_successful_reply_action = None # my_message_count 保持不变 @@ -437,17 +472,23 @@ async def handle_action( raise RuntimeError("ReplyGenerator 未初始化") # 生成告别语 generated_content = await conversation_instance.reply_generator.generate( - observation_info, conversation_info, action_type=action # action_type='say_goodbye' + observation_info, + conversation_info, + action_type=action, # action_type='say_goodbye' + ) + logger.info( + f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 生成内容: '{generated_content[:100]}...'" ) - logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 生成内容: '{generated_content[:100]}...'") # 检查生成内容 if not generated_content or generated_content.startswith("抱歉"): - logger.warning(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 生成内容为空或为错误提示,取消发送。") + logger.warning( + f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 生成内容为空或为错误提示,取消发送。" + ) final_reason = "生成内容无效" # 即使生成失败,也按计划结束对话 final_status = "done" # 标记为 done,因为目的是结束 - conversation_instance.should_continue = False # 停止对话 + conversation_instance.should_continue = False # 停止对话 logger.info(f"[私聊][{conversation_instance.private_name}] 告别语生成失败,仍按计划结束对话。") else: # 发送告别语 @@ -457,7 +498,7 @@ async def handle_action( f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 记录发送前时间戳: {timestamp_before_sending:.2f}" ) conversation_instance.state = ConversationState.SENDING - send_success = await _send_reply_internal(conversation_instance) # 调用重构后的发送函数 + send_success = await _send_reply_internal(conversation_instance) # 调用重构后的发送函数 send_end_time = time.time() if send_success: @@ -478,36 +519,42 @@ async def handle_action( if ( msg_id and msg_time - and sender_id != conversation_instance.bot_qq_str # 不是自己的消息 - and msg_time < timestamp_before_sending # 发送前 + and sender_id != conversation_instance.bot_qq_str # 不是自己的消息 + and msg_time < timestamp_before_sending # 发送前 ): message_ids_to_clear.add(msg_id) if message_ids_to_clear: await observation_info.clear_processed_messages(message_ids_to_clear) # 更新关系和情绪 - if conversation_info: # 确保 conversation_info 存在 + if conversation_info: # 确保 conversation_info 存在 conversation_info.current_instance_message_count += 1 - logger.debug(f"[私聊][{conversation_instance.private_name}] 实例消息计数(告别语后)增加到: {conversation_info.current_instance_message_count}") - - sent_reply_summary = conversation_instance.generated_reply[:50] if conversation_instance.generated_reply else "空回复" + logger.debug( + f"[私聊][{conversation_instance.private_name}] 实例消息计数(告别语后)增加到: {conversation_info.current_instance_message_count}" + ) + + sent_reply_summary = ( + conversation_instance.generated_reply[:50] + if conversation_instance.generated_reply + else "空回复" + ) event_for_emotion_update = f"你发送了告别消息: '{sent_reply_summary}...'" - if conversation_instance.emotion_updater: # 确保存在 + if conversation_instance.emotion_updater: # 确保存在 await conversation_instance.emotion_updater.update_emotion_based_on_context( conversation_info=conversation_info, observation_info=observation_info, - chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 - event_description=event_for_emotion_update + chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 + event_description=event_for_emotion_update, ) # 发送成功后结束对话 conversation_instance.should_continue = False else: # 发送失败 logger.error(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 发送告别语失败。") - final_status = "recall" # 或 "error" + final_status = "recall" # 或 "error" final_reason = "发送告别语失败" # 发送失败不能结束对话,让其自然流转或由其他逻辑结束 - conversation_instance.should_continue = True # 保持 should_continue + conversation_instance.should_continue = True # 保持 should_continue # 3. 处理重新思考目标动作 elif action == "rethink_goal": @@ -518,12 +565,14 @@ async def handle_action( await conversation_instance.goal_analyzer.analyze_goal(conversation_info, observation_info) action_successful = True # 标记成功 event_for_emotion_update = "你重新思考了对话目标和方向" - if conversation_instance.emotion_updater and conversation_info and observation_info: # 确保updater和info都存在 + if ( + conversation_instance.emotion_updater and conversation_info and observation_info + ): # 确保updater和info都存在 await conversation_instance.emotion_updater.update_emotion_based_on_context( conversation_info=conversation_info, observation_info=observation_info, - chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 - event_description=event_for_emotion_update + chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 + event_description=event_for_emotion_update, ) # 4. 处理倾听动作 @@ -533,20 +582,22 @@ async def handle_action( raise RuntimeError("Waiter 未初始化") logger.info(f"[私聊][{conversation_instance.private_name}] 动作 'listening': 进入倾听状态...") # 调用 Waiter 的倾听等待方法,内部会处理超时 - await conversation_instance.waiter.wait_listening(conversation_info) # 直接传递 conversation_info + await conversation_instance.waiter.wait_listening(conversation_info) # 直接传递 conversation_info action_successful = True # listening 动作本身执行即视为成功,后续由新消息或超时驱动 event_for_emotion_update = "你决定耐心倾听对方的发言" - if conversation_instance.emotion_updater and conversation_info and observation_info: # 确保都存在 + if conversation_instance.emotion_updater and conversation_info and observation_info: # 确保都存在 await conversation_instance.emotion_updater.update_emotion_based_on_context( conversation_info=conversation_info, observation_info=observation_info, - chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 - event_description=event_for_emotion_update + chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 + event_description=event_for_emotion_update, ) # 5. 处理结束对话动作 elif action == "end_conversation": - logger.info(f"[私聊][{conversation_instance.private_name}] 动作 'end_conversation': 收到最终结束指令,停止对话...") + logger.info( + f"[私聊][{conversation_instance.private_name}] 动作 'end_conversation': 收到最终结束指令,停止对话..." + ) action_successful = True # 标记成功 conversation_instance.should_continue = False # 设置标志以退出循环 @@ -561,12 +612,12 @@ async def handle_action( conversation_instance.state = ConversationState.IGNORED # 设置忽略状态 action_successful = True # 标记成功 event_for_emotion_update = "当前对话让你感到不适,你决定暂时不再理会对方" - if conversation_instance.emotion_updater and conversation_info and observation_info: # 确保都存在 + if conversation_instance.emotion_updater and conversation_info and observation_info: # 确保都存在 await conversation_instance.emotion_updater.update_emotion_based_on_context( conversation_info=conversation_info, observation_info=observation_info, - chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 - event_description=event_for_emotion_update + chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 + event_description=event_for_emotion_update, ) # 7. 处理等待动作 @@ -577,20 +628,20 @@ async def handle_action( logger.info(f"[私聊][{conversation_instance.private_name}] 动作 'wait': 进入等待状态...") # 调用 Waiter 的常规等待方法,内部处理超时 # wait 方法返回是否超时 (True=超时, False=未超时/被新消息中断) - timeout_occurred = await conversation_instance.waiter.wait(conversation_info) # 直接传递 conversation_info + timeout_occurred = await conversation_instance.waiter.wait(conversation_info) # 直接传递 conversation_info action_successful = True # wait 动作本身执行即视为成功 event_for_emotion_update = "" - if timeout_occurred: # 假设 timeout_occurred 能正确反映是否超时 + if timeout_occurred: # 假设 timeout_occurred 能正确反映是否超时 event_for_emotion_update = "你等待对方回复,但对方长时间没有回应" else: event_for_emotion_update = "你选择等待对方的回复(对方可能很快回复了)" - - if conversation_instance.emotion_updater and conversation_info and observation_info: # 确保都存在 + + if conversation_instance.emotion_updater and conversation_info and observation_info: # 确保都存在 await conversation_instance.emotion_updater.update_emotion_based_on_context( conversation_info=conversation_info, observation_info=observation_info, - chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 - event_description=event_for_emotion_update + chat_observer_for_history=conversation_instance.chat_observer, # 确保 chat_observer 存在 + event_description=event_for_emotion_update, ) # wait 动作完成后不需要清理消息,等待新消息或超时触发重新规划 logger.debug(f"[私聊][{conversation_instance.private_name}] Wait 动作完成,无需在此清理消息。") @@ -615,7 +666,7 @@ async def handle_action( final_status = "cancelled" final_reason = "动作处理被取消" # 取消时也重置追问状态 - if conversation_info : # 确保 conversation_info 存在 + if conversation_info: # 确保 conversation_info 存在 conversation_info.last_successful_reply_action = None raise # 重新抛出 CancelledError,让上层知道任务被取消 except Exception as handle_err: @@ -626,14 +677,14 @@ async def handle_action( final_reason = f"处理动作时出错: {handle_err}" conversation_instance.state = ConversationState.ERROR # 设置对话状态为错误 # 出错时重置追问状态 - if conversation_info: # 确保 conversation_info 存在 + if conversation_info: # 确保 conversation_info 存在 conversation_info.last_successful_reply_action = None finally: # --- 无论成功与否,都执行 --- # 1. 重置临时存储的计数值 - if conversation_info: # 确保 conversation_info 存在 + if conversation_info: # 确保 conversation_info 存在 conversation_info.other_new_messages_during_planning_count = 0 # 2. 更新动作历史记录的最终状态和原因 @@ -642,15 +693,21 @@ async def handle_action( # 如果动作标记为成功,但 final_status 仍然是初始的 "recall" 或者 "start" # (因为可能在try块中成功执行了但没有显式更新 final_status 为 "done") # 或者是 "done_no_reply" 这种特殊的成功状态 - if final_status in ["recall", "start"] and action != "send_new_message": # send_new_message + no_reply 是特殊成功 + if ( + final_status in ["recall", "start"] and action != "send_new_message" + ): # send_new_message + no_reply 是特殊成功 final_status = "done" - if not final_reason or final_reason == "动作未成功执行": # 避免覆盖已有的具体成功原因 + if not final_reason or final_reason == "动作未成功执行": # 避免覆盖已有的具体成功原因 # 为不同类型的成功动作提供更具体的默认成功原因 if action == "wait": # 检查 conversation_info.goal_list 是否存在且不为空 timeout_occurred = ( - any("分钟," in g.get("goal", "") for g in conversation_info.goal_list if isinstance(g, dict)) - if conversation_info and conversation_info.goal_list + any( + "分钟," in g.get("goal", "") + for g in conversation_info.goal_list + if isinstance(g, dict) + ) + if conversation_info and conversation_info.goal_list else False ) final_reason = "等待完成" + (" (超时)" if timeout_occurred else " (收到新消息或中断)") @@ -658,32 +715,38 @@ async def handle_action( final_reason = "进入倾听状态" elif action in ["rethink_goal", "end_conversation", "block_and_ignore", "say_goodbye"]: final_reason = f"成功执行 {action}" - elif action in ["direct_reply", "send_new_message"]: # 正常发送成功的case + elif action in ["direct_reply", "send_new_message"]: # 正常发送成功的case final_reason = "成功发送" else: final_reason = f"动作 {action} 成功完成" # 如果已经是 "done" 或 "done_no_reply",则保留它们和它们对应的 final_reason - - else: # action_successful is False + + else: # action_successful is False # 如果动作标记为失败,且 final_status 还是 "recall" (初始值) 或 "start" if final_status in ["recall", "start"]: # 尝试从 conversation_info 中获取更具体的失败原因(例如 checker 的原因) # 这个 specific_rejection_reason 是在 try 块中被设置的 - specific_rejection_reason = getattr(conversation_info, 'last_reply_rejection_reason', None) - rejected_content = getattr(conversation_info, 'last_rejected_reply_content', None) + specific_rejection_reason = getattr(conversation_info, "last_reply_rejection_reason", None) + rejected_content = getattr(conversation_info, "last_rejected_reply_content", None) - if specific_rejection_reason: # 如果有更具体的原因 + if specific_rejection_reason: # 如果有更具体的原因 final_reason = f"执行失败: {specific_rejection_reason}" - if rejected_content and specific_rejection_reason == "机器人尝试发送重复消息": # 对复读提供更清晰的日志 + if ( + rejected_content and specific_rejection_reason == "机器人尝试发送重复消息" + ): # 对复读提供更清晰的日志 final_reason += f" (内容: '{rejected_content[:30]}...')" - elif not final_reason or final_reason == "动作未成功执行": # 如果没有更具体的原因,且当前原因还是默认的 + elif not final_reason or final_reason == "动作未成功执行": # 如果没有更具体的原因,且当前原因还是默认的 final_reason = f"动作 {action} 执行失败或被意外中止" # 如果 final_status 已经是 "error" 或 "cancelled",则保留它们和它们对应的 final_reason # 更新 done_action 中的记录 # 防御性检查,确保 conversation_info, done_action 存在,并且索引有效 - if conversation_info and hasattr(conversation_info, 'done_action') and \ - conversation_info.done_action and action_index < len(conversation_info.done_action): + if ( + conversation_info + and hasattr(conversation_info, "done_action") + and conversation_info.done_action + and action_index < len(conversation_info.done_action) + ): conversation_info.done_action[action_index].update( { "status": final_status, @@ -693,14 +756,22 @@ async def handle_action( } ) else: - logger.error(f"[私聊][{conversation_instance.private_name}] 无法更新动作历史记录,索引 {action_index} 无效或列表为空。") - + logger.error( + f"[私聊][{conversation_instance.private_name}] 无法更新动作历史记录,索引 {action_index} 无效或列表为空。" + ) + # 最终日志输出 log_final_reason = final_reason if final_reason else "无明确原因" # 为成功发送的动作添加发送内容摘要 - if final_status == "done" and action_successful and \ - action in ["direct_reply", "send_new_message"] and \ - hasattr(conversation_instance, 'generated_reply') and conversation_instance.generated_reply: - log_final_reason += f" (发送内容: '{conversation_instance.generated_reply[:30]}...')" + if ( + final_status == "done" + and action_successful + and action in ["direct_reply", "send_new_message"] + and hasattr(conversation_instance, "generated_reply") + and conversation_instance.generated_reply + ): + log_final_reason += f" (发送内容: '{conversation_instance.generated_reply[:30]}...')" - logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}' 处理完成。最终状态: {final_status}, 原因: {log_final_reason}") \ No newline at end of file + logger.info( + f"[私聊][{conversation_instance.private_name}] 动作 '{action}' 处理完成。最终状态: {final_status}, 原因: {log_final_reason}" + ) diff --git a/src/plugins/PFC/chat_observer.py b/src/plugins/PFC/chat_observer.py index f4f43fea..c8fd280f 100644 --- a/src/plugins/PFC/chat_observer.py +++ b/src/plugins/PFC/chat_observer.py @@ -101,17 +101,19 @@ class ChatObserver: message: 消息数据 """ try: - if isinstance(message, dict): # 确保是字典才添加 + if isinstance(message, dict): # 确保是字典才添加 self.message_cache.append(message) # 可选:限制 message_cache 的大小,例如只保留最近 N 条 # 你可以根据你的需求调整 MAX_CACHE_SIZE # 对于情绪判断,可能不需要太长的历史,例如 5-10 条可能就足够了 # 但 ChatObserver 的 get_cached_messages 也可能被其他地方用于获取更长的历史 # 所以这里的 MAX_CACHE_SIZE 需要权衡,或者让调用者自己决定 limit - MAX_CACHE_SIZE = 30 # 例如,保留最近30条作为通用缓存 + MAX_CACHE_SIZE = 30 # 例如,保留最近30条作为通用缓存 if len(self.message_cache) > MAX_CACHE_SIZE: self.message_cache = self.message_cache[-MAX_CACHE_SIZE:] - logger.debug(f"[私聊][{self.private_name}] 消息已添加到 ChatObserver 缓存,当前缓存大小: {len(self.message_cache)}") + logger.debug( + f"[私聊][{self.private_name}] 消息已添加到 ChatObserver 缓存,当前缓存大小: {len(self.message_cache)}" + ) else: logger.warning(f"[私聊][{self.private_name}] 尝试向 message_cache 添加非字典类型消息: {type(message)}") diff --git a/src/plugins/PFC/conversation.py b/src/plugins/PFC/conversation.py index e95f21d6..4a3c9e02 100644 --- a/src/plugins/PFC/conversation.py +++ b/src/plugins/PFC/conversation.py @@ -6,7 +6,7 @@ from typing import Dict, Any, Optional from src.common.logger_manager import get_logger from maim_message import UserInfo from src.plugins.chat.chat_stream import chat_manager, ChatStream -from ..chat.message import Message # 假设 Message 类型被 _convert_to_message 使用 +from ..chat.message import Message # 假设 Message 类型被 _convert_to_message 使用 from src.config.config import global_config from ..person_info.person_info import person_info_manager from ..person_info.relationship_manager import relationship_manager @@ -30,9 +30,10 @@ from .waiter import Waiter from .reply_checker import ReplyChecker # >>> 新增导入 <<< -from .conversation_loop import run_conversation_loop # 导入新的循环函数 +from .conversation_loop import run_conversation_loop # 导入新的循环函数 from rich.traceback import install + install(extra_lines=3) logger = get_logger("pfc_conversation") @@ -44,6 +45,7 @@ logger = get_logger("pfc_conversation") # logger.error(f"配置的时区 '{configured_tz}' 无效,将使用默认时区 'Asia/Shanghai'") # TIME_ZONE = tz.gettz('Asia/Shanghai') + class Conversation: """ 对话类,负责管理单个私聊对话的状态和核心逻辑流程。 @@ -57,7 +59,7 @@ class Conversation: self.stream_id: str = stream_id self.private_name: str = private_name self.state: ConversationState = ConversationState.INIT - self.should_continue: bool = False # Manager 会在初始化后设置 + self.should_continue: bool = False # Manager 会在初始化后设置 self.ignore_until_timestamp: Optional[float] = None self.generated_reply: str = "" self.chat_stream: Optional[ChatStream] = None @@ -81,7 +83,7 @@ class Conversation: self.conversation_info: Optional[ConversationInfo] = None self.reply_checker: Optional[ReplyChecker] = None - self._initialized: bool = False # Manager 会在初始化成功后设为 True + self._initialized: bool = False # Manager 会在初始化成功后设为 True self.bot_qq_str: Optional[str] = str(global_config.BOT_QQ) if global_config.BOT_QQ else None if not self.bot_qq_str: @@ -98,9 +100,7 @@ class Conversation: return if not self.should_continue: - logger.warning( - f"[私聊][{self.private_name}] 对话实例已被 Manager 标记为不应继续,无法启动规划循环。" - ) + logger.warning(f"[私聊][{self.private_name}] 对话实例已被 Manager 标记为不应继续,无法启动规划循环。") return logger.info(f"[私聊][{self.private_name}] 对话系统启动,准备创建规划循环任务...") @@ -120,14 +120,19 @@ class Conversation: self.should_continue = False # 最终关系评估 - if self._initialized and self.relationship_updater and self.conversation_info and \ - self.observation_info and self.chat_observer: + if ( + self._initialized + and self.relationship_updater + and self.conversation_info + and self.observation_info + and self.chat_observer + ): try: logger.info(f"[私聊][{self.private_name}] 准备执行最终关系评估...") await self.relationship_updater.update_relationship_final( conversation_info=self.conversation_info, observation_info=self.observation_info, - chat_observer_for_history=self.chat_observer + chat_observer_for_history=self.chat_observer, ) logger.info(f"[私聊][{self.private_name}] 最终关系评估已调用。") except Exception as e_final_rel: @@ -141,11 +146,11 @@ class Conversation: self.idle_conversation_starter.stop() if self.observation_info and self.chat_observer: self.observation_info.unbind_from_chat_observer() - if self.mood_mng and hasattr(self.mood_mng, 'stop_mood_update') and self.mood_mng._running: # type: ignore - self.mood_mng.stop_mood_update() # type: ignore + if self.mood_mng and hasattr(self.mood_mng, "stop_mood_update") and self.mood_mng._running: # type: ignore + self.mood_mng.stop_mood_update() # type: ignore logger.info(f"[私聊][{self.private_name}] MoodManager 后台更新已停止。") - self._initialized = False # 标记为未初始化 + self._initialized = False # 标记为未初始化 logger.info(f"[私聊][{self.private_name}] 对话实例 {self.stream_id} 已停止。") # _plan_and_action_loop 方法已被移除 @@ -156,7 +161,9 @@ class Conversation: try: chat_stream_to_use = self.chat_stream or chat_manager.get_stream(self.stream_id) if not chat_stream_to_use: - logger.error(f"[私聊][{self.private_name}] 无法确定 ChatStream for stream_id {self.stream_id},无法转换消息。") + logger.error( + f"[私聊][{self.private_name}] 无法确定 ChatStream for stream_id {self.stream_id},无法转换消息。" + ) return None user_info_dict = msg_dict.get("user_info", {}) @@ -165,9 +172,13 @@ class Conversation: try: user_info = UserInfo.from_dict(user_info_dict) except Exception as e: - logger.warning(f"[私聊][{self.private_name}] 从字典创建 UserInfo 时出错: {e}, dict: {user_info_dict}") + logger.warning( + f"[私聊][{self.private_name}] 从字典创建 UserInfo 时出错: {e}, dict: {user_info_dict}" + ) if not user_info: - logger.warning(f"[私聊][{self.private_name}] 消息缺少有效的 UserInfo,无法转换。 msg_id: {msg_dict.get('message_id')}") + logger.warning( + f"[私聊][{self.private_name}] 消息缺少有效的 UserInfo,无法转换。 msg_id: {msg_dict.get('message_id')}" + ) return None return Message( @@ -181,4 +192,4 @@ class Conversation: except Exception as e: logger.error(f"[私聊][{self.private_name}] 转换消息时出错: {e}") logger.error(f"[私聊][{self.private_name}] {traceback.format_exc()}") - return None \ No newline at end of file + return None diff --git a/src/plugins/PFC/conversation_info.py b/src/plugins/PFC/conversation_info.py index 9cfa7ff9..0e7a5138 100644 --- a/src/plugins/PFC/conversation_info.py +++ b/src/plugins/PFC/conversation_info.py @@ -3,18 +3,18 @@ from typing import Optional, List, Dict, Any class ConversationInfo: def __init__(self): - self.done_action: List[Dict[str, Any]] = [] # 建议明确类型 - self.goal_list: List[Dict[str, Any]] = [] # 建议明确类型 - self.knowledge_list: List[Any] = [] # 建议明确类型 - self.memory_list: List[Any] = [] # 建议明确类型 + self.done_action: List[Dict[str, Any]] = [] # 建议明确类型 + self.goal_list: List[Dict[str, Any]] = [] # 建议明确类型 + self.knowledge_list: List[Any] = [] # 建议明确类型 + self.memory_list: List[Any] = [] # 建议明确类型 self.last_successful_reply_action: Optional[str] = None self.last_reply_rejection_reason: Optional[str] = None # 用于存储上次回复被拒原因 self.last_rejected_reply_content: Optional[str] = None # 用于存储上次被拒的回复内容 self.my_message_count: int = 0 # 用于存储连续发送了多少条消息 # --- 新增字段 --- - self.person_id: Optional[str] = None # 私聊对象的唯一ID - self.relationship_text: Optional[str] = "你们还不熟悉。" # 与当前对话者的关系描述文本 - self.current_emotion_text: Optional[str] = "心情平静。" # 机器人当前的情绪描述文本 - self.current_instance_message_count: int = 0 # 当前私聊实例中的消息计数 - # --- 新增字段结束 --- \ No newline at end of file + self.person_id: Optional[str] = None # 私聊对象的唯一ID + self.relationship_text: Optional[str] = "你们还不熟悉。" # 与当前对话者的关系描述文本 + self.current_emotion_text: Optional[str] = "心情平静。" # 机器人当前的情绪描述文本 + self.current_instance_message_count: int = 0 # 当前私聊实例中的消息计数 + # --- 新增字段结束 --- diff --git a/src/plugins/PFC/conversation_initializer.py b/src/plugins/PFC/conversation_initializer.py index 3f7dd394..472c1291 100644 --- a/src/plugins/PFC/conversation_initializer.py +++ b/src/plugins/PFC/conversation_initializer.py @@ -18,7 +18,7 @@ from .observation_info import ObservationInfo from .conversation_info import ConversationInfo from .reply_generator import ReplyGenerator from .idle_conversation_starter import IdleConversationStarter -from .pfc_KnowledgeFetcher import KnowledgeFetcher # 修正大小写 +from .pfc_KnowledgeFetcher import KnowledgeFetcher # 修正大小写 from .waiter import Waiter from .pfc_utils import get_person_id from .reply_checker import ReplyChecker @@ -31,17 +31,20 @@ if TYPE_CHECKING: logger = get_logger("pfc_initializer") -async def load_initial_history(conversation_instance: 'Conversation'): + +async def load_initial_history(conversation_instance: "Conversation"): """ 加载并处理初始的聊天记录。 之前是 Conversation 类中的 _load_initial_history 方法。 """ - if not conversation_instance.observation_info: # 确保 ObservationInfo 已初始化 + if not conversation_instance.observation_info: # 确保 ObservationInfo 已初始化 logger.warning(f"[私聊][{conversation_instance.private_name}] ObservationInfo 未初始化,无法加载历史记录。") return try: - logger.info(f"[私聊][{conversation_instance.private_name}] 为 {conversation_instance.stream_id} 加载初始聊天记录...") + logger.info( + f"[私聊][{conversation_instance.private_name}] 为 {conversation_instance.stream_id} 加载初始聊天记录..." + ) # 从聊天核心获取原始消息列表 initial_messages = get_raw_msg_before_timestamp_with_chat( chat_id=conversation_instance.stream_id, @@ -69,7 +72,9 @@ async def load_initial_history(conversation_instance: 'Conversation'): str(last_user_info.user_id) if last_user_info else None ) except Exception as e: - logger.warning(f"[私聊][{conversation_instance.private_name}] 解析最后一条消息的用户信息时出错: {e}") + logger.warning( + f"[私聊][{conversation_instance.private_name}] 解析最后一条消息的用户信息时出错: {e}" + ) conversation_instance.observation_info.last_message_sender = None else: # 如果 user_info 不是字典,也标记为未知 @@ -91,8 +96,13 @@ async def load_initial_history(conversation_instance: 'Conversation'): # 更新 ChatObserver 和 IdleStarter 的时间戳 if conversation_instance.chat_observer: # 更新观察者的最后消息时间,避免重复处理这些初始消息 - conversation_instance.chat_observer.last_message_time = conversation_instance.observation_info.last_message_time - if conversation_instance.idle_conversation_starter and conversation_instance.observation_info.last_message_time: + conversation_instance.chat_observer.last_message_time = ( + conversation_instance.observation_info.last_message_time + ) + if ( + conversation_instance.idle_conversation_starter + and conversation_instance.observation_info.last_message_time + ): # 更新空闲计时器的起始时间 await conversation_instance.idle_conversation_starter.update_last_message_time( conversation_instance.observation_info.last_message_time @@ -114,7 +124,7 @@ async def load_initial_history(conversation_instance: 'Conversation'): conversation_instance.observation_info.chat_history_str = "[加载聊天记录出错]" -async def initialize_core_components(conversation_instance: 'Conversation'): +async def initialize_core_components(conversation_instance: "Conversation"): """ 异步初始化对话实例及其所有依赖的核心组件。 之前是 Conversation 类中的 _initialize 方法。 @@ -125,31 +135,39 @@ async def initialize_core_components(conversation_instance: 'Conversation'): # return # conversation_instance._initializing_flag_from_manager = True # 标记开始初始化 - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) 开始初始化对话实例核心组件: {conversation_instance.stream_id}") + logger.info( + f"[私聊][{conversation_instance.private_name}] (Initializer) 开始初始化对话实例核心组件: {conversation_instance.stream_id}" + ) try: # 1. 初始化核心功能组件 logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 ActionPlanner...") - conversation_instance.action_planner = ActionPlanner(conversation_instance.stream_id, conversation_instance.private_name) + conversation_instance.action_planner = ActionPlanner( + conversation_instance.stream_id, conversation_instance.private_name + ) conversation_instance.relationship_updater = PfcRelationshipUpdater( - private_name=conversation_instance.private_name, - bot_name=global_config.BOT_NICKNAME + private_name=conversation_instance.private_name, bot_name=global_config.BOT_NICKNAME + ) + conversation_instance.relationship_translator = PfcRepationshipTranslator( + private_name=conversation_instance.private_name ) - conversation_instance.relationship_translator = PfcRepationshipTranslator(private_name=conversation_instance.private_name) logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcRelationship 初始化完成。") conversation_instance.emotion_updater = PfcEmotionUpdater( - private_name=conversation_instance.private_name, - bot_name=global_config.BOT_NICKNAME + private_name=conversation_instance.private_name, bot_name=global_config.BOT_NICKNAME ) logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcEmotion 初始化完成。") logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 GoalAnalyzer...") - conversation_instance.goal_analyzer = GoalAnalyzer(conversation_instance.stream_id, conversation_instance.private_name) + conversation_instance.goal_analyzer = GoalAnalyzer( + conversation_instance.stream_id, conversation_instance.private_name + ) logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 ReplyGenerator...") - conversation_instance.reply_generator = ReplyGenerator(conversation_instance.stream_id, conversation_instance.private_name) + conversation_instance.reply_generator = ReplyGenerator( + conversation_instance.stream_id, conversation_instance.private_name + ) logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 KnowledgeFetcher...") conversation_instance.knowledge_fetcher = KnowledgeFetcher(conversation_instance.private_name) @@ -161,7 +179,9 @@ async def initialize_core_components(conversation_instance: 'Conversation'): conversation_instance.direct_sender = DirectMessageSender(conversation_instance.private_name) logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 ReplyChecker...") - conversation_instance.reply_checker = ReplyChecker(conversation_instance.stream_id, conversation_instance.private_name) + conversation_instance.reply_checker = ReplyChecker( + conversation_instance.stream_id, conversation_instance.private_name + ) # 获取关联的 ChatStream logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 获取 ChatStream...") @@ -173,47 +193,60 @@ async def initialize_core_components(conversation_instance: 'Conversation'): raise ValueError(f"无法获取 stream_id {conversation_instance.stream_id} 的 ChatStream") logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 IdleConversationStarter...") - conversation_instance.idle_conversation_starter = IdleConversationStarter(conversation_instance.stream_id, conversation_instance.private_name) + conversation_instance.idle_conversation_starter = IdleConversationStarter( + conversation_instance.stream_id, conversation_instance.private_name + ) # 2. 初始化信息存储和观察组件 logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 获取 ChatObserver 实例...") - conversation_instance.chat_observer = ChatObserver.get_instance(conversation_instance.stream_id, conversation_instance.private_name) + conversation_instance.chat_observer = ChatObserver.get_instance( + conversation_instance.stream_id, conversation_instance.private_name + ) logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 ObservationInfo...") conversation_instance.observation_info = ObservationInfo(conversation_instance.private_name) - if not conversation_instance.observation_info.bot_id: # 确保 ObservationInfo 知道机器人的 ID - logger.warning(f"[私聊][{conversation_instance.private_name}] (Initializer) ObservationInfo 未能自动获取 bot_id,尝试手动设置。") + if not conversation_instance.observation_info.bot_id: # 确保 ObservationInfo 知道机器人的 ID + logger.warning( + f"[私聊][{conversation_instance.private_name}] (Initializer) ObservationInfo 未能自动获取 bot_id,尝试手动设置。" + ) conversation_instance.observation_info.bot_id = conversation_instance.bot_qq_str logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 ConversationInfo...") conversation_instance.conversation_info = ConversationInfo() # 3. 绑定观察者和信息处理器 - logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 绑定 ObservationInfo 到 ChatObserver...") - if conversation_instance.observation_info and conversation_instance.chat_observer: # 确保二者都存在 + logger.debug( + f"[私聊][{conversation_instance.private_name}] (Initializer) 绑定 ObservationInfo 到 ChatObserver..." + ) + if conversation_instance.observation_info and conversation_instance.chat_observer: # 确保二者都存在 conversation_instance.observation_info.bind_to_chat_observer(conversation_instance.chat_observer) # 4. 加载初始聊天记录 (调用本文件内的函数) await load_initial_history(conversation_instance) # 4.1 加载用户数据 - if conversation_instance.conversation_info and conversation_instance.chat_stream: # 确保 conversation_info 和 chat_stream 都存在 + if ( + conversation_instance.conversation_info and conversation_instance.chat_stream + ): # 确保 conversation_info 和 chat_stream 都存在 person_id_tuple = await get_person_id( private_name=conversation_instance.private_name, chat_stream=conversation_instance.chat_stream, ) - if person_id_tuple: # 确保元组不为空 - conversation_instance.conversation_info.person_id = person_id_tuple[0] # 第一个元素是 person_id + if person_id_tuple: # 确保元组不为空 + conversation_instance.conversation_info.person_id = person_id_tuple[0] # 第一个元素是 person_id private_platform_str = person_id_tuple[1] private_user_id_str = person_id_tuple[2] - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) 获取到 person_id: {conversation_instance.conversation_info.person_id} for {private_platform_str}:{private_user_id_str}") + logger.info( + f"[私聊][{conversation_instance.private_name}] (Initializer) 获取到 person_id: {conversation_instance.conversation_info.person_id} for {private_platform_str}:{private_user_id_str}" + ) else: - logger.warning(f"[私聊][{conversation_instance.private_name}] (Initializer) 未能从 get_person_id 获取到 person_id 相关信息。") - + logger.warning( + f"[私聊][{conversation_instance.private_name}] (Initializer) 未能从 get_person_id 获取到 person_id 相关信息。" + ) # 5. 启动需要后台运行的组件 logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 启动 ChatObserver...") - if conversation_instance.chat_observer: # 确保存在 + if conversation_instance.chat_observer: # 确保存在 conversation_instance.chat_observer.start() if conversation_instance.idle_conversation_starter: @@ -221,39 +254,65 @@ async def initialize_core_components(conversation_instance: 'Conversation'): conversation_instance.idle_conversation_starter.start() logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) 空闲对话检测器已启动") - if conversation_instance.mood_mng and hasattr(conversation_instance.mood_mng, 'start_mood_update') and \ - not conversation_instance.mood_mng._running: # type: ignore - conversation_instance.mood_mng.start_mood_update(update_interval=global_config.mood_update_interval) # type: ignore - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已启动后台更新,间隔: {global_config.mood_update_interval} 秒。") - elif conversation_instance.mood_mng and conversation_instance.mood_mng._running: # type: ignore + if ( + conversation_instance.mood_mng + and hasattr(conversation_instance.mood_mng, "start_mood_update") + and not conversation_instance.mood_mng._running + ): # type: ignore + conversation_instance.mood_mng.start_mood_update(update_interval=global_config.mood_update_interval) # type: ignore + logger.info( + f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已启动后台更新,间隔: {global_config.mood_update_interval} 秒。" + ) + elif conversation_instance.mood_mng and conversation_instance.mood_mng._running: # type: ignore logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已在运行中。") else: - logger.warning(f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 未能启动,相关功能可能受限。") + logger.warning( + f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 未能启动,相关功能可能受限。" + ) - if conversation_instance.conversation_info and conversation_instance.conversation_info.person_id and \ - conversation_instance.relationship_translator and conversation_instance.person_info_mng: # 确保都存在 + if ( + conversation_instance.conversation_info + and conversation_instance.conversation_info.person_id + and conversation_instance.relationship_translator + and conversation_instance.person_info_mng + ): # 确保都存在 try: numeric_relationship_value = await conversation_instance.person_info_mng.get_value( conversation_instance.conversation_info.person_id, "relationship_value" ) if not isinstance(numeric_relationship_value, (int, float)): from bson.decimal128 import Decimal128 + if isinstance(numeric_relationship_value, Decimal128): numeric_relationship_value = float(numeric_relationship_value.to_decimal()) else: numeric_relationship_value = 0.0 - conversation_instance.conversation_info.relationship_text = await conversation_instance.relationship_translator.translate_relationship_value_to_text(numeric_relationship_value) - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载关系文本: {conversation_instance.conversation_info.relationship_text}") + conversation_instance.conversation_info.relationship_text = ( + await conversation_instance.relationship_translator.translate_relationship_value_to_text( + numeric_relationship_value + ) + ) + logger.info( + f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载关系文本: {conversation_instance.conversation_info.relationship_text}" + ) except Exception as e_init_rel: - logger.error(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载关系文本出错: {e_init_rel}") + logger.error( + f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载关系文本出错: {e_init_rel}" + ) conversation_instance.conversation_info.relationship_text = "你们的关系是:普通。" - - if conversation_instance.conversation_info and conversation_instance.mood_mng: # 确保都存在 + + if conversation_instance.conversation_info and conversation_instance.mood_mng: # 确保都存在 try: - conversation_instance.conversation_info.current_emotion_text = conversation_instance.mood_mng.get_prompt() # type: ignore - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载情绪文本: {conversation_instance.conversation_info.current_emotion_text}") + conversation_instance.conversation_info.current_emotion_text = ( + conversation_instance.mood_mng.get_prompt() + ) # type: ignore + logger.info( + f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载情绪文本: {conversation_instance.conversation_info.current_emotion_text}" + ) except Exception as e_init_emo: - logger.error(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载情绪文本出错: {e_init_emo}") + logger.error( + f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载情绪文本出错: {e_init_emo}" + ) # 保留 ConversationInfo 中的默认值 # 6. 标记初始化成功并设置运行状态 (这些标志由PFCManager控制和检查) @@ -261,7 +320,9 @@ async def initialize_core_components(conversation_instance: 'Conversation'): # conversation_instance.should_continue = True -> 由 manager 设置 conversation_instance.state = ConversationState.ANALYZING # 设置初始状态为分析 - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) 对话实例 {conversation_instance.stream_id} 核心组件初始化完成。") + logger.info( + f"[私聊][{conversation_instance.private_name}] (Initializer) 对话实例 {conversation_instance.stream_id} 核心组件初始化完成。" + ) except Exception as e: logger.error(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化对话实例核心组件失败: {e}") @@ -270,6 +331,6 @@ async def initialize_core_components(conversation_instance: 'Conversation'): # conversation_instance._initialized = False # 由 manager 处理 # 外部(PFCManager)会捕获这个异常并处理 should_continue 和 _initialized 标志 # 以及调用 conversation_instance.stop() - raise # 将异常重新抛出,通知 PFCManager 初始化失败 + raise # 将异常重新抛出,通知 PFCManager 初始化失败 # finally: - # conversation_instance._initializing_flag_from_manager = False # 清除标志 \ No newline at end of file + # conversation_instance._initializing_flag_from_manager = False # 清除标志 diff --git a/src/plugins/PFC/conversation_loop.py b/src/plugins/PFC/conversation_loop.py index c2178ef0..80508cf0 100644 --- a/src/plugins/PFC/conversation_loop.py +++ b/src/plugins/PFC/conversation_loop.py @@ -7,8 +7,8 @@ from dateutil import tz from src.common.logger_manager import get_logger from src.config.config import global_config -from .pfc_types import ConversationState # 需要导入 ConversationState -from . import actions # 需要导入 actions 模块 +from .pfc_types import ConversationState # 需要导入 ConversationState +from . import actions # 需要导入 actions 模块 if TYPE_CHECKING: from .conversation import Conversation @@ -16,14 +16,14 @@ if TYPE_CHECKING: logger = get_logger("pfc_loop") # 时区配置 (从 conversation.py 移过来,或者考虑放到更全局的配置模块) -configured_tz = getattr(global_config, 'TIME_ZONE', 'Asia/Shanghai') +configured_tz = getattr(global_config, "TIME_ZONE", "Asia/Shanghai") TIME_ZONE = tz.gettz(configured_tz) if TIME_ZONE is None: logger.error(f"配置的时区 '{configured_tz}' 无效,将使用默认时区 'Asia/Shanghai'") - TIME_ZONE = tz.gettz('Asia/Shanghai') + TIME_ZONE = tz.gettz("Asia/Shanghai") -async def run_conversation_loop(conversation_instance: 'Conversation'): +async def run_conversation_loop(conversation_instance: "Conversation"): """ 核心的规划与行动循环 (PFC Loop)。 之前是 Conversation 类中的 _plan_and_action_loop 方法。 @@ -33,92 +33,133 @@ async def run_conversation_loop(conversation_instance: 'Conversation'): if not conversation_instance._initialized: logger.error(f"[私聊][{conversation_instance.private_name}] 尝试在未初始化状态下运行规划循环,退出。") return - - force_reflect_and_act = False # 用于强制使用反思 prompt 的标志 - + + force_reflect_and_act = False # 用于强制使用反思 prompt 的标志 + while conversation_instance.should_continue: loop_iter_start_time = time.time() logger.debug(f"[私聊][{conversation_instance.private_name}] 开始新一轮循环迭代 ({loop_iter_start_time:.2f})") - + # 更新当前时间 try: - global TIME_ZONE # 引用全局 TIME_ZONE - if TIME_ZONE is None: # 如果还未加载成功 - configured_tz_loop = getattr(global_config, 'TIME_ZONE', 'Asia/Shanghai') + global TIME_ZONE # 引用全局 TIME_ZONE + if TIME_ZONE is None: # 如果还未加载成功 + configured_tz_loop = getattr(global_config, "TIME_ZONE", "Asia/Shanghai") TIME_ZONE = tz.gettz(configured_tz_loop) if TIME_ZONE is None: logger.error(f"循环中: 配置的时区 '{configured_tz_loop}' 无效,将使用 'Asia/Shanghai'") - TIME_ZONE = tz.gettz('Asia/Shanghai') - + TIME_ZONE = tz.gettz("Asia/Shanghai") + current_time_dt = datetime.datetime.now(TIME_ZONE) - if conversation_instance.observation_info: - time_str = current_time_dt.strftime("%Y-%m-%d %H:%M:%S %Z%z") + if conversation_instance.observation_info: + time_str = current_time_dt.strftime("%Y-%m-%d %H:%M:%S %Z%z") conversation_instance.observation_info.current_time_str = time_str logger.debug(f"[私聊][{conversation_instance.private_name}] 更新 ObservationInfo 当前时间: {time_str}") else: - logger.warning(f"[私聊][{conversation_instance.private_name}] ObservationInfo 未初始化,无法更新当前时间。") + logger.warning( + f"[私聊][{conversation_instance.private_name}] ObservationInfo 未初始化,无法更新当前时间。" + ) except Exception as time_update_err: - logger.error(f"[私聊][{conversation_instance.private_name}] 更新 ObservationInfo 当前时间时出错: {time_update_err}") + logger.error( + f"[私聊][{conversation_instance.private_name}] 更新 ObservationInfo 当前时间时出错: {time_update_err}" + ) # 处理忽略状态 - if conversation_instance.ignore_until_timestamp and loop_iter_start_time < conversation_instance.ignore_until_timestamp: - if conversation_instance.idle_conversation_starter and conversation_instance.idle_conversation_starter._running: + if ( + conversation_instance.ignore_until_timestamp + and loop_iter_start_time < conversation_instance.ignore_until_timestamp + ): + if ( + conversation_instance.idle_conversation_starter + and conversation_instance.idle_conversation_starter._running + ): conversation_instance.idle_conversation_starter.stop() logger.debug(f"[私聊][{conversation_instance.private_name}] 对话被暂时忽略,暂停空闲对话检测") sleep_duration = min(30, conversation_instance.ignore_until_timestamp - loop_iter_start_time) await asyncio.sleep(sleep_duration) - continue - elif conversation_instance.ignore_until_timestamp and loop_iter_start_time >= conversation_instance.ignore_until_timestamp: - logger.info(f"[私聊][{conversation_instance.private_name}] 忽略时间已到 {conversation_instance.stream_id},准备结束对话。") - conversation_instance.ignore_until_timestamp = None - await conversation_instance.stop() # 调用 Conversation 实例的 stop 方法 - continue + continue + elif ( + conversation_instance.ignore_until_timestamp + and loop_iter_start_time >= conversation_instance.ignore_until_timestamp + ): + logger.info( + f"[私聊][{conversation_instance.private_name}] 忽略时间已到 {conversation_instance.stream_id},准备结束对话。" + ) + conversation_instance.ignore_until_timestamp = None + await conversation_instance.stop() # 调用 Conversation 实例的 stop 方法 + continue else: - if conversation_instance.idle_conversation_starter and not conversation_instance.idle_conversation_starter._running: + if ( + conversation_instance.idle_conversation_starter + and not conversation_instance.idle_conversation_starter._running + ): conversation_instance.idle_conversation_starter.start() logger.debug(f"[私聊][{conversation_instance.private_name}] 恢复空闲对话检测") # 核心规划与行动逻辑 try: # 更新关系和情绪文本 (在每次循环开始时进行) - if conversation_instance.conversation_info and conversation_instance._initialized: + if conversation_instance.conversation_info and conversation_instance._initialized: # 更新关系 - if conversation_instance.conversation_info.person_id and conversation_instance.relationship_translator and conversation_instance.person_info_mng: + if ( + conversation_instance.conversation_info.person_id + and conversation_instance.relationship_translator + and conversation_instance.person_info_mng + ): try: numeric_relationship_value = await conversation_instance.person_info_mng.get_value( conversation_instance.conversation_info.person_id, "relationship_value" ) if not isinstance(numeric_relationship_value, (int, float)): from bson.decimal128 import Decimal128 + if isinstance(numeric_relationship_value, Decimal128): numeric_relationship_value = float(numeric_relationship_value.to_decimal()) - else: + else: numeric_relationship_value = 0.0 - conversation_instance.conversation_info.relationship_text = await conversation_instance.relationship_translator.translate_relationship_value_to_text(numeric_relationship_value) + conversation_instance.conversation_info.relationship_text = ( + await conversation_instance.relationship_translator.translate_relationship_value_to_text( + numeric_relationship_value + ) + ) except Exception as e_rel: logger.error(f"[私聊][{conversation_instance.private_name}] (Loop) 更新关系文本时出错: {e_rel}") - conversation_instance.conversation_info.relationship_text = "你们的关系是:普通。" + conversation_instance.conversation_info.relationship_text = "你们的关系是:普通。" # 更新情绪 if conversation_instance.mood_mng: - conversation_instance.conversation_info.current_emotion_text = conversation_instance.mood_mng.get_prompt() # type: ignore - + conversation_instance.conversation_info.current_emotion_text = ( + conversation_instance.mood_mng.get_prompt() + ) # type: ignore + # 检查核心组件 - if not all([conversation_instance.action_planner, conversation_instance.observation_info, conversation_instance.conversation_info]): - logger.error(f"[私聊][{conversation_instance.private_name}] 核心组件未初始化,无法继续规划循环。将等待5秒后重试...") + if not all( + [ + conversation_instance.action_planner, + conversation_instance.observation_info, + conversation_instance.conversation_info, + ] + ): + logger.error( + f"[私聊][{conversation_instance.private_name}] 核心组件未初始化,无法继续规划循环。将等待5秒后重试..." + ) await asyncio.sleep(5) continue # 规划 planning_start_time = time.time() - logger.debug(f"[私聊][{conversation_instance.private_name}] --- (Loop) 开始规划 ({planning_start_time:.2f}) ---") + logger.debug( + f"[私聊][{conversation_instance.private_name}] --- (Loop) 开始规划 ({planning_start_time:.2f}) ---" + ) if conversation_instance.conversation_info: conversation_instance.conversation_info.other_new_messages_during_planning_count = 0 action, reason = await conversation_instance.action_planner.plan( conversation_instance.observation_info, - conversation_instance.conversation_info, - conversation_instance.conversation_info.last_successful_reply_action if conversation_instance.conversation_info else None, - use_reflect_prompt=force_reflect_and_act + conversation_instance.conversation_info, + conversation_instance.conversation_info.last_successful_reply_action + if conversation_instance.conversation_info + else None, + use_reflect_prompt=force_reflect_and_act, ) force_reflect_and_act = False logger.debug( @@ -138,29 +179,38 @@ async def run_conversation_loop(conversation_instance: 'Conversation'): new_messages_during_planning.append(msg) if sender_id != conversation_instance.bot_qq_str: other_new_messages_during_planning.append(msg) - + new_msg_count = len(new_messages_during_planning) other_new_msg_count = len(other_new_messages_during_planning) - - if conversation_instance.conversation_info and other_new_msg_count > 0: + + if conversation_instance.conversation_info and other_new_msg_count > 0: conversation_instance.conversation_info.current_instance_message_count += other_new_msg_count # 触发关系和情绪更新(如果需要) - if conversation_instance.relationship_updater and conversation_instance.observation_info and conversation_instance.chat_observer: + if ( + conversation_instance.relationship_updater + and conversation_instance.observation_info + and conversation_instance.chat_observer + ): await conversation_instance.relationship_updater.update_relationship_incremental( conversation_info=conversation_instance.conversation_info, observation_info=conversation_instance.observation_info, - chat_observer_for_history=conversation_instance.chat_observer + chat_observer_for_history=conversation_instance.chat_observer, ) - if conversation_instance.emotion_updater and other_new_messages_during_planning and conversation_instance.observation_info and conversation_instance.chat_observer: + if ( + conversation_instance.emotion_updater + and other_new_messages_during_planning + and conversation_instance.observation_info + and conversation_instance.chat_observer + ): last_user_msg = other_new_messages_during_planning[-1] last_user_msg_text = last_user_msg.get("processed_plain_text", "用户发了新消息") - sender_name_for_event = getattr(conversation_instance.observation_info, 'sender_name', '对方') + sender_name_for_event = getattr(conversation_instance.observation_info, "sender_name", "对方") event_desc = f"用户【{sender_name_for_event}】发送了新消息: '{last_user_msg_text[:30]}...'" await conversation_instance.emotion_updater.update_emotion_based_on_context( conversation_info=conversation_instance.conversation_info, observation_info=conversation_instance.observation_info, chat_observer_for_history=conversation_instance.chat_observer, - event_description=event_desc + event_description=event_desc, ) should_interrupt: bool = False @@ -168,15 +218,26 @@ async def run_conversation_loop(conversation_instance: 'Conversation'): if action in ["wait", "listening"] and new_msg_count > 0: should_interrupt = True interrupt_reason = f"规划 {action} 期间收到 {new_msg_count} 条新消息" - elif other_new_msg_count > 2: # Threshold for other actions + elif other_new_msg_count > 2: # Threshold for other actions should_interrupt = True interrupt_reason = f"规划 {action} 期间收到 {other_new_msg_count} 条来自他人的新消息" if should_interrupt: - logger.info(f"[私聊][{conversation_instance.private_name}] (Loop) 中断 '{action}',原因: {interrupt_reason}。重新规划...") - cancel_record = { "action": action, "plan_reason": reason, "status": "cancelled_due_to_new_messages", "time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "final_reason": interrupt_reason, } + logger.info( + f"[私聊][{conversation_instance.private_name}] (Loop) 中断 '{action}',原因: {interrupt_reason}。重新规划..." + ) + cancel_record = { + "action": action, + "plan_reason": reason, + "status": "cancelled_due_to_new_messages", + "time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "final_reason": interrupt_reason, + } if conversation_instance.conversation_info: - if not hasattr(conversation_instance.conversation_info, "done_action") or conversation_instance.conversation_info.done_action is None: + if ( + not hasattr(conversation_instance.conversation_info, "done_action") + or conversation_instance.conversation_info.done_action is None + ): conversation_instance.conversation_info.done_action = [] conversation_instance.conversation_info.done_action.append(cancel_record) conversation_instance.conversation_info.last_successful_reply_action = None @@ -185,43 +246,65 @@ async def run_conversation_loop(conversation_instance: 'Conversation'): continue # 执行动作 (调用 actions 模块的函数) - logger.debug(f"[私聊][{conversation_instance.private_name}] (Loop) 未中断,调用 actions.handle_action 执行动作 '{action}'...") + logger.debug( + f"[私聊][{conversation_instance.private_name}] (Loop) 未中断,调用 actions.handle_action 执行动作 '{action}'..." + ) if conversation_instance.conversation_info: conversation_instance.conversation_info.other_new_messages_during_planning_count = other_new_msg_count - - await actions.handle_action(conversation_instance, action, reason, conversation_instance.observation_info, conversation_instance.conversation_info) + + await actions.handle_action( + conversation_instance, + action, + reason, + conversation_instance.observation_info, + conversation_instance.conversation_info, + ) logger.debug(f"[私聊][{conversation_instance.private_name}] (Loop) actions.handle_action 完成。") # 检查是否需要反思 last_action_record = {} if conversation_instance.conversation_info and conversation_instance.conversation_info.done_action: last_action_record = conversation_instance.conversation_info.done_action[-1] - if last_action_record.get("action") == "send_new_message" and last_action_record.get("status") == "done_no_reply": + if ( + last_action_record.get("action") == "send_new_message" + and last_action_record.get("status") == "done_no_reply" + ): logger.info(f"[私聊][{conversation_instance.private_name}] (Loop) 检测到需反思,设置标志。") force_reflect_and_act = True - + # 检查结束条件 goal_ended: bool = False - if conversation_instance.conversation_info and hasattr(conversation_instance.conversation_info, "goal_list") and conversation_instance.conversation_info.goal_list: + if ( + conversation_instance.conversation_info + and hasattr(conversation_instance.conversation_info, "goal_list") + and conversation_instance.conversation_info.goal_list + ): last_goal_item = conversation_instance.conversation_info.goal_list[-1] - current_goal = last_goal_item.get("goal") if isinstance(last_goal_item, dict) else (last_goal_item if isinstance(last_goal_item, str) else None) + current_goal = ( + last_goal_item.get("goal") + if isinstance(last_goal_item, dict) + else (last_goal_item if isinstance(last_goal_item, str) else None) + ) if current_goal == "结束对话": goal_ended = True last_action_record_for_end_check = {} if conversation_instance.conversation_info and conversation_instance.conversation_info.done_action: last_action_record_for_end_check = conversation_instance.conversation_info.done_action[-1] - action_ended: bool = ( last_action_record_for_end_check.get("action") in ["end_conversation", "say_goodbye"] and last_action_record_for_end_check.get("status") == "done" ) + action_ended: bool = ( + last_action_record_for_end_check.get("action") in ["end_conversation", "say_goodbye"] + and last_action_record_for_end_check.get("status") == "done" + ) if goal_ended or action_ended: - logger.info( f"[私聊][{conversation_instance.private_name}] (Loop) 检测到结束条件,停止循环。" ) - await conversation_instance.stop() # 调用 Conversation 的 stop - continue # 虽然会 break,但 continue 更明确 + logger.info(f"[私聊][{conversation_instance.private_name}] (Loop) 检测到结束条件,停止循环。") + await conversation_instance.stop() # 调用 Conversation 的 stop + continue # 虽然会 break,但 continue 更明确 except asyncio.CancelledError: logger.info(f"[私聊][{conversation_instance.private_name}] (Loop) PFC 主循环任务被取消。") - await conversation_instance.stop() # 调用 Conversation 的 stop - break + await conversation_instance.stop() # 调用 Conversation 的 stop + break except Exception as loop_err: logger.error(f"[私聊][{conversation_instance.private_name}] (Loop) PFC 主循环出错: {loop_err}") logger.error(f"[私聊][{conversation_instance.private_name}] (Loop) {traceback.format_exc()}") @@ -235,4 +318,6 @@ async def run_conversation_loop(conversation_instance: 'Conversation'): if loop_duration < min_loop_interval: await asyncio.sleep(min_loop_interval - loop_duration) - logger.info(f"[私聊][{conversation_instance.private_name}] (Loop) PFC 循环已退出 for stream_id: {conversation_instance.stream_id}") \ No newline at end of file + logger.info( + f"[私聊][{conversation_instance.private_name}] (Loop) PFC 循环已退出 for stream_id: {conversation_instance.stream_id}" + ) diff --git a/src/plugins/PFC/observation_info.py b/src/plugins/PFC/observation_info.py index 4f5ea75f..2d2a0f27 100644 --- a/src/plugins/PFC/observation_info.py +++ b/src/plugins/PFC/observation_info.py @@ -6,13 +6,14 @@ from maim_message import UserInfo from src.common.logger import get_module_logger from src.plugins.utils.chat_message_builder import build_readable_messages from src.config.config import global_config + # 确保导入路径正确 from .chat_observer import ChatObserver from .chat_states import NotificationHandler, NotificationType, Notification logger = get_module_logger("observation_info") -TIME_ZONE = tz.gettz(global_config.TIME_ZONE if global_config else 'Asia/Shanghai') # 使用配置的时区,提供默认值 +TIME_ZONE = tz.gettz(global_config.TIME_ZONE if global_config else "Asia/Shanghai") # 使用配置的时区,提供默认值 class ObservationInfoHandler(NotificationHandler): @@ -115,10 +116,9 @@ class ObservationInfo: # 新增:发信人信息 self.sender_name: Optional[str] = None - self.sender_user_id: Optional[str] = None # 存储为字符串 + self.sender_user_id: Optional[str] = None # 存储为字符串 self.sender_platform: Optional[str] = None - # 聊天记录相关 self.chat_history: List[Dict[str, Any]] = [] # 存储已处理的消息历史 self.chat_history_str: str = "还没有聊天记录。" # 用于生成 Prompt 的历史记录字符串 @@ -146,7 +146,7 @@ class ObservationInfo: # 其他状态 self.is_typing: bool = False # 是否正在输入 (未来可能用到) self.changed: bool = False # 状态是否有变化 (用于优化) - + # 用于存储格式化的当前时间 self.current_time_str: Optional[str] = None @@ -226,16 +226,18 @@ class ObservationInfo: if not message_time or not message_id: logger.warning(f"[私聊][{self.private_name}] 收到的消息缺少 time 或 message_id: {message}") return - + # --- 新增/修改:提取并存储发信人详细信息 --- current_message_sender_id: Optional[str] = None if user_info: try: - self.sender_user_id = str(user_info.user_id) # 确保是字符串 - self.sender_name = user_info.user_nickname # 或者 user_info.card 如果私聊时card更准 + self.sender_user_id = str(user_info.user_id) # 确保是字符串 + self.sender_name = user_info.user_nickname # 或者 user_info.card 如果私聊时card更准 self.sender_platform = user_info.platform - current_message_sender_id = self.sender_user_id # 用于后续逻辑 - logger.debug(f"[私聊][{self.private_name}] 更新发信人信息: ID={self.sender_user_id}, Name={self.sender_name}, Platform={self.sender_platform}") + current_message_sender_id = self.sender_user_id # 用于后续逻辑 + logger.debug( + f"[私聊][{self.private_name}] 更新发信人信息: ID={self.sender_user_id}, Name={self.sender_name}, Platform={self.sender_platform}" + ) except AttributeError as e: logger.error(f"[私聊][{self.private_name}] 从 UserInfo 对象提取信息时出错: {e}, UserInfo: {user_info}") # 如果提取失败,将这些新字段设为 None,避免使用旧数据 @@ -250,13 +252,12 @@ class ObservationInfo: self.sender_platform = None # --- 新增/修改结束 --- - # 更新最后消息时间(所有消息) if message_time > (self.last_message_time or 0): self.last_message_time = message_time self.last_message_id = message_id self.last_message_content = processed_text - self.last_message_sender = current_message_sender_id # 使用新获取的 current_message_sender_id + self.last_message_sender = current_message_sender_id # 使用新获取的 current_message_sender_id # 更新说话者特定时间 if sender_id_str: diff --git a/src/plugins/PFC/pfc.py b/src/plugins/PFC/pfc.py index d0f3e7e6..77a8f4b7 100644 --- a/src/plugins/PFC/pfc.py +++ b/src/plugins/PFC/pfc.py @@ -99,9 +99,7 @@ class GoalAnalyzer: ) chat_history_text += f"\n--- 以下是 {observation_info.new_messages_count} 条新消息 ---\n{new_messages_str}" else: - chat_history_text += ( - "\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n" - ) + chat_history_text += "\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n" # await observation_info.clear_unprocessed_messages() persona_text = f"你的名字是{self.name},{self.personality_info}。" @@ -283,4 +281,4 @@ class GoalAnalyzer: except Exception as e: logger.error(f"[私聊][{self.private_name}]分析对话状态时出错: {str(e)}") - return False, False, f"分析出错: {str(e)}" \ No newline at end of file + return False, False, f"分析出错: {str(e)}" diff --git a/src/plugins/PFC/pfc_emotion.py b/src/plugins/PFC/pfc_emotion.py index f1018bfd..d88344f8 100644 --- a/src/plugins/PFC/pfc_emotion.py +++ b/src/plugins/PFC/pfc_emotion.py @@ -3,14 +3,15 @@ from typing import List, Dict, Any from src.plugins.PFC.chat_observer import ChatObserver from src.common.logger_manager import get_logger from src.plugins.models.utils_model import LLMRequest -from src.plugins.moods.moods import MoodManager # MoodManager 本身是单例 +from src.plugins.moods.moods import MoodManager # MoodManager 本身是单例 from src.plugins.utils.chat_message_builder import build_readable_messages from src.plugins.PFC.observation_info import ObservationInfo from src.plugins.PFC.conversation_info import ConversationInfo -from src.config.config import global_config # 导入全局配置 +from src.config.config import global_config # 导入全局配置 logger = get_logger("pfc_emotion") + class PfcEmotionUpdater: def __init__(self, private_name: str, bot_name: str): """ @@ -18,31 +19,33 @@ class PfcEmotionUpdater: """ self.private_name = private_name self.bot_name = bot_name - self.mood_mng = MoodManager.get_instance() # 获取 MoodManager 单例 + self.mood_mng = MoodManager.get_instance() # 获取 MoodManager 单例 # LLM 实例 (根据 global_config.llm_summary 配置) - llm_config_summary = getattr(global_config, 'llm_summary', None) + llm_config_summary = getattr(global_config, "llm_summary", None) if llm_config_summary and isinstance(llm_config_summary, dict): logger.info(f"[私聊][{self.private_name}] 使用 llm_summary 配置初始化情绪判断LLM。") self.llm = LLMRequest( model=llm_config_summary, - temperature=llm_config_summary.get("temperature", 0.5), # temperature 来自其自身配置或默认0.7,这里用0.5 - max_tokens=llm_config_summary.get("max_tokens", 256), # 情绪词输出不需要很多token - request_type="pfc_emotion_evaluation" + temperature=llm_config_summary.get( + "temperature", 0.5 + ), # temperature 来自其自身配置或默认0.7,这里用0.5 + max_tokens=llm_config_summary.get("max_tokens", 256), # 情绪词输出不需要很多token + request_type="pfc_emotion_evaluation", ) else: logger.error(f"[私聊][{self.private_name}] 未找到 llm_summary 配置或配置无效!情绪判断功能将受限。") - self.llm = None # LLM 未初始化 + self.llm = None # LLM 未初始化 - self.EMOTION_UPDATE_INTENSITY = getattr(global_config, 'pfc_emotion_update_intensity', 0.6) - self.EMOTION_HISTORY_COUNT = getattr(global_config, 'pfc_emotion_history_count', 5) + self.EMOTION_UPDATE_INTENSITY = getattr(global_config, "pfc_emotion_update_intensity", 0.6) + self.EMOTION_HISTORY_COUNT = getattr(global_config, "pfc_emotion_history_count", 5) async def update_emotion_based_on_context( self, conversation_info: ConversationInfo, observation_info: ObservationInfo, - chat_observer_for_history: ChatObserver, # ChatObserver 实例 - event_description: str + chat_observer_for_history: ChatObserver, # ChatObserver 实例 + event_description: str, ) -> None: if not self.llm: logger.error(f"[私聊][{self.private_name}] LLM未初始化,无法进行情绪更新。") @@ -57,19 +60,23 @@ class PfcEmotionUpdater: recent_messages_for_emotion: List[Dict[str, Any]] = [] if chat_observer_for_history: - recent_messages_for_emotion = chat_observer_for_history.get_cached_messages(limit=self.EMOTION_HISTORY_COUNT) + recent_messages_for_emotion = chat_observer_for_history.get_cached_messages( + limit=self.EMOTION_HISTORY_COUNT + ) elif observation_info.chat_history: - recent_messages_for_emotion = observation_info.chat_history[-self.EMOTION_HISTORY_COUNT:] - + recent_messages_for_emotion = observation_info.chat_history[-self.EMOTION_HISTORY_COUNT :] + readable_recent_history = await build_readable_messages( recent_messages_for_emotion, replace_bot_name=True, merge_messages=True, timestamp_mode="none" ) - current_mood_text_from_manager = self.mood_mng.current_mood.text # 从 MoodManager 获取当前情绪文本 - sender_name_for_prompt = getattr(observation_info, 'sender_name', '对方') + current_mood_text_from_manager = self.mood_mng.current_mood.text # 从 MoodManager 获取当前情绪文本 + sender_name_for_prompt = getattr(observation_info, "sender_name", "对方") if not sender_name_for_prompt: - sender_name_for_prompt = '对方' - relationship_text_for_prompt = getattr(conversation_info, 'relationship_text', '关系一般。') # 从 ConversationInfo 获取关系文本 + sender_name_for_prompt = "对方" + relationship_text_for_prompt = getattr( + conversation_info, "relationship_text", "关系一般。" + ) # 从 ConversationInfo 获取关系文本 emotion_prompt = f"""你是{self.bot_name}。你现在的心情是【{current_mood_text_from_manager}】。 你正在和用户【{sender_name_for_prompt}】私聊,你们的关系是:【{relationship_text_for_prompt}】。 @@ -86,19 +93,27 @@ class PfcEmotionUpdater: try: logger.debug(f"[私聊][{self.private_name}] 情绪判断Prompt:\n{emotion_prompt}") content, _ = await self.llm.generate_response_async(emotion_prompt) - detected_emotion_word = content.strip().replace("\"", "").replace("'", "") + detected_emotion_word = content.strip().replace('"', "").replace("'", "") logger.debug(f"[私聊][{self.private_name}] 情绪判断LLM原始返回: '{detected_emotion_word}'") - if detected_emotion_word and detected_emotion_word != "无变化" and detected_emotion_word in self.mood_mng.emotion_map: + if ( + detected_emotion_word + and detected_emotion_word != "无变化" + and detected_emotion_word in self.mood_mng.emotion_map + ): self.mood_mng.update_mood_from_emotion(detected_emotion_word, intensity=self.EMOTION_UPDATE_INTENSITY) - logger.info(f"[私聊][{self.private_name}] 基于事件 '{event_description}',情绪已更新为倾向于 '{detected_emotion_word}'。当前心情: {self.mood_mng.current_mood.text}") + logger.info( + f"[私聊][{self.private_name}] 基于事件 '{event_description}',情绪已更新为倾向于 '{detected_emotion_word}'。当前心情: {self.mood_mng.current_mood.text}" + ) elif detected_emotion_word == "无变化": logger.info(f"[私聊][{self.private_name}] 基于事件 '{event_description}',LLM判断情绪无显著变化。") else: - logger.warning(f"[私聊][{self.private_name}] LLM返回了未知的情绪词 '{detected_emotion_word}' 或未返回有效词,情绪未主动更新。") + logger.warning( + f"[私聊][{self.private_name}] LLM返回了未知的情绪词 '{detected_emotion_word}' 或未返回有效词,情绪未主动更新。" + ) except Exception as e: logger.error(f"[私聊][{self.private_name}] 情绪判断LLM调用或处理失败: {e}") # 无论LLM判断如何,都更新conversation_info中的情绪文本以供Prompt使用 - if conversation_info and self.mood_mng: # 确保conversation_info有效 - conversation_info.current_emotion_text = self.mood_mng.get_prompt() \ No newline at end of file + if conversation_info and self.mood_mng: # 确保conversation_info有效 + conversation_info.current_emotion_text = self.mood_mng.get_prompt() diff --git a/src/plugins/PFC/pfc_manager.py b/src/plugins/PFC/pfc_manager.py index 891e1906..ce6f2566 100644 --- a/src/plugins/PFC/pfc_manager.py +++ b/src/plugins/PFC/pfc_manager.py @@ -6,8 +6,9 @@ from typing import Dict, Optional from src.common.logger import get_module_logger from .conversation import Conversation from .conversation_initializer import initialize_core_components + # >>> 新增导入 <<< -from .pfc_types import ConversationState # 导入 ConversationState +from .pfc_types import ConversationState # 导入 ConversationState logger = get_module_logger("pfc_manager") @@ -17,7 +18,7 @@ class PFCManager: _instance = None _instances: Dict[str, Conversation] = {} - _initializing: Dict[str, bool] = {} # 用于防止并发初始化同一个 stream_id + _initializing: Dict[str, bool] = {} # 用于防止并发初始化同一个 stream_id @classmethod def get_instance(cls) -> "PFCManager": @@ -56,10 +57,9 @@ class PFCManager: await self._cleanup_conversation(instance) if stream_id in self._instances: del self._instances[stream_id] - if stream_id in self._initializing: # 确保也从这里移除 + if stream_id in self._initializing: # 确保也从这里移除 del self._initializing[stream_id] - conversation_instance: Optional[Conversation] = None try: logger.info(f"[私聊][{private_name}] 创建新的对话实例: {stream_id}") @@ -74,11 +74,11 @@ class PFCManager: # 检查初始化结果并启动 if conversation_instance._initialized and conversation_instance.should_continue: logger.info(f"[私聊][{private_name}] 初始化成功,调用 conversation.start() 启动主循环...") - await conversation_instance.start() # start 方法内部会创建 loop 任务 + await conversation_instance.start() # start 方法内部会创建 loop 任务 else: logger.error(f"[私聊][{private_name}] 初始化未成功完成,无法启动实例 {stream_id}。") await self._cleanup_conversation(conversation_instance) - if stream_id in self._instances: # 再次检查以防万一 + if stream_id in self._instances: # 再次检查以防万一 del self._instances[stream_id] conversation_instance = None @@ -91,8 +91,8 @@ class PFCManager: del self._instances[stream_id] conversation_instance = None finally: - if stream_id in self._initializing: # 确保在 finally 中也检查 - self._initializing[stream_id] = False # 清除初始化标记 + if stream_id in self._initializing: # 确保在 finally 中也检查 + self._initializing[stream_id] = False # 清除初始化标记 return conversation_instance @@ -176,4 +176,4 @@ class PFCManager: logger.error(f"[管理器] 移除或清理会话实例 {stream_id} 时失败: {e}") logger.error(traceback.format_exc()) else: - logger.warning(f"[管理器] 尝试移除不存在的会话实例: {stream_id}") \ No newline at end of file + logger.warning(f"[管理器] 尝试移除不存在的会话实例: {stream_id}") diff --git a/src/plugins/PFC/pfc_processor.py b/src/plugins/PFC/pfc_processor.py index 523e2261..428db544 100644 --- a/src/plugins/PFC/pfc_processor.py +++ b/src/plugins/PFC/pfc_processor.py @@ -13,6 +13,7 @@ from datetime import datetime logger = get_logger("pfc_processor") + async def _handle_error(error: Exception, context: str, message: Optional[MessageRecv] = None) -> None: """统一的错误处理函数 @@ -26,8 +27,9 @@ async def _handle_error(error: Exception, context: str, message: Optional[Messag if message and hasattr(message, "raw_message"): logger.error(f"相关消息原始内容: {message.raw_message}") + class PFCProcessor: - """ PFC 处理器,负责处理接收到的信息并计数""" + """PFC 处理器,负责处理接收到的信息并计数""" def __init__(self): """初始化 PFC 处理器,创建消息存储实例""" @@ -105,9 +107,7 @@ class PFCProcessor: """检查消息中是否包含过滤词""" for word in global_config.ban_words: if word in text: - logger.info( - f"[私聊]{userinfo.user_nickname}:{text}" - ) + logger.info(f"[私聊]{userinfo.user_nickname}:{text}") logger.info(f"[过滤词识别]消息中含有{word},filtered") return True return False @@ -117,9 +117,7 @@ class PFCProcessor: """检查消息是否匹配过滤正则表达式""" for pattern in global_config.ban_msgs_regex: if pattern.search(text): - logger.info( - f"[私聊]{userinfo.user_nickname}:{text}" - ) + logger.info(f"[私聊]{userinfo.user_nickname}:{text}") logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered") return True - return False \ No newline at end of file + return False diff --git a/src/plugins/PFC/pfc_relationship.py b/src/plugins/PFC/pfc_relationship.py index be2abf48..aa0586e3 100644 --- a/src/plugins/PFC/pfc_relationship.py +++ b/src/plugins/PFC/pfc_relationship.py @@ -3,16 +3,19 @@ from src.plugins.PFC.chat_observer import ChatObserver from src.common.logger_manager import get_logger from src.plugins.models.utils_model import LLMRequest from src.plugins.person_info.person_info import person_info_manager -from src.plugins.person_info.relationship_manager import relationship_manager # 主要用其 ensure_float 和 build_relationship_info +from src.plugins.person_info.relationship_manager import ( + relationship_manager, +) # 主要用其 ensure_float 和 build_relationship_info from src.plugins.utils.chat_message_builder import build_readable_messages from src.plugins.PFC.observation_info import ObservationInfo from src.plugins.PFC.conversation_info import ConversationInfo from src.plugins.PFC.pfc_utils import get_items_from_json -from src.config.config import global_config # 导入全局配置 (向上两级到 src/, 再到 config) +from src.config.config import global_config # 导入全局配置 (向上两级到 src/, 再到 config) logger = get_logger("pfc_relationship") + class PfcRelationshipUpdater: def __init__(self, private_name: str, bot_name: str): """ @@ -25,48 +28,50 @@ class PfcRelationshipUpdater: self.private_name = private_name self.bot_name = bot_name self.person_info_mng = person_info_manager - self.relationship_mng = relationship_manager # 复用其实例方法 + self.relationship_mng = relationship_manager # 复用其实例方法 # LLM 实例 (为关系评估创建一个新的) # 尝试读取 llm_PFC_relationship_eval 配置,如果不存在则回退 - llm_config_rel_eval = getattr(global_config, 'llm_PFC_relationship_eval', None) + llm_config_rel_eval = getattr(global_config, "llm_PFC_relationship_eval", None) if llm_config_rel_eval and isinstance(llm_config_rel_eval, dict): logger.info(f"[私聊][{self.private_name}] 使用 llm_PFC_relationship_eval 配置初始化关系评估LLM。") self.llm = LLMRequest( model=llm_config_rel_eval, - temperature=llm_config_rel_eval.get("temp", 0.5), # 判断任务通常用较低温度 + temperature=llm_config_rel_eval.get("temp", 0.5), # 判断任务通常用较低温度 max_tokens=llm_config_rel_eval.get("max_tokens", 512), - request_type="pfc_relationship_evaluation" + request_type="pfc_relationship_evaluation", ) else: - logger.warning(f"[私聊][{self.private_name}] 未找到 llm_PFC_relationship_eval 配置或配置无效,将回退使用 llm_PFC_action_planner 的配置。") - llm_config_action_planner = getattr(global_config, 'llm_PFC_action_planner', None) + logger.warning( + f"[私聊][{self.private_name}] 未找到 llm_PFC_relationship_eval 配置或配置无效,将回退使用 llm_PFC_action_planner 的配置。" + ) + llm_config_action_planner = getattr(global_config, "llm_PFC_action_planner", None) if llm_config_action_planner and isinstance(llm_config_action_planner, dict): self.llm = LLMRequest( - model=llm_config_action_planner, # 使用 action_planner 的模型配置 - temperature=llm_config_action_planner.get("temp", 0.5), # 但温度可以尝试低一些 + model=llm_config_action_planner, # 使用 action_planner 的模型配置 + temperature=llm_config_action_planner.get("temp", 0.5), # 但温度可以尝试低一些 max_tokens=llm_config_action_planner.get("max_tokens", 512), - request_type="pfc_relationship_evaluation_fallback" + request_type="pfc_relationship_evaluation_fallback", ) - else: # 极端情况,连 action_planner 的配置都没有 + else: # 极端情况,连 action_planner 的配置都没有 logger.error(f"[私聊][{self.private_name}] 无法找到任何有效的LLM配置用于关系评估!关系更新功能将受限。") - self.llm = None # LLM 未初始化 + self.llm = None # LLM 未初始化 # 从 global_config 读取参数,若无则使用默认值 - self.REL_INCREMENTAL_INTERVAL = getattr(global_config, 'pfc_relationship_incremental_interval', 10) - self.REL_INCREMENTAL_MSG_COUNT = getattr(global_config, 'pfc_relationship_incremental_msg_count', 10) - self.REL_INCREMENTAL_DEFAULT_CHANGE = getattr(global_config, 'pfc_relationship_incremental_default_change', 1.0) - self.REL_INCREMENTAL_MAX_CHANGE = getattr(global_config, 'pfc_relationship_incremental_max_change', 5.0) + self.REL_INCREMENTAL_INTERVAL = getattr(global_config, "pfc_relationship_incremental_interval", 10) + self.REL_INCREMENTAL_MSG_COUNT = getattr(global_config, "pfc_relationship_incremental_msg_count", 10) + self.REL_INCREMENTAL_DEFAULT_CHANGE = getattr(global_config, "pfc_relationship_incremental_default_change", 1.0) + self.REL_INCREMENTAL_MAX_CHANGE = getattr(global_config, "pfc_relationship_incremental_max_change", 5.0) - self.REL_FINAL_MSG_COUNT = getattr(global_config, 'pfc_relationship_final_msg_count', 30) - self.REL_FINAL_DEFAULT_CHANGE = getattr(global_config, 'pfc_relationship_final_default_change', 5.0) - self.REL_FINAL_MAX_CHANGE = getattr(global_config, 'pfc_relationship_final_max_change', 50.0) + self.REL_FINAL_MSG_COUNT = getattr(global_config, "pfc_relationship_final_msg_count", 30) + self.REL_FINAL_DEFAULT_CHANGE = getattr(global_config, "pfc_relationship_final_default_change", 5.0) + self.REL_FINAL_MAX_CHANGE = getattr(global_config, "pfc_relationship_final_max_change", 50.0) async def update_relationship_incremental( self, conversation_info: ConversationInfo, observation_info: ObservationInfo, - chat_observer_for_history: ChatObserver # ChatObserver 实例 + chat_observer_for_history: ChatObserver, # ChatObserver 实例 ) -> None: if not self.llm: logger.error(f"[私聊][{self.private_name}] LLM未初始化,无法进行增量关系更新。") @@ -75,18 +80,22 @@ class PfcRelationshipUpdater: logger.debug(f"[私聊][{self.private_name}] 增量关系更新:缺少必要信息。") return - if not (conversation_info.current_instance_message_count % self.REL_INCREMENTAL_INTERVAL == 0 \ - and conversation_info.current_instance_message_count > 0): + if not ( + conversation_info.current_instance_message_count % self.REL_INCREMENTAL_INTERVAL == 0 + and conversation_info.current_instance_message_count > 0 + ): return - logger.info(f"[私聊][{self.private_name}] 达到增量关系更新阈值 ({conversation_info.current_instance_message_count}条消息),开始评估...") + logger.info( + f"[私聊][{self.private_name}] 达到增量关系更新阈值 ({conversation_info.current_instance_message_count}条消息),开始评估..." + ) messages_for_eval: List[Dict[str, Any]] = [] if chat_observer_for_history: messages_for_eval = chat_observer_for_history.get_cached_messages(limit=self.REL_INCREMENTAL_MSG_COUNT) elif observation_info.chat_history: - messages_for_eval = observation_info.chat_history[-self.REL_INCREMENTAL_MSG_COUNT:] - + messages_for_eval = observation_info.chat_history[-self.REL_INCREMENTAL_MSG_COUNT :] + if not messages_for_eval: logger.warning(f"[私聊][{self.private_name}] 增量关系更新:没有足够的消息进行评估。") return @@ -95,8 +104,12 @@ class PfcRelationshipUpdater: messages_for_eval, replace_bot_name=True, merge_messages=False, timestamp_mode="relative" ) - current_relationship_value = await self.person_info_mng.get_value(conversation_info.person_id, "relationship_value") - current_relationship_value = self.relationship_mng.ensure_float(current_relationship_value, conversation_info.person_id) + current_relationship_value = await self.person_info_mng.get_value( + conversation_info.person_id, "relationship_value" + ) + current_relationship_value = self.relationship_mng.ensure_float( + current_relationship_value, conversation_info.person_id + ) relationship_prompt = f"""你是{self.bot_name}。你正在与{self.private_name}私聊。 你们当前的关系值大约是 {current_relationship_value:.0f} (范围通常在-1000到1000,越高越代表关系越好)。 @@ -115,9 +128,11 @@ class PfcRelationshipUpdater: logger.debug(f"[私聊][{self.private_name}] 增量关系评估LLM原始返回: {content}") success, result = get_items_from_json( - content, self.private_name, "adjustment", + content, + self.private_name, + "adjustment", default_values={"adjustment": self.REL_INCREMENTAL_DEFAULT_CHANGE}, - required_types={"adjustment": (int, float)} + required_types={"adjustment": (int, float)}, ) raw_adjustment = result.get("adjustment", self.REL_INCREMENTAL_DEFAULT_CHANGE) if not isinstance(raw_adjustment, (int, float)): @@ -129,17 +144,23 @@ class PfcRelationshipUpdater: logger.error(f"[私聊][{self.private_name}] 增量关系评估LLM调用或解析失败: {e}") new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val)) - await self.person_info_mng.update_one_field(conversation_info.person_id, "relationship_value", new_relationship_value) - logger.info(f"[私聊][{self.private_name}] 增量关系值更新:与【{self.private_name}】的关系值从 {current_relationship_value:.2f} 调整了 {adjustment_val:.2f},变为 {new_relationship_value:.2f}") + await self.person_info_mng.update_one_field( + conversation_info.person_id, "relationship_value", new_relationship_value + ) + logger.info( + f"[私聊][{self.private_name}] 增量关系值更新:与【{self.private_name}】的关系值从 {current_relationship_value:.2f} 调整了 {adjustment_val:.2f},变为 {new_relationship_value:.2f}" + ) if conversation_info.person_id: - conversation_info.relationship_text = await self.relationship_mng.build_relationship_info(conversation_info.person_id, is_id=True) + conversation_info.relationship_text = await self.relationship_mng.build_relationship_info( + conversation_info.person_id, is_id=True + ) async def update_relationship_final( self, conversation_info: ConversationInfo, observation_info: ObservationInfo, - chat_observer_for_history: ChatObserver + chat_observer_for_history: ChatObserver, ) -> None: if not self.llm: logger.error(f"[私聊][{self.private_name}] LLM未初始化,无法进行最终关系更新。") @@ -147,14 +168,14 @@ class PfcRelationshipUpdater: if not conversation_info or not conversation_info.person_id or not observation_info: logger.debug(f"[私聊][{self.private_name}] 最终关系更新:缺少必要信息。") return - + logger.info(f"[私聊][{self.private_name}] 私聊结束,开始最终关系评估...") messages_for_eval: List[Dict[str, Any]] = [] if chat_observer_for_history: messages_for_eval = chat_observer_for_history.get_cached_messages(limit=self.REL_FINAL_MSG_COUNT) elif observation_info.chat_history: - messages_for_eval = observation_info.chat_history[-self.REL_FINAL_MSG_COUNT:] + messages_for_eval = observation_info.chat_history[-self.REL_FINAL_MSG_COUNT :] if not messages_for_eval: logger.warning(f"[私聊][{self.private_name}] 最终关系更新:没有足够的消息进行评估。") @@ -164,8 +185,12 @@ class PfcRelationshipUpdater: messages_for_eval, replace_bot_name=True, merge_messages=False, timestamp_mode="relative" ) - current_relationship_value = await self.person_info_mng.get_value(conversation_info.person_id, "relationship_value") - current_relationship_value = self.relationship_mng.ensure_float(current_relationship_value, conversation_info.person_id) + current_relationship_value = await self.person_info_mng.get_value( + conversation_info.person_id, "relationship_value" + ) + current_relationship_value = self.relationship_mng.ensure_float( + current_relationship_value, conversation_info.person_id + ) relationship_prompt = f"""你是{self.bot_name}。你与{self.private_name}的私聊刚刚结束。 你们当前的关系值大约是 {current_relationship_value:.0f} (范围通常在-1000到1000,越高越好)。 @@ -184,9 +209,11 @@ class PfcRelationshipUpdater: logger.debug(f"[私聊][{self.private_name}] 最终关系评估LLM原始返回: {content}") success, result = get_items_from_json( - content, self.private_name, "final_adjustment", + content, + self.private_name, + "final_adjustment", default_values={"final_adjustment": self.REL_FINAL_DEFAULT_CHANGE}, - required_types={"final_adjustment": (int, float)} + required_types={"final_adjustment": (int, float)}, ) raw_adjustment = result.get("final_adjustment", self.REL_FINAL_DEFAULT_CHANGE) if not isinstance(raw_adjustment, (int, float)): @@ -198,11 +225,17 @@ class PfcRelationshipUpdater: logger.error(f"[私聊][{self.private_name}] 最终关系评估LLM调用或解析失败: {e}") new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val)) - await self.person_info_mng.update_one_field(conversation_info.person_id, "relationship_value", new_relationship_value) - logger.info(f"[私聊][{self.private_name}] 最终关系值更新:与【{self.private_name}】的关系值从 {current_relationship_value:.2f} 调整了 {adjustment_val:.2f},最终为 {new_relationship_value:.2f}") - - if conversation_info.person_id: # 虽然通常结束了,但更新一下无妨 - conversation_info.relationship_text = await self.relationship_mng.build_relationship_info(conversation_info.person_id, is_id=True) + await self.person_info_mng.update_one_field( + conversation_info.person_id, "relationship_value", new_relationship_value + ) + logger.info( + f"[私聊][{self.private_name}] 最终关系值更新:与【{self.private_name}】的关系值从 {current_relationship_value:.2f} 调整了 {adjustment_val:.2f},最终为 {new_relationship_value:.2f}" + ) + + if conversation_info.person_id: # 虽然通常结束了,但更新一下无妨 + conversation_info.relationship_text = await self.relationship_mng.build_relationship_info( + conversation_info.person_id, is_id=True + ) class PfcRepationshipTranslator: @@ -210,6 +243,7 @@ class PfcRepationshipTranslator: 因为对于PFC的planner来说 其暗示了选择回复 所以新建代码文件来适配PFC的决策层面""" + def __init__(self, private_name: str): self.private_name = private_name @@ -220,22 +254,22 @@ class PfcRepationshipTranslator: level_num = self._calculate_relationship_level_num(relationship_value, self.private_name) relationship_descriptions = [ - "厌恶", # level_num 0 - "冷漠", # level_num 1 - "初识", # level_num 2 - "友好", # level_num 3 - "喜欢", # level_num 4 - "暧昧" # level_num 5 + "厌恶", # level_num 0 + "冷漠", # level_num 1 + "初识", # level_num 2 + "友好", # level_num 3 + "喜欢", # level_num 4 + "暧昧", # level_num 5 ] if 0 <= level_num < len(relationship_descriptions): description = relationship_descriptions[level_num] else: - description = "普通" # 默认或错误情况 + description = "普通" # 默认或错误情况 logger.warning(f"[私聊][{self.private_name}] 计算出的 level_num ({level_num}) 无效,关系描述默认为 '普通'") return f"你们的关系是:{description}。" - + @staticmethod def _calculate_relationship_level_num(relationship_value: float, private_name: str) -> int: """ @@ -243,7 +277,9 @@ class PfcRepationshipTranslator: 这里的阈值应与 relationship_manager.py 中的保持一致 """ if not isinstance(relationship_value, (int, float)): - logger.warning(f"[私聊][{private_name}] 传入的 relationship_value '{relationship_value}' 不是有效的数值类型,默认为0。") + logger.warning( + f"[私聊][{private_name}] 传入的 relationship_value '{relationship_value}' 不是有效的数值类型,默认为0。" + ) relationship_value = 0.0 if -1000 <= relationship_value < -227: @@ -264,7 +300,7 @@ class PfcRepationshipTranslator: level_num = 5 elif relationship_value < -1000: level_num = 0 - else: # 理论上不会到这里,除非前面的条件逻辑有误 + else: # 理论上不会到这里,除非前面的条件逻辑有误 logger.warning(f"[私聊][{private_name}] 关系值 {relationship_value} 未落入任何预设范围,默认为普通。") - level_num = 2 - return level_num \ No newline at end of file + level_num = 2 + return level_num diff --git a/src/plugins/PFC/pfc_utils.py b/src/plugins/PFC/pfc_utils.py index fb571062..47430af6 100644 --- a/src/plugins/PFC/pfc_utils.py +++ b/src/plugins/PFC/pfc_utils.py @@ -101,7 +101,7 @@ def get_items_from_json( Tuple[bool, Union[Dict[str, Any], List[Dict[str, Any]]]]: (是否成功, 提取的字段字典或字典列表) """ cleaned_content = content.strip() - result: Union[Dict[str, Any], List[Dict[str, Any]]] = {} # 初始化类型 + result: Union[Dict[str, Any], List[Dict[str, Any]]] = {} # 初始化类型 # 匹配 ```json ... ``` 或 ``` ... ``` markdown_match = re.search(r"```(?:json)?\s*([\s\S]*?)\s*```", cleaned_content, re.IGNORECASE) if markdown_match: @@ -109,12 +109,11 @@ def get_items_from_json( logger.debug(f"[私聊][{private_name}] 已去除 Markdown 标记,剩余内容: {cleaned_content[:100]}...") # --- 新增结束 --- - # 设置默认值 - default_result: Dict[str, Any] = {} # 用于单对象时的默认值 + default_result: Dict[str, Any] = {} # 用于单对象时的默认值 if default_values: default_result.update(default_values) - result = default_result.copy() # 先用默认值初始化 + result = default_result.copy() # 先用默认值初始化 # 首先尝试解析为JSON数组 if allow_array: @@ -129,17 +128,17 @@ def get_items_from_json( logger.warning(f"[私聊][{private_name}] JSON数组中的元素不是字典: {item}") continue - current_item_result = default_result.copy() # 每个元素都用默认值初始化 + current_item_result = default_result.copy() # 每个元素都用默认值初始化 valid_item = True # 提取并验证字段 for field in items: if field in item: current_item_result[field] = item[field] - elif field not in default_result: # 如果字段不存在且没有默认值 + elif field not in default_result: # 如果字段不存在且没有默认值 logger.warning(f"[私聊][{private_name}] JSON数组元素缺少必要字段 '{field}': {item}") valid_item = False - break # 这个元素无效 + break # 这个元素无效 if not valid_item: continue @@ -148,25 +147,33 @@ def get_items_from_json( if required_types: for field, expected_type in required_types.items(): # 检查 current_item_result 中是否存在该字段 (可能来自 item 或 default_values) - if field in current_item_result and not isinstance(current_item_result[field], expected_type): - logger.warning(f"[私聊][{private_name}] JSON数组元素字段 '{field}' 类型错误 (应为 {expected_type.__name__}, 实际为 {type(current_item_result[field]).__name__}): {item}") + if field in current_item_result and not isinstance( + current_item_result[field], expected_type + ): + logger.warning( + f"[私聊][{private_name}] JSON数组元素字段 '{field}' 类型错误 (应为 {expected_type.__name__}, 实际为 {type(current_item_result[field]).__name__}): {item}" + ) valid_item = False break if not valid_item: continue - + # 验证字符串不为空 (只检查 items 中要求的字段) for field in items: - if field in current_item_result and isinstance(current_item_result[field], str) and not current_item_result[field].strip(): + if ( + field in current_item_result + and isinstance(current_item_result[field], str) + and not current_item_result[field].strip() + ): logger.warning(f"[私聊][{private_name}] JSON数组元素字段 '{field}' 不能为空字符串: {item}") valid_item = False break if valid_item: - valid_items_list.append(current_item_result) # 只添加完全有效的项 + valid_items_list.append(current_item_result) # 只添加完全有效的项 - if valid_items_list: # 只有当列表不为空时才认为是成功 + if valid_items_list: # 只有当列表不为空时才认为是成功 logger.debug(f"[私聊][{private_name}] 成功解析JSON数组,包含 {len(valid_items_list)} 个有效项目。") return True, valid_items_list else: @@ -184,19 +191,18 @@ def get_items_from_json( # result 重置回单个对象的默认值 result = default_result.copy() - # 尝试解析为单个JSON对象 try: # 尝试直接解析清理后的内容 json_data = json.loads(cleaned_content) if not isinstance(json_data, dict): logger.error(f"[私聊][{private_name}] 解析为单个对象,但结果不是字典类型: {type(json_data)}") - return False, default_result # 返回失败和默认值 + return False, default_result # 返回失败和默认值 except json.JSONDecodeError: # 如果直接解析失败,尝试用正则表达式查找 JSON 对象部分 (作为后备) # 这个正则比较简单,可能无法处理嵌套或复杂的 JSON - json_pattern = r"\{[\s\S]*?\}" # 使用非贪婪匹配 + json_pattern = r"\{[\s\S]*?\}" # 使用非贪婪匹配 json_match = re.search(json_pattern, cleaned_content) if json_match: try: @@ -210,23 +216,24 @@ def get_items_from_json( logger.error(f"[私聊][{private_name}] 正则提取的部分 '{potential_json_str[:100]}...' 无法解析为JSON。") return False, default_result else: - logger.error(f"[私聊][{private_name}] 无法在返回内容中找到有效的JSON对象部分。原始内容: {cleaned_content[:100]}...") + logger.error( + f"[私聊][{private_name}] 无法在返回内容中找到有效的JSON对象部分。原始内容: {cleaned_content[:100]}..." + ) return False, default_result - # 提取并验证字段 (适用于单个JSON对象) # 确保 result 是字典类型用于更新 if not isinstance(result, dict): - result = default_result.copy() # 如果之前是列表,重置为字典 + result = default_result.copy() # 如果之前是列表,重置为字典 valid_single_object = True for item in items: if item in json_data: result[item] = json_data[item] - elif item not in default_result: # 如果字段不存在且没有默认值 + elif item not in default_result: # 如果字段不存在且没有默认值 logger.error(f"[私聊][{private_name}] JSON对象缺少必要字段 '{item}'。JSON内容: {json_data}") valid_single_object = False - break # 这个对象无效 + break # 这个对象无效 if not valid_single_object: return False, default_result @@ -235,7 +242,9 @@ def get_items_from_json( if required_types: for field, expected_type in required_types.items(): if field in result and not isinstance(result[field], expected_type): - logger.error(f"[私聊][{private_name}] JSON对象字段 '{field}' 类型错误 (应为 {expected_type.__name__}, 实际为 {type(result[field]).__name__})") + logger.error( + f"[私聊][{private_name}] JSON对象字段 '{field}' 类型错误 (应为 {expected_type.__name__}, 实际为 {type(result[field]).__name__})" + ) valid_single_object = False break @@ -251,9 +260,9 @@ def get_items_from_json( if valid_single_object: logger.debug(f"[私聊][{private_name}] 成功解析并验证了单个JSON对象。") - return True, result # 返回提取并验证后的字典 + return True, result # 返回提取并验证后的字典 else: - return False, default_result # 验证失败 + return False, default_result # 验证失败 async def get_person_id(private_name: str, chat_stream: ChatStream): @@ -264,7 +273,9 @@ async def get_person_id(private_name: str, chat_stream: ChatStream): if chat_stream.user_info: private_user_id_str = str(chat_stream.user_info.user_id) private_platform_str = chat_stream.user_info.platform - logger.info(f"[私聊][{private_name}] 从 ChatStream 获取到私聊对象信息: ID={private_user_id_str}, Platform={private_platform_str}, Name={private_nickname_str}") + logger.info( + f"[私聊][{private_name}] 从 ChatStream 获取到私聊对象信息: ID={private_user_id_str}, Platform={private_platform_str}, Name={private_nickname_str}" + ) elif chat_stream.group_info is None and private_name: pass @@ -279,19 +290,21 @@ async def get_person_id(private_name: str, chat_stream: ChatStream): person_id = await person_info_manager.get_or_create_person( platform=private_platform_str, user_id=private_user_id_int, - nickname=private_name, # 使用传入的 private_name 作为昵称 + nickname=private_name, # 使用传入的 private_name 作为昵称 ) - if person_id is None: # 如果 get_or_create_person 返回 None,说明创建失败 + if person_id is None: # 如果 get_or_create_person 返回 None,说明创建失败 logger.error(f"[私聊][{private_name}] get_or_create_person 未能获取或创建 person_id。") - return None # 返回 None 表示失败 + return None # 返回 None 表示失败 - return person_id, private_platform_str, private_user_id_str # 返回获取或创建的 person_id + return person_id, private_platform_str, private_user_id_str # 返回获取或创建的 person_id except ValueError: logger.error(f"[私聊][{private_name}] 无法将 private_user_id_str ('{private_user_id_str}') 转换为整数。") - return None # 返回 None 表示失败 + return None # 返回 None 表示失败 except Exception as e_pid: logger.error(f"[私聊][{private_name}] 获取或创建 person_id 时出错: {e_pid}") - return None # 返回 None 表示失败 + return None # 返回 None 表示失败 else: - logger.warning(f"[私聊][{private_name}] 未能确定私聊对象的 user_id 或 platform,无法获取 person_id。将在收到消息后尝试。") - return None # 返回 None 表示失败 \ No newline at end of file + logger.warning( + f"[私聊][{private_name}] 未能确定私聊对象的 user_id 或 platform,无法获取 person_id。将在收到消息后尝试。" + ) + return None # 返回 None 表示失败 diff --git a/src/plugins/PFC/reply_checker.py b/src/plugins/PFC/reply_checker.py index 47578502..aa00cd96 100644 --- a/src/plugins/PFC/reply_checker.py +++ b/src/plugins/PFC/reply_checker.py @@ -1,6 +1,6 @@ from typing import Tuple, List, Dict, Any from src.common.logger import get_module_logger -from src.config.config import global_config # 为了获取 BOT_QQ +from src.config.config import global_config # 为了获取 BOT_QQ from .chat_observer import ChatObserver logger = get_module_logger("reply_checker") @@ -15,10 +15,16 @@ class ReplyChecker: self.private_name = private_name self.chat_observer = ChatObserver.get_instance(stream_id, private_name) # self.max_retries = 3 # 这个 max_retries 属性在当前设计下不再由 checker 控制,而是由 conversation.py 控制 - self.bot_qq_str = str(global_config.BOT_QQ) # 获取机器人QQ号用于识别自身消息 + self.bot_qq_str = str(global_config.BOT_QQ) # 获取机器人QQ号用于识别自身消息 async def check( - self, reply: str, goal: str, chat_history: List[Dict[str, Any]], chat_history_text: str, current_time_str: str, retry_count: int = 0 + self, + reply: str, + goal: str, + chat_history: List[Dict[str, Any]], + chat_history_text: str, + current_time_str: str, + retry_count: int = 0, ) -> Tuple[bool, str, bool]: """检查生成的回复是否与机器人之前的发言完全一致(长度大于4) @@ -37,15 +43,14 @@ class ReplyChecker: """ if not self.bot_qq_str: logger.error(f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查机器人自身消息。") - return True, "BOT_QQ未配置,跳过重复检查。", False # 无法检查则默认通过 + return True, "BOT_QQ未配置,跳过重复检查。", False # 无法检查则默认通过 if len(reply) <= 4: return True, "消息长度小于等于4字符,跳过重复检查。", False - try: - match_found = False # <--- 用于调试 - for i, msg_dict in enumerate(chat_history): # <--- 添加索引用于日志 + match_found = False # <--- 用于调试 + for i, msg_dict in enumerate(chat_history): # <--- 添加索引用于日志 if not isinstance(msg_dict, dict): continue @@ -54,7 +59,7 @@ class ReplyChecker: continue sender_id = str(user_info_data.get("user_id")) - + if sender_id == self.bot_qq_str: historical_message_text = msg_dict.get("processed_plain_text", "") # <--- 新增详细对比日志 --- START ---> @@ -62,23 +67,20 @@ class ReplyChecker: f"[私聊][{self.private_name}] ReplyChecker: 历史记录 #{i} (机器人): '{historical_message_text}' (长度 {len(historical_message_text)})" ) if reply == historical_message_text: - logger.warning( - f"[私聊][{self.private_name}] ReplyChecker: !!! 精确匹配成功 !!!" - ) - logger.warning( - f"[私聊][{self.private_name}] ReplyChecker 检测到机器人自身重复消息: '{reply}'" - ) - match_found = True # <--- 标记找到 - return (False, "机器人尝试发送重复消息", False) + logger.warning(f"[私聊][{self.private_name}] ReplyChecker: !!! 精确匹配成功 !!!") + logger.warning(f"[私聊][{self.private_name}] ReplyChecker 检测到机器人自身重复消息: '{reply}'") + match_found = True # <--- 标记找到 + return (False, "机器人尝试发送重复消息", False) # <--- 新增详细对比日志 --- END ---> - - if not match_found: # <--- 根据标记判断 - logger.debug(f"[私聊][{self.private_name}] ReplyChecker: 未找到重复。") # <--- 新增日志 + + if not match_found: # <--- 根据标记判断 + logger.debug(f"[私聊][{self.private_name}] ReplyChecker: 未找到重复。") # <--- 新增日志 return (True, "消息内容未与机器人历史发言重复。", False) except Exception as e: import traceback + logger.error(f"[私聊][{self.private_name}] ReplyChecker 检查重复时出错: 类型={type(e)}, 值={e}") logger.error(f"[私聊][{self.private_name}]{traceback.format_exc()}") # 发生未知错误时,为安全起见,默认通过,并记录原因 - return (True, f"检查重复时发生内部错误: {str(e)}", False) \ No newline at end of file + return (True, f"检查重复时发生内部错误: {str(e)}", False) diff --git a/src/plugins/PFC/reply_generator.py b/src/plugins/PFC/reply_generator.py index 1de3e937..79942b09 100644 --- a/src/plugins/PFC/reply_generator.py +++ b/src/plugins/PFC/reply_generator.py @@ -19,13 +19,13 @@ PROMPT_GER_VARIATIONS = [ ("避免使用过于正式或书面化的词语,多用生活化的口语表达", 0.8), ("如果对方的发言比较跳跃或难以理解,可以尝试用猜测或确认的语气回应", 0.8), ("如果感觉对话有点干巴,可以尝试引入一些轻松的相关小话题或者自己的小想法,但不要偏离太远", 0.8), - ("注意观察对方的情绪(如果能从文字中判断),并作出适当的回应,比如安慰、鼓励或表示理解", 0.8), - ("", 0.10) + ("注意观察对方的情绪(如果能从文字中判断),并作出适当的回应,比如安慰、鼓励或表示理解", 0.8), + ("", 0.10), ] REPLY_STYLE1_VARIATIONS = [ ("整体风格可以平和、简短", 0.3), - ("回复可以非常简洁,有时甚至用单个词、短语或者一个反问就能表达清楚", 0.10), + ("回复可以非常简洁,有时甚至用单个词、短语或者一个反问就能表达清楚", 0.10), ("尝试使用更自然的口语连接词,例如:然后/所以呢/不过嘛/倒是", 0.05), ("在表达观点时,可以说得主观一些,例如:我觉得.../我个人感觉.../要我说...", 0.10), ("**请省略主语,简短**", 0.4), @@ -37,7 +37,7 @@ REPLY_STYLE2_VARIATIONS = [ ("不要输出任何语气词", 0.10), ("在适当的时候,可以用一些感叹词来表达情绪或态度,例如:哇/啊?/啧啧/哎呀", 0.05), ("可以模糊化表达,例如:'我记得...'", 0.10), - ("对于一些无聊或者不想深入的话题,可以敷衍一下,例如:/哦这样啊/还行吧/随便啦", 0.10), + ("对于一些无聊或者不想深入的话题,可以敷衍一下,例如:/哦这样啊/还行吧/随便啦", 0.10), ("尽量用简单句和短句", 0.25), ("不要输出任何标点符号,简短", 0.30), ] @@ -156,7 +156,7 @@ class ReplyGenerator: self.llm = LLMRequest( model=global_config.llm_PFC_chat, temperature=global_config.llm_PFC_chat["temp"], - max_tokens=300, # 对于JSON输出,这个可能需要适当调整,但一般回复短,JSON结构也简单 + max_tokens=300, # 对于JSON输出,这个可能需要适当调整,但一般回复短,JSON结构也简单 request_type="reply_generation", ) self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3) @@ -184,10 +184,20 @@ class ReplyGenerator: logger.debug( f"[私聊][{self.private_name}]开始生成回复 (动作类型: {action_type}):当前目标: {conversation_info.goal_list}" ) - - chosen_prompt_ger = random.choices([style[0] for style in PROMPT_GER_VARIATIONS], weights=[style[1] for style in PROMPT_GER_VARIATIONS], k=1)[0] - chosen_reply_style1 = random.choices([style[0] for style in REPLY_STYLE1_VARIATIONS], weights=[style[1] for style in REPLY_STYLE1_VARIATIONS], k=1)[0] - chosen_reply_style2 = random.choices([style[0] for style in REPLY_STYLE2_VARIATIONS], weights=[style[1] for style in REPLY_STYLE2_VARIATIONS], k=1)[0] + + chosen_prompt_ger = random.choices( + [style[0] for style in PROMPT_GER_VARIATIONS], weights=[style[1] for style in PROMPT_GER_VARIATIONS], k=1 + )[0] + chosen_reply_style1 = random.choices( + [style[0] for style in REPLY_STYLE1_VARIATIONS], + weights=[style[1] for style in REPLY_STYLE1_VARIATIONS], + k=1, + )[0] + chosen_reply_style2 = random.choices( + [style[0] for style in REPLY_STYLE2_VARIATIONS], + weights=[style[1] for style in REPLY_STYLE2_VARIATIONS], + k=1, + )[0] # --- 构建通用 Prompt 参数 --- goals_str = "" @@ -220,16 +230,14 @@ class ReplyGenerator: elif not chat_history_text: chat_history_text = "还没有聊天记录。" else: - chat_history_text += ( - "\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n" - ) + chat_history_text += "\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n" - sender_name_str = getattr(observation_info, 'sender_name', '对方') - if not sender_name_str: - sender_name_str = '对方' + sender_name_str = getattr(observation_info, "sender_name", "对方") + if not sender_name_str: + sender_name_str = "对方" - relationship_text_str = getattr(conversation_info, 'relationship_text', '你们还不熟悉。') - current_emotion_text_str = getattr(conversation_info, 'current_emotion_text', '心情平静。') + relationship_text_str = getattr(conversation_info, "relationship_text", "你们还不熟悉。") + current_emotion_text_str = getattr(conversation_info, "current_emotion_text", "心情平静。") persona_text = f"你的名字是{self.name},{self.personality_info}。" retrieval_context = chat_history_text @@ -270,10 +278,10 @@ class ReplyGenerator: f" 内容: {last_content}\n" f" 原因: {last_reason}" ) - + # 新增:构建刷屏警告信息 for PROMPT_SEND_NEW_MESSAGE spam_warning_message = "" - if action_type == "send_new_message": # 只在 send_new_message 时构建刷屏警告 + if action_type == "send_new_message": # 只在 send_new_message 时构建刷屏警告 if conversation_info.my_message_count > 5: spam_warning_message = f"⚠️【警告】**你已连续发送{str(conversation_info.my_message_count)}条消息!请谨慎考虑是否继续发送!以免刷屏对造成对方困扰!**" elif conversation_info.my_message_count > 2: @@ -281,7 +289,6 @@ class ReplyGenerator: if spam_warning_message: spam_warning_message = f"\n{spam_warning_message}\n" - # --- 选择 Prompt --- if action_type == "send_new_message": prompt_template = PROMPT_SEND_NEW_MESSAGE @@ -289,22 +296,24 @@ class ReplyGenerator: elif action_type == "say_goodbye": prompt_template = PROMPT_FAREWELL logger.info(f"[私聊][{self.private_name}]使用 PROMPT_FAREWELL (告别语生成)") - else: + else: prompt_template = PROMPT_DIRECT_REPLY logger.info(f"[私聊][{self.private_name}]使用 PROMPT_DIRECT_REPLY (首次/非连续回复生成)") # --- 格式化最终的 Prompt --- try: current_time_value = "获取时间失败" - if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str: + if observation_info and hasattr(observation_info, "current_time_str") and observation_info.current_time_str: current_time_value = observation_info.current_time_str - + base_format_params = { "persona_text": persona_text, "goals_str": goals_str, "chat_history_text": chat_history_text, - "retrieved_memory_str": retrieved_memory_str if retrieved_memory_str else "无相关记忆。", # 确保已定义 - "retrieved_knowledge_str": retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。", # 确保已定义 + "retrieved_memory_str": retrieved_memory_str if retrieved_memory_str else "无相关记忆。", # 确保已定义 + "retrieved_knowledge_str": retrieved_knowledge_str + if retrieved_knowledge_str + else "无相关知识。", # 确保已定义 "last_rejection_info": last_rejection_info_str, "current_time_str": current_time_value, "sender_name": sender_name_str, @@ -318,17 +327,24 @@ class ReplyGenerator: if action_type == "send_new_message": current_format_params = base_format_params.copy() current_format_params["spam_warning_info"] = spam_warning_message - prompt = prompt_template.format(**current_format_params) + prompt = prompt_template.format(**current_format_params) elif action_type == "say_goodbye": farewell_params = { - k: v for k, v in base_format_params.items() if k in [ - "persona_text", "chat_history_text", "current_time_str", - "sender_name", "relationship_text", "current_emotion_text", + k: v + for k, v in base_format_params.items() + if k + in [ + "persona_text", + "chat_history_text", + "current_time_str", + "sender_name", + "relationship_text", + "current_emotion_text", ] } - + prompt = prompt_template.format(**farewell_params) - else: # direct_reply + else: # direct_reply current_format_params = base_format_params.copy() prompt = prompt_template.format(**current_format_params) @@ -336,7 +352,7 @@ class ReplyGenerator: logger.error( f"[私聊][{self.private_name}]格式化 Prompt 时出错,缺少键: {e}。请检查 Prompt 模板和传递的参数。" ) - return "抱歉,准备回复时出了点问题,请检查一下我的代码..." # 对于JSON期望的场景,这里可能也需要返回一个固定的错误JSON + return "抱歉,准备回复时出了点问题,请检查一下我的代码..." # 对于JSON期望的场景,这里可能也需要返回一个固定的错误JSON except Exception as fmt_err: logger.error(f"[私聊][{self.private_name}]格式化 Prompt 时发生未知错误: {fmt_err}") return "抱歉,准备回复时出了点内部错误,请检查一下我的代码..." @@ -361,4 +377,4 @@ class ReplyGenerator: "txt": "LLM生成回复时出错" }}""".strip() else: - return "抱歉,我现在有点混乱,让我重新思考一下..." \ No newline at end of file + return "抱歉,我现在有点混乱,让我重新思考一下..."