From 597f5025e508b54bbfe668225fcb424f9ca39268 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 24 Jun 2025 02:04:19 +0800 Subject: [PATCH 01/11] =?UTF-8?q?feat=EF=BC=9A=E6=9B=B4=E7=AE=80=E6=B4=81?= =?UTF-8?q?=E5=8F=AF=E9=9D=A0=E7=9A=84auto=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../focus_chat/planners/modify_actions.py | 52 +----- src/chat/normal_chat/normal_chat.py | 14 +- .../built_in/core_actions/_manifest.json | 10 -- src/plugins/built_in/core_actions/plugin.py | 152 ++++++++++-------- 4 files changed, 98 insertions(+), 130 deletions(-) diff --git a/src/chat/focus_chat/planners/modify_actions.py b/src/chat/focus_chat/planners/modify_actions.py index f9d347fc..cf0c00b1 100644 --- a/src/chat/focus_chat/planners/modify_actions.py +++ b/src/chat/focus_chat/planners/modify_actions.py @@ -58,6 +58,8 @@ class ActionModifier: logger.debug(f"{self.log_prefix}开始完整动作修改流程") # === 第一阶段:传统观察处理 === + chat_content = None + if observations: hfc_obs = None chat_obs = None @@ -78,7 +80,7 @@ class ActionModifier: if hfc_obs: obs = hfc_obs # 获取适用于FOCUS模式的动作 - all_actions = self.action_manager.get_using_actions_for_mode("focus") + all_actions = self.all_actions action_changes = await self.analyze_loop_actions(obs) if action_changes["add"] or action_changes["remove"]: # 合并动作变更 @@ -94,9 +96,8 @@ class ActionModifier: # 处理ChattingObservation - 传统的类型匹配检查 if chat_obs: - obs = chat_obs # 检查动作的关联类型 - chat_context = get_chat_manager().get_stream(obs.chat_id).context + chat_context = get_chat_manager().get_stream(chat_obs.chat_id).context type_mismatched_actions = [] for action_name in all_actions.keys(): @@ -128,26 +129,13 @@ class ActionModifier: f"{self.log_prefix}传统动作修改完成,当前使用动作: {list(self.action_manager.get_using_actions().keys())}" ) - # === chat_mode检查:强制移除非auto模式下的exit_focus_chat === - if global_config.chat.chat_mode != "auto": - if "exit_focus_chat" in self.action_manager.get_using_actions(): - self.action_manager.remove_action_from_using("exit_focus_chat") - logger.info( - f"{self.log_prefix}移除动作: exit_focus_chat,原因: chat_mode不为auto(当前模式: {global_config.chat.chat_mode})" - ) + # 注释:已移除exit_focus_chat动作,现在由no_reply动作处理频率检测退出专注模式 # === 第二阶段:激活类型判定 === # 如果提供了聊天上下文,则进行激活类型判定 if chat_content is not None: logger.debug(f"{self.log_prefix}开始激活类型判定阶段") - # 保存exit_focus_chat动作(如果存在) - exit_focus_action = None - if "exit_focus_chat" in self.action_manager.get_using_actions(): - exit_focus_action = self.action_manager.get_using_actions()["exit_focus_chat"] - self.action_manager.remove_action_from_using("exit_focus_chat") - logger.debug(f"{self.log_prefix}临时移除exit_focus_chat动作以进行激活类型判定") - # 获取当前使用的动作集(经过第一阶段处理,且适用于FOCUS模式) current_using_actions = self.action_manager.get_using_actions() all_registered_actions = self.action_manager.get_registered_actions() @@ -197,16 +185,7 @@ class ActionModifier: reason = removal_reasons.get(action_name, "未知原因") logger.info(f"{self.log_prefix}移除动作: {action_name},原因: {reason}") - # 恢复exit_focus_chat动作(如果之前存在) - if exit_focus_action: - # 只有在auto模式下才恢复exit_focus_chat动作 - if global_config.chat.chat_mode == "auto": - self.action_manager.add_action_to_using("exit_focus_chat") - logger.debug(f"{self.log_prefix}恢复exit_focus_chat动作") - else: - logger.debug( - f"{self.log_prefix}跳过恢复exit_focus_chat动作,原因: chat_mode不为auto(当前模式: {global_config.chat.chat_mode})" - ) + # 注释:已完全移除exit_focus_chat动作 logger.info(f"{self.log_prefix}激活类型判定完成,最终可用动作: {list(final_activated_actions.keys())}") @@ -576,30 +555,13 @@ class ActionModifier: if not recent_cycles: return result - # 统计no_reply的数量 - no_reply_count = 0 reply_sequence = [] # 记录最近的动作序列 for cycle in recent_cycles: action_result = cycle.loop_plan_info.get("action_result", {}) action_type = action_result.get("action_type", "unknown") - if action_type == "no_reply": - no_reply_count += 1 reply_sequence.append(action_type == "reply") - # 检查no_reply比例 - if len(recent_cycles) >= (4 * global_config.chat.exit_focus_threshold) and ( - no_reply_count / len(recent_cycles) - ) >= (0.7 * global_config.chat.exit_focus_threshold): - if global_config.chat.chat_mode == "auto": - result["add"].append("exit_focus_chat") - result["remove"].append("no_reply") - result["remove"].append("reply") - no_reply_ratio = no_reply_count / len(recent_cycles) - logger.info( - f"{self.log_prefix}检测到高no_reply比例: {no_reply_ratio:.2f},达到退出聊天阈值,将添加exit_focus_chat并移除no_reply/reply动作" - ) - # 计算连续回复的相关阈值 max_reply_num = int(global_config.focus_chat.consecutive_replies * 3.2) @@ -613,7 +575,7 @@ class ActionModifier: last_max_reply_num = reply_sequence[:] # 详细打印阈值和序列信息,便于调试 - logger.debug( + logger.info( f"连续回复阈值: max={max_reply_num}, sec={sec_thres_reply_num}, one={one_thres_reply_num}," f"最近reply序列: {last_max_reply_num}" ) diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 37e55f3c..b43cb1b0 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -563,21 +563,21 @@ class NormalChat: self.interest_dict.pop(msg_id, None) # 创建并行任务列表 - tasks = [] + coroutines = [] for msg_id, (message, interest_value, is_mentioned) in items_to_process: - task = process_single_message(msg_id, message, interest_value, is_mentioned) - tasks.append(task) + coroutine = process_single_message(msg_id, message, interest_value, is_mentioned) + coroutines.append(coroutine) # 并行执行所有任务,限制并发数量避免资源过度消耗 - if tasks: + if coroutines: # 使用信号量控制并发数,最多同时处理5个消息 semaphore = asyncio.Semaphore(5) - async def limited_process(task, sem): + async def limited_process(coroutine, sem): async with sem: - await task + await coroutine - limited_tasks = [limited_process(task, semaphore) for task in tasks] + limited_tasks = [limited_process(coroutine, semaphore) for coroutine in coroutines] await asyncio.gather(*limited_tasks, return_exceptions=True) except asyncio.CancelledError: diff --git a/src/plugins/built_in/core_actions/_manifest.json b/src/plugins/built_in/core_actions/_manifest.json index b690838a..1d1266f6 100644 --- a/src/plugins/built_in/core_actions/_manifest.json +++ b/src/plugins/built_in/core_actions/_manifest.json @@ -39,16 +39,6 @@ "type": "action", "name": "emoji", "description": "发送表情包辅助表达情绪" - }, - { - "type": "action", - "name": "change_to_focus_chat", - "description": "切换到专注聊天,从普通模式切换到专注模式" - }, - { - "type": "action", - "name": "exit_focus_chat", - "description": "退出专注聊天,从专注模式切换到普通模式" } ] } diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index e263c59b..a4d2ef57 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -147,9 +147,10 @@ class NoReplyAction(BaseAction): # 跳过LLM判断的配置 _skip_judge_when_tired = True - _skip_probability_light = 0.2 # 轻度疲惫跳过概率 - _skip_probability_medium = 0.4 # 中度疲惫跳过概率 - _skip_probability_heavy = 0.6 # 重度疲惫跳过概率 + _skip_probability = 0.5 + + # 新增:回复频率退出专注模式的配置 + _frequency_check_window = 600 # 频率检查窗口时间(秒) # 动作参数定义 action_parameters = {"reason": "不回复的原因"} @@ -214,6 +215,20 @@ class NoReplyAction(BaseAction): ) return True, exit_reason + # **新增**:检查回复频率,决定是否退出专注模式 + should_exit_focus = await self._check_frequency_and_exit_focus(current_time) + if should_exit_focus: + logger.info(f"{self.log_prefix} 检测到回复频率过高,退出专注模式") + # 标记退出专注模式 + self.action_data["_system_command"] = "stop_focus_chat" + exit_reason = f"{global_config.bot.nickname}(你)发现自己回复太频繁了,决定退出专注模式,稍作休息" + await self.store_action_info( + action_build_into_prompt=True, + action_prompt_display=exit_reason, + 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 @@ -314,13 +329,14 @@ class NoReplyAction(BaseAction): over_count = bot_message_count - talk_frequency_threshold # 根据超过的数量设置不同的提示词和跳过概率 + skip_probability = 0 if over_count <= 3: frequency_block = "你感觉稍微有些累,回复的有点多了。\n" elif over_count <= 5: frequency_block = "你今天说话比较多,感觉有点疲惫,想要稍微休息一下。\n" else: frequency_block = "你发现自己说话太多了,感觉很累,想要安静一会儿,除非有重要的事情否则不想回复。\n" - skip_probability = self._skip_probability_heavy + skip_probability = self._skip_probability # 根据配置和概率决定是否跳过LLM判断 if self._skip_judge_when_tired and random.random() < skip_probability: @@ -464,6 +480,64 @@ class NoReplyAction(BaseAction): ) return False, f"不回复动作执行失败: {e}" + async def _check_frequency_and_exit_focus(self, current_time: float) -> bool: + """检查回复频率,决定是否退出专注模式 + + Args: + current_time: 当前时间戳 + + Returns: + bool: 是否应该退出专注模式 + """ + try: + # 只在auto模式下进行频率检查 + if global_config.chat.chat_mode != "auto": + return False + + # 获取检查窗口内的所有消息 + window_start_time = current_time - self._frequency_check_window + all_messages = message_api.get_messages_by_time_in_chat( + chat_id=self.chat_id, + start_time=window_start_time, + end_time=current_time, + ) + + if not all_messages: + return False + + # 统计bot自己的回复数量 + bot_message_count = 0 + user_id = global_config.bot.qq_account + + for message in all_messages: + sender_id = message.get("user_id", "") + if sender_id == user_id: + bot_message_count += 1 + + # 计算当前回复频率(每分钟回复数) + window_minutes = self._frequency_check_window / 60 + current_frequency = bot_message_count / window_minutes + + # 计算阈值频率:使用 exit_focus_threshold * 1.5 + threshold_multiplier = global_config.chat.exit_focus_threshold * 1.5 + threshold_frequency = global_config.chat.talk_frequency * threshold_multiplier + + # 判断是否超过阈值 + if current_frequency > threshold_frequency: + logger.info( + f"{self.log_prefix} 回复频率检查:当前频率 {current_frequency:.2f}/分钟,超过阈值 {threshold_frequency:.2f}/分钟 (exit_threshold={global_config.chat.exit_focus_threshold} * 1.5),准备退出专注模式" + ) + return True + else: + logger.debug( + f"{self.log_prefix} 回复频率检查:当前频率 {current_frequency:.2f}/分钟,未超过阈值 {threshold_frequency:.2f}/分钟 (exit_threshold={global_config.chat.exit_focus_threshold} * 1.5)" + ) + return False + + except Exception as e: + logger.error(f"{self.log_prefix} 检查回复频率时出错: {e}") + return False + def _parse_llm_judge_response(self, response: str) -> tuple[str, str]: """解析LLM判断响应,使用JSON格式提取判断结果和理由 @@ -596,66 +670,6 @@ class EmojiAction(BaseAction): return False, f"表情发送失败: {str(e)}" -class ExitFocusChatAction(BaseAction): - """退出专注聊天动作 - 从专注模式切换到普通模式""" - - # 激活设置 - focus_activation_type = ActionActivationType.NEVER - normal_activation_type = ActionActivationType.NEVER - mode_enable = ChatMode.FOCUS - parallel_action = False - - # 动作基本信息 - action_name = "exit_focus_chat" - action_description = "退出专注聊天,从专注模式切换到普通模式" - - # LLM判断提示词 - llm_judge_prompt = """ - 判定是否需要退出专注聊天的条件: - 1. 很长时间没有回复,应该退出专注聊天 - 2. 当前内容不需要持续专注关注 - 3. 聊天内容已经完成,话题结束 - - 请回答"是"或"否"。 - """ - - # 动作参数定义 - action_parameters = {} - - # 动作使用场景 - action_require = [ - "很长时间没有回复,你决定退出专注聊天", - "当前内容不需要持续专注关注,你决定退出专注聊天", - "聊天内容已经完成,你决定退出专注聊天", - ] - - # 关联类型 - associated_types = [] - - async def execute(self) -> Tuple[bool, str]: - """执行退出专注聊天动作""" - logger.info(f"{self.log_prefix} 决定退出专注聊天: {self.reasoning}") - - try: - # 标记状态切换请求 - self._mark_state_change() - - # 重置NoReplyAction的连续计数器 - NoReplyAction.reset_consecutive_count() - - status_message = "决定退出专注聊天模式" - return True, status_message - - except Exception as e: - logger.error(f"{self.log_prefix} 退出专注聊天动作执行失败: {e}") - return False, f"退出专注聊天失败: {str(e)}" - - def _mark_state_change(self): - """标记状态切换请求""" - # 通过action_data传递状态切换命令 - self.action_data["_system_command"] = "stop_focus_chat" - logger.info(f"{self.log_prefix} 已标记状态切换命令: stop_focus_chat") - @register_plugin class CoreActionsPlugin(BasePlugin): @@ -757,6 +771,10 @@ class CoreActionsPlugin(BasePlugin): skip_probability_heavy = self.get_config("no_reply.skip_probability_heavy", 0.6) NoReplyAction._skip_probability_heavy = skip_probability_heavy + # 新增:频率检测相关配置 + frequency_check_window = self.get_config("no_reply.frequency_check_window", 600) + NoReplyAction._frequency_check_window = frequency_check_window + # --- 根据配置注册组件 --- components = [] if self.get_config("components.enable_reply", True): @@ -765,14 +783,10 @@ class CoreActionsPlugin(BasePlugin): components.append((NoReplyAction.get_action_info(), NoReplyAction)) if self.get_config("components.enable_emoji", True): components.append((EmojiAction.get_action_info(), EmojiAction)) - if self.get_config("components.enable_exit_focus", True): - components.append((ExitFocusChatAction.get_action_info(), ExitFocusChatAction)) # components.append((DeepReplyAction.get_action_info(), DeepReplyAction)) return components - - # class DeepReplyAction(BaseAction): # """回复动作 - 参与聊天回复""" @@ -895,3 +909,5 @@ class CoreActionsPlugin(BasePlugin): # data = reply[1] # reply_text += data # return reply_text + + From 6c0ed4696fe1036b0835da7bdd11f5cd35f3df58 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Jun 2025 18:05:07 +0000 Subject: [PATCH 02/11] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=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/planners/modify_actions.py | 2 +- src/plugins/built_in/core_actions/plugin.py | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/chat/focus_chat/planners/modify_actions.py b/src/chat/focus_chat/planners/modify_actions.py index cf0c00b1..adba7208 100644 --- a/src/chat/focus_chat/planners/modify_actions.py +++ b/src/chat/focus_chat/planners/modify_actions.py @@ -59,7 +59,7 @@ class ActionModifier: # === 第一阶段:传统观察处理 === chat_content = None - + if observations: hfc_obs = None chat_obs = None diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 11499185..f57306dd 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -147,10 +147,10 @@ class NoReplyAction(BaseAction): # 跳过LLM判断的配置 _skip_judge_when_tired = True - _skip_probability = 0.5 - + _skip_probability = 0.5 + # 新增:回复频率退出专注模式的配置 - _frequency_check_window = 600 # 频率检查窗口时间(秒) + _frequency_check_window = 600 # 频率检查窗口时间(秒) # 动作参数定义 action_parameters = {"reason": "不回复的原因"} @@ -482,10 +482,10 @@ class NoReplyAction(BaseAction): async def _check_frequency_and_exit_focus(self, current_time: float) -> bool: """检查回复频率,决定是否退出专注模式 - + Args: current_time: 当前时间戳 - + Returns: bool: 是否应该退出专注模式 """ @@ -493,7 +493,7 @@ class NoReplyAction(BaseAction): # 只在auto模式下进行频率检查 if global_config.chat.chat_mode != "auto": return False - + # 获取检查窗口内的所有消息 window_start_time = current_time - self._frequency_check_window all_messages = message_api.get_messages_by_time_in_chat( @@ -670,7 +670,6 @@ class EmojiAction(BaseAction): return False, f"表情发送失败: {str(e)}" - @register_plugin class CoreActionsPlugin(BasePlugin): """核心动作插件 @@ -787,6 +786,8 @@ class CoreActionsPlugin(BasePlugin): # components.append((DeepReplyAction.get_action_info(), DeepReplyAction)) return components + + # class DeepReplyAction(BaseAction): # """回复动作 - 参与聊天回复""" @@ -909,5 +910,3 @@ class CoreActionsPlugin(BasePlugin): # data = reply[1] # reply_text += data # return reply_text - - From 8a6b8118defee052b89291a23e82c6eddaaa5cb4 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 24 Jun 2025 02:15:25 +0800 Subject: [PATCH 03/11] =?UTF-8?q?fix=EF=BC=9A=E5=B0=8F=E5=B0=8F=E6=8B=86?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/built_in/core_actions/no_reply.py | 510 ++++++++++++++++++ src/plugins/built_in/core_actions/plugin.py | 497 +---------------- 2 files changed, 512 insertions(+), 495 deletions(-) create mode 100644 src/plugins/built_in/core_actions/no_reply.py 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 00000000..4d65bdca --- /dev/null +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -0,0 +1,510 @@ +import random +import time +import json +from typing import Tuple + +# 导入新插件系统 +from src.plugin_system import BaseAction, ActionActivationType, ChatMode + +# 导入依赖的系统组件 +from src.common.logger import get_logger + +# 导入API模块 - 标准Python包方式 +from src.plugin_system.apis import message_api, llm_api +from src.config.config import global_config +from datetime import datetime +from json_repair import repair_json + +logger = get_logger("core_actions") + + +class NoReplyAction(BaseAction): + """不回复动作,使用智能判断机制决定何时结束等待 + + 新的等待逻辑: + - 每0.2秒检查是否有新消息(提高响应性) + - 如果累计消息数量达到阈值(默认20条),直接结束等待 + - 有新消息时进行LLM判断,但最快1秒一次(防止过于频繁) + - 如果判断需要回复,则结束等待;否则继续等待 + - 达到最大超时时间后强制结束 + """ + + focus_activation_type = ActionActivationType.ALWAYS + # focus_activation_type = ActionActivationType.RANDOM + normal_activation_type = ActionActivationType.NEVER + mode_enable = ChatMode.FOCUS + parallel_action = False + + # 动作基本信息 + action_name = "no_reply" + action_description = "暂时不回复消息" + + # 连续no_reply计数器 + _consecutive_count = 0 + + # LLM判断的最小间隔时间 + _min_judge_interval = 1.0 # 最快1秒一次LLM判断 + + # 自动结束的消息数量阈值 + _auto_exit_message_count = 20 # 累计20条消息自动结束 + + # 最大等待超时时间 + _max_timeout = 1200 # 1200秒 + + # 跳过LLM判断的配置 + _skip_judge_when_tired = True + _skip_probability = 0.5 + + # 新增:回复频率退出专注模式的配置 + _frequency_check_window = 600 # 频率检查窗口时间(秒) + + # 动作参数定义 + action_parameters = {"reason": "不回复的原因"} + + # 动作使用场景 + action_require = ["你发送了消息,目前无人回复"] + + # 关联类型 + associated_types = [] + + async def execute(self) -> Tuple[bool, str]: + """执行不回复动作,有新消息时进行判断,但最快1秒一次""" + import asyncio + + try: + # 增加连续计数 + NoReplyAction._consecutive_count += 1 + count = NoReplyAction._consecutive_count + + reason = self.action_data.get("reason", "") + start_time = time.time() + last_judge_time = 0 # 上次进行LLM判断的时间 + min_judge_interval = self._min_judge_interval # 最小判断间隔,从配置获取 + check_interval = 0.2 # 检查新消息的间隔,设为0.2秒提高响应性 + + # 累积判断历史 + judge_history = [] # 存储每次判断的结果和理由 + + # 获取no_reply开始时的上下文消息(10条),用于后续记录 + context_messages = message_api.get_messages_by_time_in_chat( + chat_id=self.chat_id, + start_time=start_time - 600, # 获取开始前10分钟内的消息 + end_time=start_time, + limit=10, + 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=True + ) + 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}秒,结束等待") + exit_reason = ( + f"{global_config.bot.nickname}(你)等待了{self._max_timeout}秒,可以考虑一下是否要进行回复" + ) + await self.store_action_info( + action_build_into_prompt=True, + action_prompt_display=exit_reason, + action_done=True, + ) + return True, exit_reason + + # **新增**:检查回复频率,决定是否退出专注模式 + should_exit_focus = await self._check_frequency_and_exit_focus(current_time) + if should_exit_focus: + logger.info(f"{self.log_prefix} 检测到回复频率过高,退出专注模式") + # 标记退出专注模式 + self.action_data["_system_command"] = "stop_focus_chat" + exit_reason = f"{global_config.bot.nickname}(你)发现自己回复太频繁了,决定退出专注模式,稍作休息" + await self.store_action_info( + action_build_into_prompt=True, + action_prompt_display=exit_reason, + 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}条,直接结束等待") + exit_reason = f"{global_config.bot.nickname}(你)看到了{new_message_count}条新消息,可以考虑一下是否要进行回复" + await self.store_action_info( + action_build_into_prompt=True, + action_prompt_display=exit_reason, + action_done=True, + ) + return True, f"累计消息数量达到{new_message_count}条,直接结束等待 (等待时间: {elapsed_time:.1f}秒)" + + # 判定条件:累计3条消息或等待超过5秒且有新消息 + time_since_last_judge = current_time - last_judge_time + should_judge = ( + new_message_count >= 3 # 累计3条消息 + or (new_message_count > 0 and time_since_last_judge >= 5.0) # 等待超过5秒且有新消息 + ) + + if should_judge and time_since_last_judge >= min_judge_interval: + # 判断触发原因 + trigger_reason = "" + if new_message_count >= 3: + trigger_reason = f"累计{new_message_count}条消息" + elif time_since_last_judge >= 5.0: + trigger_reason = f"等待{time_since_last_judge:.1f}秒且有{new_message_count}条新消息" + + logger.info(f"{self.log_prefix} 触发判定({trigger_reason}),进行智能判断...") + + # 获取最近的消息内容用于判断 + recent_messages = message_api.get_messages_by_time_in_chat( + 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 + ) + + # 参考simple_planner构建更完整的判断信息 + # 获取时间信息 + time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" + + # 获取身份信息 + bot_name = global_config.bot.nickname + bot_nickname = "" + if global_config.bot.alias_names: + 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}" + + # 构建判断历史字符串(最多显示3条) + history_block = "" + if judge_history: + history_block = "之前的判断历史:\n" + # 只取最近的3条历史记录 + recent_history = judge_history[-3:] if len(judge_history) > 3 else judge_history + for i, (timestamp, judge_result, reason) in enumerate(recent_history, 1): + elapsed_seconds = int(timestamp - start_time) + history_block += f"{i}. 等待{elapsed_seconds}秒时判断:{judge_result},理由:{reason}\n" + history_block += "\n" + + # 检查过去10分钟的发言频率 + frequency_block = "" + should_skip_llm_judge = False # 是否跳过LLM判断 + + try: + # 获取过去10分钟的所有消息 + past_10min_time = current_time - 600 # 10分钟前 + all_messages_10min = message_api.get_messages_by_time_in_chat( + chat_id=self.chat_id, + start_time=past_10min_time, + end_time=current_time, + ) + + # 手动过滤bot自己的消息 + bot_message_count = 0 + if all_messages_10min: + user_id = global_config.bot.qq_account + + for message in all_messages_10min: + # 检查消息发送者是否是bot + sender_id = message.get("user_id", "") + + if sender_id == user_id: + bot_message_count += 1 + + talk_frequency_threshold = global_config.chat.talk_frequency * 10 + + if bot_message_count > talk_frequency_threshold: + over_count = bot_message_count - talk_frequency_threshold + + # 根据超过的数量设置不同的提示词和跳过概率 + skip_probability = 0 + if over_count <= 3: + frequency_block = "你感觉稍微有些累,回复的有点多了。\n" + elif over_count <= 5: + frequency_block = "你今天说话比较多,感觉有点疲惫,想要稍微休息一下。\n" + else: + frequency_block = "你发现自己说话太多了,感觉很累,想要安静一会儿,除非有重要的事情否则不想回复。\n" + skip_probability = self._skip_probability + + # 根据配置和概率决定是否跳过LLM判断 + if self._skip_judge_when_tired and random.random() < skip_probability: + should_skip_llm_judge = True + logger.info( + f"{self.log_prefix} 发言过多(超过{over_count}条),随机决定跳过此次LLM判断(概率{skip_probability * 100:.0f}%)" + ) + + logger.info( + f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示" + ) + else: + # 回复次数少时的正向提示 + under_count = talk_frequency_threshold - bot_message_count + + if under_count >= talk_frequency_threshold * 0.8: # 回复很少(少于20%) + frequency_block = "你感觉精力充沛,状态很好。\n" + elif under_count >= talk_frequency_threshold * 0.5: # 回复较少(少于50%) + frequency_block = "你感觉状态不错。\n" + else: # 刚好达到阈值 + frequency_block = "" + + logger.info( + f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,未超过阈值{talk_frequency_threshold},添加正向提示" + ) + + except Exception as e: + logger.warning(f"{self.log_prefix} 检查发言频率时出错: {e}") + frequency_block = "" + + # 如果决定跳过LLM判断,直接更新时间并继续等待 + if should_skip_llm_judge: + last_judge_time = time.time() # 更新判断时间,避免立即重新判断 + start_time = current_time # 更新开始时间,避免重复计算同样的消息 + continue # 跳过本次LLM判断,继续循环等待 + + # 构建判断上下文 + judge_prompt = f""" +{time_block} +{identity_block} + +你现在正在QQ群参与聊天,以下是聊天内容: +{context_str} +在以上的聊天中,你选择了暂时不回复,现在,你看到了新的聊天消息如下: +{messages_text} + +{history_block} +请注意:{frequency_block} +请你判断,是否要结束不回复的状态,重新加入聊天讨论。 + +判断标准: +1. 如果有人直接@你、提到你的名字或明确向你询问,应该回复 +2. 如果话题发生重要变化,需要你参与讨论,应该回复 +3. 如果只是普通闲聊、重复内容或与你无关的讨论,不需要回复 +4. 如果消息内容过于简单(如单纯的表情、"哈哈"等),不需要回复 +5. 参考之前的判断历史,如果情况有明显变化或持续等待时间过长,考虑调整判断 + +请用JSON格式回复你的判断,严格按照以下格式: +{{ + "should_reply": true/false, + "reason": "详细说明你的判断理由" +}} +""" + + try: + # 获取可用的模型配置 + available_models = llm_api.get_available_models() + + # 使用 utils_small 模型 + small_model = getattr(available_models, "utils_small", None) + + print(judge_prompt) + + 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, # 进一步降低温度,提高JSON输出的一致性和准确性 + ) + + # 更新上次判断时间 + last_judge_time = time.time() + + if success and response: + response = response.strip() + logger.info(f"{self.log_prefix} 模型({model_name})原始JSON响应: {response}") + + # 解析LLM的JSON响应,提取判断结果和理由 + judge_result, reason = self._parse_llm_judge_response(response) + + logger.info( + f"{self.log_prefix} JSON解析结果 - 判断: {judge_result}, 理由: {reason}" + ) + + # 将判断结果保存到历史中 + judge_history.append((current_time, judge_result, reason)) + + if judge_result == "需要回复": + logger.info(f"{self.log_prefix} 模型判断需要回复,结束等待") + + full_prompt = f"{global_config.bot.nickname}(你)的想法是:{reason}" + await self.store_action_info( + action_build_into_prompt=True, + action_prompt_display=full_prompt, + action_done=True, + ) + return True, f"检测到需要回复的消息,结束等待 (等待时间: {elapsed_time:.1f}秒)" + else: + logger.info(f"{self.log_prefix} 模型判断不需要回复,理由: {reason},继续等待") + # 更新开始时间,避免重复判断同样的消息 + start_time = current_time + else: + logger.warning(f"{self.log_prefix} 模型判断失败,继续等待") + 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(1) + + # 短暂等待后继续检查 + await asyncio.sleep(check_interval) + + except Exception as e: + logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}") + # 即使执行失败也要记录 + exit_reason = f"执行异常: {str(e)}" + full_prompt = f"{context_str}{exit_reason},你思考是否要进行回复" + await self.store_action_info( + action_build_into_prompt=True, + action_prompt_display=full_prompt, + action_done=True, + ) + return False, f"不回复动作执行失败: {e}" + + async def _check_frequency_and_exit_focus(self, current_time: float) -> bool: + """检查回复频率,决定是否退出专注模式 + + Args: + current_time: 当前时间戳 + + Returns: + bool: 是否应该退出专注模式 + """ + try: + # 只在auto模式下进行频率检查 + if global_config.chat.chat_mode != "auto": + return False + + # 获取检查窗口内的所有消息 + window_start_time = current_time - self._frequency_check_window + all_messages = message_api.get_messages_by_time_in_chat( + chat_id=self.chat_id, + start_time=window_start_time, + end_time=current_time, + ) + + if not all_messages: + return False + + # 统计bot自己的回复数量 + bot_message_count = 0 + user_id = global_config.bot.qq_account + + for message in all_messages: + sender_id = message.get("user_id", "") + if sender_id == user_id: + bot_message_count += 1 + + # 计算当前回复频率(每分钟回复数) + window_minutes = self._frequency_check_window / 60 + current_frequency = bot_message_count / window_minutes + + # 计算阈值频率:使用 exit_focus_threshold * 1.5 + threshold_multiplier = global_config.chat.exit_focus_threshold * 1.5 + threshold_frequency = global_config.chat.talk_frequency * threshold_multiplier + + # 判断是否超过阈值 + if current_frequency > threshold_frequency: + logger.info( + f"{self.log_prefix} 回复频率检查:当前频率 {current_frequency:.2f}/分钟,超过阈值 {threshold_frequency:.2f}/分钟 (exit_threshold={global_config.chat.exit_focus_threshold} * 1.5),准备退出专注模式" + ) + return True + else: + logger.debug( + f"{self.log_prefix} 回复频率检查:当前频率 {current_frequency:.2f}/分钟,未超过阈值 {threshold_frequency:.2f}/分钟 (exit_threshold={global_config.chat.exit_focus_threshold} * 1.5)" + ) + return False + + except Exception as e: + logger.error(f"{self.log_prefix} 检查回复频率时出错: {e}") + return False + + def _parse_llm_judge_response(self, response: str) -> tuple[str, str]: + """解析LLM判断响应,使用JSON格式提取判断结果和理由 + + Args: + response: LLM的原始JSON响应 + + Returns: + tuple: (判断结果, 理由) + """ + try: + # 使用repair_json修复可能有问题的JSON格式 + fixed_json_string = repair_json(response) + logger.debug(f"{self.log_prefix} repair_json修复后的响应: {fixed_json_string}") + + # 如果repair_json返回的是字符串,需要解析为Python对象 + if isinstance(fixed_json_string, str): + result_json = json.loads(fixed_json_string) + else: + # 如果repair_json直接返回了字典对象,直接使用 + result_json = fixed_json_string + + # 从JSON中提取判断结果和理由 + should_reply = result_json.get("should_reply", False) + reason = result_json.get("reason", "无法获取判断理由") + + # 转换布尔值为中文字符串 + judge_result = "需要回复" if should_reply else "不需要回复" + + logger.debug(f"{self.log_prefix} JSON解析成功 - 判断: {judge_result}, 理由: {reason}") + return judge_result, reason + + except (json.JSONDecodeError, KeyError, TypeError) as e: + logger.warning(f"{self.log_prefix} JSON解析失败,尝试文本解析: {e}") + + # 如果JSON解析失败,回退到简单的关键词匹配 + try: + response_lower = response.lower() + + if "true" in response_lower or "需要回复" in response: + judge_result = "需要回复" + reason = "从响应文本中检测到需要回复的指示" + elif "false" in response_lower or "不需要回复" in response: + judge_result = "不需要回复" + reason = "从响应文本中检测到不需要回复的指示" + else: + judge_result = "不需要回复" # 默认值 + reason = f"无法解析响应格式,使用默认判断。原始响应: {response[:100]}..." + + logger.debug(f"{self.log_prefix} 文本解析结果 - 判断: {judge_result}, 理由: {reason}") + return judge_result, reason + + except Exception as fallback_e: + logger.error(f"{self.log_prefix} 文本解析也失败: {fallback_e}") + return "不需要回复", f"解析异常: {str(e)}, 回退解析也失败: {str(fallback_e)}" + + except Exception as e: + logger.error(f"{self.log_prefix} 解析LLM响应时出错: {e}") + return "不需要回复", f"解析异常: {str(e)}" + + @classmethod + def reset_consecutive_count(cls): + """重置连续计数器""" + cls._consecutive_count = 0 + logger.debug("NoReplyAction连续计数器已重置") \ No newline at end of file diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 11499185..a189fe46 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -7,7 +7,6 @@ import random import time -import json from typing import List, Tuple, Type # 导入新插件系统 @@ -18,10 +17,8 @@ from src.plugin_system.base.config_types import ConfigField from src.common.logger import get_logger # 导入API模块 - 标准Python包方式 -from src.plugin_system.apis import emoji_api, generator_api, message_api, llm_api -from src.config.config import global_config -from datetime import datetime -from json_repair import repair_json +from src.plugin_system.apis import emoji_api, generator_api, message_api +from src.plugins.built_in.core_actions.no_reply import NoReplyAction logger = get_logger("core_actions") @@ -112,496 +109,6 @@ class ReplyAction(BaseAction): return False, f"回复失败: {str(e)}" -class NoReplyAction(BaseAction): - """不回复动作,使用智能判断机制决定何时结束等待 - - 新的等待逻辑: - - 每0.2秒检查是否有新消息(提高响应性) - - 如果累计消息数量达到阈值(默认20条),直接结束等待 - - 有新消息时进行LLM判断,但最快1秒一次(防止过于频繁) - - 如果判断需要回复,则结束等待;否则继续等待 - - 达到最大超时时间后强制结束 - """ - - focus_activation_type = ActionActivationType.ALWAYS - # focus_activation_type = ActionActivationType.RANDOM - normal_activation_type = ActionActivationType.NEVER - mode_enable = ChatMode.FOCUS - parallel_action = False - - # 动作基本信息 - action_name = "no_reply" - action_description = "暂时不回复消息" - - # 连续no_reply计数器 - _consecutive_count = 0 - - # LLM判断的最小间隔时间 - _min_judge_interval = 1.0 # 最快1秒一次LLM判断 - - # 自动结束的消息数量阈值 - _auto_exit_message_count = 20 # 累计20条消息自动结束 - - # 最大等待超时时间 - _max_timeout = 1200 # 1200秒 - - # 跳过LLM判断的配置 - _skip_judge_when_tired = True - _skip_probability = 0.5 - - # 新增:回复频率退出专注模式的配置 - _frequency_check_window = 600 # 频率检查窗口时间(秒) - - # 动作参数定义 - action_parameters = {"reason": "不回复的原因"} - - # 动作使用场景 - action_require = ["你发送了消息,目前无人回复"] - - # 关联类型 - associated_types = [] - - async def execute(self) -> Tuple[bool, str]: - """执行不回复动作,有新消息时进行判断,但最快1秒一次""" - import asyncio - - try: - # 增加连续计数 - NoReplyAction._consecutive_count += 1 - count = NoReplyAction._consecutive_count - - reason = self.action_data.get("reason", "") - start_time = time.time() - last_judge_time = 0 # 上次进行LLM判断的时间 - min_judge_interval = self._min_judge_interval # 最小判断间隔,从配置获取 - check_interval = 0.2 # 检查新消息的间隔,设为0.2秒提高响应性 - - # 累积判断历史 - judge_history = [] # 存储每次判断的结果和理由 - - # 获取no_reply开始时的上下文消息(10条),用于后续记录 - context_messages = message_api.get_messages_by_time_in_chat( - chat_id=self.chat_id, - start_time=start_time - 600, # 获取开始前10分钟内的消息 - end_time=start_time, - limit=10, - 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=True - ) - 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}秒,结束等待") - exit_reason = ( - f"{global_config.bot.nickname}(你)等待了{self._max_timeout}秒,可以考虑一下是否要进行回复" - ) - await self.store_action_info( - action_build_into_prompt=True, - action_prompt_display=exit_reason, - action_done=True, - ) - return True, exit_reason - - # **新增**:检查回复频率,决定是否退出专注模式 - should_exit_focus = await self._check_frequency_and_exit_focus(current_time) - if should_exit_focus: - logger.info(f"{self.log_prefix} 检测到回复频率过高,退出专注模式") - # 标记退出专注模式 - self.action_data["_system_command"] = "stop_focus_chat" - exit_reason = f"{global_config.bot.nickname}(你)发现自己回复太频繁了,决定退出专注模式,稍作休息" - await self.store_action_info( - action_build_into_prompt=True, - action_prompt_display=exit_reason, - 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}条,直接结束等待") - exit_reason = f"{global_config.bot.nickname}(你)看到了{new_message_count}条新消息,可以考虑一下是否要进行回复" - await self.store_action_info( - action_build_into_prompt=True, - action_prompt_display=exit_reason, - action_done=True, - ) - return True, f"累计消息数量达到{new_message_count}条,直接结束等待 (等待时间: {elapsed_time:.1f}秒)" - - # 判定条件:累计3条消息或等待超过5秒且有新消息 - time_since_last_judge = current_time - last_judge_time - should_judge = ( - new_message_count >= 3 # 累计3条消息 - or (new_message_count > 0 and time_since_last_judge >= 5.0) # 等待超过5秒且有新消息 - ) - - if should_judge and time_since_last_judge >= min_judge_interval: - # 判断触发原因 - trigger_reason = "" - if new_message_count >= 3: - trigger_reason = f"累计{new_message_count}条消息" - elif time_since_last_judge >= 5.0: - trigger_reason = f"等待{time_since_last_judge:.1f}秒且有{new_message_count}条新消息" - - logger.info(f"{self.log_prefix} 触发判定({trigger_reason}),进行智能判断...") - - # 获取最近的消息内容用于判断 - recent_messages = message_api.get_messages_by_time_in_chat( - 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 - ) - - # 参考simple_planner构建更完整的判断信息 - # 获取时间信息 - time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - # 获取身份信息 - bot_name = global_config.bot.nickname - bot_nickname = "" - if global_config.bot.alias_names: - 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}" - - # 构建判断历史字符串(最多显示3条) - history_block = "" - if judge_history: - history_block = "之前的判断历史:\n" - # 只取最近的3条历史记录 - recent_history = judge_history[-3:] if len(judge_history) > 3 else judge_history - for i, (timestamp, judge_result, reason) in enumerate(recent_history, 1): - elapsed_seconds = int(timestamp - start_time) - history_block += f"{i}. 等待{elapsed_seconds}秒时判断:{judge_result},理由:{reason}\n" - history_block += "\n" - - # 检查过去10分钟的发言频率 - frequency_block = "" - should_skip_llm_judge = False # 是否跳过LLM判断 - - try: - # 获取过去10分钟的所有消息 - past_10min_time = current_time - 600 # 10分钟前 - all_messages_10min = message_api.get_messages_by_time_in_chat( - chat_id=self.chat_id, - start_time=past_10min_time, - end_time=current_time, - ) - - # 手动过滤bot自己的消息 - bot_message_count = 0 - if all_messages_10min: - user_id = global_config.bot.qq_account - - for message in all_messages_10min: - # 检查消息发送者是否是bot - sender_id = message.get("user_id", "") - - if sender_id == user_id: - bot_message_count += 1 - - talk_frequency_threshold = global_config.chat.talk_frequency * 10 - - if bot_message_count > talk_frequency_threshold: - over_count = bot_message_count - talk_frequency_threshold - - # 根据超过的数量设置不同的提示词和跳过概率 - skip_probability = 0 - if over_count <= 3: - frequency_block = "你感觉稍微有些累,回复的有点多了。\n" - elif over_count <= 5: - frequency_block = "你今天说话比较多,感觉有点疲惫,想要稍微休息一下。\n" - else: - frequency_block = "你发现自己说话太多了,感觉很累,想要安静一会儿,除非有重要的事情否则不想回复。\n" - skip_probability = self._skip_probability - - # 根据配置和概率决定是否跳过LLM判断 - if self._skip_judge_when_tired and random.random() < skip_probability: - should_skip_llm_judge = True - logger.info( - f"{self.log_prefix} 发言过多(超过{over_count}条),随机决定跳过此次LLM判断(概率{skip_probability * 100:.0f}%)" - ) - - logger.info( - f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示" - ) - else: - # 回复次数少时的正向提示 - under_count = talk_frequency_threshold - bot_message_count - - if under_count >= talk_frequency_threshold * 0.8: # 回复很少(少于20%) - frequency_block = "你感觉精力充沛,状态很好。\n" - elif under_count >= talk_frequency_threshold * 0.5: # 回复较少(少于50%) - frequency_block = "你感觉状态不错。\n" - else: # 刚好达到阈值 - frequency_block = "" - - logger.info( - f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,未超过阈值{talk_frequency_threshold},添加正向提示" - ) - - except Exception as e: - logger.warning(f"{self.log_prefix} 检查发言频率时出错: {e}") - frequency_block = "" - - # 如果决定跳过LLM判断,直接更新时间并继续等待 - if should_skip_llm_judge: - last_judge_time = time.time() # 更新判断时间,避免立即重新判断 - start_time = current_time # 更新开始时间,避免重复计算同样的消息 - continue # 跳过本次LLM判断,继续循环等待 - - # 构建判断上下文 - judge_prompt = f""" -{time_block} -{identity_block} - -你现在正在QQ群参与聊天,以下是聊天内容: -{context_str} -在以上的聊天中,你选择了暂时不回复,现在,你看到了新的聊天消息如下: -{messages_text} - -{history_block} -请注意:{frequency_block} -请你判断,是否要结束不回复的状态,重新加入聊天讨论。 - -判断标准: -1. 如果有人直接@你、提到你的名字或明确向你询问,应该回复 -2. 如果话题发生重要变化,需要你参与讨论,应该回复 -3. 如果只是普通闲聊、重复内容或与你无关的讨论,不需要回复 -4. 如果消息内容过于简单(如单纯的表情、"哈哈"等),不需要回复 -5. 参考之前的判断历史,如果情况有明显变化或持续等待时间过长,考虑调整判断 - -请用JSON格式回复你的判断,严格按照以下格式: -{{ - "should_reply": true/false, - "reason": "详细说明你的判断理由" -}} -""" - - try: - # 获取可用的模型配置 - available_models = llm_api.get_available_models() - - # 使用 utils_small 模型 - small_model = getattr(available_models, "utils_small", None) - - print(judge_prompt) - - 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, # 进一步降低温度,提高JSON输出的一致性和准确性 - ) - - # 更新上次判断时间 - last_judge_time = time.time() - - if success and response: - response = response.strip() - logger.info(f"{self.log_prefix} 模型({model_name})原始JSON响应: {response}") - - # 解析LLM的JSON响应,提取判断结果和理由 - judge_result, reason = self._parse_llm_judge_response(response) - - logger.info( - f"{self.log_prefix} JSON解析结果 - 判断: {judge_result}, 理由: {reason}" - ) - - # 将判断结果保存到历史中 - judge_history.append((current_time, judge_result, reason)) - - if judge_result == "需要回复": - logger.info(f"{self.log_prefix} 模型判断需要回复,结束等待") - - full_prompt = f"{global_config.bot.nickname}(你)的想法是:{reason}" - await self.store_action_info( - action_build_into_prompt=True, - action_prompt_display=full_prompt, - action_done=True, - ) - return True, f"检测到需要回复的消息,结束等待 (等待时间: {elapsed_time:.1f}秒)" - else: - logger.info(f"{self.log_prefix} 模型判断不需要回复,理由: {reason},继续等待") - # 更新开始时间,避免重复判断同样的消息 - start_time = current_time - else: - logger.warning(f"{self.log_prefix} 模型判断失败,继续等待") - 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(1) - - # 短暂等待后继续检查 - await asyncio.sleep(check_interval) - - except Exception as e: - logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}") - # 即使执行失败也要记录 - exit_reason = f"执行异常: {str(e)}" - full_prompt = f"{context_str}{exit_reason},你思考是否要进行回复" - await self.store_action_info( - action_build_into_prompt=True, - action_prompt_display=full_prompt, - action_done=True, - ) - return False, f"不回复动作执行失败: {e}" - - async def _check_frequency_and_exit_focus(self, current_time: float) -> bool: - """检查回复频率,决定是否退出专注模式 - - Args: - current_time: 当前时间戳 - - Returns: - bool: 是否应该退出专注模式 - """ - try: - # 只在auto模式下进行频率检查 - if global_config.chat.chat_mode != "auto": - return False - - # 获取检查窗口内的所有消息 - window_start_time = current_time - self._frequency_check_window - all_messages = message_api.get_messages_by_time_in_chat( - chat_id=self.chat_id, - start_time=window_start_time, - end_time=current_time, - ) - - if not all_messages: - return False - - # 统计bot自己的回复数量 - bot_message_count = 0 - user_id = global_config.bot.qq_account - - for message in all_messages: - sender_id = message.get("user_id", "") - if sender_id == user_id: - bot_message_count += 1 - - # 计算当前回复频率(每分钟回复数) - window_minutes = self._frequency_check_window / 60 - current_frequency = bot_message_count / window_minutes - - # 计算阈值频率:使用 exit_focus_threshold * 1.5 - threshold_multiplier = global_config.chat.exit_focus_threshold * 1.5 - threshold_frequency = global_config.chat.talk_frequency * threshold_multiplier - - # 判断是否超过阈值 - if current_frequency > threshold_frequency: - logger.info( - f"{self.log_prefix} 回复频率检查:当前频率 {current_frequency:.2f}/分钟,超过阈值 {threshold_frequency:.2f}/分钟 (exit_threshold={global_config.chat.exit_focus_threshold} * 1.5),准备退出专注模式" - ) - return True - else: - logger.debug( - f"{self.log_prefix} 回复频率检查:当前频率 {current_frequency:.2f}/分钟,未超过阈值 {threshold_frequency:.2f}/分钟 (exit_threshold={global_config.chat.exit_focus_threshold} * 1.5)" - ) - return False - - except Exception as e: - logger.error(f"{self.log_prefix} 检查回复频率时出错: {e}") - return False - - def _parse_llm_judge_response(self, response: str) -> tuple[str, str]: - """解析LLM判断响应,使用JSON格式提取判断结果和理由 - - Args: - response: LLM的原始JSON响应 - - Returns: - tuple: (判断结果, 理由) - """ - try: - # 使用repair_json修复可能有问题的JSON格式 - fixed_json_string = repair_json(response) - logger.debug(f"{self.log_prefix} repair_json修复后的响应: {fixed_json_string}") - - # 如果repair_json返回的是字符串,需要解析为Python对象 - if isinstance(fixed_json_string, str): - result_json = json.loads(fixed_json_string) - else: - # 如果repair_json直接返回了字典对象,直接使用 - result_json = fixed_json_string - - # 从JSON中提取判断结果和理由 - should_reply = result_json.get("should_reply", False) - reason = result_json.get("reason", "无法获取判断理由") - - # 转换布尔值为中文字符串 - judge_result = "需要回复" if should_reply else "不需要回复" - - logger.debug(f"{self.log_prefix} JSON解析成功 - 判断: {judge_result}, 理由: {reason}") - return judge_result, reason - - except (json.JSONDecodeError, KeyError, TypeError) as e: - logger.warning(f"{self.log_prefix} JSON解析失败,尝试文本解析: {e}") - - # 如果JSON解析失败,回退到简单的关键词匹配 - try: - response_lower = response.lower() - - if "true" in response_lower or "需要回复" in response: - judge_result = "需要回复" - reason = "从响应文本中检测到需要回复的指示" - elif "false" in response_lower or "不需要回复" in response: - judge_result = "不需要回复" - reason = "从响应文本中检测到不需要回复的指示" - else: - judge_result = "不需要回复" # 默认值 - reason = f"无法解析响应格式,使用默认判断。原始响应: {response[:100]}..." - - logger.debug(f"{self.log_prefix} 文本解析结果 - 判断: {judge_result}, 理由: {reason}") - return judge_result, reason - - except Exception as fallback_e: - logger.error(f"{self.log_prefix} 文本解析也失败: {fallback_e}") - return "不需要回复", f"解析异常: {str(e)}, 回退解析也失败: {str(fallback_e)}" - - except Exception as e: - logger.error(f"{self.log_prefix} 解析LLM响应时出错: {e}") - return "不需要回复", f"解析异常: {str(e)}" - - @classmethod - def reset_consecutive_count(cls): - """重置连续计数器""" - cls._consecutive_count = 0 - logger.debug("NoReplyAction连续计数器已重置") class EmojiAction(BaseAction): From f1763f7e96e89b48993df75679014ad148b363d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Jun 2025 18:16:23 +0000 Subject: [PATCH 04/11] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=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/plugins/built_in/core_actions/no_reply.py | 14 +++++++------- src/plugins/built_in/core_actions/plugin.py | 2 -- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index 4d65bdca..fb0e5b66 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -53,10 +53,10 @@ class NoReplyAction(BaseAction): # 跳过LLM判断的配置 _skip_judge_when_tired = True - _skip_probability = 0.5 - + _skip_probability = 0.5 + # 新增:回复频率退出专注模式的配置 - _frequency_check_window = 600 # 频率检查窗口时间(秒) + _frequency_check_window = 600 # 频率检查窗口时间(秒) # 动作参数定义 action_parameters = {"reason": "不回复的原因"} @@ -388,10 +388,10 @@ class NoReplyAction(BaseAction): async def _check_frequency_and_exit_focus(self, current_time: float) -> bool: """检查回复频率,决定是否退出专注模式 - + Args: current_time: 当前时间戳 - + Returns: bool: 是否应该退出专注模式 """ @@ -399,7 +399,7 @@ class NoReplyAction(BaseAction): # 只在auto模式下进行频率检查 if global_config.chat.chat_mode != "auto": return False - + # 获取检查窗口内的所有消息 window_start_time = current_time - self._frequency_check_window all_messages = message_api.get_messages_by_time_in_chat( @@ -507,4 +507,4 @@ class NoReplyAction(BaseAction): def reset_consecutive_count(cls): """重置连续计数器""" cls._consecutive_count = 0 - logger.debug("NoReplyAction连续计数器已重置") \ No newline at end of file + logger.debug("NoReplyAction连续计数器已重置") diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 459cce0b..3421a4c8 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -109,8 +109,6 @@ class ReplyAction(BaseAction): return False, f"回复失败: {str(e)}" - - class EmojiAction(BaseAction): """表情动作 - 发送表情包""" From 43259c201d9679496f8bbd447b3052eff9a16513 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 24 Jun 2025 02:30:48 +0800 Subject: [PATCH 05/11] =?UTF-8?q?fix=EF=BC=9A=E6=94=B9=E4=BA=86=E4=B8=8B?= =?UTF-8?q?=E5=85=B3=E7=B3=BB=E6=9E=84=E5=BB=BA=E6=8F=90=E7=A4=BA=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/person_info/relationship_manager.py | 49 +++++++++++++------------ 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index 9f49213a..6cc46cb1 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -423,8 +423,7 @@ class RelationshipManager: 请根据你对ta过去的了解,和ta最近的行为,修改,整合,原有的了解,总结出对用户 {person_name}(昵称:{nickname})新的了解。 -了解可以包含性格,关系,感受,态度,你推测的ta的性别,年龄,外貌,身份,习惯,爱好,重要事件,重要经历等等内容。也可以包含其他点。 -关注友好和不友好的因素,不要忽略。 +了解请包含性格,对你的态度,你推测的ta的年龄,身份,习惯,爱好,重要事件和其他重要属性这几方面内容。 请严格按照以下给出的信息,不要新增额外内容。 你之前对他的了解是: @@ -467,23 +466,23 @@ class RelationshipManager: relation_value_prompt = f""" 你的名字是{global_config.bot.nickname}。 -你对{person_name}的了解如下: -{compressed_summary} +你最近对{person_name}的了解如下: +{points_text} 请根据以上信息,评估你和{person_name}的关系,给出两个维度的值:熟悉度和好感度。 -1. **熟悉度 (familiarity_value)**: 0-100的整数,表示你对ta的熟悉程度。 - - 0: 完全陌生 - - 25: 有点眼熟 - - 50: 比较熟悉 - - 75: 很熟悉 - - 100: 非常熟悉,了如指掌 +1. 了解度 (familiarity_value): 0-100的整数,表示这些信息让你对ta的了解增进程度。 + - 0: 没有任何进一步了解 + - 25: 有点进一步了解 + - 50: 有进一步了解 + - 75: 有更多了解 + - 100: 有了更多重要的了解 -2. **好感度 (liking_value)**: 0-100的整数,表示你对ta的喜好程度。 +2. **好感度 (liking_value)**: 0-100的整数,表示这些信息让你对ta的喜。 - 0: 非常厌恶 - 25: 有点反感 - 50: 中立/无感 - 75: 有点喜欢 - - 100: 非常喜欢/挚友 + - 100: 非常喜欢/开心对这个人 请严格按照json格式输出,不要有其他多余内容: {{ @@ -500,19 +499,23 @@ class RelationshipManager: # 从LLM获取新生成的值 new_familiarity_value = int(relation_value_json.get("familiarity_value", 0)) new_liking_value = int(relation_value_json.get("liking_value", 50)) + + if new_familiarity_value > 25: + old_familiarity_value = await person_info_manager.get_value(person_id, "familiarity_value") or 0 + old_familiarity_value += (new_familiarity_value - 25 /75) + + + if new_liking_value > 50: + liking_value = await person_info_manager.get_value(person_id, "liking_value") or 50 + liking_value += (new_liking_value - 50 /50) + if new_liking_value < 50: + liking_value = await person_info_manager.get_value(person_id, "liking_value") or 50 + liking_value -= (50 - new_liking_value /50) * 1.5 - # 获取数据库中的旧值,如果不存在则使用默认值 - old_familiarity_value = await person_info_manager.get_value(person_id, "familiarity_value") or 0 - old_liking_value = await person_info_manager.get_value(person_id, "liking_value") or 50 - - # 计算平均值 - final_familiarity_value = (old_familiarity_value + new_familiarity_value) // 2 - final_liking_value = (old_liking_value + new_liking_value) // 2 - - await person_info_manager.update_one_field(person_id, "familiarity_value", final_familiarity_value) - await person_info_manager.update_one_field(person_id, "liking_value", final_liking_value) + await person_info_manager.update_one_field(person_id, "familiarity_value", liking_value) + await person_info_manager.update_one_field(person_id, "liking_value", liking_value) logger.info( - f"更新了与 {person_name} 的关系值: 熟悉度={final_familiarity_value}, 好感度={final_liking_value}" + f"更新了与 {person_name} 的关系值: 熟悉度={liking_value}, 好感度={liking_value}" ) except (json.JSONDecodeError, ValueError, TypeError) as e: logger.error(f"解析relation_value JSON失败或值无效: {e}, 响应: {relation_value_response}") From cec1910f07ed7d75802485729b7825a2be19c94e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Jun 2025 18:40:43 +0000 Subject: [PATCH 06/11] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=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/person_info/relationship_manager.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index 6cc46cb1..1af9e7a8 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -499,24 +499,21 @@ class RelationshipManager: # 从LLM获取新生成的值 new_familiarity_value = int(relation_value_json.get("familiarity_value", 0)) new_liking_value = int(relation_value_json.get("liking_value", 50)) - + if new_familiarity_value > 25: old_familiarity_value = await person_info_manager.get_value(person_id, "familiarity_value") or 0 - old_familiarity_value += (new_familiarity_value - 25 /75) - - + old_familiarity_value += new_familiarity_value - 25 / 75 + if new_liking_value > 50: liking_value = await person_info_manager.get_value(person_id, "liking_value") or 50 - liking_value += (new_liking_value - 50 /50) + liking_value += new_liking_value - 50 / 50 if new_liking_value < 50: liking_value = await person_info_manager.get_value(person_id, "liking_value") or 50 - liking_value -= (50 - new_liking_value /50) * 1.5 + liking_value -= (50 - new_liking_value / 50) * 1.5 await person_info_manager.update_one_field(person_id, "familiarity_value", liking_value) await person_info_manager.update_one_field(person_id, "liking_value", liking_value) - logger.info( - f"更新了与 {person_name} 的关系值: 熟悉度={liking_value}, 好感度={liking_value}" - ) + logger.info(f"更新了与 {person_name} 的关系值: 熟悉度={liking_value}, 好感度={liking_value}") except (json.JSONDecodeError, ValueError, TypeError) as e: logger.error(f"解析relation_value JSON失败或值无效: {e}, 响应: {relation_value_response}") From 012d303efa45d6e63e589a1c1b530f1340d12d77 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 24 Jun 2025 03:23:49 +0800 Subject: [PATCH 07/11] =?UTF-8?q?fix=EF=BC=9A=E4=BC=98=E5=8C=96=E9=80=80?= =?UTF-8?q?=E5=87=BAfocus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/built_in/core_actions/no_reply.py | 85 ++++++++++++++++--- src/plugins/built_in/core_actions/plugin.py | 4 +- 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index fb0e5b66..78a5193c 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -49,7 +49,7 @@ class NoReplyAction(BaseAction): _auto_exit_message_count = 20 # 累计20条消息自动结束 # 最大等待超时时间 - _max_timeout = 1200 # 1200秒 + _max_timeout = 600 # 1200秒 # 跳过LLM判断的配置 _skip_judge_when_tired = True @@ -110,9 +110,11 @@ class NoReplyAction(BaseAction): # 检查是否超时 if elapsed_time >= self._max_timeout: - logger.info(f"{self.log_prefix} 达到最大等待时间{self._max_timeout}秒,结束等待") + logger.info(f"{self.log_prefix} 达到最大等待时间{self._max_timeout}秒,退出专注模式") + # 标记退出专注模式 + self.action_data["_system_command"] = "stop_focus_chat" exit_reason = ( - f"{global_config.bot.nickname}(你)等待了{self._max_timeout}秒,可以考虑一下是否要进行回复" + f"{global_config.bot.nickname}(你)等待了{self._max_timeout}秒,感觉群里没有新内容,决定退出专注模式,稍作休息" ) await self.store_action_info( action_build_into_prompt=True, @@ -135,6 +137,20 @@ class NoReplyAction(BaseAction): ) return True, exit_reason + # **新增**:检查过去10分钟是否完全没有发言,如果是则退出专注模式 + should_exit_no_activity = await self._check_no_activity_and_exit_focus(current_time) + if should_exit_no_activity: + logger.info(f"{self.log_prefix} 检测到过去10分钟完全没有发言,退出专注模式") + # 标记退出专注模式 + self.action_data["_system_command"] = "stop_focus_chat" + exit_reason = f"{global_config.bot.nickname}(你)发现自己过去10分钟完全没有说话,感觉可能不太活跃,决定退出专注模式" + await self.store_action_info( + action_build_into_prompt=True, + action_prompt_display=exit_reason, + 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 @@ -181,9 +197,6 @@ class NoReplyAction(BaseAction): 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 @@ -276,12 +289,10 @@ class NoReplyAction(BaseAction): # 如果决定跳过LLM判断,直接更新时间并继续等待 if should_skip_llm_judge: last_judge_time = time.time() # 更新判断时间,避免立即重新判断 - start_time = current_time # 更新开始时间,避免重复计算同样的消息 continue # 跳过本次LLM判断,继续循环等待 # 构建判断上下文 judge_prompt = f""" -{time_block} {identity_block} 你现在正在QQ群参与聊天,以下是聊天内容: @@ -367,9 +378,14 @@ class NoReplyAction(BaseAction): 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(1) + if elapsed_time < 60: + if int(elapsed_time) % 10 == 0 and int(elapsed_time) > 0: + logger.info(f"{self.log_prefix} 已等待{elapsed_time:.0f}秒,等待新消息...") + await asyncio.sleep(1) + else: + if int(elapsed_time) % 60 == 0 and int(elapsed_time) > 0: + logger.info(f"{self.log_prefix} 已等待{elapsed_time/60:.0f}分钟,等待新消息...") + await asyncio.sleep(1) # 短暂等待后继续检查 await asyncio.sleep(check_interval) @@ -444,6 +460,53 @@ class NoReplyAction(BaseAction): logger.error(f"{self.log_prefix} 检查回复频率时出错: {e}") return False + async def _check_no_activity_and_exit_focus(self, current_time: float) -> bool: + """检查过去10分钟是否完全没有发言,决定是否退出专注模式 + + Args: + current_time: 当前时间戳 + + Returns: + bool: 是否应该退出专注模式 + """ + try: + # 只在auto模式下进行检查 + if global_config.chat.chat_mode != "auto": + return False + + # 获取过去10分钟的所有消息 + past_10min_time = current_time - 600 # 10分钟前 + all_messages = message_api.get_messages_by_time_in_chat( + chat_id=self.chat_id, + start_time=past_10min_time, + end_time=current_time, + ) + + if not all_messages: + # 如果完全没有消息,也不需要退出专注模式 + return False + + # 统计bot自己的回复数量 + bot_message_count = 0 + user_id = global_config.bot.qq_account + + for message in all_messages: + sender_id = message.get("user_id", "") + if sender_id == user_id: + bot_message_count += 1 + + # 如果过去10分钟bot一条消息也没有发送,退出专注模式 + if bot_message_count == 0: + logger.info(f"{self.log_prefix} 过去10分钟bot完全没有发言,准备退出专注模式") + return True + else: + logger.debug(f"{self.log_prefix} 过去10分钟bot发言{bot_message_count}条,继续保持专注模式") + return False + + except Exception as e: + logger.error(f"{self.log_prefix} 检查无活动状态时出错: {e}") + return False + def _parse_llm_judge_response(self, response: str) -> tuple[str, str]: """解析LLM判断响应,使用JSON格式提取判断结果和理由 diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 3421a4c8..d1b7925c 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -204,7 +204,7 @@ class CoreActionsPlugin(BasePlugin): config_schema = { "plugin": { "enabled": ConfigField(type=bool, default=True, description="是否启用插件"), - "config_version": ConfigField(type=str, default="0.0.9", description="配置文件版本"), + "config_version": ConfigField(type=str, default="0.1.0", description="配置文件版本"), }, "components": { "enable_reply": ConfigField(type=bool, default=True, description="是否启用'回复'动作"), @@ -260,7 +260,7 @@ class CoreActionsPlugin(BasePlugin): auto_exit_message_count = self.get_config("no_reply.auto_exit_message_count", 20) NoReplyAction._auto_exit_message_count = auto_exit_message_count - max_timeout = self.get_config("no_reply.max_timeout", 1200) + max_timeout = self.get_config("no_reply.max_timeout", 600) NoReplyAction._max_timeout = max_timeout skip_judge_when_tired = self.get_config("no_reply.skip_judge_when_tired", True) From 2003607ff52c354e682a21676f7212e6ea9abc95 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 23 Jun 2025 19:24:33 +0000 Subject: [PATCH 08/11] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=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/plugins/built_in/core_actions/no_reply.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index 78a5193c..bb4c6f00 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -12,7 +12,6 @@ from src.common.logger import get_logger # 导入API模块 - 标准Python包方式 from src.plugin_system.apis import message_api, llm_api from src.config.config import global_config -from datetime import datetime from json_repair import repair_json logger = get_logger("core_actions") @@ -113,9 +112,7 @@ class NoReplyAction(BaseAction): logger.info(f"{self.log_prefix} 达到最大等待时间{self._max_timeout}秒,退出专注模式") # 标记退出专注模式 self.action_data["_system_command"] = "stop_focus_chat" - exit_reason = ( - f"{global_config.bot.nickname}(你)等待了{self._max_timeout}秒,感觉群里没有新内容,决定退出专注模式,稍作休息" - ) + exit_reason = f"{global_config.bot.nickname}(你)等待了{self._max_timeout}秒,感觉群里没有新内容,决定退出专注模式,稍作休息" await self.store_action_info( action_build_into_prompt=True, action_prompt_display=exit_reason, @@ -197,7 +194,6 @@ class NoReplyAction(BaseAction): messages=recent_messages, timestamp_mode="normal_no_YMD", truncate=False, show_actions=False ) - # 获取身份信息 bot_name = global_config.bot.nickname bot_nickname = "" @@ -384,7 +380,7 @@ class NoReplyAction(BaseAction): await asyncio.sleep(1) else: if int(elapsed_time) % 60 == 0 and int(elapsed_time) > 0: - logger.info(f"{self.log_prefix} 已等待{elapsed_time/60:.0f}分钟,等待新消息...") + logger.info(f"{self.log_prefix} 已等待{elapsed_time / 60:.0f}分钟,等待新消息...") await asyncio.sleep(1) # 短暂等待后继续检查 From 480a07d20ab522806b51e0960c4a40872f96a209 Mon Sep 17 00:00:00 2001 From: UnCLAS-Prommer Date: Tue, 24 Jun 2025 10:29:11 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E5=B0=8F=E5=B0=8F=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/plugins/quick-start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins/quick-start.md b/docs/plugins/quick-start.md index 0ee06bd6..50943830 100644 --- a/docs/plugins/quick-start.md +++ b/docs/plugins/quick-start.md @@ -67,7 +67,7 @@ class HelloWorldPlugin(BasePlugin): - 首先,我们在plugin.py中定义了一个HelloWorldPulgin插件类,继承自 `BasePlugin` ,提供基本功能。 - 通过给类加上,`@register_plugin` 装饰器,我们告诉系统"这是一个插件" -- `plugin_name` 等是插件的基本信息,必须填写 +- `plugin_name` 等是插件的基本信息,必须填写,**此部分必须与目录名称相同,否则插件无法使用** - `get_plugin_components()` 返回插件的功能组件,现在我们没有定义任何action(动作)或者command(指令),是空的 ### 3. 测试基础插件 From 436f650885b7c0d7e10d344e6504c689b261ed57 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 24 Jun 2025 15:26:07 +0800 Subject: [PATCH 10/11] =?UTF-8?q?fix=EF=BC=9A=E4=B8=8D=E8=B6=B3=E6=8C=82?= =?UTF-8?q?=E9=BD=BF=E6=94=B9=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/message_receive/message_sender.py | 2 +- src/chat/message_receive/storage.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/chat/message_receive/message_sender.py b/src/chat/message_receive/message_sender.py index 030b8b3f..6cb256d3 100644 --- a/src/chat/message_receive/message_sender.py +++ b/src/chat/message_receive/message_sender.py @@ -8,7 +8,7 @@ from src.common.message.api import get_global_api # from ...common.database import db # 数据库依赖似乎不需要了,注释掉 from .message import MessageSending, MessageThinking, MessageSet -from .storage import MessageStorage +from src.chat.message_receive.storage import MessageStorage from ...config.config import global_config from ..utils.utils import truncate_message, calculate_typing_time, count_messages_between diff --git a/src/chat/message_receive/storage.py b/src/chat/message_receive/storage.py index ebaf32b2..def345bd 100644 --- a/src/chat/message_receive/storage.py +++ b/src/chat/message_receive/storage.py @@ -18,7 +18,12 @@ class MessageStorage: # 莫越权 救世啊 pattern = r".*?|.*?|.*?" + # print(message) + processed_plain_text = message.processed_plain_text + + # print(processed_plain_text) + if processed_plain_text: filtered_processed_plain_text = re.sub(pattern, "", processed_plain_text, flags=re.DOTALL) else: From 14d9f3b2bd991245438d109d2b710ccfae9c9bb3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 24 Jun 2025 07:26:51 +0000 Subject: [PATCH 11/11] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=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/message_receive/storage.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/chat/message_receive/storage.py b/src/chat/message_receive/storage.py index def345bd..ac781884 100644 --- a/src/chat/message_receive/storage.py +++ b/src/chat/message_receive/storage.py @@ -19,11 +19,11 @@ class MessageStorage: pattern = r".*?|.*?|.*?" # print(message) - + processed_plain_text = message.processed_plain_text - + # print(processed_plain_text) - + if processed_plain_text: filtered_processed_plain_text = re.sub(pattern, "", processed_plain_text, flags=re.DOTALL) else: