willing基类大致完成

This commit is contained in:
meng_xi_pan
2025-04-09 12:32:18 +08:00
parent 58dc9e0840
commit b468b67cf1
4 changed files with 194 additions and 98 deletions

View File

@@ -63,7 +63,7 @@ class MainSystem:
asyncio.create_task(person_info_manager.personal_habit_deduction()) 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: if not self._message_manager_started:

View File

@@ -54,7 +54,6 @@ class ReasoningChat:
) )
message_manager.add_message(thinking_message) message_manager.add_message(thinking_message)
willing_manager.change_reply_willing_sent(chat)
return thinking_id return thinking_id
@@ -126,7 +125,7 @@ class ReasoningChat:
) )
message_manager.add_message(bot_message) 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) ori_response = ",".join(response_set)
stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text) stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text)
@@ -178,7 +177,16 @@ class ReasoningChat:
# 查询缓冲器结果会整合前面跳过的消息改变processed_plain_text # 查询缓冲器结果会整合前面跳过的消息改变processed_plain_text
buffer_result = await message_buffer.query_buffer_result(message) 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: if not buffer_result:
await willing_manager.bombing_buffer_message_handle(message.message_info.message_id)
if message.message_segment.type == "text": if message.message_segment.type == "text":
logger.info(f"触发缓冲,已炸飞消息:{message.processed_plain_text}") logger.info(f"触发缓冲,已炸飞消息:{message.processed_plain_text}")
elif message.message_segment.type == "image": elif message.message_segment.type == "image":
@@ -187,45 +195,21 @@ class ReasoningChat:
logger.info("触发缓冲,已炸飞消息列") logger.info("触发缓冲,已炸飞消息列")
return 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)
# 计算回复意愿 if message.message_info.additional_config:
current_willing = willing_manager.get_willing(chat_stream=chat) if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys():
willing_manager.set_willing(chat.stream_id, current_willing) reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"]
# 意愿激活
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"]
do_reply = False do_reply = False
if random() < reply_probability: if random() < reply_probability:
do_reply = True do_reply = True
# 回复前处理
await willing_manager.before_generate_reply_handle(message.message_info.message_id)
# 创建思考消息 # 创建思考消息
timer1 = time.time() timer1 = time.time()
thinking_id = await self._create_thinking_message(message, chat, userinfo, messageinfo) thinking_id = await self._create_thinking_message(message, chat, userinfo, messageinfo)
@@ -260,12 +244,21 @@ class ReasoningChat:
timer2 = time.time() timer2 = time.time()
timing_results["更新关系情绪"] = timer2 - timer1 timing_results["更新关系情绪"] = timer2 - timer1
# 回复后处理
await willing_manager.after_generate_reply_handle(message.message_info.message_id)
# 输出性能计时结果 # 输出性能计时结果
if do_reply: if do_reply:
timing_str = " | ".join([f"{step}: {duration:.2f}" for step, duration in timing_results.items()]) timing_str = " | ".join([f"{step}: {duration:.2f}" for step, duration in timing_results.items()])
trigger_msg = message.processed_plain_text trigger_msg = message.processed_plain_text
response_msg = " ".join(response_set) if response_set else "无回复" response_msg = " ".join(response_set) if response_set else "无回复"
logger.info(f"触发消息: {trigger_msg[:20]}... | 推理消息: {response_msg[:20]}... | 性能计时: {timing_str}") 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: def _check_ban_words(self, text: str, chat, userinfo) -> bool:
"""检查消息中是否包含过滤词""" """检查消息中是否包含过滤词"""

View File

@@ -55,7 +55,6 @@ class ThinkFlowChat:
) )
message_manager.add_message(thinking_message) message_manager.add_message(thinking_message)
willing_manager.change_reply_willing_sent(chat)
return thinking_id return thinking_id
@@ -146,7 +145,7 @@ class ThinkFlowChat:
await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt) 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) ori_response = ",".join(response_set)
stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text) stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text)
@@ -203,7 +202,16 @@ class ThinkFlowChat:
# 查询缓冲器结果会整合前面跳过的消息改变processed_plain_text # 查询缓冲器结果会整合前面跳过的消息改变processed_plain_text
buffer_result = await message_buffer.query_buffer_result(message) 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: if not buffer_result:
await willing_manager.bombing_buffer_message_handle(message.message_info.message_id)
if message.message_segment.type == "text": if message.message_segment.type == "text":
logger.info(f"触发缓冲,已炸飞消息:{message.processed_plain_text}") logger.info(f"触发缓冲,已炸飞消息:{message.processed_plain_text}")
elif message.message_segment.type == "image": elif message.message_segment.type == "image":
@@ -212,52 +220,29 @@ class ThinkFlowChat:
logger.info("触发缓冲,已炸飞消息列") logger.info("触发缓冲,已炸飞消息列")
return return
# 处理提及
is_mentioned, reply_probability = is_mentioned_bot_in_message(message)
# 计算回复意愿 # 计算回复意愿
current_willing_old = willing_manager.get_willing(chat_stream=chat) # 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_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4
# current_willing = (current_willing_old + current_willing_new) / 2 # # current_willing = (current_willing_old + current_willing_new) / 2
# 有点bug # # 有点bug
current_willing = current_willing_old # 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)
# 意愿激活 if message.message_info.additional_config:
timer1 = time.time() if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys():
real_reply_probability = await willing_manager.change_reply_willing_received( reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"]
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"]
do_reply = False do_reply = False
if random() < reply_probability: if random() < reply_probability:
try: try:
do_reply = True do_reply = True
# 回复前处理
await willing_manager.before_generate_reply_handle(message.message_info.message_id)
# 创建思考消息 # 创建思考消息
try: try:
timer1 = time.time() timer1 = time.time()
@@ -333,6 +318,9 @@ class ThinkFlowChat:
except Exception as e: except Exception as e:
logger.error(f"心流更新关系情绪失败: {e}") logger.error(f"心流更新关系情绪失败: {e}")
# 回复后处理
await willing_manager.after_generate_reply_handle(message.message_info.message_id)
except Exception as e: except Exception as e:
logger.error(f"心流处理消息失败: {e}") logger.error(f"心流处理消息失败: {e}")
@@ -342,6 +330,12 @@ class ThinkFlowChat:
trigger_msg = message.processed_plain_text trigger_msg = message.processed_plain_text
response_msg = " ".join(response_set) if response_set else "无回复" response_msg = " ".join(response_set) if response_set else "无回复"
logger.info(f"触发消息: {trigger_msg[:20]}... | 思维消息: {response_msg[:20]}... | 性能计时: {timing_str}") 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: def _check_ban_words(self, text: str, chat, userinfo) -> bool:
"""检查消息中是否包含过滤词""" """检查消息中是否包含过滤词"""

View File

@@ -1,22 +1,144 @@
from typing import Optional
from src.common.logger import get_module_logger
from ..config.config import global_config from src.common.logger import LogConfig, WILLING_STYLE_CONFIG, LoguruLogger, get_module_logger
from .mode_classical import WillingManager as ClassicalWillingManager from dataclasses import dataclass
from .mode_dynamic import WillingManager as DynamicWillingManager from ..config.config import global_config, BotConfig
from .mode_custom import WillingManager as CustomWillingManager from ..chat.chat_stream import ChatStream, GroupInfo
from src.common.logger import LogConfig, WILLING_STYLE_CONFIG 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( willing_config = LogConfig(
# 使用消息发送专用样式 # 使用消息发送专用样式
console_format=WILLING_STYLE_CONFIG["console_format"], console_format=WILLING_STYLE_CONFIG["console_format"],
file_format=WILLING_STYLE_CONFIG["file_format"], file_format=WILLING_STYLE_CONFIG["file_format"],
) )
logger = get_module_logger("willing", config=willing_config) 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实例 根据配置初始化并返回对应的WillingManager实例
@@ -24,20 +146,7 @@ def init_willing_manager() -> Optional[object]:
对应mode的WillingManager实例 对应mode的WillingManager实例
""" """
mode = global_config.willing_mode.lower() mode = global_config.willing_mode.lower()
return BaseWillingManager.create(mode)
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()
# 全局willing_manager对象 # 全局willing_manager对象
willing_manager = init_willing_manager() willing_manager = init_willing_manager()