From efe81fa346e6bcd18851f2ad985ef1aa677c0f5a Mon Sep 17 00:00:00 2001 From: Windpicker-owo <3431391539@qq.com> Date: Wed, 3 Sep 2025 22:19:00 +0800 Subject: [PATCH] =?UTF-8?q?fix(chat):=20=E4=BC=98=E5=8C=96breaking?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E7=9A=84=E5=85=B4=E8=B6=A3=E5=80=BC?= =?UTF-8?q?=E7=B4=AF=E7=A7=AF=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构heartFC_chat中的消息处理机制,使用累积兴趣值替代最近三次记录来判断是否进入breaking模式。主要变更包括: - 将breaking模式判断基于累积兴趣值而非最近3次记录 - 在消息成功处理时重置累积兴趣值 - 调整阈值计算方式,使用聊天频率进行动态调整 - 修复send_api中的消息查找函数,提高回复消息匹配准确性 这些改动提高了对话节奏控制的稳定性,使breaking模式触发更加合理。 --- src/chat/chat_loop/cycle_processor.py | 2 +- src/chat/chat_loop/heartFC_chat.py | 65 ++++++----- src/chat/chat_loop/hfc_context.py | 8 +- src/chat/planner_actions/planner.py | 10 +- src/plugin_system/apis/send_api.py | 157 ++++++++------------------ 5 files changed, 105 insertions(+), 137 deletions(-) diff --git a/src/chat/chat_loop/cycle_processor.py b/src/chat/chat_loop/cycle_processor.py index 3d8f44dc0..637c90c85 100644 --- a/src/chat/chat_loop/cycle_processor.py +++ b/src/chat/chat_loop/cycle_processor.py @@ -128,7 +128,7 @@ class CycleProcessor: x0 = 1.0 # 控制曲线中心点 return 1.0 / (1.0 + math.exp(-k * (interest_val - x0))) - normal_mode_probability = calculate_normal_mode_probability(interest_value) / global_config.chat.get_current_talk_frequency(self.context.stream_id) + normal_mode_probability = calculate_normal_mode_probability(interest_value) * 0.5 / global_config.chat.get_current_talk_frequency(self.context.stream_id) # 根据概率决定使用哪种模式 if random.random() < normal_mode_probability: diff --git a/src/chat/chat_loop/heartFC_chat.py b/src/chat/chat_loop/heartFC_chat.py index b8922d092..c7fd0c2ee 100644 --- a/src/chat/chat_loop/heartFC_chat.py +++ b/src/chat/chat_loop/heartFC_chat.py @@ -255,10 +255,12 @@ class HeartFChatting: for message in recent_messages: await self.cycle_processor.observe(interest_value = interest_value) - # 如果成功观察,增加能量值 + # 如果成功观察,增加能量值并重置累积兴趣值 if has_new_messages: self.context.energy_value += 1 / global_config.chat.focus_value - logger.info(f"{self.context.log_prefix} 能量值增加,当前能量值:{self.context.energy_value:.1f}") + # 重置累积兴趣值,因为消息已经被成功处理 + self.context.breaking_accumulated_interest = 0.0 + logger.info(f"{self.context.log_prefix} 能量值增加,当前能量值:{self.context.energy_value:.1f},重置累积兴趣值") self._check_focus_exit() @@ -384,61 +386,70 @@ class HeartFChatting: if self.context.no_reply_consecutive <= 3: self.context.focus_energy = 1 else: - # 计算最近三次记录的兴趣度总和 - total_recent_interest = sum(self.recent_interest_records) - - # 获取当前聊天频率和意愿系数 - talk_frequency = global_config.chat.get_current_talk_frequency(self.context.stream_id) - + # 使用累积兴趣值而不是最近3次的记录 + total_interest = self.context.breaking_accumulated_interest + # 计算调整后的阈值 - adjusted_threshold = 3 / talk_frequency + adjusted_threshold = 1 / global_config.chat.get_current_talk_frequency(self.context.stream_id) - logger.info(f"{self.context.log_prefix} 最近三次兴趣度总和: {total_recent_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}") + logger.info(f"{self.context.log_prefix} 累积兴趣值: {total_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}") - # 如果兴趣度总和小于阈值,进入breaking形式 - if total_recent_interest < adjusted_threshold: - logger.info(f"{self.context.log_prefix} 兴趣度不足,进入breaking形式") + # 如果累积兴趣值小于阈值,进入breaking形式 + if total_interest < adjusted_threshold: + logger.info(f"{self.context.log_prefix} 累积兴趣度不足,进入breaking形式") self.context.focus_energy = random.randint(3, 6) else: - logger.info(f"{self.context.log_prefix} 兴趣度充足") + logger.info(f"{self.context.log_prefix} 累积兴趣度充足,使用waiting形式") self.context.focus_energy = 1 async def _should_process_messages(self, new_message: List[Dict[str, Any]]) -> tuple[bool,float]: """ 统一判断是否应该处理消息的函数 根据当前循环模式和消息内容决定是否继续处理 - """ + """ new_message_count = len(new_message) - # talk_frequency = global_config.chat.get_current_talk_frequency(self.context.chat_stream.stream_id) - modified_exit_count_threshold = self.context.focus_energy / global_config.chat.focus_value - modified_exit_interest_threshold = 3 / global_config.chat.focus_value - total_interest = 0.0 + talk_frequency = global_config.chat.get_current_talk_frequency(self.context.chat_stream.stream_id) + + modified_exit_count_threshold = self.context.focus_energy * 0.5 / talk_frequency + modified_exit_interest_threshold = 1.5 / talk_frequency + + # 计算当前批次消息的兴趣值 + batch_interest = 0.0 for msg_dict in new_message: interest_value = msg_dict.get("interest_value", 0.0) if msg_dict.get("processed_plain_text", ""): - total_interest += interest_value + batch_interest += interest_value + + # 在breaking形式下累积所有消息的兴趣值 + if new_message_count > 0: + self.context.breaking_accumulated_interest += batch_interest + total_interest = self.context.breaking_accumulated_interest + else: + total_interest = self.context.breaking_accumulated_interest if new_message_count >= modified_exit_count_threshold: - # 记录兴趣度到列表 + # 记录兴趣度到列表 self.recent_interest_records.append(total_interest) + # 重置累积兴趣值,因为已经达到了消息数量阈值 + self.context.breaking_accumulated_interest = 0.0 logger.info( - f"{self.context.log_prefix} 累计消息数量达到{new_message_count}条(>{modified_exit_count_threshold:.1f}),结束等待" + f"{self.context.log_prefix} 累计消息数量达到{new_message_count}条(>{modified_exit_count_threshold:.1f}),结束等待,累积兴趣值: {total_interest:.2f}" ) - # logger.info(self.context.last_read_time) - # logger.info(new_message) return True,total_interest/new_message_count # 检查累计兴趣值 if new_message_count > 0: # 只在兴趣值变化时输出log if not hasattr(self, "_last_accumulated_interest") or total_interest != self._last_accumulated_interest: - logger.info(f"{self.context.log_prefix} breaking形式当前累计兴趣值: {total_interest:.2f}, 专注度: {global_config.chat.focus_value:.1f}") + logger.info(f"{self.context.log_prefix} breaking形式当前累积兴趣值: {total_interest:.2f}, 专注度: {global_config.chat.focus_value:.1f}") self._last_accumulated_interest = total_interest if total_interest >= modified_exit_interest_threshold: - # 记录兴趣度到列表 + # 记录兴趣度到列表 self.recent_interest_records.append(total_interest) + # 重置累积兴趣值,因为已经达到了兴趣值阈值 + self.context.breaking_accumulated_interest = 0.0 logger.info( f"{self.context.log_prefix} 累计兴趣值达到{total_interest:.2f}(>{modified_exit_interest_threshold:.1f}),结束等待" ) @@ -447,7 +458,7 @@ class HeartFChatting: # 每10秒输出一次等待状态 if int(time.time() - self.context.last_read_time) > 0 and int(time.time() - self.context.last_read_time) % 10 == 0: logger.info( - f"{self.context.log_prefix} 已等待{time.time() - self.last_read_time:.0f}秒,累计{new_message_count}条消息,累计兴趣{total_interest:.1f},继续等待..." + f"{self.context.log_prefix} 已等待{time.time() - self.last_read_time:.0f}秒,累计{new_message_count}条消息,累积兴趣{total_interest:.1f},继续等待..." ) await asyncio.sleep(0.5) diff --git a/src/chat/chat_loop/hfc_context.py b/src/chat/chat_loop/hfc_context.py index a4cf2a313..c924f713d 100644 --- a/src/chat/chat_loop/hfc_context.py +++ b/src/chat/chat_loop/hfc_context.py @@ -47,6 +47,9 @@ class HfcContext: self.last_message_time = time.time() self.last_read_time = time.time() - 10 + + # 从聊天流恢复breaking累积兴趣值 + self.breaking_accumulated_interest = getattr(self.chat_stream, 'breaking_accumulated_interest', 0.0) self.action_manager = ActionManager() @@ -63,6 +66,8 @@ class HfcContext: self.focus_energy = 1 self.no_reply_consecutive = 0 self.total_interest = 0.0 + # breaking形式下的累积兴趣值 + self.breaking_accumulated_interest = 0.0 # 引用HeartFChatting实例,以便其他组件可以调用其方法 self.chat_instance = None @@ -72,4 +77,5 @@ class HfcContext: self.chat_stream.energy_value = self.energy_value self.chat_stream.sleep_pressure = self.sleep_pressure self.chat_stream.focus_energy = self.focus_energy - self.chat_stream.no_reply_consecutive = self.no_reply_consecutive \ No newline at end of file + self.chat_stream.no_reply_consecutive = self.no_reply_consecutive + self.chat_stream.breaking_accumulated_interest = self.breaking_accumulated_interest \ No newline at end of file diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index ff87ec0d5..291c19a66 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -480,10 +480,18 @@ class ActionPlanner: mentioned_bonus = "\n- 有人提到你,或者at你" if mode == ChatMode.FOCUS: - no_action_block = """重要说明: + no_action_block = """ - 'no_reply' 表示不进行回复,等待合适的回复时机 - 当你刚刚发送了消息,没有人回复时,选择no_reply - 当你一次发送了太多消息,为了避免打扰聊天节奏,选择no_reply +动作:no_reply +动作描述:不进行回复,等待合适的回复时机 +- 当你刚刚发送了消息,没有人回复时,选择no_reply +- 当你一次发送了太多消息,为了避免打扰聊天节奏,选择no_reply +{{ + "action": "no_reply", + "reason":"不回复的原因" +}} """ else: # NORMAL Mode no_action_block = """重要说明: diff --git a/src/plugin_system/apis/send_api.py b/src/plugin_system/apis/send_api.py index f21c8abb7..c652383fb 100644 --- a/src/plugin_system/apis/send_api.py +++ b/src/plugin_system/apis/send_api.py @@ -51,6 +51,55 @@ logger = get_logger("send_api") # 适配器命令响应等待池 _adapter_response_pool: Dict[str, asyncio.Future] = {} +def message_dict_to_message_recv(message_dict: Dict[str, Any]) -> Optional[MessageRecv]: + """查找要回复的消息 + + Args: + message_dict: 消息字典 + + Returns: + Optional[MessageRecv]: 找到的消息,如果没找到则返回None + """ + # 构建MessageRecv对象 + user_info = { + "platform": message_dict.get("user_platform", ""), + "user_id": message_dict.get("user_id", ""), + "user_nickname": message_dict.get("user_nickname", ""), + "user_cardname": message_dict.get("user_cardname", ""), + } + + group_info = {} + if message_dict.get("chat_info_group_id"): + group_info = { + "platform": message_dict.get("chat_info_group_platform", ""), + "group_id": message_dict.get("chat_info_group_id", ""), + "group_name": message_dict.get("chat_info_group_name", ""), + } + + format_info = {"content_format": "", "accept_format": ""} + template_info = {"template_items": {}} + + message_info = { + "platform": message_dict.get("chat_info_platform", ""), + "message_id": message_dict.get("message_id"), + "time": message_dict.get("time"), + "group_info": group_info, + "user_info": user_info, + "additional_config": message_dict.get("additional_config"), + "format_info": format_info, + "template_info": template_info, + } + + message_dict = { + "message_info": message_info, + "raw_message": message_dict.get("processed_plain_text"), + "processed_plain_text": message_dict.get("processed_plain_text"), + } + + message_recv = MessageRecv(message_dict) + + logger.info(f"[SendAPI] 找到匹配的回复消息,发送者: {message_dict.get('user_nickname', '')}") + return message_recv def put_adapter_response(request_id: str, response_data: dict) -> None: """将适配器响应放入响应池""" @@ -139,7 +188,7 @@ async def _send_to_target( message_segment = Seg(type=message_type, data=content) # type: ignore if reply_to_message: - anchor_message = MessageRecv(message_dict=reply_to_message) + anchor_message = message_dict_to_message_recv(message_dict=reply_to_message) anchor_message.update_chat_stream(target_stream) reply_to_platform_id = ( f"{anchor_message.message_info.platform}:{anchor_message.message_info.user_info.user_id}" @@ -184,112 +233,6 @@ async def _send_to_target( return False -async def _find_reply_message(target_stream, reply_to: str) -> Optional[MessageRecv]: - # sourcery skip: inline-variable, use-named-expression - """查找要回复的消息 - - Args: - target_stream: 目标聊天流 - reply_to: 回复格式,如"发送者:消息内容"或"发送者:消息内容" - - Returns: - Optional[MessageRecv]: 找到的消息,如果没找到则返回None - """ - try: - # 解析reply_to参数 - if ":" in reply_to: - parts = reply_to.split(":", 1) - elif ":" in reply_to: - parts = reply_to.split(":", 1) - else: - logger.warning(f"[SendAPI] reply_to格式不正确: {reply_to}") - return None - - if len(parts) != 2: - logger.warning(f"[SendAPI] reply_to格式不正确: {reply_to}") - return None - - sender = parts[0].strip() - text = parts[1].strip() - - # 获取聊天流的最新20条消息 - reverse_talking_message = get_raw_msg_before_timestamp_with_chat( - target_stream.stream_id, - time.time(), # 当前时间之前的消息 - 20, # 最新的20条消息 - ) - - # 反转列表,使最新的消息在前面 - reverse_talking_message = list(reversed(reverse_talking_message)) - - find_msg = None - for message in reverse_talking_message: - user_id = message["user_id"] - platform = message["chat_info_platform"] - person_id = get_person_info_manager().get_person_id(platform, user_id) - person_name = await get_person_info_manager().get_value(person_id, "person_name") - if person_name == sender: - translate_text = message["processed_plain_text"] - - # 使用独立函数处理用户引用格式 - translate_text = await replace_user_references_async(translate_text, platform) - - similarity = difflib.SequenceMatcher(None, text, translate_text).ratio() - if similarity >= 0.9: - find_msg = message - break - - if not find_msg: - logger.info("[SendAPI] 未找到匹配的回复消息") - return None - - # 构建MessageRecv对象 - user_info = { - "platform": find_msg.get("user_platform", ""), - "user_id": find_msg.get("user_id", ""), - "user_nickname": find_msg.get("user_nickname", ""), - "user_cardname": find_msg.get("user_cardname", ""), - } - - group_info = {} - if find_msg.get("chat_info_group_id"): - group_info = { - "platform": find_msg.get("chat_info_group_platform", ""), - "group_id": find_msg.get("chat_info_group_id", ""), - "group_name": find_msg.get("chat_info_group_name", ""), - } - - format_info = {"content_format": "", "accept_format": ""} - template_info = {"template_items": {}} - - message_info = { - "platform": target_stream.platform, - "message_id": find_msg.get("message_id"), - "time": find_msg.get("time"), - "group_info": group_info, - "user_info": user_info, - "additional_config": find_msg.get("additional_config"), - "format_info": format_info, - "template_info": template_info, - } - - message_dict = { - "message_info": message_info, - "raw_message": find_msg.get("processed_plain_text"), - "processed_plain_text": find_msg.get("processed_plain_text"), - } - - find_rec_msg = MessageRecv(message_dict) - find_rec_msg.update_chat_stream(target_stream) - - logger.info(f"[SendAPI] 找到匹配的回复消息,发送者: {sender}") - return find_rec_msg - - except Exception as e: - logger.error(f"[SendAPI] 查找回复消息时出错: {e}") - traceback.print_exc() - return None - # ============================================================================= # 公共API函数 - 预定义类型的发送函数