diff --git a/README.md b/README.md index 9b057508..403b42e0 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ > - QQ 机器人存在被限制风险,请自行了解,谨慎使用。 > - 由于程序处于开发中,可能消耗较多 token。 +## 麦麦MC项目(早期开发) +[让麦麦玩MC](https://github.com/MaiM-with-u/Maicraft) + +交流群:1058573197 + ## 💬 讨论 **技术交流群:** diff --git a/changelogs/changelog.md b/changelogs/changelog.md index da96cb72..a26088c7 100644 --- a/changelogs/changelog.md +++ b/changelogs/changelog.md @@ -1,15 +1,15 @@ # Changelog -TODO:回复频率动态控制 - -## [0.10.2] - 2025-8-24 +## [0.10.2] - 2025-8-31 ### 🌟 主要功能更改 +- 大幅优化了聊天逻辑,更易配置 - 记忆系统重新启用,更好更优秀 - 更好的event系统 -- 为空回复添加重试机制 +- 现在支持提及100%回复 ### 细节功能更改 +- 为空回复添加重试机制 - 修复tts插件可能的复读问题 diff --git a/src/chat/frequency_control/focus_value_control.py b/src/chat/frequency_control/focus_value_control.py index 290dcc9e..be820760 100644 --- a/src/chat/frequency_control/focus_value_control.py +++ b/src/chat/frequency_control/focus_value_control.py @@ -3,26 +3,7 @@ from src.config.config import global_config from src.chat.frequency_control.utils import parse_stream_config_to_chat_id -class FocusValueControl: - def __init__(self, chat_id: str): - self.chat_id = chat_id - self.focus_value_adjust: float = 1 - - def get_current_focus_value(self) -> float: - return get_current_focus_value(self.chat_id) * self.focus_value_adjust - - -class FocusValueControlManager: - def __init__(self): - self.focus_value_controls: dict[str, FocusValueControl] = {} - - def get_focus_value_control(self, chat_id: str) -> FocusValueControl: - if chat_id not in self.focus_value_controls: - self.focus_value_controls[chat_id] = FocusValueControl(chat_id) - return self.focus_value_controls[chat_id] - - -def get_current_focus_value(chat_id: Optional[str] = None) -> float: +def get_config_base_focus_value(chat_id: Optional[str] = None) -> float: """ 根据当前时间和聊天流获取对应的 focus_value """ @@ -139,5 +120,3 @@ def get_global_focus_value() -> Optional[float]: return None - -focus_value_control = FocusValueControlManager() diff --git a/src/chat/frequency_control/frequency_control.py b/src/chat/frequency_control/frequency_control.py new file mode 100644 index 00000000..63fb0e33 --- /dev/null +++ b/src/chat/frequency_control/frequency_control.py @@ -0,0 +1,275 @@ +import time +from typing import Optional, Dict, List +from src.plugin_system.apis import message_api +from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager +from src.common.logger import get_logger +from src.config.config import global_config +from src.chat.frequency_control.talk_frequency_control import get_config_base_talk_frequency +from src.chat.frequency_control.focus_value_control import get_config_base_focus_value + +logger = get_logger("frequency_control") + + +class FrequencyControl: + """ + 频率控制类,可以根据最近时间段的发言数量和发言人数动态调整频率 + """ + + def __init__(self, chat_id: str): + self.chat_id = chat_id + self.chat_stream: ChatStream = get_chat_manager().get_stream(self.chat_id) + if not self.chat_stream: + raise ValueError(f"无法找到聊天流: {self.chat_id}") + self.log_prefix = f"[{get_chat_manager().get_stream_name(self.chat_id) or self.chat_id}]" + # 发言频率调整值 + self.talk_frequency_adjust: float = 1.0 + self.talk_frequency_external_adjust: float = 1.0 + # 专注度调整值 + self.focus_value_adjust: float = 1.0 + self.focus_value_external_adjust: float = 1.0 + + # 动态调整相关参数 + self.last_update_time = time.time() + self.update_interval = 60 # 每60秒更新一次 + + # 历史数据缓存 + self._message_count_cache = 0 + self._user_count_cache = 0 + self._last_cache_time = 0 + self._cache_duration = 30 # 缓存30秒 + + # 调整参数 + self.min_adjust = 0.3 # 最小调整值 + self.max_adjust = 2.0 # 最大调整值 + + # 基准值(可根据实际情况调整) + self.base_message_count = 5 # 基准消息数量 + self.base_user_count = 3 # 基准用户数量 + + # 平滑因子 + self.smoothing_factor = 0.3 + + + def get_dynamic_talk_frequency_adjust(self) -> float: + """ + 获取纯动态调整值(不包含配置文件基础值) + + Returns: + float: 动态调整值 + """ + self._update_talk_frequency_adjust() + return self.talk_frequency_adjust + + def get_dynamic_focus_value_adjust(self) -> float: + """ + 获取纯动态调整值(不包含配置文件基础值) + + Returns: + float: 动态调整值 + """ + self._update_focus_value_adjust() + return self.focus_value_adjust + + def _update_talk_frequency_adjust(self): + """ + 更新发言频率调整值 + 适合人少话多的时候:人少但消息多,提高回复频率 + """ + current_time = time.time() + + # 检查是否需要更新 + if current_time - self.last_update_time < self.update_interval: + return + + try: + # 获取最近30分钟的数据(发言频率更敏感) + recent_messages = message_api.get_messages_by_time_in_chat( + chat_id=self.chat_stream.stream_id, + start_time=current_time - 1800, # 30分钟前 + end_time=current_time, + filter_mai=True, + filter_command=True + ) + + # 计算消息数量和用户数量 + message_count = len(recent_messages) + user_ids = set() + for msg in recent_messages: + if msg.user_info and msg.user_info.user_id: + user_ids.add(msg.user_info.user_id) + user_count = len(user_ids) + + # 发言频率调整逻辑:人少话多时提高回复频率 + if user_count > 0: + # 计算人均消息数 + messages_per_user = message_count / user_count + # 基准人均消息数 + base_messages_per_user = self.base_message_count / self.base_user_count if self.base_user_count > 0 else 1.0 + + # 如果人均消息数高,说明活跃度高,提高回复频率 + if messages_per_user > base_messages_per_user: + # 人少话多:提高回复频率 + target_talk_adjust = min(self.max_adjust, messages_per_user / base_messages_per_user) + else: + # 活跃度一般:保持正常 + target_talk_adjust = 1.0 + else: + target_talk_adjust = 1.0 + + # 限制调整范围 + target_talk_adjust = max(self.min_adjust, min(self.max_adjust, target_talk_adjust)) + + # 平滑调整 + self.talk_frequency_adjust = ( + self.talk_frequency_adjust * (1 - self.smoothing_factor) + + target_talk_adjust * self.smoothing_factor + ) + + logger.info( + f"{self.log_prefix} 发言频率调整更新: " + f"消息数={message_count}, 用户数={user_count}, " + f"人均消息数={message_count/user_count if user_count > 0 else 0:.2f}, " + f"调整值={self.talk_frequency_adjust:.2f}" + ) + + except Exception as e: + logger.error(f"{self.log_prefix} 更新发言频率调整值时出错: {e}") + + def _update_focus_value_adjust(self): + """ + 更新专注度调整值 + 适合人多话多的时候:人多且消息多,提高专注度(LLM消耗更多,但回复更精准) + """ + current_time = time.time() + + # 检查是否需要更新 + if current_time - self.last_update_time < self.update_interval: + return + + try: + # 获取最近1小时的数据 + recent_messages = message_api.get_messages_by_time_in_chat( + chat_id=self.chat_stream.stream_id, + start_time=current_time - 3600, # 1小时前 + end_time=current_time, + filter_mai=True, + filter_command=True + ) + + # 计算消息数量和用户数量 + message_count = len(recent_messages) + user_ids = set() + for msg in recent_messages: + if msg.user_info and msg.user_info.user_id: + user_ids.add(msg.user_info.user_id) + user_count = len(user_ids) + + # 专注度调整逻辑:人多话多时提高专注度 + if user_count > 0 and self.base_user_count > 0: + # 计算用户活跃度比率 + user_ratio = user_count / self.base_user_count + # 计算消息活跃度比率 + message_ratio = message_count / self.base_message_count if self.base_message_count > 0 else 1.0 + + # 如果用户多且消息多,提高专注度 + if user_ratio > 1.2 and message_ratio > 1.2: + # 人多话多:提高专注度,消耗更多LLM资源但回复更精准 + target_focus_adjust = min(self.max_adjust, (user_ratio + message_ratio) / 2) + elif user_ratio > 1.5: + # 用户特别多:适度提高专注度 + target_focus_adjust = min(self.max_adjust, 1.0 + (user_ratio - 1.0) * 0.3) + else: + # 正常情况:保持默认专注度 + target_focus_adjust = 1.0 + else: + target_focus_adjust = 1.0 + + # 限制调整范围 + target_focus_adjust = max(self.min_adjust, min(self.max_adjust, target_focus_adjust)) + + # 平滑调整 + self.focus_value_adjust = ( + self.focus_value_adjust * (1 - self.smoothing_factor) + + target_focus_adjust * self.smoothing_factor + ) + + logger.info( + f"{self.log_prefix} 专注度调整更新: " + f"消息数={message_count}, 用户数={user_count}, " + f"用户比率={user_count/self.base_user_count if self.base_user_count > 0 else 0:.2f}, " + f"消息比率={message_count/self.base_message_count if self.base_message_count > 0 else 0:.2f}, " + f"调整值={self.focus_value_adjust:.2f}" + ) + + except Exception as e: + logger.error(f"{self.log_prefix} 更新专注度调整值时出错: {e}") + + def get_final_talk_frequency(self) -> float: + return get_config_base_talk_frequency(self.chat_stream.stream_id) * self.get_dynamic_talk_frequency_adjust() * self.talk_frequency_external_adjust + + def get_final_focus_value(self) -> float: + return get_config_base_focus_value(self.chat_stream.stream_id) * self.get_dynamic_focus_value_adjust() * self.focus_value_external_adjust + + + def set_adjustment_parameters( + self, + min_adjust: Optional[float] = None, + max_adjust: Optional[float] = None, + base_message_count: Optional[int] = None, + base_user_count: Optional[int] = None, + smoothing_factor: Optional[float] = None, + update_interval: Optional[int] = None + ): + """ + 设置调整参数 + + Args: + min_adjust: 最小调整值 + max_adjust: 最大调整值 + base_message_count: 基准消息数量 + base_user_count: 基准用户数量 + smoothing_factor: 平滑因子 + update_interval: 更新间隔(秒) + """ + if min_adjust is not None: + self.min_adjust = max(0.1, min_adjust) + if max_adjust is not None: + self.max_adjust = max(1.0, max_adjust) + if base_message_count is not None: + self.base_message_count = max(1, base_message_count) + if base_user_count is not None: + self.base_user_count = max(1, base_user_count) + if smoothing_factor is not None: + self.smoothing_factor = max(0.0, min(1.0, smoothing_factor)) + if update_interval is not None: + self.update_interval = max(10, update_interval) + + +class FrequencyControlManager: + """ + 频率控制管理器,管理多个聊天流的频率控制实例 + """ + + def __init__(self): + self.frequency_control_dict: Dict[str, FrequencyControl] = {} + + def get_or_create_frequency_control(self, chat_id: str) -> FrequencyControl: + """ + 获取或创建指定聊天流的频率控制实例 + + Args: + chat_id: 聊天流ID + + Returns: + FrequencyControl: 频率控制实例 + """ + if chat_id not in self.frequency_control_dict: + self.frequency_control_dict[chat_id] = FrequencyControl(chat_id) + return self.frequency_control_dict[chat_id] + +# 创建全局实例 +frequency_control_manager = FrequencyControlManager() + + + + diff --git a/src/chat/frequency_control/talk_frequency_control.py b/src/chat/frequency_control/talk_frequency_control.py index ad81fbd8..11728e26 100644 --- a/src/chat/frequency_control/talk_frequency_control.py +++ b/src/chat/frequency_control/talk_frequency_control.py @@ -3,26 +3,7 @@ from src.config.config import global_config from src.chat.frequency_control.utils import parse_stream_config_to_chat_id -class TalkFrequencyControl: - def __init__(self, chat_id: str): - self.chat_id = chat_id - self.talk_frequency_adjust: float = 1 - - def get_current_talk_frequency(self) -> float: - return get_current_talk_frequency(self.chat_id) * self.talk_frequency_adjust - - -class TalkFrequencyControlManager: - def __init__(self): - self.talk_frequency_controls = {} - - def get_talk_frequency_control(self, chat_id: str) -> TalkFrequencyControl: - if chat_id not in self.talk_frequency_controls: - self.talk_frequency_controls[chat_id] = TalkFrequencyControl(chat_id) - return self.talk_frequency_controls[chat_id] - - -def get_current_talk_frequency(chat_id: Optional[str] = None) -> float: +def get_config_base_talk_frequency(chat_id: Optional[str] = None) -> float: """ 根据当前时间和聊天流获取对应的 talk_frequency @@ -145,4 +126,3 @@ def get_global_frequency() -> Optional[float]: return None -talk_frequency_control = TalkFrequencyControlManager() diff --git a/src/chat/heart_flow/heartFC_chat.py b/src/chat/heart_flow/heartFC_chat.py index 18aec433..33b73427 100644 --- a/src/chat/heart_flow/heartFC_chat.py +++ b/src/chat/heart_flow/heartFC_chat.py @@ -18,8 +18,7 @@ from src.chat.planner_actions.action_modifier import ActionModifier from src.chat.planner_actions.action_manager import ActionManager from src.chat.heart_flow.hfc_utils import CycleDetail from src.chat.heart_flow.hfc_utils import send_typing, stop_typing -from src.chat.frequency_control.talk_frequency_control import talk_frequency_control -from src.chat.frequency_control.focus_value_control import focus_value_control +from src.chat.frequency_control.frequency_control import frequency_control_manager from src.chat.express.expression_learner import expression_learner_manager from src.person_info.person_info import Person from src.plugin_system.base.component_types import ChatMode, EventType, ActionInfo @@ -85,8 +84,7 @@ class HeartFChatting: self.expression_learner = expression_learner_manager.get_expression_learner(self.stream_id) - self.talk_frequency_control = talk_frequency_control.get_talk_frequency_control(self.stream_id) - self.focus_value_control = focus_value_control.get_focus_value_control(self.stream_id) + self.frequency_control = frequency_control_manager.get_or_create_frequency_control(self.stream_id) self.action_manager = ActionManager() self.action_planner = ActionPlanner(chat_id=self.stream_id, action_manager=self.action_manager) @@ -101,15 +99,8 @@ class HeartFChatting: self._cycle_counter = 0 self._current_cycle_detail: CycleDetail = None # type: ignore - self.reply_timeout_count = 0 - self.plan_timeout_count = 0 - self.last_read_time = time.time() - 10 - self.focus_energy = 1 - self.no_action_consecutive = 0 - # 最近三次no_action的新消息兴趣度记录 - self.recent_interest_records: deque = deque(maxlen=3) async def start(self): """检查是否需要启动主循环,如果未激活则启动。""" @@ -187,87 +178,14 @@ class HeartFChatting: f"耗时: {self._current_cycle_detail.end_time - self._current_cycle_detail.start_time:.1f}秒, " # type: ignore f"选择动作: {action_type}" + (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "") ) - - def _determine_form_type(self) -> None: - """判断使用哪种形式的no_action""" - # 如果连续no_action次数少于3次,使用waiting形式 - if self.no_action_consecutive <= 3: - self.focus_energy = 1 - else: - # 计算最近三次记录的兴趣度总和 - total_recent_interest = sum(self.recent_interest_records) - - # 计算调整后的阈值 - adjusted_threshold = 1 / self.talk_frequency_control.get_current_talk_frequency() - - logger.info( - f"{self.log_prefix} 最近三次兴趣度总和: {total_recent_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}" - ) - - # 如果兴趣度总和小于阈值,进入breaking形式 - if total_recent_interest < adjusted_threshold: - logger.info(f"{self.log_prefix} 兴趣度不足,进入休息") - self.focus_energy = random.randint(3, 6) - else: - logger.info(f"{self.log_prefix} 兴趣度充足,等待新消息") - self.focus_energy = 1 - - async def _should_process_messages(self, new_message: List["DatabaseMessages"]) -> tuple[bool, float]: - """ - 判断是否应该处理消息 - - Args: - new_message: 新消息列表 - mode: 当前聊天模式 - - Returns: - bool: 是否应该处理消息 - """ - new_message_count = len(new_message) - talk_frequency = self.talk_frequency_control.get_current_talk_frequency() - - modified_exit_count_threshold = self.focus_energy * 0.5 / talk_frequency - modified_exit_interest_threshold = 1.5 / talk_frequency + + async def caculate_interest_value(self, recent_messages_list: List["DatabaseMessages"]) -> float: total_interest = 0.0 - for msg in new_message: + for msg in recent_messages_list: interest_value = msg.interest_value if interest_value is not None and msg.processed_plain_text: total_interest += float(interest_value) - - if new_message_count >= modified_exit_count_threshold: - self.recent_interest_records.append(total_interest) - logger.info( - f"{self.log_prefix} 累计消息数量达到{new_message_count}条(>{modified_exit_count_threshold:.1f}),结束等待" - ) - # logger.info(self.last_read_time) - # logger.info(new_message) - return True, total_interest / new_message_count if new_message_count > 0 else 0.0 - - # 检查累计兴趣值 - if new_message_count > 0: - # 只在兴趣值变化时输出log - if not hasattr(self, "_last_accumulated_interest") or total_interest != self._last_accumulated_interest: - logger.info( - f"{self.log_prefix} 休息中,新消息:{new_message_count}条,累计兴趣值: {total_interest:.2f}, 活跃度: {talk_frequency:.1f}" - ) - self._last_accumulated_interest = total_interest - - if total_interest >= modified_exit_interest_threshold: - # 记录兴趣度到列表 - self.recent_interest_records.append(total_interest) - logger.info( - f"{self.log_prefix} 累计兴趣值达到{total_interest:.2f}(>{modified_exit_interest_threshold:.1f}),结束等待" - ) - return True, total_interest / new_message_count if new_message_count > 0 else 0.0 - - # 每10秒输出一次等待状态 - if int(time.time() - self.last_read_time) > 0 and int(time.time() - self.last_read_time) % 15 == 0: - logger.debug( - f"{self.log_prefix} 已等待{time.time() - self.last_read_time:.0f}秒,累计{new_message_count}条消息,累计兴趣{total_interest:.1f},继续等待..." - ) - await asyncio.sleep(0.5) - - return False, 0.0 + return total_interest / len(recent_messages_list) async def _loopbody(self): recent_messages_list = message_api.get_messages_by_time_in_chat( @@ -279,16 +197,13 @@ class HeartFChatting: filter_mai=True, filter_command=True, ) - # 统一的消息处理逻辑 - should_process, interest_value = await self._should_process_messages(recent_messages_list) - - if should_process: + + if recent_messages_list: self.last_read_time = time.time() - await self._observe(interest_value=interest_value) - + await self._observe(interest_value=await self.caculate_interest_value(recent_messages_list),recent_messages_list=recent_messages_list) else: # Normal模式:消息数量不足,等待 - await asyncio.sleep(0.5) + await asyncio.sleep(0.2) return True return True @@ -342,8 +257,7 @@ class HeartFChatting: return loop_info, reply_text, cycle_timers - async def _observe(self, interest_value: float = 0.0) -> bool: - action_type = "no_action" + async def _observe(self, interest_value: float = 0.0,recent_messages_list: List["DatabaseMessages"] = []) -> bool: reply_text = "" # 初始化reply_text变量,避免UnboundLocalError # 使用sigmoid函数将interest_value转换为概率 @@ -362,22 +276,28 @@ class HeartFChatting: normal_mode_probability = ( calculate_normal_mode_probability(interest_value) * 2 - * self.talk_frequency_control.get_current_talk_frequency() + * self.frequency_control.get_final_talk_frequency() ) + + #对呼唤名字进行增幅 + for msg in recent_messages_list: + if msg.reply_probability_boost is not None and msg.reply_probability_boost > 0.0: + normal_mode_probability += msg.reply_probability_boost + if global_config.chat.mentioned_bot_reply and msg.is_mentioned: + normal_mode_probability += global_config.chat.mentioned_bot_reply + if global_config.chat.at_bot_inevitable_reply and msg.is_at: + normal_mode_probability += global_config.chat.at_bot_inevitable_reply + - # 根据概率决定使用哪种模式 + # 根据概率决定使用直接回复 + interest_triggerd = False + focus_triggerd = False + if random.random() < normal_mode_probability: - mode = ChatMode.NORMAL + interest_triggerd = True logger.info( - f"{self.log_prefix} 有兴趣({interest_value:.2f}),在{normal_mode_probability * 100:.0f}%概率下选择回复" + f"{self.log_prefix} 有新消息,在{normal_mode_probability * 100:.0f}%概率下选择回复" ) - else: - mode = ChatMode.FOCUS - - # 创建新的循环信息 - cycle_timers, thinking_id = self.start_cycle() - - logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考") if s4u_config.enable_s4u: await send_typing() @@ -386,16 +306,21 @@ class HeartFChatting: await self.expression_learner.trigger_learning_for_chat() available_actions: Dict[str, ActionInfo] = {} - if random.random() > self.focus_value_control.get_current_focus_value() and mode == ChatMode.FOCUS: - # 如果激活度没有激活,并且聊天活跃度低,有可能不进行plan,相当于不在电脑前,不进行认真思考 - action_to_use_info = [ - ActionPlannerInfo( - action_type="no_action", - reasoning="专注不足", - action_data={}, - ) - ] - else: + + #如果兴趣度不足以激活 + if not interest_triggerd: + #看看专注值够不够 + if random.random() < self.frequency_control.get_final_focus_value(): + #专注值足够,仍然进入正式思考 + focus_triggerd = True #都没触发,路边 + + + # 任意一种触发都行 + if interest_triggerd or focus_triggerd: + # 进入正式思考模式 + cycle_timers, thinking_id = self.start_cycle() + logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考") + # 第一步:动作检查 with Timer("动作检查", cycle_timers): try: @@ -433,103 +358,93 @@ class HeartFChatting: ): return False with Timer("规划器", cycle_timers): + # 根据不同触发,进入不同plan + if focus_triggerd: + mode = ChatMode.FOCUS + else: + mode = ChatMode.NORMAL + action_to_use_info, _ = await self.action_planner.plan( mode=mode, loop_start_time=self.last_read_time, available_actions=available_actions, ) - # for action in action_to_use_info: - # print(action.action_type) + # 3. 并行执行所有动作 + action_tasks = [ + asyncio.create_task( + self._execute_action(action, action_to_use_info, thinking_id, available_actions, cycle_timers) + ) + for action in action_to_use_info + ] - # 3. 并行执行所有动作 - action_tasks = [ - asyncio.create_task( - self._execute_action(action, action_to_use_info, thinking_id, available_actions, cycle_timers) - ) - for action in action_to_use_info - ] + # 并行执行所有任务 + results = await asyncio.gather(*action_tasks, return_exceptions=True) - # 并行执行所有任务 - results = await asyncio.gather(*action_tasks, return_exceptions=True) + # 处理执行结果 + reply_loop_info = None + reply_text_from_reply = "" + action_success = False + action_reply_text = "" + action_command = "" - # 处理执行结果 - reply_loop_info = None - reply_text_from_reply = "" - action_success = False - action_reply_text = "" - action_command = "" + for i, result in enumerate(results): + if isinstance(result, BaseException): + logger.error(f"{self.log_prefix} 动作执行异常: {result}") + continue - for i, result in enumerate(results): - if isinstance(result, BaseException): - logger.error(f"{self.log_prefix} 动作执行异常: {result}") - continue + _cur_action = action_to_use_info[i] + if result["action_type"] != "reply": + action_success = result["success"] + action_reply_text = result["reply_text"] + action_command = result.get("command", "") + elif result["action_type"] == "reply": + if result["success"]: + reply_loop_info = result["loop_info"] + reply_text_from_reply = result["reply_text"] + else: + logger.warning(f"{self.log_prefix} 回复动作执行失败") - _cur_action = action_to_use_info[i] - if result["action_type"] != "reply": - action_success = result["success"] - action_reply_text = result["reply_text"] - action_command = result.get("command", "") - elif result["action_type"] == "reply": - if result["success"]: - reply_loop_info = result["loop_info"] - reply_text_from_reply = result["reply_text"] - else: - logger.warning(f"{self.log_prefix} 回复动作执行失败") - - # 构建最终的循环信息 - if reply_loop_info: - # 如果有回复信息,使用回复的loop_info作为基础 - loop_info = reply_loop_info - # 更新动作执行信息 - loop_info["loop_action_info"].update( - { - "action_taken": action_success, - "command": action_command, - "taken_time": time.time(), + # 构建最终的循环信息 + if reply_loop_info: + # 如果有回复信息,使用回复的loop_info作为基础 + loop_info = reply_loop_info + # 更新动作执行信息 + loop_info["loop_action_info"].update( + { + "action_taken": action_success, + "command": action_command, + "taken_time": time.time(), + } + ) + reply_text = reply_text_from_reply + else: + # 没有回复信息,构建纯动作的loop_info + loop_info = { + "loop_plan_info": { + "action_result": action_to_use_info, + }, + "loop_action_info": { + "action_taken": action_success, + "reply_text": action_reply_text, + "command": action_command, + "taken_time": time.time(), + }, } - ) - reply_text = reply_text_from_reply - else: - # 没有回复信息,构建纯动作的loop_info - loop_info = { - "loop_plan_info": { - "action_result": action_to_use_info, - }, - "loop_action_info": { - "action_taken": action_success, - "reply_text": action_reply_text, - "command": action_command, - "taken_time": time.time(), - }, - } - reply_text = action_reply_text + reply_text = action_reply_text + + + self.end_cycle(loop_info, cycle_timers) + self.print_cycle_info(cycle_timers) - if s4u_config.enable_s4u: - await stop_typing() - await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text) + """S4U内容,暂时保留""" + if s4u_config.enable_s4u: + await stop_typing() + await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text) + """S4U内容,暂时保留""" - self.end_cycle(loop_info, cycle_timers) - self.print_cycle_info(cycle_timers) - - # await self.willing_manager.after_generate_reply_handle(message_data.get("message_id", "")) - - action_type = action_to_use_info[0].action_type if action_to_use_info else "no_action" - - # 管理no_action计数器:当执行了非no_action动作时,重置计数器 - if action_type != "no_action": - # no_action逻辑已集成到heartFC_chat.py中,直接重置计数器 - self.recent_interest_records.clear() - self.no_action_consecutive = 0 - logger.debug(f"{self.log_prefix} 执行了{action_type}动作,重置no_action计数器") return True - if action_type == "no_action": - self.no_action_consecutive += 1 - self._determine_form_type() - - return True - async def _main_chat_loop(self): """主循环,持续进行计划并可能回复消息,直到被外部取消。""" try: diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index b1dccdaf..ac424c66 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -32,10 +32,10 @@ async def _calculate_interest(message: MessageRecv) -> Tuple[float, list[str]]: Returns: Tuple[float, bool, list[str]]: (兴趣度, 是否被提及, 关键词) """ - if message.is_picid: + if message.is_picid or message.is_emoji: return 0.0, [] - is_mentioned, _ = is_mentioned_bot_in_message(message) + is_mentioned,is_at,reply_probability_boost = is_mentioned_bot_in_message(message) interested_rate = 0.0 with Timer("记忆激活"): @@ -79,17 +79,13 @@ async def _calculate_interest(message: MessageRecv) -> Tuple[float, list[str]]: # 确保在范围内 base_interest = min(max(base_interest, 0.01), 0.3) - interested_rate += base_interest - - if is_mentioned: - interest_increase_on_mention = 2 - interested_rate += interest_increase_on_mention - - message.interest_value = interested_rate + message.interest_value = base_interest message.is_mentioned = is_mentioned - - return interested_rate, keywords + message.is_at = is_at + message.reply_probability_boost = reply_probability_boost + + return base_interest, keywords class HeartFCMessageReceiver: diff --git a/src/chat/message_receive/message.py b/src/chat/message_receive/message.py index 66a1c029..5f97e7c8 100644 --- a/src/chat/message_receive/message.py +++ b/src/chat/message_receive/message.py @@ -108,6 +108,8 @@ class MessageRecv(Message): self.has_picid = False self.is_voice = False self.is_mentioned = None + self.is_at = False + self.reply_probability_boost = 0.0 self.is_notify = False self.is_command = False diff --git a/src/chat/message_receive/storage.py b/src/chat/message_receive/storage.py index c9de76ec..3d84f270 100644 --- a/src/chat/message_receive/storage.py +++ b/src/chat/message_receive/storage.py @@ -56,6 +56,8 @@ class MessageStorage: filtered_display_message = "" interest_value = 0 is_mentioned = False + is_at = False + reply_probability_boost = 0.0 reply_to = message.reply_to priority_mode = "" priority_info = {} @@ -70,6 +72,8 @@ class MessageStorage: filtered_display_message = "" interest_value = message.interest_value is_mentioned = message.is_mentioned + is_at = message.is_at + reply_probability_boost = message.reply_probability_boost reply_to = "" priority_mode = message.priority_mode priority_info = message.priority_info @@ -100,6 +104,8 @@ class MessageStorage: # Flattened chat_info reply_to=reply_to, is_mentioned=is_mentioned, + is_at=is_at, + reply_probability_boost=reply_probability_boost, chat_info_stream_id=chat_info_dict.get("stream_id"), chat_info_platform=chat_info_dict.get("platform"), chat_info_user_platform=user_info_from_chat.get("platform"), diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index 61bc0675..f9e02b08 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -65,7 +65,6 @@ def init_prompt(): 动作描述:参与聊天回复,发送文本进行表达 - 你想要闲聊或者随便附和 - 有人提到了你,但是你还没有回应 -- {mentioned_bonus} - 如果你刚刚进行了回复,不要对同一个话题重复回应 {{ "action": "reply", @@ -93,7 +92,6 @@ def init_prompt(): 现在,最新的聊天消息引起了你的兴趣,你想要对其中的消息进行回复,回复标准如下: - 你想要闲聊或者随便附和 - 有人提到了你,但是你还没有回应 -- {mentioned_bonus} - 如果你刚刚进行了回复,不要对同一个话题重复回应 你之前的动作记录: @@ -465,7 +463,7 @@ class ActionPlanner: ) ) - logger.info(f"{self.log_prefix}副规划器返回了{len(action_planner_infos)}个action") + logger.debug(f"{self.log_prefix}副规划器返回了{len(action_planner_infos)}个action") return action_planner_infos async def plan( @@ -553,7 +551,7 @@ class ActionPlanner: for i, (action_name, action_info) in enumerate(action_items): sub_planner_lists[i % sub_planner_num].append((action_name, action_info)) - logger.info( + logger.debug( f"{self.log_prefix}成功将{sub_planner_actions_num}个actions分配到{sub_planner_num}个子列表中" ) for i, action_list in enumerate(sub_planner_lists): @@ -585,7 +583,7 @@ class ActionPlanner: for sub_result in sub_plan_results: all_sub_planner_results.extend(sub_result) - logger.info(f"{self.log_prefix}所有副规划器共返回了{len(all_sub_planner_results)}个action") + logger.info(f"{self.log_prefix}小脑决定执行{len(all_sub_planner_results)}个动作") # --- 构建提示词 (调用修改后的 PromptBuilder 方法) --- prompt, message_id_list = await self.build_planner_prompt( @@ -777,12 +775,6 @@ class ActionPlanner: else: actions_before_now_block = "" - mentioned_bonus = "" - if global_config.chat.mentioned_bot_inevitable_reply: - mentioned_bonus = "\n- 有人提到你" - if global_config.chat.at_bot_inevitable_reply: - mentioned_bonus = "\n- 有人提到你,或者at你" - chat_context_description = "你现在正在一个群聊中" chat_target_name = None if not is_group_chat and chat_target_info: @@ -838,7 +830,6 @@ class ActionPlanner: chat_context_description=chat_context_description, chat_content_block=chat_content_block, actions_before_now_block=actions_before_now_block, - mentioned_bonus=mentioned_bonus, # action_options_text=action_options_block, moderation_prompt=moderation_prompt_block, name_block=name_block, @@ -850,7 +841,6 @@ class ActionPlanner: time_block=time_block, chat_context_description=chat_context_description, chat_content_block=chat_content_block, - mentioned_bonus=mentioned_bonus, moderation_prompt=moderation_prompt_block, name_block=name_block, actions_before_now_block=actions_before_now_block, diff --git a/src/chat/utils/utils.py b/src/chat/utils/utils.py index 88562fff..79b18906 100644 --- a/src/chat/utils/utils.py +++ b/src/chat/utils/utils.py @@ -43,15 +43,15 @@ def db_message_to_str(message_dict: dict) -> str: return result -def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, float]: +def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, bool, float]: """检查消息是否提到了机器人""" - keywords = [global_config.bot.nickname] - nicknames = global_config.bot.alias_names + keywords = [global_config.bot.nickname] + list(global_config.bot.alias_names) reply_probability = 0.0 is_at = False is_mentioned = False - if message.is_mentioned is not None: - return bool(message.is_mentioned), message.is_mentioned + + # 这部分怎么处理啊啊啊啊 + #我觉得可以给消息加一个 reply_probability_boost字段 if ( message.message_info.additional_config is not None and message.message_info.additional_config.get("is_mentioned") is not None @@ -59,18 +59,15 @@ def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, float]: try: reply_probability = float(message.message_info.additional_config.get("is_mentioned")) # type: ignore is_mentioned = True - return is_mentioned, reply_probability + return is_mentioned, is_at, reply_probability except Exception as e: logger.warning(str(e)) logger.warning( f"消息中包含不合理的设置 is_mentioned: {message.message_info.additional_config.get('is_mentioned')}" ) - if global_config.bot.nickname in message.processed_plain_text: - is_mentioned = True - - for alias_name in global_config.bot.alias_names: - if alias_name in message.processed_plain_text: + for keyword in keywords: + if keyword in message.processed_plain_text: is_mentioned = True # 判断是否被@ @@ -78,10 +75,6 @@ def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, float]: is_at = True is_mentioned = True - # print(f"message.processed_plain_text: {message.processed_plain_text}") - # print(f"is_mentioned: {is_mentioned}") - # print(f"is_at: {is_at}") - if is_at and global_config.chat.at_bot_inevitable_reply: reply_probability = 1.0 logger.debug("被@,回复概率设置为100%") @@ -104,13 +97,10 @@ def is_mentioned_bot_in_message(message: MessageRecv) -> tuple[bool, float]: for keyword in keywords: if keyword in message_content: is_mentioned = True - for nickname in nicknames: - if nickname in message_content: - is_mentioned = True - if is_mentioned and global_config.chat.mentioned_bot_inevitable_reply: + if is_mentioned and global_config.chat.mentioned_bot_reply: reply_probability = 1.0 logger.debug("被提及,回复概率设置为100%") - return is_mentioned, reply_probability + return is_mentioned, is_at, reply_probability async def get_embedding(text, request_type="embedding") -> Optional[List[float]]: diff --git a/src/common/data_models/database_data_model.py b/src/common/data_models/database_data_model.py index b752cbb7..bf4a5f52 100644 --- a/src/common/data_models/database_data_model.py +++ b/src/common/data_models/database_data_model.py @@ -67,6 +67,8 @@ class DatabaseMessages(BaseDataModel): key_words: Optional[str] = None, key_words_lite: Optional[str] = None, is_mentioned: Optional[bool] = None, + is_at: Optional[bool] = None, + reply_probability_boost: Optional[float] = None, processed_plain_text: Optional[str] = None, display_message: Optional[str] = None, priority_mode: Optional[str] = None, @@ -104,6 +106,9 @@ class DatabaseMessages(BaseDataModel): self.key_words_lite = key_words_lite self.is_mentioned = is_mentioned + self.is_at = is_at + self.reply_probability_boost = reply_probability_boost + self.processed_plain_text = processed_plain_text self.display_message = display_message @@ -171,6 +176,8 @@ class DatabaseMessages(BaseDataModel): "key_words": self.key_words, "key_words_lite": self.key_words_lite, "is_mentioned": self.is_mentioned, + "is_at": self.is_at, + "reply_probability_boost": self.reply_probability_boost, "processed_plain_text": self.processed_plain_text, "display_message": self.display_message, "priority_mode": self.priority_mode, diff --git a/src/common/database/database_model.py b/src/common/database/database_model.py index 330bfa7d..14ce741d 100644 --- a/src/common/database/database_model.py +++ b/src/common/database/database_model.py @@ -137,7 +137,8 @@ class Messages(BaseModel): key_words_lite = TextField(null=True) is_mentioned = BooleanField(null=True) - + is_at = BooleanField(null=True) + reply_probability_boost = DoubleField(null=True) # 从 chat_info 扁平化而来的字段 chat_info_stream_id = TextField() chat_info_platform = TextField() diff --git a/src/common/logger.py b/src/common/logger.py index d2872b4e..ab0fd849 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -355,6 +355,7 @@ MODULE_COLORS = { # 核心模块 "main": "\033[1;97m", # 亮白色+粗体 (主程序) + "memory": "\033[38;5;34m", # 天蓝色 "config": "\033[93m", # 亮黄色 "common": "\033[95m", # 亮紫色 @@ -366,10 +367,9 @@ MODULE_COLORS = { "llm_models": "\033[36m", # 青色 "remote": "\033[38;5;242m", # 深灰色,更不显眼 "planner": "\033[36m", - "memory": "\033[38;5;117m", # 天蓝色 - "hfc": "\033[38;5;81m", # 稍微暗一些的青色,保持可读 - "action_manager": "\033[38;5;208m", # 橙色,不与replyer重复 - # 关系系统 + + + "relation": "\033[38;5;139m", # 柔和的紫色,不刺眼 # 聊天相关模块 "normal_chat": "\033[38;5;81m", # 亮蓝绿色 diff --git a/src/config/config.py b/src/config/config.py index 80e275f8..99b8d00b 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -56,7 +56,7 @@ TEMPLATE_DIR = os.path.join(PROJECT_ROOT, "template") # 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码 # 对该字段的更新,请严格参照语义化版本规范:https://semver.org/lang/zh-CN/ -MMC_VERSION = "0.10.2-snapshot.1" +MMC_VERSION = "0.10.2-snapshot.2" def get_key_comment(toml_table, key): diff --git a/src/config/official_configs.py b/src/config/official_configs.py index 7d9d950b..4d3758ce 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -71,14 +71,14 @@ class ChatConfig(ConfigBase): interest_rate_mode: Literal["fast", "accurate"] = "fast" """兴趣值计算模式,fast为快速计算,accurate为精确计算""" - mentioned_bot_inevitable_reply: bool = False - """提及 bot 必然回复""" + mentioned_bot_reply: float = 1 + """提及 bot 必然回复,1为100%回复,0为不额外增幅""" planner_size: float = 1.5 """副规划器大小,越小,麦麦的动作执行能力越精细,但是消耗更多token,调大可以缓解429类错误""" - at_bot_inevitable_reply: bool = False - """@bot 必然回复""" + at_bot_inevitable_reply: float = 1 + """@bot 必然回复,1为100%回复,0为不额外增幅""" talk_frequency: float = 0.5 """回复频率阈值""" diff --git a/src/plugin_system/apis/frequency_api.py b/src/plugin_system/apis/frequency_api.py index 0b0fe3cf..448050b9 100644 --- a/src/plugin_system/apis/frequency_api.py +++ b/src/plugin_system/apis/frequency_api.py @@ -1,27 +1,26 @@ from src.common.logger import get_logger -from src.chat.frequency_control.focus_value_control import focus_value_control -from src.chat.frequency_control.talk_frequency_control import talk_frequency_control +from src.chat.frequency_control.frequency_control import frequency_control_manager logger = get_logger("frequency_api") def get_current_focus_value(chat_id: str) -> float: - return focus_value_control.get_focus_value_control(chat_id).get_current_focus_value() + return frequency_control_manager.get_or_create_frequency_control(chat_id).get_final_focus_value() def get_current_talk_frequency(chat_id: str) -> float: - return talk_frequency_control.get_talk_frequency_control(chat_id).get_current_talk_frequency() + return frequency_control_manager.get_or_create_frequency_control(chat_id).get_final_talk_frequency() def set_focus_value_adjust(chat_id: str, focus_value_adjust: float) -> None: - focus_value_control.get_focus_value_control(chat_id).focus_value_adjust = focus_value_adjust + frequency_control_manager.get_or_create_frequency_control(chat_id).focus_value_external_adjust = focus_value_adjust def set_talk_frequency_adjust(chat_id: str, talk_frequency_adjust: float) -> None: - talk_frequency_control.get_talk_frequency_control(chat_id).talk_frequency_adjust = talk_frequency_adjust + frequency_control_manager.get_or_create_frequency_control(chat_id).talk_frequency_external_adjust = talk_frequency_adjust def get_focus_value_adjust(chat_id: str) -> float: - return focus_value_control.get_focus_value_control(chat_id).focus_value_adjust + return frequency_control_manager.get_or_create_frequency_control(chat_id).focus_value_external_adjust def get_talk_frequency_adjust(chat_id: str) -> float: - return talk_frequency_control.get_talk_frequency_control(chat_id).talk_frequency_adjust + return frequency_control_manager.get_or_create_frequency_control(chat_id).talk_frequency_external_adjust diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 3805051c..d2e9a007 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "6.7.2" +version = "6.8.0" #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- #如果你想要修改配置文件,请递增version的值 @@ -59,17 +59,17 @@ expression_groups = [ [chat] #麦麦的聊天设置 talk_frequency = 0.5 -# 麦麦活跃度,越高,麦麦回复越多,范围0-1 +# 麦麦活跃度,越高,麦麦越容易回复,范围0-1 focus_value = 0.5 # 麦麦的专注度,越高越容易持续连续对话,可能消耗更多token, 范围0-1 +mentioned_bot_reply = 1 # 提及时,回复概率增幅,1为100%回复,0为不额外增幅 +at_bot_inevitable_reply = 1 # at时,回复概率增幅,1为100%回复,0为不额外增幅 + max_context_size = 20 # 上下文长度 planner_size = 2.5 # 副规划器大小,越小,麦麦的动作执行能力越精细,但是消耗更多token,调大可以缓解429类错误 -mentioned_bot_inevitable_reply = true # 提及 bot 大概率回复 -at_bot_inevitable_reply = true # @bot 或 提及bot 大概率回复 - focus_value_adjust = [ ["", "8:00,1", "12:00,0.8", "18:00,1", "01:00,0.3"], ["qq:114514:group", "12:20,0.6", "16:10,0.5", "20:10,0.8", "00:10,0.3"],