From b468b67cf1fe0004bd117193e1c1af3e8192304d Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 12:32:18 +0800 Subject: [PATCH 01/24] =?UTF-8?q?willing=E5=9F=BA=E7=B1=BB=E5=A4=A7?= =?UTF-8?q?=E8=87=B4=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.py | 2 +- .../reasoning_chat/reasoning_chat.py | 63 ++++--- .../think_flow_chat/think_flow_chat.py | 72 ++++---- src/plugins/willing/willing_manager.py | 155 +++++++++++++++--- 4 files changed, 194 insertions(+), 98 deletions(-) diff --git a/src/main.py b/src/main.py index aa6f908bf..4cb195e86 100644 --- a/src/main.py +++ b/src/main.py @@ -63,7 +63,7 @@ class MainSystem: asyncio.create_task(person_info_manager.personal_habit_deduction()) # 启动愿望管理器 - await willing_manager.ensure_started() + await willing_manager.async_task_starter() # 启动消息处理器 if not self._message_manager_started: diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index 683bef463..d6f106d27 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -54,7 +54,6 @@ class ReasoningChat: ) message_manager.add_message(thinking_message) - willing_manager.change_reply_willing_sent(chat) return thinking_id @@ -126,7 +125,7 @@ class ReasoningChat: ) message_manager.add_message(bot_message) - async def _update_relationship(self, message, response_set): + async def _update_relationship(self, message: MessageRecv, response_set): """更新关系情绪""" ori_response = ",".join(response_set) stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text) @@ -178,7 +177,16 @@ class ReasoningChat: # 查询缓冲器结果,会整合前面跳过的消息,改变processed_plain_text buffer_result = await message_buffer.query_buffer_result(message) + + # 处理提及 + is_mentioned, reply_probability = is_mentioned_bot_in_message(message) + + # 意愿管理器:设置当前message信息 + willing_manager.setup(message, chat, is_mentioned, interested_rate) + + # 处理缓冲器结果 if not buffer_result: + await willing_manager.bombing_buffer_message_handle(message.message_info.message_id) if message.message_segment.type == "text": logger.info(f"触发缓冲,已炸飞消息:{message.processed_plain_text}") elif message.message_segment.type == "image": @@ -187,45 +195,21 @@ class ReasoningChat: logger.info("触发缓冲,已炸飞消息列") return - # 处理提及 - is_mentioned, reply_probability = is_mentioned_bot_in_message(message) + # 获取回复概率 + if reply_probability != 1: + reply_probability = await willing_manager.get_reply_probability(message.message_info.message_id) - # 计算回复意愿 - current_willing = willing_manager.get_willing(chat_stream=chat) - willing_manager.set_willing(chat.stream_id, current_willing) - - # 意愿激活 - timer1 = time.time() - real_reply_probability = await willing_manager.change_reply_willing_received( - chat_stream=chat, - is_mentioned_bot=is_mentioned, - config=global_config, - is_emoji=message.is_emoji, - interested_rate=interested_rate, - sender_id=str(message.message_info.user_info.user_id), - ) - if reply_probability != 1 or (groupinfo and (groupinfo.group_id not in global_config.talk_allowed_groups)): - reply_probability = real_reply_probability - timer2 = time.time() - timing_results["意愿激活"] = timer2 - timer1 - - # 打印消息信息 - mes_name = chat.group_info.group_name if chat.group_info else "私聊" - current_time = time.strftime("%H:%M:%S", time.localtime(messageinfo.time)) - logger.info( - f"[{current_time}][{mes_name}]" - f"{chat.user_info.user_nickname}:" - f"{message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" - ) - - if message.message_info.additional_config: - if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): - reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] + if message.message_info.additional_config: + if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): + reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] do_reply = False if random() < reply_probability: do_reply = True + # 回复前处理 + await willing_manager.before_generate_reply_handle(message.message_info.message_id) + # 创建思考消息 timer1 = time.time() thinking_id = await self._create_thinking_message(message, chat, userinfo, messageinfo) @@ -260,12 +244,21 @@ class ReasoningChat: timer2 = time.time() timing_results["更新关系情绪"] = timer2 - timer1 + # 回复后处理 + await willing_manager.after_generate_reply_handle(message.message_info.message_id) + # 输出性能计时结果 if do_reply: timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()]) trigger_msg = message.processed_plain_text response_msg = " ".join(response_set) if response_set else "无回复" logger.info(f"触发消息: {trigger_msg[:20]}... | 推理消息: {response_msg[:20]}... | 性能计时: {timing_str}") + else: + # 不回复处理 + await willing_manager.not_reply_handle(message.message_info.message_id) + + # 意愿管理器:注销当前message信息 + willing_manager.delete(message.message_info.message_id) def _check_ban_words(self, text: str, chat, userinfo) -> bool: """检查消息中是否包含过滤词""" diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index f845770d3..7351bffed 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -55,7 +55,6 @@ class ThinkFlowChat: ) message_manager.add_message(thinking_message) - willing_manager.change_reply_willing_sent(chat) return thinking_id @@ -146,7 +145,7 @@ class ThinkFlowChat: await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt) - async def _update_relationship(self, message, response_set): + async def _update_relationship(self, message: MessageRecv, response_set): """更新关系情绪""" ori_response = ",".join(response_set) stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text) @@ -203,7 +202,16 @@ class ThinkFlowChat: # 查询缓冲器结果,会整合前面跳过的消息,改变processed_plain_text buffer_result = await message_buffer.query_buffer_result(message) + + # 处理提及 + is_mentioned, reply_probability = is_mentioned_bot_in_message(message) + + # 意愿管理器:设置当前message信息 + willing_manager.setup(message, chat, is_mentioned, interested_rate) + + # 处理缓冲器结果 if not buffer_result: + await willing_manager.bombing_buffer_message_handle(message.message_info.message_id) if message.message_segment.type == "text": logger.info(f"触发缓冲,已炸飞消息:{message.processed_plain_text}") elif message.message_segment.type == "image": @@ -212,52 +220,29 @@ class ThinkFlowChat: logger.info("触发缓冲,已炸飞消息列") return - # 处理提及 - is_mentioned, reply_probability = is_mentioned_bot_in_message(message) - # 计算回复意愿 - current_willing_old = willing_manager.get_willing(chat_stream=chat) - # current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4 - # current_willing = (current_willing_old + current_willing_new) / 2 - # 有点bug - current_willing = current_willing_old + # current_willing_old = willing_manager.get_willing(chat_stream=chat) + # # current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4 + # # current_willing = (current_willing_old + current_willing_new) / 2 + # # 有点bug + # current_willing = current_willing_old - willing_manager.set_willing(chat.stream_id, current_willing) + # 获取回复概率 + if reply_probability != 1: + reply_probability = await willing_manager.get_reply_probability(message.message_info.message_id) - # 意愿激活 - timer1 = time.time() - real_reply_probability = await willing_manager.change_reply_willing_received( - chat_stream=chat, - is_mentioned_bot=is_mentioned, - config=global_config, - is_emoji=message.is_emoji, - interested_rate=interested_rate, - sender_id=str(message.message_info.user_info.user_id), - ) - if reply_probability != 1 or (groupinfo and (groupinfo.group_id not in global_config.talk_allowed_groups)): - reply_probability = real_reply_probability - timer2 = time.time() - timing_results["意愿激活"] = timer2 - timer1 - logger.debug(f"意愿激活: {reply_probability}") - - # 打印消息信息 - mes_name = chat.group_info.group_name if chat.group_info else "私聊" - current_time = time.strftime("%H:%M:%S", time.localtime(messageinfo.time)) - logger.info( - f"[{current_time}][{mes_name}]" - f"{chat.user_info.user_nickname}:" - f"{message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" - ) - - if message.message_info.additional_config: - if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): - reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] + if message.message_info.additional_config: + if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): + reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] do_reply = False if random() < reply_probability: try: do_reply = True + # 回复前处理 + await willing_manager.before_generate_reply_handle(message.message_info.message_id) + # 创建思考消息 try: timer1 = time.time() @@ -333,6 +318,9 @@ class ThinkFlowChat: except Exception as e: logger.error(f"心流更新关系情绪失败: {e}") + # 回复后处理 + await willing_manager.after_generate_reply_handle(message.message_info.message_id) + except Exception as e: logger.error(f"心流处理消息失败: {e}") @@ -342,6 +330,12 @@ class ThinkFlowChat: trigger_msg = message.processed_plain_text response_msg = " ".join(response_set) if response_set else "无回复" logger.info(f"触发消息: {trigger_msg[:20]}... | 思维消息: {response_msg[:20]}... | 性能计时: {timing_str}") + else: + # 不回复处理 + await willing_manager.not_reply_handle(message.message_info.message_id) + + # 意愿管理器:注销当前message信息 + willing_manager.delete(message.message_info.message_id) def _check_ban_words(self, text: str, chat, userinfo) -> bool: """检查消息中是否包含过滤词""" diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index 06aaebc13..696e8be69 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -1,22 +1,144 @@ -from typing import Optional -from src.common.logger import get_module_logger -from ..config.config import global_config -from .mode_classical import WillingManager as ClassicalWillingManager -from .mode_dynamic import WillingManager as DynamicWillingManager -from .mode_custom import WillingManager as CustomWillingManager -from src.common.logger import LogConfig, WILLING_STYLE_CONFIG +from src.common.logger import LogConfig, WILLING_STYLE_CONFIG, LoguruLogger, get_module_logger +from dataclasses import dataclass +from ..config.config import global_config, BotConfig +from ..chat.chat_stream import ChatStream, GroupInfo +from ..chat.message import MessageRecv +from ..person_info.person_info import person_info_manager, PersonInfoManager +from abc import ABC, abstractmethod +import importlib +from typing import Dict, Optional +import asyncio + +""" +基类方法概览: +async_task_starter 在程序启动时执行,在其中用asyncio.create_task启动你想要执行的异步任务 +before_generate_reply_handle 确定要回复后,在生成回复前的处理 +after_generate_reply_handle 确定要回复后,在生成回复后的处理 +not_reply_handle 确定不回复后的处理 +get_reply_probability 获取回复概率(必须实现) +bombing_buffer_message_handle 缓冲器炸飞消息后的处理 +get_willing 获取某聊天流意愿 +set_willing 设置某聊天流意愿 +规范说明: +模块文件命名: `mode_{manager_type}.py` +示例: 若 `manager_type="aggressive"`,则模块文件应为 `mode_aggressive.py` +类命名: `{manager_type}WillingManager` (首字母大写) +示例: 在 `mode_aggressive.py` 中,类名应为 `AggressiveWillingManager` +""" willing_config = LogConfig( # 使用消息发送专用样式 console_format=WILLING_STYLE_CONFIG["console_format"], file_format=WILLING_STYLE_CONFIG["file_format"], ) - logger = get_module_logger("willing", config=willing_config) +@dataclass +class WillingInfo: + """此类保存意愿模块常用的参数 + + Attributes: + message (MessageRecv): 原始消息对象 + chat (ChatStream): 聊天流对象 + person_info_manager (PersonInfoManager): 用户信息管理对象 + chat_id (str): 当前聊天流的标识符 + person_id (str): 发送者的个人信息的标识符 + group_id (str): 群组ID(如果是私聊则为空) + is_mentioned_bot (bool): 是否提及了bot + is_emoji (bool): 是否为表情包 + interested_rate (float): 兴趣度 + """ + message: MessageRecv + chat: ChatStream + person_info_manager: PersonInfoManager + chat_id: str + person_id: str + group_info: Optional[GroupInfo] + is_mentioned_bot: bool + is_emoji: bool + interested_rate: float + # current_mood: float 当前心情? -def init_willing_manager() -> Optional[object]: +class BaseWillingManager(ABC): + """回复意愿管理基类""" + + @classmethod + def create(cls, manager_type: str) -> 'BaseWillingManager': + try: + module = importlib.import_module(f".mode_{manager_type}", __package__) + manager_class = getattr(module, f"{manager_type.capitalize()}WillingManager") + if not issubclass(manager_class, cls): + manager_class = getattr(module, "ClassicalWillingManager") + logger.info("未找到当前意愿模式对应文件,使用经典配方~") + return manager_class() + except (ImportError, AttributeError) as e: + logger.error(f"Failed to create willing manager: {str(e)}") + raise ValueError(f"Invalid willing manager type: {manager_type}") from e + + def __init__(self): + self.chat_reply_willing: Dict[str, float] = {} # 存储每个聊天流的回复意愿(chat_id) + self.ongoing_messages: Dict[str, WillingInfo] = {} # 当前正在进行的消息(message_id) + self.lock = asyncio.Lock() + self.global_config: BotConfig = global_config + self.logger: LoguruLogger = logger + + def setup(self, message: MessageRecv, chat: ChatStream, is_mentioned_bot: bool, interested_rate: float): + person_id = person_info_manager.get_person_id(chat.platform, chat.user_info.user_id) + self.ongoing_messages[message.message_info.message_id] = WillingInfo( + message=message, + chat=chat, + person_info_manager=person_info_manager, + chat_id=chat.stream_id, + person_id=person_id, + group_info=chat.group_info, + is_mentioned_bot=is_mentioned_bot, + is_emoji=message.is_emoji, + interested_rate=interested_rate, + ) + + def delete(self, message_id: str): + del_message = self.ongoing_messages.pop(message_id, None) + if not del_message: + logger.debug(f"删除异常,当前消息{message_id}不存在") + + async def async_task_starter(self) -> None: + """抽象方法:异步任务启动器""" + pass + + async def before_generate_reply_handle(self, message_id: str): + """抽象方法:回复前处理""" + pass + + async def after_generate_reply_handle(self, message_id: str): + """抽象方法:回复后处理""" + pass + + async def not_reply_handle(self, message_id: str): + """抽象方法:不回复处理""" + pass + + @abstractmethod + async def get_reply_probability(self, message_id: str): + """抽象方法:获取回复概率""" + raise NotImplementedError + + async def bombing_buffer_message_handle(self, message_id: str): + """抽象方法:炸飞消息处理""" + pass + + async def get_willing(self, chat_id: str): + """获取指定聊天流的回复意愿""" + async with self.lock: + return self.chat_reply_willing.get(chat_id, 0) + + async def set_willing(self, chat_id: str, willing: float): + """设置指定聊天流的回复意愿""" + async with self.lock: + self.chat_reply_willing[chat_id] = willing + + +def init_willing_manager() -> BaseWillingManager: """ 根据配置初始化并返回对应的WillingManager实例 @@ -24,20 +146,7 @@ def init_willing_manager() -> Optional[object]: 对应mode的WillingManager实例 """ mode = global_config.willing_mode.lower() - - if mode == "classical": - logger.info("使用经典回复意愿管理器") - return ClassicalWillingManager() - elif mode == "dynamic": - logger.info("使用动态回复意愿管理器") - return DynamicWillingManager() - elif mode == "custom": - logger.warning(f"自定义的回复意愿管理器模式: {mode}") - return CustomWillingManager() - else: - logger.warning(f"未知的回复意愿管理器模式: {mode}, 将使用经典模式") - return ClassicalWillingManager() - + return BaseWillingManager.create(mode) # 全局willing_manager对象 willing_manager = init_willing_manager() From d270a85e34a82f14845a12f07a2ca14bb07d9bef Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 12:32:47 +0800 Subject: [PATCH 02/24] =?UTF-8?q?=E9=80=82=E9=85=8D=E7=BB=8F=E5=85=B8?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/mode_classical.py | 104 ++++++++------------------ src/plugins/willing/mode_custom.py | 100 +------------------------ 2 files changed, 35 insertions(+), 169 deletions(-) diff --git a/src/plugins/willing/mode_classical.py b/src/plugins/willing/mode_classical.py index d9450f028..05a6bc7fd 100644 --- a/src/plugins/willing/mode_classical.py +++ b/src/plugins/willing/mode_classical.py @@ -1,14 +1,10 @@ import asyncio -from typing import Dict -from ..chat.chat_stream import ChatStream -from ..config.config import global_config +from .willing_manager import BaseWillingManager -class WillingManager: +class ClassicalWillingManager(BaseWillingManager): def __init__(self): - self.chat_reply_willing: Dict[str, float] = {} # 存储每个聊天流的回复意愿 - self._decay_task = None - self._started = False + super().__init__() async def _decay_reply_willing(self): """定期衰减回复意愿""" @@ -16,87 +12,51 @@ class WillingManager: await asyncio.sleep(1) for chat_id in self.chat_reply_willing: self.chat_reply_willing[chat_id] = max(0, self.chat_reply_willing[chat_id] * 0.9) + self.logger.info(self.chat_reply_willing[chat_id]) - def get_willing(self, chat_stream: ChatStream) -> float: - """获取指定聊天流的回复意愿""" - if chat_stream: - return self.chat_reply_willing.get(chat_stream.stream_id, 0) - return 0 + async def async_task_starter(self): + asyncio.create_task(self._decay_reply_willing) - def set_willing(self, chat_id: str, willing: float): - """设置指定聊天流的回复意愿""" - self.chat_reply_willing[chat_id] = willing - - async def change_reply_willing_received( - self, - chat_stream: ChatStream, - is_mentioned_bot: bool = False, - config=None, - is_emoji: bool = False, - interested_rate: float = 0, - sender_id: str = None, - ) -> float: - """改变指定聊天流的回复意愿并返回回复概率""" - chat_id = chat_stream.stream_id + async def get_reply_probability(self, message_id): + willing_info = self.ongoing_messages[message_id] + chat_id = willing_info.chat_id current_willing = self.chat_reply_willing.get(chat_id, 0) - interested_rate = interested_rate * config.response_interested_rate_amplifier + interested_rate = willing_info.interested_rate * self.global_config.response_interested_rate_amplifier if interested_rate > 0.4: current_willing += interested_rate - 0.3 - if is_mentioned_bot and current_willing < 1.0: + if willing_info.is_mentioned_bot and current_willing < 1.0: current_willing += 1 - elif is_mentioned_bot: + elif willing_info.is_mentioned_bot: current_willing += 0.05 - if is_emoji: - current_willing *= global_config.emoji_response_penalty + if willing_info.is_emoji: + if self.global_config.emoji_response_penalty != 0: + current_willing *= self.global_config.emoji_response_penalty + else: + return 0 self.chat_reply_willing[chat_id] = min(current_willing, 3.0) - reply_probability = min(max((current_willing - 0.5), 0.01) * config.response_willing_amplifier * 2, 1) + reply_probability = min(max((current_willing - 0.5), 0.01) * self.global_config.response_willing_amplifier * 2, 1) # 检查群组权限(如果是群聊) - if chat_stream.group_info and config: - if chat_stream.group_info.group_id not in config.talk_allowed_groups: - current_willing = 0 - reply_probability = 0 - - if chat_stream.group_info.group_id in config.talk_frequency_down_groups: - reply_probability = reply_probability / config.down_frequency_rate + if willing_info.group_info and willing_info.group_info.group_id in self.global_config.talk_frequency_down_groups: + reply_probability = reply_probability / self.global_config.down_frequency_rate return reply_probability + + async def before_generate_reply_handle(self, message_id): + chat_id = self.ongoing_messages[message_id].chat_id + current_willing = self.chat_reply_willing.get(chat_id, 0) + self.chat_reply_willing[chat_id] = max(0, current_willing - 1.8) - def change_reply_willing_sent(self, chat_stream: ChatStream): - """发送消息后降低聊天流的回复意愿""" - if chat_stream: - chat_id = chat_stream.stream_id - current_willing = self.chat_reply_willing.get(chat_id, 0) - self.chat_reply_willing[chat_id] = max(0, current_willing - 1.8) - - def change_reply_willing_not_sent(self, chat_stream: ChatStream): - """未发送消息后降低聊天流的回复意愿""" - if chat_stream: - chat_id = chat_stream.stream_id - current_willing = self.chat_reply_willing.get(chat_id, 0) - self.chat_reply_willing[chat_id] = max(0, current_willing - 0) - - def change_reply_willing_after_sent(self, chat_stream: ChatStream): - """发送消息后提高聊天流的回复意愿""" - if chat_stream: - chat_id = chat_stream.stream_id - current_willing = self.chat_reply_willing.get(chat_id, 0) - if current_willing < 1: - self.chat_reply_willing[chat_id] = min(1, current_willing + 0.4) - - async def ensure_started(self): - """确保衰减任务已启动""" - if not self._started: - if self._decay_task is None: - self._decay_task = asyncio.create_task(self._decay_reply_willing()) - self._started = True - - -# 创建全局实例 -willing_manager = WillingManager() + async def after_generate_reply_handle(self, message_id): + chat_id = self.ongoing_messages[message_id].chat_id + current_willing = self.chat_reply_willing.get(chat_id, 0) + if current_willing < 1: + self.chat_reply_willing[chat_id] = min(1, current_willing + 0.4) + + \ No newline at end of file diff --git a/src/plugins/willing/mode_custom.py b/src/plugins/willing/mode_custom.py index 0f32c0c75..786c779b4 100644 --- a/src/plugins/willing/mode_custom.py +++ b/src/plugins/willing/mode_custom.py @@ -1,101 +1,7 @@ -import asyncio -from typing import Dict -from ..chat.chat_stream import ChatStream +from .willing_manager import BaseWillingManager -class WillingManager: +class CustomWillingManager(BaseWillingManager): def __init__(self): - self.chat_reply_willing: Dict[str, float] = {} # 存储每个聊天流的回复意愿 - self._decay_task = None - self._started = False + super().__init__() - async def _decay_reply_willing(self): - """定期衰减回复意愿""" - while True: - await asyncio.sleep(1) - for chat_id in self.chat_reply_willing: - self.chat_reply_willing[chat_id] = max(0, self.chat_reply_willing[chat_id] * 0.9) - - def get_willing(self, chat_stream: ChatStream) -> float: - """获取指定聊天流的回复意愿""" - if chat_stream: - return self.chat_reply_willing.get(chat_stream.stream_id, 0) - return 0 - - def set_willing(self, chat_id: str, willing: float): - """设置指定聊天流的回复意愿""" - self.chat_reply_willing[chat_id] = willing - - async def change_reply_willing_received( - self, - chat_stream: ChatStream, - is_mentioned_bot: bool = False, - config=None, - is_emoji: bool = False, - interested_rate: float = 0, - sender_id: str = None, - ) -> float: - """改变指定聊天流的回复意愿并返回回复概率""" - chat_id = chat_stream.stream_id - current_willing = self.chat_reply_willing.get(chat_id, 0) - - interested_rate = interested_rate * config.response_interested_rate_amplifier - - if interested_rate > 0.4: - current_willing += interested_rate - 0.3 - - if is_mentioned_bot and current_willing < 1.0: - current_willing += 1 - elif is_mentioned_bot: - current_willing += 0.05 - - if is_emoji: - current_willing *= 0.2 - - self.chat_reply_willing[chat_id] = min(current_willing, 3.0) - - reply_probability = min(max((current_willing - 0.5), 0.01) * config.response_willing_amplifier * 2, 1) - - # 检查群组权限(如果是群聊) - if chat_stream.group_info and config: - if chat_stream.group_info.group_id not in config.talk_allowed_groups: - current_willing = 0 - reply_probability = 0 - - if chat_stream.group_info.group_id in config.talk_frequency_down_groups: - reply_probability = reply_probability / config.down_frequency_rate - - return reply_probability - - def change_reply_willing_sent(self, chat_stream: ChatStream): - """发送消息后降低聊天流的回复意愿""" - if chat_stream: - chat_id = chat_stream.stream_id - current_willing = self.chat_reply_willing.get(chat_id, 0) - self.chat_reply_willing[chat_id] = max(0, current_willing - 1.8) - - def change_reply_willing_not_sent(self, chat_stream: ChatStream): - """未发送消息后降低聊天流的回复意愿""" - if chat_stream: - chat_id = chat_stream.stream_id - current_willing = self.chat_reply_willing.get(chat_id, 0) - self.chat_reply_willing[chat_id] = max(0, current_willing - 0) - - def change_reply_willing_after_sent(self, chat_stream: ChatStream): - """发送消息后提高聊天流的回复意愿""" - if chat_stream: - chat_id = chat_stream.stream_id - current_willing = self.chat_reply_willing.get(chat_id, 0) - if current_willing < 1: - self.chat_reply_willing[chat_id] = min(1, current_willing + 0.4) - - async def ensure_started(self): - """确保衰减任务已启动""" - if not self._started: - if self._decay_task is None: - self._decay_task = asyncio.create_task(self._decay_reply_willing()) - self._started = True - - -# 创建全局实例 -willing_manager = WillingManager() From 99f5308b693d495d65c27b17b346dedc6dd6ad2f Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 16:01:02 +0800 Subject: [PATCH 03/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E7=82=B9?= =?UTF-8?q?=E7=BB=8F=E5=85=B8=E6=A8=A1=E5=BC=8Fbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/logger.py | 2 +- src/plugins/willing/mode_classical.py | 21 ++++++++++++++++++--- src/plugins/willing/willing_manager.py | 2 ++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/common/logger.py b/src/common/logger.py index 9e118622d..6abbafdc9 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -284,7 +284,7 @@ WILLING_STYLE_CONFIG = { }, "simple": { "console_format": ( - "{time:MM-DD HH:mm} | 意愿 | {message}" + "{time:MM-DD HH:mm} | 意愿 | {message}" ), # noqa: E501 "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 意愿 | {message}"), }, diff --git a/src/plugins/willing/mode_classical.py b/src/plugins/willing/mode_classical.py index 05a6bc7fd..d6086474f 100644 --- a/src/plugins/willing/mode_classical.py +++ b/src/plugins/willing/mode_classical.py @@ -1,10 +1,12 @@ import asyncio from .willing_manager import BaseWillingManager +import time class ClassicalWillingManager(BaseWillingManager): def __init__(self): super().__init__() + self._decay_task: asyncio.Task = None async def _decay_reply_willing(self): """定期衰减回复意愿""" @@ -12,10 +14,10 @@ class ClassicalWillingManager(BaseWillingManager): await asyncio.sleep(1) for chat_id in self.chat_reply_willing: self.chat_reply_willing[chat_id] = max(0, self.chat_reply_willing[chat_id] * 0.9) - self.logger.info(self.chat_reply_willing[chat_id]) async def async_task_starter(self): - asyncio.create_task(self._decay_reply_willing) + if self._decay_task is None or self._decay_task.done(): + self._decay_task = asyncio.create_task(self._decay_reply_willing()) async def get_reply_probability(self, message_id): willing_info = self.ongoing_messages[message_id] @@ -32,11 +34,12 @@ class ClassicalWillingManager(BaseWillingManager): elif willing_info.is_mentioned_bot: current_willing += 0.05 + is_emoji_not_reply = False if willing_info.is_emoji: if self.global_config.emoji_response_penalty != 0: current_willing *= self.global_config.emoji_response_penalty else: - return 0 + is_emoji_not_reply = True self.chat_reply_willing[chat_id] = min(current_willing, 3.0) @@ -46,6 +49,18 @@ class ClassicalWillingManager(BaseWillingManager): if willing_info.group_info and willing_info.group_info.group_id in self.global_config.talk_frequency_down_groups: reply_probability = reply_probability / self.global_config.down_frequency_rate + if is_emoji_not_reply: + reply_probability = 0 + + # 打印消息信息 + mes_name = willing_info.chat.group_info.group_name if willing_info.chat.group_info else "私聊" + current_time = time.strftime("%H:%M:%S", time.localtime(willing_info.message.message_info.time)) + self.logger.info( + f"[{current_time}][{mes_name}]" + f"{willing_info.chat.user_info.user_nickname}:" + f"{willing_info.message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" + ) + return reply_probability async def before_generate_reply_handle(self, message_id): diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index 696e8be69..133ecbbee 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -71,6 +71,8 @@ class BaseWillingManager(ABC): if not issubclass(manager_class, cls): manager_class = getattr(module, "ClassicalWillingManager") logger.info("未找到当前意愿模式对应文件,使用经典配方~") + else: + logger.info(f"成功载入willing模式:{manager_type}") return manager_class() except (ImportError, AttributeError) as e: logger.error(f"Failed to create willing manager: {str(e)}") From 1ca2b89f82046addd8bd4962d46627c3af44a752 Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 16:50:51 +0800 Subject: [PATCH 04/24] =?UTF-8?q?=E9=80=82=E9=85=8D=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/mode_classical.py | 2 +- src/plugins/willing/mode_dynamic.py | 108 ++++++++++---------------- 2 files changed, 44 insertions(+), 66 deletions(-) diff --git a/src/plugins/willing/mode_classical.py b/src/plugins/willing/mode_classical.py index d6086474f..2a4c39aa3 100644 --- a/src/plugins/willing/mode_classical.py +++ b/src/plugins/willing/mode_classical.py @@ -16,7 +16,7 @@ class ClassicalWillingManager(BaseWillingManager): self.chat_reply_willing[chat_id] = max(0, self.chat_reply_willing[chat_id] * 0.9) async def async_task_starter(self): - if self._decay_task is None or self._decay_task.done(): + if self._decay_task is None: self._decay_task = asyncio.create_task(self._decay_reply_willing()) async def get_reply_probability(self, message_id): diff --git a/src/plugins/willing/mode_dynamic.py b/src/plugins/willing/mode_dynamic.py index 3d2ca6e77..a17c276b4 100644 --- a/src/plugins/willing/mode_dynamic.py +++ b/src/plugins/willing/mode_dynamic.py @@ -2,15 +2,12 @@ import asyncio import random import time from typing import Dict -from src.common.logger import get_module_logger -from ..config.config import global_config -from ..chat.chat_stream import ChatStream - -logger = get_module_logger("mode_dynamic") +from .willing_manager import BaseWillingManager -class WillingManager: +class DynamicWillingManager(BaseWillingManager): def __init__(self): + super().__init__() self.chat_reply_willing: Dict[str, float] = {} # 存储每个聊天流的回复意愿 self.chat_high_willing_mode: Dict[str, bool] = {} # 存储每个聊天流是否处于高回复意愿期 self.chat_msg_count: Dict[str, int] = {} # 存储每个聊天流接收到的消息数量 @@ -22,7 +19,13 @@ class WillingManager: self.chat_conversation_context: Dict[str, bool] = {} # 标记是否处于对话上下文中 self._decay_task = None self._mode_switch_task = None - self._started = False + + + async def async_task_starter(self): + if self._decay_task is None: + self._decay_task = asyncio.create_task(self._decay_reply_willing()) + if self._mode_switch_task is None: + self._mode_switch_task = asyncio.create_task(self._mode_switch_check()) async def _decay_reply_willing(self): """定期衰减回复意愿""" @@ -75,28 +78,17 @@ class WillingManager: self.chat_high_willing_mode[chat_id] = False self.chat_reply_willing[chat_id] = 0.1 # 设置为最低回复意愿 self.chat_low_willing_duration[chat_id] = random.randint(600, 1200) # 10-20分钟 - logger.debug(f"聊天流 {chat_id} 切换到低回复意愿期,持续 {self.chat_low_willing_duration[chat_id]} 秒") + self.logger.debug(f"聊天流 {chat_id} 切换到低回复意愿期,持续 {self.chat_low_willing_duration[chat_id]} 秒") else: # 从低回复期切换到高回复期 self.chat_high_willing_mode[chat_id] = True self.chat_reply_willing[chat_id] = 1.0 # 设置为较高回复意愿 self.chat_high_willing_duration[chat_id] = random.randint(180, 240) # 3-4分钟 - logger.debug(f"聊天流 {chat_id} 切换到高回复意愿期,持续 {self.chat_high_willing_duration[chat_id]} 秒") + self.logger.debug(f"聊天流 {chat_id} 切换到高回复意愿期,持续 {self.chat_high_willing_duration[chat_id]} 秒") self.chat_last_mode_change[chat_id] = time.time() self.chat_msg_count[chat_id] = 0 # 重置消息计数 - def get_willing(self, chat_stream: ChatStream) -> float: - """获取指定聊天流的回复意愿""" - stream = chat_stream - if stream: - return self.chat_reply_willing.get(stream.stream_id, 0) - return 0 - - def set_willing(self, chat_id: str, willing: float): - """设置指定聊天流的回复意愿""" - self.chat_reply_willing[chat_id] = willing - def _ensure_chat_initialized(self, chat_id: str): """确保聊天流的所有数据已初始化""" if chat_id not in self.chat_reply_willing: @@ -113,20 +105,13 @@ class WillingManager: if chat_id not in self.chat_conversation_context: self.chat_conversation_context[chat_id] = False - async def change_reply_willing_received( - self, - chat_stream: ChatStream, - topic: str = None, - is_mentioned_bot: bool = False, - config=None, - is_emoji: bool = False, - interested_rate: float = 0, - sender_id: str = None, - ) -> float: + async def get_reply_probability(self, message_id): """改变指定聊天流的回复意愿并返回回复概率""" # 获取或创建聊天流 - stream = chat_stream + willing_info = self.ongoing_messages[message_id] + stream = willing_info.chat chat_id = stream.stream_id + sender_id = str(willing_info.message.message_info.user_info.user_id) current_time = time.time() self._ensure_chat_initialized(chat_id) @@ -147,23 +132,23 @@ class WillingManager: if sender_id and sender_id == last_sender and current_time - last_reply_time < 120 and msg_count <= 5: in_conversation_context = True self.chat_conversation_context[chat_id] = True - logger.debug("检测到追问 (同一用户), 提高回复意愿") + self.logger.debug("检测到追问 (同一用户), 提高回复意愿") current_willing += 0.3 # 特殊情况处理 - if is_mentioned_bot: + if willing_info.is_mentioned_bot: current_willing += 0.5 in_conversation_context = True self.chat_conversation_context[chat_id] = True - logger.debug(f"被提及, 当前意愿: {current_willing}") + self.logger.debug(f"被提及, 当前意愿: {current_willing}") - if is_emoji: - current_willing = global_config.emoji_response_penalty * 0.1 - logger.debug(f"表情包, 当前意愿: {current_willing}") + if willing_info.is_emoji: + current_willing = self.global_config.emoji_response_penalty * 0.1 + self.logger.debug(f"表情包, 当前意愿: {current_willing}") # 根据话题兴趣度适当调整 - if interested_rate > 0.5: - current_willing += (interested_rate - 0.5) * 0.5 * global_config.response_interested_rate_amplifier + if willing_info.interested_rate > 0.5: + current_willing += (willing_info.interested_rate - 0.5) * 0.5 * self.global_config.response_interested_rate_amplifier # 根据当前模式计算回复概率 base_probability = 0.0 @@ -171,7 +156,7 @@ class WillingManager: if in_conversation_context: # 在对话上下文中,降低基础回复概率 base_probability = 0.5 if is_high_mode else 0.25 - logger.debug(f"处于对话上下文中,基础回复概率: {base_probability}") + self.logger.debug(f"处于对话上下文中,基础回复概率: {base_probability}") elif is_high_mode: # 高回复周期:4-8句话有50%的概率会回复一次 base_probability = 0.50 if 4 <= msg_count <= 8 else 0.2 @@ -180,12 +165,12 @@ class WillingManager: base_probability = 0.30 if msg_count >= 15 else 0.03 * min(msg_count, 10) # 考虑回复意愿的影响 - reply_probability = base_probability * current_willing * global_config.response_willing_amplifier + reply_probability = base_probability * current_willing * self.global_config.response_willing_amplifier # 检查群组权限(如果是群聊) - if chat_stream.group_info and config: - if chat_stream.group_info.group_id in config.talk_frequency_down_groups: - reply_probability = reply_probability / global_config.down_frequency_rate + if willing_info.group_info: + if willing_info.group_info.group_id in self.global_config.talk_frequency_down_groups: + reply_probability = reply_probability / self.global_config.down_frequency_rate # 限制最大回复概率 reply_probability = min(reply_probability, 0.75) # 设置最大回复概率为75% @@ -197,11 +182,21 @@ class WillingManager: self.chat_last_sender_id[chat_id] = sender_id self.chat_reply_willing[chat_id] = min(current_willing, 3.0) + + # 打印消息信息 + mes_name = willing_info.chat.group_info.group_name if willing_info.chat.group_info else "私聊" + current_time = time.strftime("%H:%M:%S", time.localtime(willing_info.message.message_info.time)) + self.logger.info( + f"[{current_time}][{mes_name}]" + f"{willing_info.chat.user_info.user_nickname}:" + f"{willing_info.message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" + ) + return reply_probability - def change_reply_willing_sent(self, chat_stream: ChatStream): + async def before_generate_reply_handle(self, message_id): """开始思考后降低聊天流的回复意愿""" - stream = chat_stream + stream = self.ongoing_messages[message_id].chat if stream: chat_id = stream.stream_id self._ensure_chat_initialized(chat_id) @@ -219,9 +214,9 @@ class WillingManager: # 重置消息计数 self.chat_msg_count[chat_id] = 0 - def change_reply_willing_not_sent(self, chat_stream: ChatStream): + async def not_reply_handle(self, message_id): """决定不回复后提高聊天流的回复意愿""" - stream = chat_stream + stream = self.ongoing_messages[message_id].chat if stream: chat_id = stream.stream_id self._ensure_chat_initialized(chat_id) @@ -240,20 +235,3 @@ class WillingManager: self.chat_reply_willing[chat_id] = min(2.0, current_willing + willing_increase) - def change_reply_willing_after_sent(self, chat_stream: ChatStream): - """发送消息后提高聊天流的回复意愿""" - # 由于已经在sent中处理,这个方法保留但不再需要额外调整 - pass - - async def ensure_started(self): - """确保所有任务已启动""" - if not self._started: - if self._decay_task is None: - self._decay_task = asyncio.create_task(self._decay_reply_willing()) - if self._mode_switch_task is None: - self._mode_switch_task = asyncio.create_task(self._mode_switch_check()) - self._started = True - - -# 创建全局实例 -willing_manager = WillingManager() From 9e8ef948ccd62bec03e8b6663cad8ec675efc1af Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 17:19:10 +0800 Subject: [PATCH 05/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat_module/reasoning_chat/reasoning_chat.py | 1 + .../chat_module/think_flow_chat/think_flow_chat.py | 1 + src/plugins/willing/willing_manager.py | 11 ++++++----- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index d6f106d27..6743b5aec 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -187,6 +187,7 @@ class ReasoningChat: # 处理缓冲器结果 if not buffer_result: await willing_manager.bombing_buffer_message_handle(message.message_info.message_id) + willing_manager.delete(message.message_info.message_id) if message.message_segment.type == "text": logger.info(f"触发缓冲,已炸飞消息:{message.processed_plain_text}") elif message.message_segment.type == "image": diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 7351bffed..13a150d20 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -212,6 +212,7 @@ class ThinkFlowChat: # 处理缓冲器结果 if not buffer_result: await willing_manager.bombing_buffer_message_handle(message.message_info.message_id) + willing_manager.delete(message.message_info.message_id) if message.message_segment.type == "text": logger.info(f"触发缓冲,已炸飞消息:{message.processed_plain_text}") elif message.message_segment.type == "image": diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index 133ecbbee..05a29b7cd 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -69,14 +69,15 @@ class BaseWillingManager(ABC): module = importlib.import_module(f".mode_{manager_type}", __package__) manager_class = getattr(module, f"{manager_type.capitalize()}WillingManager") if not issubclass(manager_class, cls): - manager_class = getattr(module, "ClassicalWillingManager") - logger.info("未找到当前意愿模式对应文件,使用经典配方~") + raise else: logger.info(f"成功载入willing模式:{manager_type}") return manager_class() - except (ImportError, AttributeError) as e: - logger.error(f"Failed to create willing manager: {str(e)}") - raise ValueError(f"Invalid willing manager type: {manager_type}") from e + except: + module = importlib.import_module(f".mode_classical", __package__) + manager_class = getattr(module, "ClassicalWillingManager") + logger.info("未找到当前意愿模式对应文件,使用经典配方~") + return manager_class() def __init__(self): self.chat_reply_willing: Dict[str, float] = {} # 存储每个聊天流的回复意愿(chat_id) From 838fb772331880819c741685be239c66711576d7 Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 17:23:32 +0800 Subject: [PATCH 06/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=B8=8B?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E7=9A=84=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/bot_config_template.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 70cf0e0b7..76ef81d01 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -98,9 +98,7 @@ ban_msgs_regex = [ ] [willing] -willing_mode = "classical" # 回复意愿模式 经典模式 -# willing_mode = "dynamic" # 动态模式(不兼容,需要维护) -# willing_mode = "custom" # 自定义模式(可自行调整 +willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,动态模式:dynamic,自定义模式:custom response_willing_amplifier = 1 # 麦麦回复意愿放大系数,一般为1 response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数 down_frequency_rate = 3 # 降低回复频率的群组回复意愿降低系数 除法 From 2120addc3cf0897add8f315f43829c74a5ea927b Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 17:34:29 +0800 Subject: [PATCH 07/24] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E6=94=B9=E5=96=84?= =?UTF-8?q?=E7=AB=8B=E5=9C=BA=E6=83=85=E6=84=9F=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat_module/think_flow_chat/think_flow_generator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py index 4087b0b89..5f094d1cd 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py @@ -131,6 +131,7 @@ class ResponseGenerator: - "中立":不表达明确立场或无关回应 2. 从"开心,愤怒,悲伤,惊讶,平静,害羞,恐惧,厌恶,困惑"中选出最匹配的1个情感标签 3. 按照"立场-情绪"的格式直接输出结果,例如:"反对-愤怒" + 4. 考虑回复者的人格设定为{global_config.personality_core} 对话示例: 被回复:「A就是笨」 From cbf40df38760363f97603464cf53de663cd12de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E6=BA=AA=E7=95=94?= <130263765+na10xi27da@users.noreply.github.com> Date: Wed, 9 Apr 2025 17:54:44 +0800 Subject: [PATCH 08/24] =?UTF-8?q?=E6=BC=8F=E4=BA=86=E4=B8=80=E5=A4=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat_module/reasoning_chat/reasoning_generator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_generator.py b/src/plugins/chat_module/reasoning_chat/reasoning_generator.py index eca5d0956..580cd097d 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_generator.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_generator.py @@ -143,6 +143,7 @@ class ResponseGenerator: - "中立":不表达明确立场或无关回应 2. 从"开心,愤怒,悲伤,惊讶,平静,害羞,恐惧,厌恶,困惑"中选出最匹配的1个情感标签 3. 按照"立场-情绪"的格式直接输出结果,例如:"反对-愤怒" + 4. 考虑回复者的人格设定为{global_config.personality_core} 对话示例: 被回复:「A就是笨」 From 503d608101ab37439035c60eb44e9ccc3d1e560c Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 18:04:41 +0800 Subject: [PATCH 09/24] ruff --- src/plugins/willing/willing_manager.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index 05a29b7cd..a3ee0842b 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -69,13 +69,15 @@ class BaseWillingManager(ABC): module = importlib.import_module(f".mode_{manager_type}", __package__) manager_class = getattr(module, f"{manager_type.capitalize()}WillingManager") if not issubclass(manager_class, cls): - raise + raise TypeError( + f"Manager class {manager_class.__name__} is not a subclass of {cls.__name__}" + ) else: logger.info(f"成功载入willing模式:{manager_type}") return manager_class() - except: - module = importlib.import_module(f".mode_classical", __package__) - manager_class = getattr(module, "ClassicalWillingManager") + except (ImportError, AttributeError, TypeError) as e: + module = importlib.import_module(".mode_classical", __package__) + manager_class = module.ClassicalWillingManager logger.info("未找到当前意愿模式对应文件,使用经典配方~") return manager_class() @@ -105,18 +107,22 @@ class BaseWillingManager(ABC): if not del_message: logger.debug(f"删除异常,当前消息{message_id}不存在") + @abstractmethod async def async_task_starter(self) -> None: """抽象方法:异步任务启动器""" pass + @abstractmethod async def before_generate_reply_handle(self, message_id: str): """抽象方法:回复前处理""" pass + @abstractmethod async def after_generate_reply_handle(self, message_id: str): """抽象方法:回复后处理""" pass + @abstractmethod async def not_reply_handle(self, message_id: str): """抽象方法:不回复处理""" pass @@ -126,6 +132,7 @@ class BaseWillingManager(ABC): """抽象方法:获取回复概率""" raise NotImplementedError + @abstractmethod async def bombing_buffer_message_handle(self, message_id: str): """抽象方法:炸飞消息处理""" pass From 2272ce16c886736f19be721680e804723e52f9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E6=BA=AA=E7=95=94?= <130263765+na10xi27da@users.noreply.github.com> Date: Wed, 9 Apr 2025 18:10:30 +0800 Subject: [PATCH 10/24] ruff --- src/plugins/willing/willing_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index a3ee0842b..f2a61ffd6 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -78,6 +78,7 @@ class BaseWillingManager(ABC): except (ImportError, AttributeError, TypeError) as e: module = importlib.import_module(".mode_classical", __package__) manager_class = module.ClassicalWillingManager + logger.debug(f"加载willing模式{manager_type}失败,原因: {str(e)}。") logger.info("未找到当前意愿模式对应文件,使用经典配方~") return manager_class() From 9d590446ca6eec367ae2941f2cf9fa372a7ac56c Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 18:17:38 +0800 Subject: [PATCH 11/24] ruffffff --- src/plugins/willing/mode_classical.py | 6 ++++++ src/plugins/willing/mode_dynamic.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/plugins/willing/mode_classical.py b/src/plugins/willing/mode_classical.py index 2a4c39aa3..df9952b4e 100644 --- a/src/plugins/willing/mode_classical.py +++ b/src/plugins/willing/mode_classical.py @@ -73,5 +73,11 @@ class ClassicalWillingManager(BaseWillingManager): current_willing = self.chat_reply_willing.get(chat_id, 0) if current_willing < 1: self.chat_reply_willing[chat_id] = min(1, current_willing + 0.4) + + async def bombing_buffer_message_handle(self, message_id): + return await super().bombing_buffer_message_handle(message_id) + + async def not_reply_handle(self, message_id): + return await super().not_reply_handle(message_id) \ No newline at end of file diff --git a/src/plugins/willing/mode_dynamic.py b/src/plugins/willing/mode_dynamic.py index a17c276b4..0727307ce 100644 --- a/src/plugins/willing/mode_dynamic.py +++ b/src/plugins/willing/mode_dynamic.py @@ -235,3 +235,9 @@ class DynamicWillingManager(BaseWillingManager): self.chat_reply_willing[chat_id] = min(2.0, current_willing + willing_increase) + async def bombing_buffer_message_handle(self, message_id): + return await super().bombing_buffer_message_handle(message_id) + + async def after_generate_reply_handle(self, message_id): + return await super().after_generate_reply_handle(message_id) + From f8475d9a806eea5f36d4d097b5bd626cf53d149a Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Wed, 9 Apr 2025 18:22:41 +0800 Subject: [PATCH 12/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E7=82=B9?= =?UTF-8?q?=E7=82=B9=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/willing_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index a3ee0842b..f1cc24e4f 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -78,7 +78,7 @@ class BaseWillingManager(ABC): except (ImportError, AttributeError, TypeError) as e: module = importlib.import_module(".mode_classical", __package__) manager_class = module.ClassicalWillingManager - logger.info("未找到当前意愿模式对应文件,使用经典配方~") + logger.info(f"载入当前意愿模式{manager_type}失败,使用经典配方~~~~") return manager_class() def __init__(self): From b4ead93ed187ff7c6e87018083577ee1e99aee60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E6=BA=AA=E7=95=94?= <130263765+na10xi27da@users.noreply.github.com> Date: Thu, 10 Apr 2025 22:17:42 +0800 Subject: [PATCH 13/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E7=82=B9?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/willing_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index 31a0b2663..1bb2674ee 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -12,12 +12,14 @@ import asyncio """ 基类方法概览: +以下6个方法是你必须在子类重写的(哪怕什么都不干): async_task_starter 在程序启动时执行,在其中用asyncio.create_task启动你想要执行的异步任务 before_generate_reply_handle 确定要回复后,在生成回复前的处理 after_generate_reply_handle 确定要回复后,在生成回复后的处理 not_reply_handle 确定不回复后的处理 -get_reply_probability 获取回复概率(必须实现) +get_reply_probability 获取回复概率 bombing_buffer_message_handle 缓冲器炸飞消息后的处理 +以下2个方法根据你的实现可以做调整: get_willing 获取某聊天流意愿 set_willing 设置某聊天流意愿 规范说明: From 02b747015d60e271c0fab6e05128676a4b8463aa Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Fri, 11 Apr 2025 00:30:54 +0800 Subject: [PATCH 14/24] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/mode_classical.py | 6 ++++++ src/plugins/willing/mode_dynamic.py | 5 +++++ src/plugins/willing/willing_manager.py | 10 ++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/plugins/willing/mode_classical.py b/src/plugins/willing/mode_classical.py index df9952b4e..4bc3fb6e9 100644 --- a/src/plugins/willing/mode_classical.py +++ b/src/plugins/willing/mode_classical.py @@ -79,5 +79,11 @@ class ClassicalWillingManager(BaseWillingManager): async def not_reply_handle(self, message_id): return await super().not_reply_handle(message_id) + + async def get_variable_parameters(self): + return await super().get_variable_parameters() + + async def set_variable_parameters(self, parameters): + return await super().set_variable_parameters(parameters) \ No newline at end of file diff --git a/src/plugins/willing/mode_dynamic.py b/src/plugins/willing/mode_dynamic.py index 0727307ce..b9c324fca 100644 --- a/src/plugins/willing/mode_dynamic.py +++ b/src/plugins/willing/mode_dynamic.py @@ -241,3 +241,8 @@ class DynamicWillingManager(BaseWillingManager): async def after_generate_reply_handle(self, message_id): return await super().after_generate_reply_handle(message_id) + async def get_variable_parameters(self): + return await super().get_variable_parameters() + + async def set_variable_parameters(self, parameters): + return await super().set_variable_parameters(parameters) \ No newline at end of file diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index 31a0b2663..11442ae77 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -148,6 +148,16 @@ class BaseWillingManager(ABC): async with self.lock: self.chat_reply_willing[chat_id] = willing + @abstractmethod + async def get_variable_parameters(self) -> Dict[str, str]: + """抽象方法:获取可变参数""" + pass + + @abstractmethod + async def set_variable_parameters(self, parameters: Dict[str, any]): + """抽象方法:设置可变参数""" + pass + def init_willing_manager() -> BaseWillingManager: """ From 4d358b60d215e13ebb39bb3e4538578b10105f3d Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Fri, 11 Apr 2025 00:36:21 +0800 Subject: [PATCH 15/24] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/willing_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/willing/willing_manager.py b/src/plugins/willing/willing_manager.py index 647ced3ff..07e02a29b 100644 --- a/src/plugins/willing/willing_manager.py +++ b/src/plugins/willing/willing_manager.py @@ -12,13 +12,15 @@ import asyncio """ 基类方法概览: -以下6个方法是你必须在子类重写的(哪怕什么都不干): +以下8个方法是你必须在子类重写的(哪怕什么都不干): async_task_starter 在程序启动时执行,在其中用asyncio.create_task启动你想要执行的异步任务 before_generate_reply_handle 确定要回复后,在生成回复前的处理 after_generate_reply_handle 确定要回复后,在生成回复后的处理 not_reply_handle 确定不回复后的处理 get_reply_probability 获取回复概率 bombing_buffer_message_handle 缓冲器炸飞消息后的处理 +get_variable_parameters 获取可变参数组,返回一个字典,key为参数名称,value为参数描述(此方法是为拆分全局设置准备) +set_variable_parameters 设置可变参数组,你需要传入一个字典,key为参数名称,value为参数值(此方法是为拆分全局设置准备) 以下2个方法根据你的实现可以做调整: get_willing 获取某聊天流意愿 set_willing 设置某聊天流意愿 From 2b21ce80cd1a2bb54be97b62a28f29c26bc96455 Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Fri, 11 Apr 2025 03:32:31 +0800 Subject: [PATCH 16/24] =?UTF-8?q?=E6=84=9F=E8=A7=89=E6=AD=A4=E5=A4=84?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E6=94=BE=E5=AE=BD=E5=88=A4=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/person_info/relationship_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/person_info/relationship_manager.py b/src/plugins/person_info/relationship_manager.py index f64e2851c..726bb1dbb 100644 --- a/src/plugins/person_info/relationship_manager.py +++ b/src/plugins/person_info/relationship_manager.py @@ -43,12 +43,12 @@ class RelationshipManager: "厌恶", ] - if label in positive_list and stance != "反对": + if label in positive_list: if 7 > self.positive_feedback_value >= 0: self.positive_feedback_value += 1 elif self.positive_feedback_value < 0: self.positive_feedback_value = 0 - elif label in negative_list and stance != "支持": + elif label in negative_list: if -7 < self.positive_feedback_value <= 0: self.positive_feedback_value -= 1 elif self.positive_feedback_value > 0: From df0bc69c84001e8827851690b0244e32c60ae94c Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Fri, 11 Apr 2025 06:14:19 +0800 Subject: [PATCH 17/24] =?UTF-8?q?=E6=96=B0=E5=A2=9Ewilling=E6=A8=A1?= =?UTF-8?q?=E5=BC=8Fmxp=E6=A8=A1=E5=BC=8F=EF=BC=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/mode_mxp.py | 231 ++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 src/plugins/willing/mode_mxp.py diff --git a/src/plugins/willing/mode_mxp.py b/src/plugins/willing/mode_mxp.py new file mode 100644 index 000000000..12ed8774b --- /dev/null +++ b/src/plugins/willing/mode_mxp.py @@ -0,0 +1,231 @@ +""" +Mxp 模式(MxpWillingManager)是一个基于意愿值动态调整的回复管理器。 +其主要特点包括: +1. 通过消息频率、提及、兴趣度等多种因素动态调整回复意愿值。 +2. 支持意愿值的衰减机制,确保意愿值不会无限增长。 +3. 提供对单聊、群聊等不同场景的特殊处理。 +4. 结合关系等级、表情包惩罚等多种参数,计算最终的回复概率。 +5. 支持异步任务,用于定期更新意愿值和基础意愿值。 + +该模式适用于需要精细化控制回复行为的场景,能够根据用户行为和聊天环境动态调整回复策略。 +""" +from .willing_manager import BaseWillingManager +from typing import Dict +import asyncio +import time +import math + +class MxpWillingManager(BaseWillingManager): + """Mxp意愿管理器""" + def __init__(self): + super().__init__() + self.chat_person_reply_willing: Dict[str, Dict[str, float]] = {} # chat_id: {person_id: 意愿值} + self.chat_new_message_time: Dict[str, list[float]] = {} # 聊天流ID: 消息时间 + self.last_response_person: list = ["", "", 0] # 上次回复的用户信息 + + # 可变参数 + self.intention_decay_rate = 0.93 # 意愿衰减率 + self.message_expiration_time = 120 # 消息过期时间(秒) + self.number_of_message_storage = 10 # 消息存储数量 + self.basic_maximum_willing = 0.5 # 基础最大意愿值 + self.mention_willing_gain = 0.6 # 提及意愿增益 + self.interest_willing_gain = 0.3 # 兴趣意愿增益 + self.emoji_response_penalty = self.global_config.emoji_response_penalty # 表情包回复惩罚 + self.down_frequency_rate = self.global_config.down_frequency_rate # 降低回复频率的群组惩罚系数 + self.single_chat_gain = 0.15 # 单聊增益 + + async def async_task_starter(self) -> None: + """异步任务启动器""" + asyncio.create_task(self._return_to_basic_willing()) + asyncio.create_task(self._chat_new_message_to_change_basic_willing()) + + async def before_generate_reply_handle(self, message_id: str): + """回复前处理""" + pass + + async def after_generate_reply_handle(self, message_id: str): + """回复后处理""" + async with self.lock: + w_info = self.ongoing_messages[message_id] + rel_value = await w_info.person_info_manager.get_value(w_info.person_id, "relationship_value") + rel_level = self._get_relationship_level_num(rel_value) + self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] += rel_level * 0.05 + + if (w_info.chat_id == self.last_response_person[0] + and w_info.person_id == self.last_response_person[1] + and self.last_response_person[2] < 3): + self.last_response_person[2] += 1 + else: + self.last_response_person = [w_info.chat_id, w_info.person_id, 0] + + async def not_reply_handle(self, message_id: str): + """不回复处理""" + async with self.lock: + w_info = self.ongoing_messages[message_id] + if w_info.is_mentioned_bot: + self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] += 0.2 + if self.last_response_person[0] == w_info.chat_id and self.last_response_person[1] == w_info.person_id: + self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] += self.single_chat_gain *self.last_response_person[2] + + async def get_reply_probability(self, message_id: str): + """获取回复概率""" + async with self.lock: + w_info = self.ongoing_messages[message_id] + current_willing = self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] + + if w_info.is_mentioned_bot: + current_willing += self.mention_willing_gain / (int(current_willing) + 1) + + if w_info.interested_rate > 0: + current_willing += math.atan(w_info.interested_rate / 2) / math.pi * 2 * self.interest_willing_gain + + self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] = current_willing + + rel_value = await w_info.person_info_manager.get_value(w_info.person_id, "relationship_value") + rel_level = self._get_relationship_level_num(rel_value) + current_willing += rel_level * 0.1 + + if self.last_response_person[0] == w_info.chat_id and self.last_response_person[1] == w_info.person_id: + current_willing += self.single_chat_gain * self.last_response_person[2] + + chat_ongoing_messages = [msg for msg in self.ongoing_messages.values() if msg.chat_id == w_info.chat_id] + chat_person_ogoing_messages = [msg for msg in chat_ongoing_messages if msg.person_id == w_info.person_id] + if len(chat_person_ogoing_messages) >= 2: + current_willing = 0 + elif len(chat_ongoing_messages) == 2: + current_willing -= 0.5 + elif len(chat_ongoing_messages) == 3: + current_willing -= 1.5 + elif len(chat_ongoing_messages) >= 4: + current_willing = 0 + + probability = self._willing_to_probability(current_willing) + + if w_info.is_emoji: + probability *= self.emoji_response_penalty + + if w_info.group_info and w_info.group_info.group_id in self.global_config.talk_frequency_down_groups: + probability /= self.down_frequency_rate + + # 打印消息信息 + mes_name = w_info.chat.group_info.group_name if w_info.chat.group_info else "私聊" + current_time = time.strftime("%H:%M:%S", time.localtime(w_info.message.message_info.time)) + self.logger.info( + f"[{current_time}][{mes_name}]" + f"{w_info.chat.user_info.user_nickname}:" + f"{w_info.message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{probability * 100:.1f}%]" + ) + + return probability + + async def bombing_buffer_message_handle(self, message_id: str): + """炸飞消息处理""" + async with self.lock: + w_info = self.ongoing_messages[message_id] + self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] += 0.1 + + async def _return_to_basic_willing(self): + """使每个人的意愿恢复到chat基础意愿""" + while True: + await asyncio.sleep(3) + async with self.lock: + for chat_id, person_willing in self.chat_person_reply_willing.items(): + for person_id, willing in person_willing.items(): + if chat_id not in self.chat_reply_willing: + self.logger.debug(f"聊天流{chat_id}不存在,错误") + continue + basic_willing = self.chat_reply_willing[chat_id] + person_willing[person_id] = basic_willing + (willing - basic_willing) * self.intention_decay_rate + + def setup(self, message, chat, is_mentioned_bot, interested_rate): + super().setup(message, chat, is_mentioned_bot, interested_rate) + + self.chat_reply_willing[chat.stream_id] = self.chat_reply_willing.get(chat.stream_id, self.basic_maximum_willing) + self.chat_person_reply_willing[chat.stream_id] = self.chat_person_reply_willing.get(chat.stream_id, {}) + self.chat_person_reply_willing[chat.stream_id][self.ongoing_messages[message.message_info.message_id].person_id] = \ + self.chat_person_reply_willing[chat.stream_id].get(self.ongoing_messages[message.message_info.message_id].person_id, + self.chat_reply_willing[chat.stream_id]) + + if chat.stream_id not in self.chat_new_message_time: + self.chat_new_message_time[chat.stream_id] = [] + self.chat_new_message_time[chat.stream_id].append(time.time()) + self.logger.info(self.chat_new_message_time[chat.stream_id]) + if len(self.chat_new_message_time[chat.stream_id]) > self.number_of_message_storage: + self.chat_new_message_time[chat.stream_id].pop(0) + + def _willing_to_probability(self, willing: float) -> float: + """意愿值转化为概率""" + willing = max(0, willing) + if willing < 2: + probability = math.atan(willing * 2) / math.pi * 2 + else: + probability = math.atan(willing * 4) / math.pi * 2 + return probability + + async def _chat_new_message_to_change_basic_willing(self): + """聊天流新消息改变基础意愿""" + while True: + update_time = 20 + await asyncio.sleep(update_time) + async with self.lock: + for chat_id, message_times in self.chat_new_message_time.items(): + + # 清理过期消息 + current_time = time.time() + message_times = [msg_time for msg_time in message_times if current_time - msg_time < self.message_expiration_time] + self.chat_new_message_time[chat_id] = message_times + + if len(message_times) < self.number_of_message_storage: + self.chat_reply_willing[chat_id] = self.basic_maximum_willing + update_time = 20 + elif len(message_times) == self.number_of_message_storage: + time_interval = current_time - message_times[0] + basic_willing = self.basic_maximum_willing * math.sqrt(time_interval / self.message_expiration_time) + self.chat_reply_willing[chat_id] = basic_willing + update_time = 17 * math.sqrt(time_interval / self.message_expiration_time) + 3 + else: + self.logger.debug(f"聊天流{chat_id}消息时间数量异常,数量:{len(message_times)}") + self.chat_reply_willing[chat_id] = 0 + + async def get_variable_parameters(self) -> Dict[str, str]: + """获取可变参数""" + return { + "intention_decay_rate": "意愿衰减率", + "message_expiration_time": "消息过期时间(秒)", + "number_of_message_storage": "消息存储数量", + "basic_maximum_willing": "基础最大意愿值", + "mention_willing_gain": "提及意愿增益", + "interest_willing_gain": "兴趣意愿增益", + "emoji_response_penalty": "表情包回复惩罚", + "down_frequency_rate": "降低回复频率的群组惩罚系数", + "single_chat_gain": "单聊增益(不仅是私聊)" + } + + async def set_variable_parameters(self, parameters: Dict[str, any]): + """设置可变参数""" + async with self.lock: + for key, value in parameters.items(): + if hasattr(self, key): + setattr(self, key, value) + self.logger.debug(f"参数 {key} 已更新为 {value}") + else: + self.logger.debug(f"尝试设置未知参数 {key}") + + def _get_relationship_level_num(self, relationship_value) -> int: + """关系等级计算""" + if -1000 <= relationship_value < -227: + level_num = 0 + elif -227 <= relationship_value < -73: + level_num = 1 + elif -73 <= relationship_value < 227: + level_num = 2 + elif 227 <= relationship_value < 587: + level_num = 3 + elif 587 <= relationship_value < 900: + level_num = 4 + elif 900 <= relationship_value <= 1000: + level_num = 5 + else: + level_num = 5 if relationship_value > 1000 else 0 + return level_num - 2 + From d081f9441e75ca081d4adfdc6888fa4076f0522d Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Fri, 11 Apr 2025 11:07:58 +0800 Subject: [PATCH 18/24] =?UTF-8?q?=E4=BF=AE=E6=94=B9logger=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat_module/reasoning_chat/reasoning_chat.py | 9 +++++++++ .../chat_module/think_flow_chat/think_flow_chat.py | 9 +++++++++ src/plugins/willing/mode_classical.py | 9 --------- src/plugins/willing/mode_dynamic.py | 9 --------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index 6743b5aec..9286422b8 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -204,6 +204,15 @@ class ReasoningChat: if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] + # 打印消息信息 + mes_name = chat.group_info.group_name if chat.group_info else "私聊" + current_time = time.strftime("%H:%M:%S", time.localtime(message.message_info.time)) + logger.info( + f"[{current_time}][{mes_name}]" + f"{chat.user_info.user_nickname}:" + f"{message.processed_plain_text}[回复意愿:{await willing_manager.get_willing(chat.stream_id):.2f}][概率:{reply_probability * 100:.1f}%]" + ) + do_reply = False if random() < reply_probability: do_reply = True diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 13a150d20..a0ba5d7af 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -236,6 +236,15 @@ class ThinkFlowChat: if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] + # 打印消息信息 + mes_name = chat.group_info.group_name if chat.group_info else "私聊" + current_time = time.strftime("%H:%M:%S", time.localtime(message.message_info.time)) + logger.info( + f"[{current_time}][{mes_name}]" + f"{chat.user_info.user_nickname}:" + f"{message.processed_plain_text}[回复意愿:{await willing_manager.get_willing(chat.stream_id):.2f}][概率:{reply_probability * 100:.1f}%]" + ) + do_reply = False if random() < reply_probability: try: diff --git a/src/plugins/willing/mode_classical.py b/src/plugins/willing/mode_classical.py index 4bc3fb6e9..56c46275f 100644 --- a/src/plugins/willing/mode_classical.py +++ b/src/plugins/willing/mode_classical.py @@ -52,15 +52,6 @@ class ClassicalWillingManager(BaseWillingManager): if is_emoji_not_reply: reply_probability = 0 - # 打印消息信息 - mes_name = willing_info.chat.group_info.group_name if willing_info.chat.group_info else "私聊" - current_time = time.strftime("%H:%M:%S", time.localtime(willing_info.message.message_info.time)) - self.logger.info( - f"[{current_time}][{mes_name}]" - f"{willing_info.chat.user_info.user_nickname}:" - f"{willing_info.message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" - ) - return reply_probability async def before_generate_reply_handle(self, message_id): diff --git a/src/plugins/willing/mode_dynamic.py b/src/plugins/willing/mode_dynamic.py index b9c324fca..523c05244 100644 --- a/src/plugins/willing/mode_dynamic.py +++ b/src/plugins/willing/mode_dynamic.py @@ -183,15 +183,6 @@ class DynamicWillingManager(BaseWillingManager): self.chat_reply_willing[chat_id] = min(current_willing, 3.0) - # 打印消息信息 - mes_name = willing_info.chat.group_info.group_name if willing_info.chat.group_info else "私聊" - current_time = time.strftime("%H:%M:%S", time.localtime(willing_info.message.message_info.time)) - self.logger.info( - f"[{current_time}][{mes_name}]" - f"{willing_info.chat.user_info.user_nickname}:" - f"{willing_info.message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" - ) - return reply_probability async def before_generate_reply_handle(self, message_id): From 5c487b2e682c9f00b1056636f3378b25eb086ddd Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Fri, 11 Apr 2025 11:55:17 +0800 Subject: [PATCH 19/24] =?UTF-8?q?=E6=9B=B4=E6=AD=A3log=EF=BC=8C=E5=AE=8C?= =?UTF-8?q?=E5=96=84mxp=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reasoning_chat/reasoning_chat.py | 5 ++- .../think_flow_chat/think_flow_chat.py | 4 +- src/plugins/willing/mode_mxp.py | 37 ++++++++++--------- template/bot_config_template.toml | 4 +- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index 9286422b8..3c3eb4471 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -198,6 +198,7 @@ class ReasoningChat: # 获取回复概率 if reply_probability != 1: + is_willing = True reply_probability = await willing_manager.get_reply_probability(message.message_info.message_id) if message.message_info.additional_config: @@ -207,12 +208,12 @@ class ReasoningChat: # 打印消息信息 mes_name = chat.group_info.group_name if chat.group_info else "私聊" current_time = time.strftime("%H:%M:%S", time.localtime(message.message_info.time)) + willing_log = f"[回复意愿:{await willing_manager.get_willing(chat.stream_id):.2f}]" if is_willing else "" logger.info( f"[{current_time}][{mes_name}]" f"{chat.user_info.user_nickname}:" - f"{message.processed_plain_text}[回复意愿:{await willing_manager.get_willing(chat.stream_id):.2f}][概率:{reply_probability * 100:.1f}%]" + f"{message.processed_plain_text}{willing_log}[概率:{reply_probability * 100:.1f}%]" ) - do_reply = False if random() < reply_probability: do_reply = True diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index a0ba5d7af..ed4d75847 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -230,6 +230,7 @@ class ThinkFlowChat: # 获取回复概率 if reply_probability != 1: + is_willing = True reply_probability = await willing_manager.get_reply_probability(message.message_info.message_id) if message.message_info.additional_config: @@ -239,10 +240,11 @@ class ThinkFlowChat: # 打印消息信息 mes_name = chat.group_info.group_name if chat.group_info else "私聊" current_time = time.strftime("%H:%M:%S", time.localtime(message.message_info.time)) + willing_log = f"[回复意愿:{await willing_manager.get_willing(chat.stream_id):.2f}]" if is_willing else "" logger.info( f"[{current_time}][{mes_name}]" f"{chat.user_info.user_nickname}:" - f"{message.processed_plain_text}[回复意愿:{await willing_manager.get_willing(chat.stream_id):.2f}][概率:{reply_probability * 100:.1f}%]" + f"{message.processed_plain_text}{willing_log}[概率:{reply_probability * 100:.1f}%]" ) do_reply = False diff --git a/src/plugins/willing/mode_mxp.py b/src/plugins/willing/mode_mxp.py index 12ed8774b..62aa9a70e 100644 --- a/src/plugins/willing/mode_mxp.py +++ b/src/plugins/willing/mode_mxp.py @@ -1,13 +1,21 @@ """ -Mxp 模式(MxpWillingManager)是一个基于意愿值动态调整的回复管理器。 -其主要特点包括: -1. 通过消息频率、提及、兴趣度等多种因素动态调整回复意愿值。 -2. 支持意愿值的衰减机制,确保意愿值不会无限增长。 -3. 提供对单聊、群聊等不同场景的特殊处理。 -4. 结合关系等级、表情包惩罚等多种参数,计算最终的回复概率。 -5. 支持异步任务,用于定期更新意愿值和基础意愿值。 +Mxp 模式:梦溪畔独家赞助 +此模式的一些参数不会在配置文件中显示,要修改请在可变参数下修改 +同时一些全局设置对此模式无效 +此模式的可变参数暂时比较草率,需要调参仙人的大手 +此模式的特点: +1.每个聊天流的每个用户的意愿是独立的 +2.接入关系系统,关系会影响意愿值 +3.会根据群聊的热度来调整基础意愿值 +4.限制同时思考的消息数量,防止喷射 +5.拥有单聊增益,无论在群里还是私聊,只要bot一直和你聊,就会增加意愿值 +6.意愿分为衰减意愿+临时意愿 -该模式适用于需要精细化控制回复行为的场景,能够根据用户行为和聊天环境动态调整回复策略。 +如果你发现本模式出现了bug +上上策是询问智慧的小草神() +上策是询问万能的千石可乐 +中策是发issue +下下策是询问一个菜鸟(@梦溪畔) """ from .willing_manager import BaseWillingManager from typing import Dict @@ -22,6 +30,7 @@ class MxpWillingManager(BaseWillingManager): self.chat_person_reply_willing: Dict[str, Dict[str, float]] = {} # chat_id: {person_id: 意愿值} self.chat_new_message_time: Dict[str, list[float]] = {} # 聊天流ID: 消息时间 self.last_response_person: list = ["", "", 0] # 上次回复的用户信息 + self.temporary_willing: float = 0 # 临时意愿值 # 可变参数 self.intention_decay_rate = 0.93 # 意愿衰减率 @@ -107,14 +116,7 @@ class MxpWillingManager(BaseWillingManager): if w_info.group_info and w_info.group_info.group_id in self.global_config.talk_frequency_down_groups: probability /= self.down_frequency_rate - # 打印消息信息 - mes_name = w_info.chat.group_info.group_name if w_info.chat.group_info else "私聊" - current_time = time.strftime("%H:%M:%S", time.localtime(w_info.message.message_info.time)) - self.logger.info( - f"[{current_time}][{mes_name}]" - f"{w_info.chat.user_info.user_nickname}:" - f"{w_info.message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{probability * 100:.1f}%]" - ) + self.temporary_willing = current_willing return probability @@ -149,7 +151,6 @@ class MxpWillingManager(BaseWillingManager): if chat.stream_id not in self.chat_new_message_time: self.chat_new_message_time[chat.stream_id] = [] self.chat_new_message_time[chat.stream_id].append(time.time()) - self.logger.info(self.chat_new_message_time[chat.stream_id]) if len(self.chat_new_message_time[chat.stream_id]) > self.number_of_message_storage: self.chat_new_message_time[chat.stream_id].pop(0) @@ -229,3 +230,5 @@ class MxpWillingManager(BaseWillingManager): level_num = 5 if relationship_value > 1000 else 0 return level_num - 2 + async def get_willing(self, chat_id): + return self.temporary_willing \ No newline at end of file diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 76ef81d01..5053082cd 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "1.2.5" +version = "1.2.6" #以下是给开发人员阅读的,一般用户不需要阅读 @@ -98,7 +98,7 @@ ban_msgs_regex = [ ] [willing] -willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,动态模式:dynamic,自定义模式:custom +willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,动态模式:dynamic,mxp模式:mxp,自定义模式:custom(需要你自己实现) response_willing_amplifier = 1 # 麦麦回复意愿放大系数,一般为1 response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数 down_frequency_rate = 3 # 降低回复频率的群组回复意愿降低系数 除法 From 3238946053e0abd9c43b9776971904d2cdc7b419 Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Fri, 11 Apr 2025 12:07:56 +0800 Subject: [PATCH 20/24] =?UTF-8?q?=E8=B0=83=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/mode_mxp.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/willing/mode_mxp.py b/src/plugins/willing/mode_mxp.py index 62aa9a70e..6a22aa778 100644 --- a/src/plugins/willing/mode_mxp.py +++ b/src/plugins/willing/mode_mxp.py @@ -41,7 +41,7 @@ class MxpWillingManager(BaseWillingManager): self.interest_willing_gain = 0.3 # 兴趣意愿增益 self.emoji_response_penalty = self.global_config.emoji_response_penalty # 表情包回复惩罚 self.down_frequency_rate = self.global_config.down_frequency_rate # 降低回复频率的群组惩罚系数 - self.single_chat_gain = 0.15 # 单聊增益 + self.single_chat_gain = 0.12 # 单聊增益 async def async_task_starter(self) -> None: """异步任务启动器""" @@ -74,7 +74,8 @@ class MxpWillingManager(BaseWillingManager): if w_info.is_mentioned_bot: self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] += 0.2 if self.last_response_person[0] == w_info.chat_id and self.last_response_person[1] == w_info.person_id: - self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] += self.single_chat_gain *self.last_response_person[2] + self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] +=\ + self.single_chat_gain * (2 * self.last_response_person[2] - 1) if self.last_response_person[2] else 0 async def get_reply_probability(self, message_id: str): """获取回复概率""" @@ -95,7 +96,7 @@ class MxpWillingManager(BaseWillingManager): current_willing += rel_level * 0.1 if self.last_response_person[0] == w_info.chat_id and self.last_response_person[1] == w_info.person_id: - current_willing += self.single_chat_gain * self.last_response_person[2] + current_willing += self.single_chat_gain * (2 * self.last_response_person[2] - 1) if self.last_response_person[2] else 0 chat_ongoing_messages = [msg for msg in self.ongoing_messages.values() if msg.chat_id == w_info.chat_id] chat_person_ogoing_messages = [msg for msg in chat_ongoing_messages if msg.person_id == w_info.person_id] From ffe831ed10ebd4728860325234d01a0a803ffe36 Mon Sep 17 00:00:00 2001 From: meng_xi_pan Date: Fri, 11 Apr 2025 12:53:45 +0800 Subject: [PATCH 21/24] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=8D=95=E8=81=8A?= =?UTF-8?q?=E5=A2=9E=E7=9B=8A=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/willing/mode_mxp.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/plugins/willing/mode_mxp.py b/src/plugins/willing/mode_mxp.py index 6a22aa778..b17e76702 100644 --- a/src/plugins/willing/mode_mxp.py +++ b/src/plugins/willing/mode_mxp.py @@ -29,7 +29,7 @@ class MxpWillingManager(BaseWillingManager): super().__init__() self.chat_person_reply_willing: Dict[str, Dict[str, float]] = {} # chat_id: {person_id: 意愿值} self.chat_new_message_time: Dict[str, list[float]] = {} # 聊天流ID: 消息时间 - self.last_response_person: list = ["", "", 0] # 上次回复的用户信息 + self.last_response_person: Dict[str, tuple[str, int]] = {} # 上次回复的用户信息 self.temporary_willing: float = 0 # 临时意愿值 # 可变参数 @@ -60,12 +60,12 @@ class MxpWillingManager(BaseWillingManager): rel_level = self._get_relationship_level_num(rel_value) self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] += rel_level * 0.05 - if (w_info.chat_id == self.last_response_person[0] - and w_info.person_id == self.last_response_person[1] - and self.last_response_person[2] < 3): - self.last_response_person[2] += 1 + now_chat_new_person = self.last_response_person.get(w_info.chat_id, ["", 0]) + if now_chat_new_person[0] == w_info.person_id: + if now_chat_new_person[1] < 2: + now_chat_new_person[1] += 1 else: - self.last_response_person = [w_info.chat_id, w_info.person_id, 0] + self.last_response_person[w_info.chat_id] = [w_info.person_id, 0] async def not_reply_handle(self, message_id: str): """不回复处理""" @@ -73,9 +73,9 @@ class MxpWillingManager(BaseWillingManager): w_info = self.ongoing_messages[message_id] if w_info.is_mentioned_bot: self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] += 0.2 - if self.last_response_person[0] == w_info.chat_id and self.last_response_person[1] == w_info.person_id: + if w_info.chat_id in self.last_response_person and self.last_response_person[w_info.chat_id][0] == w_info.person_id: self.chat_person_reply_willing[w_info.chat_id][w_info.person_id] +=\ - self.single_chat_gain * (2 * self.last_response_person[2] - 1) if self.last_response_person[2] else 0 + self.single_chat_gain * (2 * self.last_response_person[w_info.chat_id][1] + 1) async def get_reply_probability(self, message_id: str): """获取回复概率""" @@ -95,8 +95,8 @@ class MxpWillingManager(BaseWillingManager): rel_level = self._get_relationship_level_num(rel_value) current_willing += rel_level * 0.1 - if self.last_response_person[0] == w_info.chat_id and self.last_response_person[1] == w_info.person_id: - current_willing += self.single_chat_gain * (2 * self.last_response_person[2] - 1) if self.last_response_person[2] else 0 + if w_info.chat_id in self.last_response_person and self.last_response_person[w_info.chat_id][0] == w_info.person_id: + current_willing += self.single_chat_gain * (2 * self.last_response_person[w_info.chat_id][1] + 1) chat_ongoing_messages = [msg for msg in self.ongoing_messages.values() if msg.chat_id == w_info.chat_id] chat_person_ogoing_messages = [msg for msg in chat_ongoing_messages if msg.person_id == w_info.person_id] From 44a1a93e9a289efdec02d3821df79f3a87519b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E6=BA=AA=E7=95=94?= <130263765+na10xi27da@users.noreply.github.com> Date: Fri, 11 Apr 2025 13:00:10 +0800 Subject: [PATCH 22/24] ruff --- src/plugins/willing/mode_classical.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/willing/mode_classical.py b/src/plugins/willing/mode_classical.py index 56c46275f..74f24350f 100644 --- a/src/plugins/willing/mode_classical.py +++ b/src/plugins/willing/mode_classical.py @@ -1,7 +1,5 @@ import asyncio from .willing_manager import BaseWillingManager -import time - class ClassicalWillingManager(BaseWillingManager): def __init__(self): @@ -77,4 +75,4 @@ class ClassicalWillingManager(BaseWillingManager): async def set_variable_parameters(self, parameters): return await super().set_variable_parameters(parameters) - \ No newline at end of file + From eefce3a04b0d9b956e92b1fc7d5c5e1af0ec6902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E6=BA=AA=E7=95=94?= <130263765+na10xi27da@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:00:03 +0800 Subject: [PATCH 23/24] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat_module/reasoning_chat/reasoning_chat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index 3c3eb4471..e0f32cc29 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -197,6 +197,7 @@ class ReasoningChat: return # 获取回复概率 + is_willing = False if reply_probability != 1: is_willing = True reply_probability = await willing_manager.get_reply_probability(message.message_info.message_id) From d004a26a0acb82e9204cc1560460f61db35c8723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E6=BA=AA=E7=95=94?= <130263765+na10xi27da@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:00:41 +0800 Subject: [PATCH 24/24] =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat_module/think_flow_chat/think_flow_chat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index ed4d75847..1f68676bd 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -229,6 +229,7 @@ class ThinkFlowChat: # current_willing = current_willing_old # 获取回复概率 + is_willing = False if reply_probability != 1: is_willing = True reply_probability = await willing_manager.get_reply_probability(message.message_info.message_id)