diff --git a/interest_monitor_gui.py b/interest_monitor_gui.py index 28c5ecc14..3dbcb28fb 100644 --- a/interest_monitor_gui.py +++ b/interest_monitor_gui.py @@ -246,7 +246,7 @@ class InterestMonitorApp: self.stream_sub_minds[stream_id] = subflow_entry.get("sub_mind", "N/A") self.stream_chat_states[stream_id] = subflow_entry.get("sub_chat_state", "N/A") self.stream_threshold_status[stream_id] = subflow_entry.get("is_above_threshold", False) - self.stream_last_active[stream_id] = subflow_entry.get("last_active_time") # 存储原始时间戳 + self.stream_last_active[stream_id] = subflow_entry.get("last_changed_state_time") # 存储原始时间戳 self.stream_last_interaction[stream_id] = subflow_entry.get( "last_interaction_time" ) # 存储原始时间戳 diff --git a/src/heart_flow/README.md b/src/heart_flow/README.md index ca6603e30..a25cbe9fb 100644 --- a/src/heart_flow/README.md +++ b/src/heart_flow/README.md @@ -101,10 +101,12 @@ c HeartFChatting工作方式 - **文件**: `subheartflow_manager.py` - **职责**: - 作为 `Heartflow` 的成员变量存在。 + - **在初始化时接收并持有 `Heartflow` 的 `MaiStateInfo` 实例。** - 负责所有 `SubHeartflow` 实例的生命周期管理,包括: - - 创建和获取 (`create_or_get_subheartflow`)。 - - 停止和清理 (`stop_subheartflow`, `cleanup_inactive_subheartflows`)。 - - 根据 `Heartflow` 的状态和限制条件,激活、停用或调整子心流的状态。 + - 创建和获取 (`get_or_create_subheartflow`)。 + - 停止和清理 (`sleep_subheartflow`, `cleanup_inactive_subheartflows`)。 + - 根据 `Heartflow` 的状态 (`self.mai_state_info`) 和限制条件,激活、停用或调整子心流的状态(例如 `enforce_subheartflow_limits`, `activate_random_subflows_to_chat`, `evaluate_interest_and_promote`)。 + - **注意**: 不再提供直接获取所有 ID (`get_all_subheartflows_ids`) 或单个子心流 (`get_subheartflow`) 的公共方法。 ### 1.5. 消息处理与回复流程 (Message Processing vs. Replying Flow) - **关注点分离**: 系统严格区分了接收和处理传入消息的流程与决定和生成回复的流程。 @@ -125,9 +127,10 @@ c HeartFChatting工作方式 ### 2.1. Heart Flow 整体控制 - **控制者**: 主心流 (`Heartflow`) - **核心职责**: - - 通过其成员 `SubHeartflowManager` 创建和管理子心流。 + - 通过其成员 `SubHeartflowManager` 创建和管理子心流(**在创建 `SubHeartflowManager` 时会传入自身的 `MaiStateInfo`**)。 - 通过其成员 `self.current_state: MaiStateInfo` 控制整体行为模式。 - 管理系统级后台任务。 + - **注意**: 不再提供直接获取所有子心流 ID (`get_all_subheartflows_streams_ids`) 的公共方法。 ### 2.2. Heart Flow 状态 (`MaiStateInfo`) - **定义与管理**: `Heartflow` 持有 `MaiStateInfo` 的实例 (`self.current_state`) 来管理其状态。状态的枚举定义在 `my_state_manager.py` 中的 `MaiState`。 @@ -146,10 +149,10 @@ c HeartFChatting工作方式 * `ChatState.FOCUSED` (专注/认真水群): 专注聊天模式。激活 `HeartFlowChatInstance`。 - **选择**: 子心流可以根据外部指令(来自 `SubHeartflowManager`)或内部逻辑(未来的扩展)选择进入 `ABSENT` 状态(不回复不观察),或进入 `CHAT` / `FOCUSED` 中的一种回复模式。 - **状态转换机制** (由 `SubHeartflowManager` 驱动): - - **激活 `CHAT`**: 当 `Heartflow` 状态从 `OFFLINE` 变为允许聊天的状态时,`SubHeartflowManager` 会根据限制,选择部分 `ABSENT` 状态的子心流,**检查当前 CHAT 状态数量是否达到上限**,如果未达上限,则调用其 `set_chat_state` 方法将其转换为 `CHAT`。 - - **激活 `FOCUSED`**: `SubHeartflowManager` 会定期评估处于 `CHAT` 状态的子心流的兴趣度 (`InterestChatting.start_hfc_probability`),若满足条件且**检查当前 FOCUSED 状态数量未达上限**,则调用 `set_chat_state` 将其提升为 `FOCUSED`。 - - **停用/回退**: `SubHeartflowManager` 可能因 `Heartflow` 状态变化、达到数量限制、长时间不活跃或随机概率等原因,调用 `set_chat_state` 将子心流状态设置为 `ABSENT` 或从 `FOCUSED` 回退到 `CHAT`。 - - **注意**: `set_chat_state` 方法本身只负责执行状态转换和管理内部聊天实例(`NormalChatInstance`/`HeartFlowChatInstance`),不再进行限额检查。限额检查的责任完全由调用方(即 `SubHeartflowManager` 中的相关方法)承担。 + - **激活 `CHAT`**: 当 `Heartflow` 状态从 `OFFLINE` 变为允许聊天的状态时,`SubHeartflowManager` 会根据限制(通过 `self.mai_state_info` 获取),选择部分 `ABSENT` 状态的子心流,**检查当前 CHAT 状态数量是否达到上限**,如果未达上限,则调用其 `change_chat_state` 方法将其转换为 `CHAT`。 + - **激活 `FOCUSED`**: `SubHeartflowManager` 会定期评估处于 `CHAT` 状态的子心流的兴趣度 (`InterestChatting.start_hfc_probability`),若满足条件且**检查当前 FOCUSED 状态数量未达上限**(通过 `self.mai_state_info` 获取限制),则调用 `change_chat_state` 将其提升为 `FOCUSED`。 + - **停用/回退**: `SubHeartflowManager` 可能因 `Heartflow` 状态变化、达到数量限制、长时间不活跃或随机概率等原因,调用 `change_chat_state` 将子心流状态设置为 `ABSENT` 或从 `FOCUSED` 回退到 `CHAT`。 + - **注意**: `change_chat_state` 方法本身只负责执行状态转换和管理内部聊天实例(`NormalChatInstance`/`HeartFlowChatInstance`),不再进行限额检查。限额检查的责任完全由调用方(即 `SubHeartflowManager` 中的相关方法,这些方法会使用内部存储的 `mai_state_info` 来获取限制)承担。 ## 3. 聊天实例详解 (Chat Instances Explained) @@ -185,23 +188,24 @@ c HeartFChatting工作方式 1. **启动**: `Heartflow` 启动,初始化 `MaiStateInfo` (例如 `OFFLINE`) 和 `SubHeartflowManager`。 2. **状态变化**: 用户操作或内部逻辑使 `Heartflow` 的 `current_state` 变为 `NORMAL_CHAT`。 -3. **管理器响应**: `SubHeartflowManager` 检测到状态变化,根据 `NORMAL_CHAT` 的限制,调用 `create_or_get_subheartflow` 获取或创建子心流,并通过 `set_chat_state` 将部分子心流状态从 `ABSENT` 激活为 `CHAT`。 +3. **管理器响应**: `SubHeartflowManager` 检测到状态变化,根据 `NORMAL_CHAT` 的限制,调用 `get_or_create_subheartflow` 获取或创建子心流,并通过 `change_chat_state` 将部分子心流状态从 `ABSENT` 激活为 `CHAT`。 4. **子心流激活**: 被激活的 `SubHeartflow` 启动其 `NormalChatInstance`。 5. **信息接收**: 该 `SubHeartflow` 的 `ChattingObservation` 开始从数据库拉取新消息。 6. **普通回复**: `NormalChatInstance` 处理观察到的信息,执行普通回复逻辑。 7. **兴趣评估**: `SubHeartflowManager` 定期评估该子心流的 `InterestChatting` 状态。 -8. **提升状态**: 若兴趣度达标且 `Heartflow` 状态允许,`SubHeartflowManager` 调用该子心流的 `set_chat_state` 将其状态提升为 `FOCUSED`。 +8. **提升状态**: 若兴趣度达标且 `Heartflow` 状态允许,`SubHeartflowManager` 调用该子心流的 `change_chat_state` 将其状态提升为 `FOCUSED`。 9. **子心流切换**: `SubHeartflow` 内部停止 `NormalChatInstance`,启动 `HeartFlowChatInstance`。 10. **专注回复**: `HeartFlowChatInstance` 开始根据其逻辑进行更深入的交互。 -11. **状态回落/停用**: 若 `Heartflow` 状态变为 `OFFLINE`,`SubHeartflowManager` 会调用所有子心流的 `set_chat_state(ChatState.ABSENT)`,使其停止活动。 +11. **状态回落/停用**: 若 `Heartflow` 状态变为 `OFFLINE`,`SubHeartflowManager` 会调用所有子心流的 `change_chat_state(ChatState.ABSENT)`,使其停止活动。 ## 5. 使用与配置 (Usage and Configuration) ### 5.1. 使用说明 (Code Examples) -- **(内部)创建/获取子心流** (由 `SubHeartflowManager` 调用): +- **(内部)创建/获取子心流** (由 `SubHeartflowManager` 调用, 示例): ```python - # subheartflow_manager.py - new_subflow = SubHeartflow(subheartflow_id, mai_states) + # subheartflow_manager.py (get_or_create_subheartflow 内部) + # 注意:mai_states 现在是 self.mai_state_info + new_subflow = SubHeartflow(subheartflow_id, self.mai_state_info) await new_subflow.initialize() observation = ChattingObservation(chat_id=subheartflow_id) new_subflow.add_observation(observation) diff --git a/src/heart_flow/background_tasks.py b/src/heart_flow/background_tasks.py index f5131a59a..c66a6128c 100644 --- a/src/heart_flow/background_tasks.py +++ b/src/heart_flow/background_tasks.py @@ -49,7 +49,6 @@ class BackgroundTaskManager: self.update_interval = update_interval self.cleanup_interval = cleanup_interval self.log_interval = log_interval - self.inactive_threshold = inactive_threshold # For cleanup task self.interest_eval_interval = interest_eval_interval # 存储兴趣评估间隔 self.random_deactivation_interval = random_deactivation_interval # 存储随机停用间隔 @@ -217,21 +216,35 @@ class BackgroundTaskManager: current_state == self.mai_state_info.mai_status.OFFLINE and previous_status != self.mai_state_info.mai_status.OFFLINE ): - logger.info("[后台任务] 主状态离线,触发子流停用") + logger.info("检测到离线,停用所有子心流") await self.subheartflow_manager.deactivate_all_subflows() async def _perform_cleanup_work(self): - """执行一轮子心流清理操作。""" - flows_to_stop = self.subheartflow_manager.cleanup_inactive_subheartflows(self.inactive_threshold) - if flows_to_stop: - logger.info(f"[Background Task Cleanup] Attempting to stop {len(flows_to_stop)} inactive flows...") - stopped_count = 0 - for flow_id, reason in flows_to_stop: - if await self.subheartflow_manager.stop_subheartflow(flow_id, f"定期清理: {reason}"): - stopped_count += 1 - logger.info(f"[Background Task Cleanup] Cleanup cycle finished. Stopped {stopped_count} inactive flows.") - # else: - # logger.debug("[Background Task Cleanup] Cleanup cycle finished. No inactive flows found.") + """执行子心流清理任务 + 1. 获取需要清理的不活跃子心流列表 + 2. 逐个停止这些子心流 + 3. 记录清理结果 + """ + # 获取需要清理的子心流列表(包含ID和原因) + flows_to_stop = self.subheartflow_manager.get_inactive_subheartflows() + + if not flows_to_stop: + return # 没有需要清理的子心流直接返回 + + logger.info(f"准备删除 {len(flows_to_stop)} 个不活跃(1h)子心流") + stopped_count = 0 + + # 逐个停止子心流 + for flow_id in flows_to_stop: + success = await self.subheartflow_manager.delete_subflow(flow_id) + if success: + stopped_count += 1 + logger.debug(f"[清理任务] 已停止子心流 {flow_id}") + + # 记录最终清理结果 + logger.info(f"[清理任务] 清理完成, 共停止 {stopped_count}/{len(flows_to_stop)} 个子心流") + + async def _perform_logging_work(self): """执行一轮状态日志记录。""" diff --git a/src/heart_flow/heartflow.py b/src/heart_flow/heartflow.py index 7fbc0f58a..3f7fa0f12 100644 --- a/src/heart_flow/heartflow.py +++ b/src/heart_flow/heartflow.py @@ -47,8 +47,8 @@ class Heartflow: self.current_state: MaiStateInfo = MaiStateInfo() # 当前状态信息 self.mai_state_manager: MaiStateManager = MaiStateManager() # 状态决策管理器 - # 子心流管理 - self.subheartflow_manager: SubHeartflowManager = SubHeartflowManager() # 子心流管理器 + # 子心流管理 (在初始化时传入 current_state) + self.subheartflow_manager: SubHeartflowManager = SubHeartflowManager(self.current_state) # LLM模型配置 self.llm_model = LLMRequest( @@ -75,23 +75,18 @@ class Heartflow: inactive_threshold=INACTIVE_THRESHOLD_SECONDS, ) - async def create_subheartflow(self, subheartflow_id: Any) -> Optional["SubHeartflow"]: + async def get_or_create_subheartflow(self, subheartflow_id: Any) -> Optional["SubHeartflow"]: """获取或创建一个新的SubHeartflow实例 - 委托给 SubHeartflowManager""" - return await self.subheartflow_manager.create_or_get_subheartflow(subheartflow_id, self.current_state) + # 不再需要传入 self.current_state + return await self.subheartflow_manager.get_or_create_subheartflow(subheartflow_id) - def get_subheartflow(self, subheartflow_id: Any) -> Optional["SubHeartflow"]: - """获取指定ID的SubHeartflow实例""" - return self.subheartflow_manager.get_subheartflow(subheartflow_id) - - def get_all_subheartflows_streams_ids(self) -> list[Any]: - """获取当前所有活跃的子心流的 ID 列表 - 委托给 SubHeartflowManager""" - return self.subheartflow_manager.get_all_subheartflows_ids() async def heartflow_start_working(self): """启动后台任务""" await self.background_task_manager.start_tasks() logger.info("[Heartflow] 后台任务已启动") + # 根本不会用到这个函数吧,那样麦麦直接死了 async def stop_working(self): """停止所有任务和子心流""" logger.info("[Heartflow] 正在停止任务和子心流...") diff --git a/src/heart_flow/interest_logger.py b/src/heart_flow/interest_logger.py index 62063f073..7802f87b4 100644 --- a/src/heart_flow/interest_logger.py +++ b/src/heart_flow/interest_logger.py @@ -58,7 +58,7 @@ class InterestLogger: return results for subheartflow in all_flows: - if self.subheartflow_manager.get_subheartflow(subheartflow.subheartflow_id): + if self.subheartflow_manager.get_or_create_subheartflow(subheartflow.subheartflow_id): tasks.append( asyncio.create_task(subheartflow.get_full_state(), name=f"get_state_{subheartflow.subheartflow_id}") ) diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py index dd9364f30..b6cbdd228 100644 --- a/src/heart_flow/sub_heartflow.py +++ b/src/heart_flow/sub_heartflow.py @@ -2,7 +2,7 @@ from .observation import Observation, ChattingObservation import asyncio from src.config.config import global_config import time -from typing import Optional, List, Dict, Callable +from typing import Optional, List, Dict, Callable, Tuple import traceback from src.common.logger import get_module_logger, LogConfig, SUB_HEARTFLOW_STYLE_CONFIG # noqa: E402 import random @@ -41,7 +41,6 @@ class InterestChatting: increase_rate=probability_increase_rate_per_second, decay_factor=global_config.probability_decay_factor_per_second, max_probability=max_reply_probability, - state_change_callback: Optional[Callable[[ChatState], None]] = None, ): # 基础属性初始化 self.interest_level: float = 0.0 @@ -69,13 +68,23 @@ class InterestChatting: self.above_threshold = False self.start_hfc_probability = 0.0 + + async def initialize(self): + async with self._task_lock: + if self._is_running: + logger.debug("后台兴趣更新任务已在运行中。") + return - @classmethod - async def create(cls, *args, **kwargs): - """异步工厂方法,用于创建并初始化 InterestChatting 实例""" - instance = cls(*args, **kwargs) - await instance.start_updates(instance.update_interval) - return instance + # 清理已完成或已取消的任务 + if self.update_task and (self.update_task.done() or self.update_task.cancelled()): + self.update_task = None + + if not self.update_task: + self._stop_event.clear() + self._is_running = True + self.update_task = asyncio.create_task(self._run_update_loop(self.update_interval)) + logger.debug("后台兴趣更新任务已创建并启动。") + def add_interest_dict(self, message: MessageRecv, interest_value: float, is_mentioned: bool): self.interest_dict[message.message_info.message_id] = (message, interest_value, is_mentioned) @@ -123,11 +132,11 @@ class InterestChatting: if self.start_hfc_probability != 0: self.start_hfc_probability -= 0.1 - async def increase_interest(self, current_time: float, value: float): + async def increase_interest(self, value: float): self.interest_level += value self.interest_level = min(self.interest_level, self.max_interest) - async def decrease_interest(self, current_time: float, value: float): + async def decrease_interest(self, value: float): self.interest_level -= value self.interest_level = max(self.interest_level, 0.0) @@ -176,22 +185,6 @@ class InterestChatting: self._is_running = False logger.info("InterestChatting 更新循环已停止。") - async def start_updates(self, update_interval: float = 1.0): - """启动后台更新任务,使用锁确保并发安全""" - async with self._task_lock: - if self._is_running: - logger.debug("后台兴趣更新任务已在运行中。") - return - - # 清理已完成或已取消的任务 - if self.update_task and (self.update_task.done() or self.update_task.cancelled()): - self.update_task = None - - if not self.update_task: - self._stop_event.clear() - self._is_running = True - self.update_task = asyncio.create_task(self._run_update_loop(update_interval)) - logger.debug("后台兴趣更新任务已创建并启动。") async def stop_updates(self): """停止后台更新任务,使用锁确保并发安全""" @@ -241,12 +234,14 @@ class SubHeartflow: # 这个聊天流的状态 self.chat_state: ChatStateInfo = ChatStateInfo() + self.chat_state_changed_time: float = time.time() + self.chat_state_last_time: float = 0 + self.history_chat_state: List[Tuple[ChatState, float]] = [] # 兴趣检测器 - self.interest_chatting = None + self.interest_chatting: InterestChatting = InterestChatting() # 活动状态管理 - self.last_active_time = time.time() # 最后活跃时间 self.should_stop = False # 停止标志 self.task: Optional[asyncio.Task] = None # 后台任务 @@ -269,23 +264,12 @@ class SubHeartflow: self.log_prefix = chat_manager.get_stream_name(self.subheartflow_id) or self.subheartflow_id async def initialize(self): - """异步初始化方法,创建兴趣检测器""" - self.interest_chatting = await InterestChatting.create(state_change_callback=self.set_chat_state) - logger.debug(f"{self.log_prefix} InterestChatting 实例已创建并初始化。") + """异步初始化方法,创建兴趣流""" + await self.interest_chatting.initialize() + logger.debug(f"{self.log_prefix} InterestChatting 实例已初始化。") - async def add_time_current_state(self, add_time: float): - """增加当前状态的时间""" - self.current_state_time += add_time - - async def change_to_state_chat(self): - """改变到随便水群状态""" - self.current_state_time = 120 - self._start_normal_chat() - - async def change_to_state_focused(self): - """改变到认真水群状态""" - self.current_state_time = 60 - self._start_heart_fc_chat() + def update_last_chat_state_time(self): + self.chat_state_last_time = time.time() - self.chat_state_changed_time async def _stop_normal_chat(self): """ @@ -381,11 +365,11 @@ class SubHeartflow: self.heart_fc_instance = None # 创建或初始化异常,清理实例 return False - async def set_chat_state(self, new_state: "ChatState"): + async def change_chat_state(self, new_state: "ChatState"): """更新sub_heartflow的聊天状态,并管理 HeartFChatting 和 NormalChat 实例及任务""" current_state = self.chat_state.chat_status + if current_state == new_state: - # logger.trace(f"{self.log_prefix} 状态已为 {current_state.value}, 无需更改。") # 减少日志噪音 return log_prefix = self.log_prefix @@ -422,9 +406,14 @@ class SubHeartflow: # --- 更新状态和最后活动时间 --- if state_changed: - logger.info(f"{log_prefix} 麦麦的聊天状态从 {current_state.value} 变更为 {new_state.value}") + self.update_last_chat_state_time() + self.history_chat_state.append((current_state, self.chat_state_last_time)) + + logger.info(f"{log_prefix} 麦麦的聊天状态从 {current_state.value} (持续了 {self.chat_state_last_time} 秒) 变更为 {new_state.value}") + self.chat_state.chat_status = new_state - self.last_active_time = time.time() + self.chat_state_last_time = 0 + self.chat_state_changed_time = time.time() else: # 如果因为某些原因(如启动失败)没有成功改变状态,记录一下 logger.debug( @@ -479,9 +468,6 @@ class SubHeartflow: async def should_evaluate_reply(self) -> bool: return await self.interest_chatting.should_evaluate_reply() - async def add_interest_dict_entry(self, message: MessageRecv, interest_value: float, is_mentioned: bool): - self.interest_chatting.add_interest_dict(message, interest_value, is_mentioned) - def get_interest_dict(self) -> Dict[str, tuple[MessageRecv, float, bool]]: return self.interest_chatting.interest_dict @@ -495,7 +481,7 @@ class SubHeartflow: "interest_state": interest_state, "current_mind": self.sub_mind.current_mind, "chat_state": self.chat_state.chat_status.value, - "last_active_time": self.last_active_time, + "last_changed_state_time": self.last_changed_state_time, } async def shutdown(self): diff --git a/src/heart_flow/subheartflow_manager.py b/src/heart_flow/subheartflow_manager.py index ce9ec39a7..d586fd43b 100644 --- a/src/heart_flow/subheartflow_manager.py +++ b/src/heart_flow/subheartflow_manager.py @@ -23,41 +23,29 @@ subheartflow_manager_log_config = LogConfig( logger = get_module_logger("subheartflow_manager", config=subheartflow_manager_log_config) # 子心流管理相关常量 -INACTIVE_THRESHOLD_SECONDS = 1200 # 子心流不活跃超时时间(秒) +INACTIVE_THRESHOLD_SECONDS = 3600 # 子心流不活跃超时时间(秒) class SubHeartflowManager: """管理所有活跃的 SubHeartflow 实例。""" - def __init__(self): + def __init__(self, mai_state_info: MaiStateInfo): self.subheartflows: Dict[Any, "SubHeartflow"] = {} self._lock = asyncio.Lock() # 用于保护 self.subheartflows 的访问 + self.mai_state_info: MaiStateInfo = mai_state_info # 存储传入的 MaiStateInfo 实例 def get_all_subheartflows(self) -> List["SubHeartflow"]: """获取所有当前管理的 SubHeartflow 实例列表 (快照)。""" return list(self.subheartflows.values()) - - def get_all_subheartflows_ids(self) -> List[Any]: - """获取所有当前管理的 SubHeartflow ID 列表。""" - return list(self.subheartflows.keys()) - - def get_subheartflow(self, subheartflow_id: Any) -> Optional["SubHeartflow"]: - """获取指定 ID 的 SubHeartflow 实例。""" - # 注意:这里没有加锁,假设读取操作相对安全或在已知上下文中调用 - # 如果并发写操作很多,get 也应该加锁 - subflow = self.subheartflows.get(subheartflow_id) - if subflow: - subflow.last_active_time = time.time() # 获取时更新活动时间 - return subflow - - async def create_or_get_subheartflow( - self, subheartflow_id: Any, mai_states: MaiStateInfo + + async def get_or_create_subheartflow( + self, subheartflow_id: Any ) -> Optional["SubHeartflow"]: """获取或创建指定ID的子心流实例 Args: subheartflow_id: 子心流唯一标识符 - mai_states: 当前麦麦状态信息 + # mai_states 参数已被移除,使用 self.mai_state_info Returns: 成功返回SubHeartflow实例,失败返回None @@ -75,8 +63,8 @@ class SubHeartflowManager: return subflow try: - # 初始化子心流 - new_subflow = SubHeartflow(subheartflow_id, mai_states) + # 初始化子心流, 传入存储的 mai_state_info + new_subflow = SubHeartflow(subheartflow_id, self.mai_state_info) # 异步初始化 await new_subflow.initialize() @@ -98,7 +86,7 @@ class SubHeartflowManager: logger.error(f"创建子心流 {subheartflow_id} 失败: {e}", exc_info=True) return None - async def stop_subheartflow(self, subheartflow_id: Any, reason: str) -> bool: + async def sleep_subheartflow(self, subheartflow_id: Any, reason: str) -> bool: """停止指定的子心流并清理资源""" subheartflow = self.subheartflows.get(subheartflow_id) if not subheartflow: @@ -111,7 +99,7 @@ class SubHeartflowManager: # 设置状态为ABSENT释放资源 if subheartflow.chat_state.chat_status != ChatState.ABSENT: logger.debug(f"[子心流管理] 设置 {stream_name} 状态为ABSENT") - await subheartflow.set_chat_state(ChatState.ABSENT) + await subheartflow.change_chat_state(ChatState.ABSENT) else: logger.debug(f"[子心流管理] {stream_name} 已是ABSENT状态") except Exception as e: @@ -135,27 +123,26 @@ class SubHeartflowManager: logger.warning(f"[子心流管理] {stream_name} 已被提前移除") return False - def cleanup_inactive_subheartflows(self, max_age_seconds=INACTIVE_THRESHOLD_SECONDS): - """识别并返回需要清理的不活跃子心流(id, 原因)""" + def get_inactive_subheartflows(self, max_age_seconds=INACTIVE_THRESHOLD_SECONDS): + """识别并返回需要清理的不活跃(处于ABSENT状态超过一小时)子心流(id, 原因)""" current_time = time.time() flows_to_stop = [] for subheartflow_id, subheartflow in list(self.subheartflows.items()): - # 只检查有interest_chatting的子心流 - if hasattr(subheartflow, "interest_chatting") and subheartflow.interest_chatting: - last_interact = subheartflow.interest_chatting.last_interaction_time - if max_age_seconds and (current_time - last_interact) > max_age_seconds: - reason = f"不活跃时间({current_time - last_interact:.0f}s) > 阈值({max_age_seconds}s)" - name = chat_manager.get_stream_name(subheartflow_id) or subheartflow_id - logger.debug(f"[清理] 标记 {name} 待移除: {reason}") - flows_to_stop.append((subheartflow_id, reason)) - - if flows_to_stop: - logger.info(f"[清理] 发现 {len(flows_to_stop)} 个不活跃子心流") + state = subheartflow.chat_state.chat_status + if state != ChatState.ABSENT: + continue + subheartflow.update_last_chat_state_time() + absent_last_time = subheartflow.chat_state_last_time + if max_age_seconds and (current_time - absent_last_time) > max_age_seconds: + flows_to_stop.append(subheartflow_id) + return flows_to_stop - async def enforce_subheartflow_limits(self, current_mai_state: MaiState): + async def enforce_subheartflow_limits(self): """根据主状态限制停止超额子心流(优先停不活跃的)""" + # 使用 self.mai_state_info 获取当前状态和限制 + current_mai_state = self.mai_state_info.get_current_state() normal_limit = current_mai_state.get_normal_chat_max_num() focused_limit = current_mai_state.get_focused_chat_max_num() logger.debug(f"[限制] 状态:{current_mai_state.value}, 普通限:{normal_limit}, 专注限:{focused_limit}") @@ -178,7 +165,7 @@ class SubHeartflowManager: logger.info(f"[限制] 普通聊天超额({len(normal_flows)}>{normal_limit}), 停止{excess}个") normal_flows.sort(key=lambda x: x[1]) for flow_id, _ in normal_flows[:excess]: - if await self.stop_subheartflow(flow_id, f"普通聊天超额(限{normal_limit})"): + if await self.sleep_subheartflow(flow_id, f"普通聊天超额(限{normal_limit})"): stopped += 1 # 处理专注聊天超额(需重新统计) @@ -192,7 +179,7 @@ class SubHeartflowManager: logger.info(f"[限制] 专注聊天超额({len(focused_flows)}>{focused_limit}), 停止{excess}个") focused_flows.sort(key=lambda x: x[1]) for flow_id, _ in focused_flows[:excess]: - if await self.stop_subheartflow(flow_id, f"专注聊天超额(限{focused_limit})"): + if await self.sleep_subheartflow(flow_id, f"专注聊天超额(限{focused_limit})"): stopped += 1 if stopped: @@ -200,8 +187,10 @@ class SubHeartflowManager: else: logger.debug(f"[限制] 无需停止, 当前总数:{len(self.subheartflows)}") - async def activate_random_subflows_to_chat(self, current_mai_state: MaiState): + async def activate_random_subflows_to_chat(self): """主状态激活时,随机选择ABSENT子心流进入CHAT状态""" + # 使用 self.mai_state_info 获取当前状态和限制 + current_mai_state = self.mai_state_info.get_current_state() limit = current_mai_state.get_normal_chat_max_num() if limit <= 0: logger.info("[激活] 当前状态不允许CHAT子心流") @@ -236,7 +225,7 @@ class SubHeartflowManager: # --- 结束限额检查 --- # # 移除 states_num 参数 - await flow.set_chat_state(ChatState.CHAT) + await flow.change_chat_state(ChatState.CHAT) if flow.chat_state.chat_status == ChatState.CHAT: activated_count += 1 @@ -246,25 +235,43 @@ class SubHeartflowManager: logger.info(f"[激活] 完成, 成功激活{activated_count}个子心流") async def deactivate_all_subflows(self): - """停用所有子心流(主状态变为OFFLINE时调用)""" - logger.info("[停用] 开始停用所有子心流") - flow_ids = list(self.subheartflows.keys()) + """将所有子心流的状态更改为 ABSENT (例如主状态变为OFFLINE时调用)""" + # logger.info("[停用] 开始将所有子心流状态设置为 ABSENT") + # 使用 list() 创建一个当前值的快照,防止在迭代时修改字典 + flows_to_update = list(self.subheartflows.values()) - if not flow_ids: - logger.info("[停用] 无活跃子心流") + if not flows_to_update: + logger.debug("[停用] 无活跃子心流,无需操作") return - stopped_count = 0 - for flow_id in flow_ids: - if await self.stop_subheartflow(flow_id, "主状态离线"): - stopped_count += 1 + changed_count = 0 + for subflow in flows_to_update: + flow_id = subflow.subheartflow_id + stream_name = chat_manager.get_stream_name(flow_id) or flow_id + # 再次检查子心流是否仍然存在于管理器中,以防万一在迭代过程中被移除 - logger.info(f"[停用] 完成, 尝试停止{len(flow_ids)}个, 成功{stopped_count}个") + if subflow.chat_state.chat_status != ChatState.ABSENT: + logger.debug(f"正在将子心流 {stream_name} 的状态从 {subflow.chat_state.chat_status.value} 更改为 ABSENT") + try: + # 调用 change_chat_state 将状态设置为 ABSENT + await subflow.change_chat_state(ChatState.ABSENT) + # 验证状态是否真的改变了 + if flow_id in self.subheartflows and self.subheartflows[flow_id].chat_state.chat_status == ChatState.ABSENT: + changed_count += 1 + else: + logger.warning(f"[停用] 尝试更改子心流 {stream_name} 状态后,状态仍未变为 ABSENT 或子心流已消失。") + except Exception as e: + logger.error(f"[停用] 更改子心流 {stream_name} 状态为 ABSENT 时出错: {e}", exc_info=True) + else: + logger.debug(f"[停用] 子心流 {stream_name} 已处于 ABSENT 状态,无需更改。") - async def evaluate_interest_and_promote(self, current_mai_state: MaiStateInfo): + logger.info(f"下限完成,共处理 {len(flows_to_update)} 个子心流,成功将 {changed_count} 个子心流的状态更改为 ABSENT。") + + async def evaluate_interest_and_promote(self): """评估子心流兴趣度,满足条件且未达上限则提升到FOCUSED状态(基于start_hfc_probability)""" log_prefix = "[兴趣评估]" - current_state = current_mai_state.get_current_state() + # 使用 self.mai_state_info 获取当前状态和限制 + current_state = self.mai_state_info.get_current_state() focused_limit = current_state.get_focused_chat_max_num() if int(time.time()) % 20 == 0: # 每20秒输出一次 @@ -312,7 +319,7 @@ class SubHeartflowManager: ) # 执行状态提升 - await current_subflow.set_chat_state(ChatState.FOCUSED) + await current_subflow.change_chat_state(ChatState.FOCUSED) # 验证提升结果 if ( @@ -354,7 +361,7 @@ class SubHeartflowManager: # --- 状态设置 --- # # 注意:这里传递的状态数量是 *停用前* 的状态数量 - await current_subflow.set_chat_state(ChatState.ABSENT) + await current_subflow.change_chat_state(ChatState.ABSENT) # --- 状态验证 (可选) --- final_subflow = self.subheartflows.get(flow_id) @@ -419,44 +426,18 @@ class SubHeartflowManager: ) logger.debug(f"[子心流管理器] 更新了{updated_count}个子心流的主想法") - async def deactivate_subflow(self, subheartflow_id: Any): - """停用并移除指定的子心流。""" + async def delete_subflow(self, subheartflow_id: Any): + """删除指定的子心流。""" async with self._lock: subflow = self.subheartflows.pop(subheartflow_id, None) if subflow: - logger.info(f"正在停用 SubHeartflow: {subheartflow_id}...") + logger.info(f"正在删除 SubHeartflow: {subheartflow_id}...") try: - # --- 调用 shutdown 方法 --- + # 调用 shutdown 方法确保资源释放 await subflow.shutdown() - # --- 结束调用 --- - logger.info(f"SubHeartflow {subheartflow_id} 已成功停用。") + logger.info(f"SubHeartflow {subheartflow_id} 已成功删除。") except Exception as e: - logger.error(f"停用 SubHeartflow {subheartflow_id} 时出错: {e}", exc_info=True) + logger.error(f"删除 SubHeartflow {subheartflow_id} 时出错: {e}", exc_info=True) else: - logger.warning(f"尝试停用不存在的 SubHeartflow: {subheartflow_id}") + logger.warning(f"尝试删除不存在的 SubHeartflow: {subheartflow_id}") - async def cleanup_inactive_subflows(self, inactive_threshold_seconds: int): - """清理长时间不活跃的子心流。""" - current_time = time.time() - inactive_ids = [] - # 不加锁地迭代,识别不活跃的 ID - for sub_id, subflow in self.subheartflows.items(): - # 检查 last_active_time 是否存在且是数值 - last_active = getattr(subflow, "last_active_time", 0) - if isinstance(last_active, (int, float)): - if current_time - last_active > inactive_threshold_seconds: - inactive_ids.append(sub_id) - logger.info( - f"发现不活跃的 SubHeartflow: {sub_id} (上次活跃: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(last_active))})" - ) - else: - logger.warning(f"SubHeartflow {sub_id} 的 last_active_time 无效: {last_active}。跳过清理检查。") - - if inactive_ids: - logger.info(f"准备清理 {len(inactive_ids)} 个不活跃的 SubHeartflows: {inactive_ids}") - # 逐个停用(deactivate_subflow 会加锁) - tasks = [self.deactivate_subflow(sub_id) for sub_id in inactive_ids] - await asyncio.gather(*tasks) - logger.info("不活跃的 SubHeartflows 清理完成。") - # else: - # logger.debug("没有发现不活跃的 SubHeartflows 需要清理。") diff --git a/src/plugins/heartFC_chat/heartflow_processor.py b/src/plugins/heartFC_chat/heartflow_processor.py index da7b479ba..1f771688d 100644 --- a/src/plugins/heartFC_chat/heartflow_processor.py +++ b/src/plugins/heartFC_chat/heartflow_processor.py @@ -138,7 +138,7 @@ class HeartFCProcessor: group_info=groupinfo, ) - subheartflow = await heartflow.create_subheartflow(chat.stream_id) + subheartflow = await heartflow.get_or_create_subheartflow(chat.stream_id) message.update_chat_stream(chat) await message.process() @@ -166,9 +166,8 @@ class HeartFCProcessor: # 6. 兴趣度计算与更新 interested_rate, is_mentioned = await self._calculate_interest(message) - current_time = time.time() - await subheartflow.interest_chatting.increase_interest(current_time, value=interested_rate) - await subheartflow.add_interest_dict_entry(message, interested_rate, is_mentioned) + await subheartflow.interest_chatting.increase_interest(value=interested_rate) + await subheartflow.interest_chatting.add_interest_dict(message, interested_rate, is_mentioned) # 7. 日志记录 mes_name = chat.group_info.group_name if chat.group_info else "私聊" diff --git a/src/plugins/heartFC_chat/heartflow_prompt_builder.py b/src/plugins/heartFC_chat/heartflow_prompt_builder.py index 661c4e8af..584205a73 100644 --- a/src/plugins/heartFC_chat/heartflow_prompt_builder.py +++ b/src/plugins/heartFC_chat/heartflow_prompt_builder.py @@ -63,7 +63,6 @@ def init_prompt(): - 话题无关/无聊/不感兴趣 - 最后一条消息是你自己发的且无人回应你 - 讨论你不懂的专业话题 -- 讨论你不想参与的话题 - 你发送了太多消息 2. 文字回复(text_reply)适用: