diff --git a/src/chat/chat_loop/heartFC_chat.py b/src/chat/chat_loop/heartFC_chat.py index 5e1f44118..c60772e8b 100644 --- a/src/chat/chat_loop/heartFC_chat.py +++ b/src/chat/chat_loop/heartFC_chat.py @@ -607,26 +607,32 @@ class HeartFChatting: available_actions=available_actions, ) - - - # 3. 并行执行所有动作 - async def execute_action(action_info,actions): - """执行单个动作的通用函数""" - try: - if action_info["action_type"] == "no_reply": - # 直接处理no_reply逻辑,不再通过动作系统 - reason = action_info.get("reasoning", "选择不回复") - logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}") - - # 存储no_reply信息到数据库 - await database_api.store_action_info( - chat_stream=self.chat_stream, - action_build_into_prompt=False, - action_prompt_display=reason, - action_done=True, - thinking_id=thinking_id, - action_data={"reason": reason}, - action_name="no_reply", + action_data["loop_start_time"] = loop_start_time + + # 在私聊的专注模式下,如果规划动作为no_reply,则强制改为reply + is_private_chat = self.chat_stream.group_info is None + if self.loop_mode == ChatMode.FOCUS and is_private_chat and action_type == "no_reply": + action_type = "reply" + logger.info(f"{self.log_prefix} 私聊专注模式下强制回复") + + if action_type == "reply": + logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复") + elif is_parallel: + logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复, 同时执行{action_type}动作") + else: + # 只有在gen_task存在时才进行相关操作 + if gen_task: + if not gen_task.done(): + gen_task.cancel() + logger.debug(f"{self.log_prefix} 已取消预生成的回复任务") + logger.info( + f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复,但选择执行{action_type},不发表回复" + ) + elif generation_result := gen_task.result(): + content = " ".join([item[1] for item in generation_result if item[0] == "text"]) + logger.debug(f"{self.log_prefix} 预生成的回复任务已完成") + logger.info( + f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复" ) return { diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py new file mode 100644 index 000000000..2da146b0c --- /dev/null +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -0,0 +1,81 @@ +from typing import Tuple, List +from collections import deque + +# 导入新插件系统 +from src.plugin_system import BaseAction, ActionActivationType, ChatMode +from src.plugin_system.base.component_types import ChatType + +# 导入依赖的系统组件 +from src.common.logger import get_logger + + +logger = get_logger("no_reply_action") + + +class NoReplyAction(BaseAction): + """不回复动作,支持waiting和breaking两种形式.""" + + focus_activation_type = ActionActivationType.NEVER + normal_activation_type = ActionActivationType.NEVER + mode_enable = ChatMode.FOCUS + parallel_action = False + + # 动作基本信息 + action_name = "no_reply" + action_description = "暂时不回复消息" + + # 最近三次no_reply的新消息兴趣度记录 + _recent_interest_records: deque = deque(maxlen=3) + + # 兴趣值退出阈值 + _interest_exit_threshold = 3.0 + # 消息数量退出阈值 + _min_exit_message_count = 3 + _max_exit_message_count = 6 + + # 动作参数定义 + action_parameters = {} + + # 动作使用场景 + action_require = [""] + + # 关联类型 + associated_types = [] + + async def execute(self) -> Tuple[bool, str]: + """执行不回复动作""" + + try: + reason = self.action_data.get("reason", "") + + logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}") + + await self.store_action_info( + action_build_into_prompt=False, + action_prompt_display=reason, + action_done=True, + ) + return True, reason + + except Exception as e: + logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}") + exit_reason = f"执行异常: {str(e)}" + full_prompt = f"no_reply执行异常: {exit_reason},你思考是否要进行回复" + await self.store_action_info( + action_build_into_prompt=True, + action_prompt_display=full_prompt, + action_done=True, + ) + return False, f"不回复动作执行失败: {e}" + + @classmethod + def reset_consecutive_count(cls): + """重置连续计数器和兴趣度记录""" + cls._recent_interest_records.clear() + logger.debug("NoReplyAction连续计数器和兴趣度记录已重置") + + @classmethod + def get_recent_interest_records(cls) -> List[float]: + """获取最近的兴趣度记录""" + return list(cls._recent_interest_records) +