diff --git a/src/chat/chat_loop/cycle_processor.py b/src/chat/chat_loop/cycle_processor.py index fe993f484..581de2409 100644 --- a/src/chat/chat_loop/cycle_processor.py +++ b/src/chat/chat_loop/cycle_processor.py @@ -135,6 +135,16 @@ class CycleProcessor: action_type = "no_action" reply_text = "" # 初始化reply_text变量,避免UnboundLocalError + + # 开始新的思考循环 + cycle_timers, thinking_id = self.cycle_tracker.start_cycle() + logger.info(f"{self.log_prefix} 开始第{self.context.cycle_counter}次思考") + + if ENABLE_S4U and self.context.chat_stream and self.context.chat_stream.user_info: + await send_typing(self.context.chat_stream.user_info.user_id) + + loop_start_time = time.time() + # 使用sigmoid函数将interest_value转换为概率 # 当interest_value为0时,概率接近0(使用Focus模式) # 当interest_value很高时,概率接近1(使用Normal模式) @@ -188,7 +198,7 @@ class CycleProcessor: # 第一步:动作修改 with Timer("动作修改", cycle_timers): try: - await self.action_modifier.modify_actions() + await self.action_modifier.modify_actions(mode=mode) available_actions = self.context.action_manager.get_using_actions() except Exception as e: logger.error(f"{self.context.log_prefix} 动作修改失败: {e}") diff --git a/src/chat/planner_actions/action_modifier.py b/src/chat/planner_actions/action_modifier.py index e9cc1d106..5d4cd636c 100644 --- a/src/chat/planner_actions/action_modifier.py +++ b/src/chat/planner_actions/action_modifier.py @@ -10,7 +10,7 @@ from src.llm_models.utils_model import LLMRequest from src.chat.message_receive.chat_stream import get_chat_manager, ChatMessageContext from src.chat.planner_actions.action_manager import ActionManager from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages -from src.plugin_system.base.component_types import ActionInfo, ActionActivationType +from src.plugin_system.base.component_types import ActionInfo, ActionActivationType, ChatMode from src.plugin_system.core.global_announcement_manager import global_announcement_manager if TYPE_CHECKING: @@ -43,10 +43,7 @@ class ActionModifier: self._cache_expiry_time = 30 # 缓存过期时间(秒) self._last_context_hash = None # 上次上下文的哈希值 - async def modify_actions( - self, - message_content: str = "", - ): # sourcery skip: use-named-expression + async def modify_actions(self, mode: ChatMode, message_content: str = ""): """ 动作修改流程,整合传统观察处理和新的激活类型判定 @@ -146,6 +143,7 @@ class ActionModifier: removals_s3 = await self._get_deactivated_actions_by_type( current_using_actions, chat_content, + mode, ) # 应用第三阶段的移除 @@ -178,6 +176,7 @@ class ActionModifier: self, actions_with_info: Dict[str, ActionInfo], chat_content: str = "", + mode: ChatMode = ChatMode.NORMAL, ) -> List[tuple[str, str]]: """ 根据激活类型过滤,返回需要停用的动作列表及原因 @@ -198,34 +197,26 @@ class ActionModifier: random.shuffle(actions_to_check) for action_name, action_info in actions_to_check: - activation_type = action_info.activation_type or action_info.focus_activation_type + if mode == ChatMode.FOCUS: + activation_type = action_info.focus_activation_type + else: + activation_type = action_info.normal_activation_type if activation_type == ActionActivationType.ALWAYS: - continue # 总是激活,无需处理 - + continue elif activation_type == ActionActivationType.RANDOM: - probability = action_info.random_activation_probability - probability = action_info.random_activation_probability - if random.random() >= probability: - reason = f"RANDOM类型未触发(概率{probability})" - deactivated_actions.append((action_name, reason)) - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}") - + if random.random() >= action_info.random_activation_probability: + deactivated_actions.append((action_name, f"RANDOM类型未触发(概率{action_info.random_activation_probability})")) elif activation_type == ActionActivationType.KEYWORD: if not self._check_keyword_activation(action_name, action_info, chat_content): - keywords = action_info.activation_keywords - reason = f"关键词未匹配(关键词: {keywords})" - deactivated_actions.append((action_name, reason)) - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}") - + deactivated_actions.append((action_name, f"关键词未匹配(关键词: {action_info.activation_keywords})")) elif activation_type == ActionActivationType.LLM_JUDGE: llm_judge_actions[action_name] = action_info - + elif activation_type == ActionActivationType.KEYWORD_OR_LLM_JUDGE: + if not self._check_keyword_activation(action_name, action_info, chat_content): + llm_judge_actions[action_name] = action_info elif activation_type == ActionActivationType.NEVER: - reason = "激活类型为never" - deactivated_actions.append((action_name, reason)) - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: 激活类型为never") - + deactivated_actions.append((action_name, "激活类型为never")) else: logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理") diff --git a/src/chat/planner_actions/plan_generator.py b/src/chat/planner_actions/plan_generator.py index 5dd1b680c..740d5bb98 100644 --- a/src/chat/planner_actions/plan_generator.py +++ b/src/chat/planner_actions/plan_generator.py @@ -2,14 +2,14 @@ PlanGenerator: 负责搜集和汇总所有决策所需的信息,生成一个未经筛选的“原始计划” (Plan)。 """ import time -from typing import Dict, Optional, Tuple +from typing import Dict, Optional, Tuple, List from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat from src.chat.utils.utils import get_chat_type_and_target_info from src.common.data_models.database_data_model import DatabaseMessages from src.common.data_models.info_data_model import Plan, TargetPersonInfo from src.config.config import global_config -from src.plugin_system.base.component_types import ActionInfo, ChatMode, ComponentType +from src.plugin_system.base.component_types import ActionActivationType, ActionInfo, ChatMode, ComponentType from src.plugin_system.core.component_registry import component_registry @@ -57,13 +57,13 @@ class PlanGenerator: if chat_target_info_dict: target_info = TargetPersonInfo(**chat_target_info_dict) - available_actions = self._get_available_actions() chat_history_raw = get_raw_msg_before_timestamp_with_chat( chat_id=self.chat_id, timestamp=time.time(), limit=int(global_config.chat.max_context_size), ) chat_history = [DatabaseMessages(**msg) for msg in chat_history_raw] + available_actions = self._get_available_actions(mode, chat_history) plan = Plan( @@ -75,36 +75,41 @@ class PlanGenerator: ) return plan - def _get_available_actions(self) -> Dict[str, "ActionInfo"]: + def _get_available_actions(self, mode: ChatMode, chat_history: List[DatabaseMessages]) -> Dict[str, "ActionInfo"]: """ - 从 ActionManager 和组件注册表中获取当前所有可用的动作。 - - 它会合并已注册的动作和系统级动作(如 "no_reply"), - 并以字典形式返回。 - - Returns: - Dict[str, "ActionInfo"]: 一个字典,键是动作名称,值是 ActionInfo 对象。 + 根据当前的聊天模式和激活类型,筛选出可用的动作。 """ - current_available_actions_dict = self.action_manager.get_using_actions() - all_registered_actions: Dict[str, ActionInfo] = component_registry.get_components_by_type( # type: ignore - ComponentType.ACTION - ) - - current_available_actions = {} - for action_name in current_available_actions_dict: - if action_name in all_registered_actions: - current_available_actions[action_name] = all_registered_actions[action_name] + all_actions: Dict[str, ActionInfo] = component_registry.get_components_by_type(ComponentType.ACTION) # type: ignore + available_actions = {} + latest_message_text = chat_history[-1].processed_plain_text if chat_history else "" + for name, info in all_actions.items(): + # 根据当前模式选择对应的激活类型 + activation_type = info.focus_activation_type if mode == ChatMode.FOCUS else info.normal_activation_type + + if activation_type in [ActionActivationType.ALWAYS, ActionActivationType.LLM_JUDGE]: + available_actions[name] = info + elif activation_type == ActionActivationType.KEYWORD: + if any(kw.lower() in latest_message_text.lower() for kw in info.activation_keywords): + available_actions[name] = info + elif activation_type == ActionActivationType.KEYWORD_OR_LLM_JUDGE: + if any(kw.lower() in latest_message_text.lower() for kw in info.activation_keywords): + available_actions[name] = info + else: + # 即使关键词不匹配,也将其添加到可用动作中,交由LLM判断 + available_actions[name] = info + elif activation_type == ActionActivationType.NEVER: + pass # 永不激活 + else: + logger.warning(f"未知的激活类型: {activation_type},跳过处理") + + # 添加系统级动作 no_reply_info = ActionInfo( name="no_reply", component_type=ComponentType.ACTION, description="系统级动作:选择不回复消息的决策", - action_parameters={}, - activation_keywords=[], plugin_name="SYSTEM", - enabled=True, - parallel_action=False, ) - current_available_actions["no_reply"] = no_reply_info - - return current_available_actions \ No newline at end of file + available_actions["no_reply"] = no_reply_info + + return available_actions \ No newline at end of file diff --git a/src/plugin_system/base/base_action.py b/src/plugin_system/base/base_action.py index 14bfb19d2..97a0495c3 100644 --- a/src/plugin_system/base/base_action.py +++ b/src/plugin_system/base/base_action.py @@ -459,7 +459,6 @@ class BaseAction(ABC): focus_activation_type = getattr(cls, "focus_activation_type", ActionActivationType.ALWAYS) normal_activation_type = getattr(cls, "normal_activation_type", ActionActivationType.ALWAYS) - # 处理activation_type:如果插件中声明了就用插件的值,否则默认使用focus_activation_type activation_type = getattr(cls, "activation_type", focus_activation_type) return ActionInfo( diff --git a/src/plugin_system/base/component_types.py b/src/plugin_system/base/component_types.py index 0bcb0060e..06e84411e 100644 --- a/src/plugin_system/base/component_types.py +++ b/src/plugin_system/base/component_types.py @@ -31,6 +31,7 @@ class ActionActivationType(Enum): LLM_JUDGE = "llm_judge" # LLM判定是否启动该action到planner RANDOM = "random" # 随机启用action到planner KEYWORD = "keyword" # 关键词触发启用action到planner + KEYWORD_OR_LLM_JUDGE = "keyword_or_llm_judge" # From Elysia with love~ 献给我最棒的伙伴! def __str__(self): return self.value