From 02950ab538a8225c6d3fec7ea9a3c2617b39fc2f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 22 Jun 2025 17:16:36 +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 --- src/chat/focus_chat/heartFC_chat.py | 3 +- src/chat/focus_chat/info/obs_info.py | 4 +- .../expression_selector_processor.py | 2 +- .../focus_chat/planners/planner_simple.py | 39 +++--- .../observation/chatting_observation.py | 4 +- src/plugins/built_in/core_actions/plugin.py | 121 +++++++++--------- 6 files changed, 83 insertions(+), 90 deletions(-) diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index 8d90232cf..7e5139c51 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -660,7 +660,6 @@ class HeartFChatting: } with Timer("执行动作", cycle_timers): - action_type, action_data, reasoning = ( plan_result.get("action_result", {}).get("action_type", "error"), plan_result.get("action_result", {}).get("action_data", {}), @@ -675,7 +674,7 @@ class HeartFChatting: action_str = action_type logger.debug(f"{self.log_prefix} 麦麦想要:'{action_str}'") - + success, reply_text, command = await self._handle_action( action_type, reasoning, action_data, cycle_timers, thinking_id ) diff --git a/src/chat/focus_chat/info/obs_info.py b/src/chat/focus_chat/info/obs_info.py index 2cb4d3cb7..9cc1e1e9b 100644 --- a/src/chat/focus_chat/info/obs_info.py +++ b/src/chat/focus_chat/info/obs_info.py @@ -80,7 +80,7 @@ class ObsInfo(InfoBase): chat_target (str): 聊天目标,可以是 "private"(私聊)、"group"(群聊)或 "other"(其他) """ self.data["chat_target"] = chat_target - + def set_chat_id(self, chat_id: str) -> None: """设置聊天ID @@ -88,7 +88,7 @@ class ObsInfo(InfoBase): chat_id (str): 聊天ID """ self.data["chat_id"] = chat_id - + def get_chat_id(self) -> Optional[str]: """获取聊天ID diff --git a/src/chat/focus_chat/info_processors/expression_selector_processor.py b/src/chat/focus_chat/info_processors/expression_selector_processor.py index 1c630ed81..ed0a8c7c1 100644 --- a/src/chat/focus_chat/info_processors/expression_selector_processor.py +++ b/src/chat/focus_chat/info_processors/expression_selector_processor.py @@ -149,7 +149,7 @@ class ExpressionSelectorProcessor(BaseProcessor): if observations: for observation in observations: if isinstance(observation, ChattingObservation): - # chat_info = observation.get_observe_info() + # chat_info = observation.get_observe_info() chat_info = observation.talking_message_str_truncate_short break diff --git a/src/chat/focus_chat/planners/planner_simple.py b/src/chat/focus_chat/planners/planner_simple.py index da0d5e4f1..5f42fee18 100644 --- a/src/chat/focus_chat/planners/planner_simple.py +++ b/src/chat/focus_chat/planners/planner_simple.py @@ -42,7 +42,7 @@ def init_prompt(): """, "simple_planner_prompt", ) - + Prompt( """ {time_block} @@ -62,19 +62,19 @@ def init_prompt(): "simple_planner_prompt_private", ) -# Prompt( -# """ -# 动作:{action_name} -# 该动作的描述:{action_description} -# 使用该动作的场景: -# {action_require} -# 输出要求: -# {{ -# "action": "{action_name}",{action_parameters} -# }} -# """, -# "action_prompt", -# ) + # Prompt( + # """ + # 动作:{action_name} + # 该动作的描述:{action_description} + # 使用该动作的场景: + # {action_require} + # 输出要求: + # {{ + # "action": "{action_name}",{action_parameters} + # }} + # """, + # "action_prompt", + # ) Prompt( """ {action_require} @@ -84,7 +84,7 @@ def init_prompt(): """, "action_prompt", ) - + Prompt( """ {action_require} @@ -96,7 +96,6 @@ def init_prompt(): ) - class ActionPlanner(BasePlanner): def __init__(self, log_prefix: str, action_manager: ActionManager): super().__init__(log_prefix, action_manager) @@ -141,7 +140,7 @@ class ActionPlanner(BasePlanner): relation_info = "" selected_expressions = [] chat_id = None # 添加chat_id变量 - + for info in all_plan_info: if isinstance(info, ObsInfo): observed_messages = info.get_talking_message() @@ -170,7 +169,9 @@ class ActionPlanner(BasePlanner): # 如果获取成功,更新is_group_chat if is_group_chat_updated is not None: is_group_chat = is_group_chat_updated - logger.debug(f"{self.log_prefix}获取到聊天信息 - 群聊: {is_group_chat}, 目标信息: {chat_target_info}") + logger.debug( + f"{self.log_prefix}获取到聊天信息 - 群聊: {is_group_chat}, 目标信息: {chat_target_info}" + ) except Exception as e: logger.warning(f"{self.log_prefix}获取聊天目标信息失败: {e}") chat_target_info = None @@ -372,7 +373,7 @@ class ActionPlanner(BasePlanner): action_options_block = "" # 根据聊天类型选择不同的动作prompt模板 action_template_name = "action_prompt_private" if not is_group_chat else "action_prompt" - + for using_actions_name, using_actions_info in current_available_actions.items(): using_action_prompt = await global_prompt_manager.get_prompt_async(action_template_name) diff --git a/src/chat/heart_flow/observation/chatting_observation.py b/src/chat/heart_flow/observation/chatting_observation.py index e33b5364c..9f86a0f2a 100644 --- a/src/chat/heart_flow/observation/chatting_observation.py +++ b/src/chat/heart_flow/observation/chatting_observation.py @@ -232,11 +232,11 @@ class ChattingObservation(Observation): truncate=True, show_actions=True, ) - + # 构建简短版本 - 使用最新一半的消息 half_count = len(self.talking_message) // 2 recent_messages = self.talking_message[-half_count:] if half_count > 0 else self.talking_message - + self.talking_message_str_short = build_readable_messages( messages=recent_messages, timestamp_mode="lite", diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 250da154d..62a993d15 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -56,8 +56,6 @@ class ReplyAction(BaseAction): logger.info(f"{self.log_prefix} 决定回复: {self.reasoning}") start_time = self.action_data.get("loop_start_time", time.time()) - - try: success, reply_set = await generator_api.generate_reply( @@ -68,7 +66,6 @@ class ReplyAction(BaseAction): is_group=self.is_group, ) - # 检查从start_time以来的新消息数量 # 获取动作触发时间或使用默认值 current_time = time.time() @@ -89,15 +86,14 @@ class ReplyAction(BaseAction): data = reply_seg[1] if not first_replyed: if need_reply: - await self.send_text(content=data, reply_to=self.action_data.get("reply_to", ""),typing=False) + await self.send_text(content=data, reply_to=self.action_data.get("reply_to", ""), typing=False) first_replyed = True else: - await self.send_text(content=data,typing=False) + await self.send_text(content=data, typing=False) first_replyed = True else: - await self.send_text(content=data,typing=True) + await self.send_text(content=data, typing=True) reply_text += data - # 存储动作记录 await self.store_action_info( @@ -118,7 +114,7 @@ class ReplyAction(BaseAction): class NoReplyAction(BaseAction): """不回复动作,使用智能判断机制决定何时结束等待 - + 新的等待逻辑: - 每0.2秒检查是否有新消息(提高响应性) - 如果累计消息数量达到阈值(默认20条),直接结束等待 @@ -139,13 +135,13 @@ class NoReplyAction(BaseAction): # 连续no_reply计数器 _consecutive_count = 0 - + # LLM判断的最小间隔时间 _min_judge_interval = 1.0 # 最快1秒一次LLM判断 - + # 自动结束的消息数量阈值 _auto_exit_message_count = 20 # 累计20条消息自动结束 - + # 最大等待超时时间 _max_timeout = 1200 # 1200秒 @@ -161,7 +157,7 @@ class NoReplyAction(BaseAction): async def execute(self) -> Tuple[bool, str]: """执行不回复动作,有新消息时进行判断,但最快1秒一次""" import asyncio - + try: # 增加连续计数 NoReplyAction._consecutive_count += 1 @@ -172,33 +168,30 @@ class NoReplyAction(BaseAction): last_judge_time = 0 # 上次进行LLM判断的时间 min_judge_interval = self._min_judge_interval # 最小判断间隔,从配置获取 check_interval = 0.2 # 检查新消息的间隔,设为0.2秒提高响应性 - + # 获取no_reply开始时的上下文消息(5条),用于后续记录 context_messages = message_api.get_messages_by_time_in_chat( chat_id=self.chat_id, start_time=start_time - 300, # 获取开始前5分钟内的消息 end_time=start_time, limit=5, - limit_mode="latest" + limit_mode="latest", ) - + # 构建上下文字符串 context_str = "" if context_messages: context_str = message_api.build_readable_messages( - messages=context_messages, - timestamp_mode="normal_no_YMD", - truncate=False, - show_actions=False + messages=context_messages, timestamp_mode="normal_no_YMD", truncate=False, show_actions=False ) context_str = f"当时选择no_reply前的聊天上下文:\n{context_str}\n" - + logger.info(f"{self.log_prefix} 选择不回复(第{count}次),开始智能等待,原因: {reason}") while True: current_time = time.time() elapsed_time = current_time - start_time - + # 检查是否超时 if elapsed_time >= self._max_timeout: logger.info(f"{self.log_prefix} 达到最大等待时间{self._max_timeout}秒,结束等待") @@ -210,12 +203,12 @@ class NoReplyAction(BaseAction): action_done=True, ) return True, exit_reason - + # 检查是否有新消息 new_message_count = message_api.count_new_messages( chat_id=self.chat_id, start_time=start_time, end_time=current_time ) - + # 如果累计消息数量达到阈值,直接结束等待 if new_message_count >= self._auto_exit_message_count: logger.info(f"{self.log_prefix} 累计消息数量达到{new_message_count}条,直接结束等待") @@ -227,31 +220,28 @@ class NoReplyAction(BaseAction): action_done=True, ) return True, f"累计消息数量达到{new_message_count}条,直接结束等待 (等待时间: {elapsed_time:.1f}秒)" - + # 如果有新消息且距离上次判断>=1秒,进行LLM判断 if new_message_count > 0 and (current_time - last_judge_time) >= min_judge_interval: logger.info(f"{self.log_prefix} 检测到{new_message_count}条新消息,进行智能判断...") - + # 获取最近的消息内容用于判断 recent_messages = message_api.get_messages_by_time_in_chat( - chat_id=self.chat_id, + chat_id=self.chat_id, start_time=start_time, end_time=current_time, ) - + if recent_messages: # 使用message_api构建可读的消息字符串 messages_text = message_api.build_readable_messages( - messages=recent_messages, - timestamp_mode="normal_no_YMD", - truncate=False, - show_actions=False + messages=recent_messages, timestamp_mode="normal_no_YMD", truncate=False, show_actions=False ) - + # 参考simple_planner构建更完整的判断信息 # 获取时间信息 time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - + # 获取身份信息 bot_name = global_config.bot.nickname bot_nickname = "" @@ -259,7 +249,7 @@ class NoReplyAction(BaseAction): bot_nickname = f",也有人叫你{','.join(global_config.bot.alias_names)}" bot_core_personality = global_config.personality.personality_core identity_block = f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}" - + # 构建判断上下文 judge_prompt = f""" {time_block} @@ -282,35 +272,35 @@ class NoReplyAction(BaseAction): 判断:需要回复/不需要回复 理由:[说明你的判断理由] """ - + try: # 获取可用的模型配置 available_models = llm_api.get_available_models() - + # 使用 utils_small 模型 - small_model = getattr(available_models, 'utils_small', None) - + small_model = getattr(available_models, "utils_small", None) + if small_model: # 使用小模型进行判断 success, response, reasoning, model_name = await llm_api.generate_with_model( prompt=judge_prompt, model_config=small_model, request_type="plugin.no_reply_judge", - temperature=0.7 # 降低温度,提高判断的一致性 + temperature=0.7, # 降低温度,提高判断的一致性 ) - + # 更新上次判断时间 last_judge_time = time.time() - + if success and response: response = response.strip() logger.info(f"{self.log_prefix} 模型({model_name})原始判断结果: {response}") - + # 解析LLM响应,提取判断结果和理由 judge_result, reason = self._parse_llm_judge_response(response) - + logger.info(f"{self.log_prefix} 解析结果 - 判断: {judge_result}, 理由: {reason}") - + if judge_result == "需要回复": logger.info(f"{self.log_prefix} 模型判断需要回复,结束等待") full_prompt = f"你的想法是:{reason},你思考是否要进行回复" @@ -329,15 +319,15 @@ class NoReplyAction(BaseAction): else: logger.warning(f"{self.log_prefix} 未找到可用的模型配置,继续等待") last_judge_time = time.time() # 即使失败也更新时间,避免频繁重试 - + except Exception as e: logger.error(f"{self.log_prefix} 模型判断异常: {e},继续等待") last_judge_time = time.time() # 异常时也更新时间,避免频繁重试 - + # 每10秒输出一次等待状态 if int(elapsed_time) % 10 == 0 and int(elapsed_time) > 0: logger.info(f"{self.log_prefix} 已等待{elapsed_time:.0f}秒,继续监听...") - + # 短暂等待后继续检查 await asyncio.sleep(check_interval) @@ -366,49 +356,49 @@ class NoReplyAction(BaseAction): def _parse_llm_judge_response(self, response: str) -> tuple[str, str]: """解析LLM判断响应,提取判断结果和理由 - + Args: response: LLM的原始响应 - + Returns: tuple: (判断结果, 理由) """ try: - lines = response.strip().split('\n') + lines = response.strip().split("\n") judge_result = "不需要回复" # 默认值 reason = "解析失败,使用默认判断" - + for line in lines: line = line.strip() - if line.startswith('判断:') or line.startswith('判断:'): + if line.startswith("判断:") or line.startswith("判断:"): # 提取判断结果 - result_part = line.split(':', 1)[-1] if ':' in line else line.split(':', 1)[-1] + result_part = line.split(":", 1)[-1] if ":" in line else line.split(":", 1)[-1] result_part = result_part.strip() - + if "需要回复" in result_part: judge_result = "需要回复" elif "不需要回复" in result_part: judge_result = "不需要回复" - - elif line.startswith('理由:') or line.startswith('理由:'): + + elif line.startswith("理由:") or line.startswith("理由:"): # 提取理由 - reason_part = line.split(':', 1)[-1] if ':' in line else line.split(':', 1)[-1] + reason_part = line.split(":", 1)[-1] if ":" in line else line.split(":", 1)[-1] reason = reason_part.strip() - + # 如果没有找到标准格式,尝试简单的关键词匹配 if reason == "解析失败,使用默认判断": if "需要回复" in response: judge_result = "需要回复" reason = "检测到'需要回复'关键词" elif "不需要回复" in response: - judge_result = "不需要回复" + judge_result = "不需要回复" reason = "检测到'不需要回复'关键词" else: reason = f"无法解析响应格式,原文: {response[:50]}..." - + logger.debug(f"{self.log_prefix} 解析LLM响应 - 判断: {judge_result}, 理由: {reason}") return judge_result, reason - + except Exception as e: logger.error(f"{self.log_prefix} 解析LLM响应时出错: {e}") return "不需要回复", f"解析异常: {str(e)}" @@ -486,7 +476,6 @@ class EmojiAction(BaseAction): return False, f"表情发送失败: {str(e)}" - class ExitFocusChatAction(BaseAction): """退出专注聊天动作 - 从专注模式切换到普通模式""" @@ -588,8 +577,12 @@ class CoreActionsPlugin(BasePlugin): }, "no_reply": { "max_timeout": ConfigField(type=int, default=1200, description="最大等待超时时间(秒)"), - "min_judge_interval": ConfigField(type=float, default=1.0, description="LLM判断的最小间隔时间(秒),防止过于频繁"), - "auto_exit_message_count": ConfigField(type=int, default=20, description="累计消息数量达到此阈值时自动结束等待"), + "min_judge_interval": ConfigField( + type=float, default=1.0, description="LLM判断的最小间隔时间(秒),防止过于频繁" + ), + "auto_exit_message_count": ConfigField( + type=int, default=20, description="累计消息数量达到此阈值时自动结束等待" + ), "random_probability": ConfigField( type=float, default=0.8, description="Focus模式下,随机选择不回复的概率(0.0到1.0)", example=0.8 ),