From ed3d8098414b3d6774bae3af228bf7d16e5d8095 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 30 Apr 2025 01:41:00 +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 --- scripts/interest_monitor_gui.py | 8 ++- src/heart_flow/mai_state_manager.py | 11 ++-- src/plugins/heartFC_chat/heartFC_chat.py | 76 ++++++++++++++---------- 3 files changed, 57 insertions(+), 38 deletions(-) diff --git a/scripts/interest_monitor_gui.py b/scripts/interest_monitor_gui.py index 4e688a301..adb83f729 100644 --- a/scripts/interest_monitor_gui.py +++ b/scripts/interest_monitor_gui.py @@ -156,8 +156,12 @@ class InterestMonitorApp: self.mind_text_scroll = tk.Scrollbar(self.frame_mind_history) self.mind_text_scroll.pack(side=tk.RIGHT, fill=tk.Y) self.mind_text = tk.Text( - self.frame_mind_history, height=25, state="disabled", wrap="word", font=("微软雅黑", 12), - yscrollcommand=self.mind_text_scroll.set + self.frame_mind_history, + height=25, + state="disabled", + wrap="word", + font=("微软雅黑", 12), + yscrollcommand=self.mind_text_scroll.set, ) self.mind_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=1, padx=5, pady=5) self.mind_text_scroll.config(command=self.mind_text.yview) diff --git a/src/heart_flow/mai_state_manager.py b/src/heart_flow/mai_state_manager.py index a3ff34e1d..48eead1a1 100644 --- a/src/heart_flow/mai_state_manager.py +++ b/src/heart_flow/mai_state_manager.py @@ -9,9 +9,9 @@ logger = get_logger("mai_state") # -- 状态相关的可配置参数 (可以从 glocal_config 加载) -- -enable_unlimited_hfc_chat = True # 调试用:无限专注聊天 +enable_unlimited_hfc_chat = True # 调试用:无限专注聊天 # enable_unlimited_hfc_chat = False -prevent_offline_state = True # 调试用:防止进入离线状态 +prevent_offline_state = True # 调试用:防止进入离线状态 # 不同状态下普通聊天的最大消息数 MAX_NORMAL_CHAT_NUM_PEEKING = 30 @@ -25,6 +25,7 @@ MAX_FOCUSED_CHAT_NUM_FOCUSED = 40 # -- 状态定义 -- + class MaiState(enum.Enum): """ 聊天状态: @@ -204,8 +205,10 @@ class MaiStateManager: if time_limit_exceeded: next_state_candidate = random.choices(choices_list, weights=weights, k=1)[0] resolved_candidate = _resolve_offline(next_state_candidate) - logger.debug(f"规则{rule_id}:时间到,随机选择 {next_state_candidate.value},resolve 为 {resolved_candidate.value}") - next_state = resolved_candidate # 直接使用解析后的状态 + logger.debug( + f"规则{rule_id}:时间到,随机选择 {next_state_candidate.value},resolve 为 {resolved_candidate.value}" + ) + next_state = resolved_candidate # 直接使用解析后的状态 # 注意:enable_unlimited_hfc_chat 优先级高于 prevent_offline_state # 如果触发了这个,它会覆盖上面规则2设置的 next_state diff --git a/src/plugins/heartFC_chat/heartFC_chat.py b/src/plugins/heartFC_chat/heartFC_chat.py index 25cf854af..c15c4f83f 100644 --- a/src/plugins/heartFC_chat/heartFC_chat.py +++ b/src/plugins/heartFC_chat/heartFC_chat.py @@ -1,7 +1,7 @@ import asyncio import time import traceback -import random # <--- 添加导入 +import random # <--- 添加导入 from typing import List, Optional, Dict, Any, Deque, Callable, Coroutine from collections import deque from src.plugins.chat.message import MessageRecv, BaseMessageInfo, MessageThinking, MessageSending @@ -32,7 +32,7 @@ from src.individuality.individuality import Individuality INITIAL_DURATION = 60.0 -WAITING_TIME_THRESHOLD = 300 # 等待新消息时间阈值,单位秒 +WAITING_TIME_THRESHOLD = 300 # 等待新消息时间阈值,单位秒 logger = get_logger("interest") # Logger Name Changed @@ -48,11 +48,11 @@ class ActionManager: def __init__(self): # 初始化为默认动作集 self._available_actions: Dict[str, str] = DEFAULT_ACTIONS.copy() - self._original_actions_backup: Optional[Dict[str, str]] = None # 用于临时移除时的备份 + self._original_actions_backup: Optional[Dict[str, str]] = None # 用于临时移除时的备份 def get_available_actions(self) -> Dict[str, str]: """获取当前可用的动作集""" - return self._available_actions.copy() # 返回副本以防外部修改 + return self._available_actions.copy() # 返回副本以防外部修改 def add_action(self, action_name: str, description: str) -> bool: """ @@ -518,7 +518,7 @@ class HeartFChatting: logger.error(f"{self.log_prefix} 处理{action}时出错: {e}") # 出错时也重置计数器 self._lian_xu_bu_hui_fu_ci_shu = 0 - self._lian_xu_deng_dai_shi_jian = 0.0 # 重置累计等待时间 + self._lian_xu_deng_dai_shi_jian = 0.0 # 重置累计等待时间 return False, "" async def _handle_text_reply(self, reasoning: str, emoji_query: str, cycle_timers: dict) -> tuple[bool, str]: @@ -541,7 +541,7 @@ class HeartFChatting: """ # 重置连续不回复计数器 self._lian_xu_bu_hui_fu_ci_shu = 0 - self._lian_xu_deng_dai_shi_jian = 0.0 # 重置累计等待时间 + self._lian_xu_deng_dai_shi_jian = 0.0 # 重置累计等待时间 # 获取锚点消息 anchor_message = await self._get_anchor_message() @@ -597,7 +597,7 @@ class HeartFChatting: bool: 是否发送成功 """ logger.info(f"{self.log_prefix} 决定回复表情({emoji_query}): {reasoning}") - self._lian_xu_deng_dai_shi_jian = 0.0 # 重置累计等待时间(即使不计数也保持一致性) + self._lian_xu_deng_dai_shi_jian = 0.0 # 重置累计等待时间(即使不计数也保持一致性) try: anchor = await self._get_anchor_message() @@ -633,17 +633,16 @@ class HeartFChatting: observation = self.observations[0] if self.observations else None try: - dang_qian_deng_dai = 0.0 # 初始化本次等待时间 + dang_qian_deng_dai = 0.0 # 初始化本次等待时间 with Timer("等待新消息", cycle_timers): # 等待新消息、超时或关闭信号,并获取结果 await self._wait_for_new_message(observation, planner_start_db_time, self.log_prefix) # 从计时器获取实际等待时间 dang_qian_deng_dai = cycle_timers.get("等待新消息", 0.0) - if not self._shutting_down: self._lian_xu_bu_hui_fu_ci_shu += 1 - self._lian_xu_deng_dai_shi_jian += dang_qian_deng_dai # 累加等待时间 + self._lian_xu_deng_dai_shi_jian += dang_qian_deng_dai # 累加等待时间 logger.debug( f"{self.log_prefix} 连续不回复计数增加: {self._lian_xu_bu_hui_fu_ci_shu}/{self.CONSECUTIVE_NO_REPLY_THRESHOLD}, " f"本次等待: {dang_qian_deng_dai:.2f}秒, 累计等待: {self._lian_xu_deng_dai_shi_jian:.2f}秒" @@ -651,8 +650,10 @@ class HeartFChatting: # 检查是否同时达到次数和时间阈值 time_threshold = 0.66 * WAITING_TIME_THRESHOLD * self.CONSECUTIVE_NO_REPLY_THRESHOLD - if (self._lian_xu_bu_hui_fu_ci_shu >= self.CONSECUTIVE_NO_REPLY_THRESHOLD and - self._lian_xu_deng_dai_shi_jian >= time_threshold): + if ( + self._lian_xu_bu_hui_fu_ci_shu >= self.CONSECUTIVE_NO_REPLY_THRESHOLD + and self._lian_xu_deng_dai_shi_jian >= time_threshold + ): logger.info( f"{self.log_prefix} 连续不回复达到阈值 ({self._lian_xu_bu_hui_fu_ci_shu}次) " f"且累计等待时间达到 {self._lian_xu_deng_dai_shi_jian:.2f}秒 (阈值 {time_threshold}秒)," @@ -668,7 +669,6 @@ class HeartFChatting: ) # else: 次数和时间都未达到阈值,不做处理 - return True except asyncio.CancelledError: @@ -793,9 +793,9 @@ class HeartFChatting: logger.info(f"{self.log_prefix}[Planner] 开始{'重新' if is_re_planned else ''}执行规划器") # --- 新增:检查历史动作并调整可用动作 --- - lian_xu_wen_ben_hui_fu = 0 # 连续文本回复次数 + lian_xu_wen_ben_hui_fu = 0 # 连续文本回复次数 actions_to_remove_temporarily = [] - probability_roll = random.random() # 在循环外掷骰子一次,用于概率判断 + probability_roll = random.random() # 在循环外掷骰子一次,用于概率判断 # 反向遍历最近的循环历史 for cycle in reversed(self._cycle_history): @@ -804,9 +804,11 @@ class HeartFChatting: if cycle.action_type == "text_reply": lian_xu_wen_ben_hui_fu += 1 else: - break # 遇到非文本回复,中断计数 + break # 遇到非文本回复,中断计数 # 检查最近的3个循环即可,避免检查过多历史 (如果历史很长) - if len(self._cycle_history) > 0 and cycle.cycle_id <= self._cycle_history[0].cycle_id + (len(self._cycle_history) - 4): + if len(self._cycle_history) > 0 and cycle.cycle_id <= self._cycle_history[0].cycle_id + ( + len(self._cycle_history) - 4 + ): break logger.debug(f"{self.log_prefix}[Planner] 检测到连续文本回复次数: {lian_xu_wen_ben_hui_fu}") @@ -816,13 +818,15 @@ class HeartFChatting: logger.info(f"{self.log_prefix}[Planner] 连续回复 >= 3 次,强制移除 text_reply 和 emoji_reply") actions_to_remove_temporarily.extend(["text_reply", "emoji_reply"]) elif lian_xu_wen_ben_hui_fu == 2: - if probability_roll < 0.8: # 80% 概率 + if probability_roll < 0.8: # 80% 概率 logger.info(f"{self.log_prefix}[Planner] 连续回复 2 次,80% 概率移除 text_reply 和 emoji_reply (触发)") actions_to_remove_temporarily.extend(["text_reply", "emoji_reply"]) else: - logger.info(f"{self.log_prefix}[Planner] 连续回复 2 次,80% 概率移除 text_reply 和 emoji_reply (未触发)") + logger.info( + f"{self.log_prefix}[Planner] 连续回复 2 次,80% 概率移除 text_reply 和 emoji_reply (未触发)" + ) elif lian_xu_wen_ben_hui_fu == 1: - if probability_roll < 0.4: # 40% 概率 + if probability_roll < 0.4: # 40% 概率 logger.info(f"{self.log_prefix}[Planner] 连续回复 1 次,40% 概率移除 text_reply (触发)") actions_to_remove_temporarily.append("text_reply") else: @@ -847,7 +851,9 @@ class HeartFChatting: # --- 新增:应用临时动作移除 --- if actions_to_remove_temporarily: self.action_manager.temporarily_remove_actions(actions_to_remove_temporarily) - logger.debug(f"{self.log_prefix}[Planner] 临时移除的动作: {actions_to_remove_temporarily}, 当前可用: {list(self.action_manager.get_available_actions().keys())}") + logger.debug( + f"{self.log_prefix}[Planner] 临时移除的动作: {actions_to_remove_temporarily}, 当前可用: {list(self.action_manager.get_available_actions().keys())}" + ) # --- 构建提示词 --- replan_prompt_str = "" @@ -862,7 +868,7 @@ class HeartFChatting: # --- 调用 LLM --- try: planner_tools = self.action_manager.get_planner_tool_definition() - logger.debug(f"{self.log_prefix}[Planner] 本次使用的工具定义: {planner_tools}") # 记录本次使用的工具 + logger.debug(f"{self.log_prefix}[Planner] 本次使用的工具定义: {planner_tools}") # 记录本次使用的工具 _response_text, _reasoning_content, tool_calls = await self.planner_llm.generate_response_tool_async( prompt=prompt, tools=planner_tools, @@ -914,13 +920,15 @@ class HeartFChatting: action = "no_reply" reasoning = f"LLM返回了当前不可用的动作: {extracted_action}" emoji_query = "" - llm_error = False # 视为逻辑修正而非 LLM 错误 + llm_error = False # 视为逻辑修正而非 LLM 错误 # --- 检查 'no_reply' 是否也恰好被移除了 (极端情况) --- if "no_reply" not in self.action_manager.get_available_actions(): - logger.error(f"{self.log_prefix}[Planner] 严重错误:'no_reply' 动作也不可用!无法执行任何动作。") - action = "error" # 回退到错误状态 - reasoning = "无法执行任何有效动作,包括 no_reply" - llm_error = True + logger.error( + f"{self.log_prefix}[Planner] 严重错误:'no_reply' 动作也不可用!无法执行任何动作。" + ) + action = "error" # 回退到错误状态 + reasoning = "无法执行任何有效动作,包括 no_reply" + llm_error = True else: # 动作有效且可用,使用提取的值 action = extracted_action @@ -944,11 +952,13 @@ class HeartFChatting: # 如果没有有效的工具调用,我们需要检查 'no_reply' 是否是当前唯一可用的动作 available_actions = list(self.action_manager.get_available_actions().keys()) if available_actions == ["no_reply"]: - logger.info(f"{self.log_prefix}[Planner] LLM未返回工具调用,但当前唯一可用动作是 'no_reply',将执行 'no_reply'") + logger.info( + f"{self.log_prefix}[Planner] LLM未返回工具调用,但当前唯一可用动作是 'no_reply',将执行 'no_reply'" + ) action = "no_reply" reasoning = "LLM未返回工具调用,且当前仅 'no_reply' 可用" emoji_query = "" - llm_error = False # 视为逻辑选择而非错误 + llm_error = False # 视为逻辑选择而非错误 else: reasoning = "LLM未返回有效的工具调用" logger.warning(f"{self.log_prefix}[Planner] {reasoning}") @@ -963,9 +973,11 @@ class HeartFChatting: llm_error = True # --- 新增:确保动作恢复 --- finally: - if actions_to_remove_temporarily: # 只有当确实移除了动作时才需要恢复 - self.action_manager.restore_actions() - logger.debug(f"{self.log_prefix}[Planner] 恢复了原始动作集, 当前可用: {list(self.action_manager.get_available_actions().keys())}") + if actions_to_remove_temporarily: # 只有当确实移除了动作时才需要恢复 + self.action_manager.restore_actions() + logger.debug( + f"{self.log_prefix}[Planner] 恢复了原始动作集, 当前可用: {list(self.action_manager.get_available_actions().keys())}" + ) # --- 结束:确保动作恢复 --- # --- 结束 LLM 决策 --- #