From a549034bbc24b899866e5250f08d65ab6e1a6cfa Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Sat, 12 Jul 2025 22:36:08 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=BF=AE=E5=A4=8Dno=5Freply?= =?UTF-8?q?=E8=B5=B7=E5=A7=8B=E6=97=B6=E9=97=B4=EF=BC=8C=E7=A7=BB=E9=99=A4?= =?UTF-8?q?normal=E6=B6=88=E6=81=AF=E7=AE=A1=E7=90=86=E5=99=A8=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E5=B9=B6=E8=A1=8C=E7=94=9F=E6=88=90=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=EF=BC=8C=E4=B8=BAfocus=E6=8F=90=E4=BE=9B=E9=80=80?= =?UTF-8?q?=E5=87=BA=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/focus_chat/heartFC_chat.py | 104 +++++++++++------- src/chat/focus_chat/hfc_utils.py | 69 ------------ src/config/official_configs.py | 5 - src/plugins/built_in/core_actions/no_reply.py | 4 +- src/plugins/built_in/core_actions/plugin.py | 2 +- template/bot_config_template.toml | 2 - 6 files changed, 66 insertions(+), 120 deletions(-) diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index fc5418ed3..8c0368756 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -1,8 +1,7 @@ import asyncio import time import traceback -from collections import deque -from typing import Optional, Deque, List +from typing import Optional, List from src.chat.message_receive.chat_stream import get_chat_manager from rich.traceback import install @@ -16,9 +15,9 @@ from src.config.config import global_config from src.person_info.relationship_builder_manager import relationship_builder_manager from src.chat.focus_chat.hfc_utils import CycleDetail from random import random -from src.chat.focus_chat.hfc_utils import create_thinking_message_from_dict, add_messages_to_manager,get_recent_message_stats,cleanup_thinking_message_by_id +from src.chat.focus_chat.hfc_utils import get_recent_message_stats from src.person_info.person_info import get_person_info_manager -from src.plugin_system.apis import generator_api +from src.plugin_system.apis import generator_api,send_api,message_api from src.chat.willing.willing_manager import get_willing_manager from .priority_manager import PriorityManager from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat @@ -201,14 +200,22 @@ class HeartFChatting: async def _loopbody(self): if self.loop_mode == "focus": + + self.energy_value -= 5 * (1/global_config.chat.exit_focus_threshold) + if self.energy_value <= 0: + self.loop_mode = "normal" + return True + + return await self._observe() elif self.loop_mode == "normal": new_messages_data = get_raw_msg_by_timestamp_with_chat( chat_id=self.stream_id, timestamp_start=self.last_read_time, timestamp_end=time.time(),limit=10,limit_mode="earliest",fliter_bot=True ) - if len(new_messages_data) > 5: + if len(new_messages_data) > 4 * global_config.chat.auto_focus_threshold: self.loop_mode = "focus" + self.energy_value = 100 return True if new_messages_data: @@ -228,10 +235,8 @@ class HeartFChatting: # 创建新的循环信息 cycle_timers, thinking_id = self.start_cycle() - logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考") + logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考[模式:{self.loop_mode}]") - if message_data: - await create_thinking_message_from_dict(message_data,self.chat_stream,thinking_id) async with global_prompt_manager.async_message_scope( self.chat_stream.context.get_template_name() @@ -257,7 +262,14 @@ class HeartFChatting: #如果normal,开始一个回复生成进程,先准备好回复(其实是和planer同时进行的) if self.loop_mode == "normal": - gen_task = asyncio.create_task(self._generate_normal_response(message_data, available_actions)) + person_info_manager = get_person_info_manager() + person_id = person_info_manager.get_person_id( + message_data.get("chat_info_platform"), message_data.get("user_id") + ) + person_name = await person_info_manager.get_value(person_id, "person_name") + reply_to_str = f"{person_name}:{message_data.get('processed_plain_text')}" + + gen_task = asyncio.create_task(self._generate_response(message_data, available_actions,reply_to_str)) with Timer("规划器", cycle_timers): @@ -299,6 +311,7 @@ class HeartFChatting: if action_type == "no_action": + # 等待回复生成完毕 gather_timeout = global_config.chat.thinking_timeout try: response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout) @@ -308,7 +321,7 @@ class HeartFChatting: if response_set: content = " ".join([item[1] for item in response_set if item[0] == "text"]) - + # 模型炸了,没有回复内容生成 if not response_set or ( action_type not in ["no_action"] and not is_parallel ): @@ -318,25 +331,15 @@ class HeartFChatting: logger.info( f"[{self.log_prefix}] {global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复" ) - # 如果模型未生成回复,移除思考消息 - await cleanup_thinking_message_by_id(self.chat_stream.stream_id,thinking_id,self.log_prefix) return False logger.info(f"[{self.log_prefix}] {global_config.bot.nickname} 决定的回复内容: {content}") - # 提取回复文本 - reply_texts = [item[1] for item in response_set if item[0] == "text"] - if not reply_texts: - logger.info(f"[{self.log_prefix}] 回复内容中没有文本,不发送消息") - await cleanup_thinking_message_by_id(self.chat_stream.stream_id,thinking_id,self.log_prefix) - return False # 发送回复 (不再需要传入 chat) - await add_messages_to_manager(message_data, reply_texts, thinking_id,self.chat_stream.stream_id) + await self._send_response(response_set, reply_to_str, loop_start_time) - return response_set if response_set else False - - + return True @@ -465,7 +468,7 @@ class HeartFChatting: # 新增:消息计数和疲惫检查 if action == "reply" and success: self._message_count += 1 - current_threshold = self._get_current_fatigue_threshold() + current_threshold = max(10, int(30 / global_config.chat.exit_focus_threshold)) logger.info( f"{self.log_prefix} 已发送第 {self._message_count} 条消息(动态阈值: {current_threshold}, exit_focus_threshold: {global_config.chat.exit_focus_threshold})" ) @@ -486,14 +489,6 @@ class HeartFChatting: return command return "" - def _get_current_fatigue_threshold(self) -> int: - """动态获取当前的疲惫阈值,基于exit_focus_threshold配置 - - Returns: - int: 当前的疲惫阈值 - """ - return max(10, int(30 / global_config.chat.exit_focus_threshold)) - async def shutdown(self): """优雅关闭HeartFChatting实例,取消活动循环任务""" @@ -653,21 +648,14 @@ class HeartFChatting: return True - async def _generate_normal_response( - self, message_data: dict, available_actions: Optional[list] + async def _generate_response( + self, message_data: dict, available_actions: Optional[list],reply_to:str ) -> Optional[list]: """生成普通回复""" try: - person_info_manager = get_person_info_manager() - person_id = person_info_manager.get_person_id( - message_data.get("chat_info_platform"), message_data.get("user_id") - ) - person_name = await person_info_manager.get_value(person_id, "person_name") - reply_to_str = f"{person_name}:{message_data.get('processed_plain_text')}" - success, reply_set = await generator_api.generate_reply( chat_stream=self.chat_stream, - reply_to=reply_to_str, + reply_to=reply_to, available_actions=available_actions, enable_tool=global_config.tool.enable_in_normal_chat, request_type="normal.replyer", @@ -682,3 +670,37 @@ class HeartFChatting: except Exception as e: logger.error(f"[{self.log_prefix}] 回复生成出现错误:{str(e)} {traceback.format_exc()}") return None + + + async def _send_response( + self, reply_set, reply_to, thinking_start_time + ): + current_time = time.time() + new_message_count = message_api.count_new_messages( + chat_id=self.chat_stream.stream_id, start_time=thinking_start_time, end_time=current_time + ) + + need_reply = new_message_count >= random.randint(2, 4) + + logger.info( + f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,{'使用' if need_reply else '不使用'}引用回复" + ) + + reply_text = "" + first_replyed = False + for reply_seg in reply_set: + data = reply_seg[1] + if not first_replyed: + if need_reply: + await send_api.text_to_stream(text=data, stream_id=self.chat_stream.stream_id, reply_to=reply_to, typing=False) + first_replyed = True + else: + await send_api.text_to_stream(text=data, stream_id=self.chat_stream.stream_id, typing=False) + first_replyed = True + else: + await send_api.text_to_stream(text=data, stream_id=self.chat_stream.stream_id, typing=True) + reply_text += data + + return reply_text + + \ No newline at end of file diff --git a/src/chat/focus_chat/hfc_utils.py b/src/chat/focus_chat/hfc_utils.py index 4921170d7..a7a4fe122 100644 --- a/src/chat/focus_chat/hfc_utils.py +++ b/src/chat/focus_chat/hfc_utils.py @@ -7,11 +7,7 @@ from typing import Dict, Any from src.config.config import global_config from src.chat.message_receive.message import MessageThinking from src.chat.message_receive.normal_message_sender import message_manager -from typing import List -from maim_message import Seg from src.common.message_repository import count_messages -from ..message_receive.message import MessageSending, MessageSet, message_from_db_dict -from src.chat.message_receive.chat_stream import get_chat_manager @@ -123,71 +119,6 @@ async def cleanup_thinking_message_by_id(chat_id: str, thinking_id: str, log_pre except Exception as e: logger.error(f"{log_prefix} 清理思考消息 {thinking_id} 时出错: {e}") - - -async def add_messages_to_manager( - message_data: dict, response_set: List[str], thinking_id, chat_id - ) -> Optional[MessageSending]: - """发送回复消息""" - - chat_stream = get_chat_manager().get_stream(chat_id) - - container = await message_manager.get_container(chat_id) # 使用 self.stream_id - thinking_message = None - - for msg in container.messages[:]: - # print(msg) - if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id: - thinking_message = msg - container.messages.remove(msg) - break - - if not thinking_message: - logger.warning(f"[{chat_id}] 未找到对应的思考消息 {thinking_id},可能已超时被移除") - return None - - thinking_start_time = thinking_message.thinking_start_time - message_set = MessageSet(chat_stream, thinking_id) # 使用 self.chat_stream - - sender_info = UserInfo( - user_id=message_data.get("user_id"), - user_nickname=message_data.get("user_nickname"), - platform=message_data.get("chat_info_platform"), - ) - - reply = message_from_db_dict(message_data) - - - mark_head = False - first_bot_msg = None - for msg in response_set: - if global_config.debug.debug_show_chat_mode: - msg += "ⁿ" - message_segment = Seg(type="text", data=msg) - bot_message = MessageSending( - message_id=thinking_id, - chat_stream=chat_stream, # 使用 self.chat_stream - bot_user_info=UserInfo( - user_id=global_config.bot.qq_account, - user_nickname=global_config.bot.nickname, - platform=message_data.get("chat_info_platform"), - ), - sender_info=sender_info, - message_segment=message_segment, - reply=reply, - is_head=not mark_head, - is_emoji=False, - thinking_start_time=thinking_start_time, - apply_set_reply_logic=True, - ) - if not mark_head: - mark_head = True - first_bot_msg = bot_message - message_set.add_message(bot_message) - - await message_manager.add_message(message_set) - - return first_bot_msg def get_recent_message_stats(minutes: int = 30, chat_id: str = None) -> dict: diff --git a/src/config/official_configs.py b/src/config/official_configs.py index 7e2efbeba..49914139b 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -280,17 +280,12 @@ class NormalChatConfig(ConfigBase): at_bot_inevitable_reply: bool = False """@bot 必然回复""" - enable_planner: bool = False - """是否启用动作规划器""" @dataclass class FocusChatConfig(ConfigBase): """专注聊天配置类""" - think_interval: float = 1 - """思考间隔(秒)""" - consecutive_replies: float = 1 """连续回复能力,值越高,麦麦连续回复的概率越高""" diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index 99337e515..f68652d5c 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -61,8 +61,8 @@ class NoReplyAction(BaseAction): count = NoReplyAction._consecutive_count reason = self.action_data.get("reason", "") - start_time = time.time() - check_interval = 1.0 # 每秒检查一次 + start_time = self.action_data.get("loop_start_time", time.time()) + check_interval = 0.6 # 每秒检查一次 # 随机生成本次等待需要的新消息数量阈值 exit_message_count_threshold = random.randint(self._min_exit_message_count, self._max_exit_message_count) diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 548902225..8ef6f75fe 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -98,7 +98,7 @@ class ReplyAction(BaseAction): ) # 根据新消息数量决定是否使用reply_to - need_reply = new_message_count >= random.randint(2, 5) + need_reply = new_message_count >= random.randint(2, 4) logger.info( f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,{'使用' if need_reply else '不使用'}引用回复" ) diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index d4c158f65..f4470060b 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -119,10 +119,8 @@ willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数 mentioned_bot_inevitable_reply = true # 提及 bot 必然回复 at_bot_inevitable_reply = true # @bot 必然回复(包含提及) -enable_planner = true # 是否启用动作规划器(与focus_chat共享actions) [focus_chat] #专注聊天 -think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗 consecutive_replies = 1 # 连续回复能力,值越高,麦麦连续回复的概率越高 [tool]