feat(sleep): 细化唤醒机制,实现分聊天的起床气

先前的唤醒与起床气机制是全局性的,一个聊天中的频繁消息会导致Bot对所有聊天都表现出“起床气”状态,这在多聊天场景下体验不佳。

本次更新将唤醒机制与具体的 `chat_id` 进行绑定,实现了更精细化的交互逻辑:
- Bot被吵醒后,其“起床气”状态将只针对吵醒它的聊天生效。
- 在此期间,Bot会忽略其他所有聊天的消息,专注于处理来自触发唤醒的聊天的消息。
- Prompt现在会优先注入起床气相关的描述,确保响应符合当前状态。

此外,为了更准确地捕捉唤醒意图,在群聊中,即使没有@提及,只要消息包含Bot的昵称或别名,也会被视为一次有效“提及”,从而累加唤醒值。
This commit is contained in:
tt-P607
2025-09-27 00:58:41 +08:00
parent 28a2a4b0c8
commit 7f39f6f649
4 changed files with 57 additions and 9 deletions

View File

@@ -180,8 +180,14 @@ class MessageManager:
for message in unread_messages:
is_mentioned = message.is_mentioned or False
if not is_mentioned and not is_private:
bot_names = [global_config.bot.nickname] + global_config.bot.alias_names
if any(name in message.processed_plain_text for name in bot_names):
is_mentioned = True
logger.debug(f"通过关键词 '{next((name for name in bot_names if name in message.processed_plain_text), '')}' 匹配将消息标记为 'is_mentioned'")
if is_private or is_mentioned:
if self.wakeup_manager.add_wakeup_value(is_private, is_mentioned):
if self.wakeup_manager.add_wakeup_value(is_private, is_mentioned, chat_id=stream_id):
was_woken_up = True
break # 一旦被吵醒,就跳出循环并处理消息
@@ -190,6 +196,12 @@ class MessageManager:
return # 退出,不处理消息
logger.info(f"Bot被聊天流 {stream_id} 中的消息吵醒,继续处理。")
elif self.sleep_manager.is_woken_up():
angry_chat_id = self.wakeup_manager.angry_chat_id
if stream_id != angry_chat_id:
logger.debug(f"Bot处于WOKEN_UP状态但当前流 {stream_id} 不是触发唤醒的流 {angry_chat_id},跳过处理。")
return # 退出,不处理此流的消息
logger.info(f"Bot处于WOKEN_UP状态处理触发唤醒的流 {stream_id}")
# --- 睡眠状态检查结束 ---
logger.debug(f"开始处理聊天流 {stream_id}{len(unread_messages)} 条未读消息")

View File

@@ -39,6 +39,10 @@ class SleepManager:
"""判断当前是否处于正在睡觉的状态。"""
return self.context.current_state == SleepState.SLEEPING
def is_woken_up(self) -> bool:
"""判断当前是否处于被吵醒的状态。"""
return self.context.current_state == SleepState.WOKEN_UP
async def update_sleep_state(self, wakeup_manager: Optional["WakeUpManager"] = None):
"""
更新睡眠状态的核心方法,实现状态机的主要逻辑。

View File

@@ -28,6 +28,7 @@ class WakeUpManager:
"""
self.sleep_manager = sleep_manager
self.context = WakeUpContext() # 使用新的上下文管理器
self.angry_chat_id: Optional[str] = None
self.last_decay_time = time.time()
self._decay_task: Optional[asyncio.Task] = None
self.is_running = False
@@ -87,7 +88,11 @@ class WakeUpManager:
self.context.is_angry = False
# 通知情绪管理系统清除愤怒状态
from src.mood.mood_manager import mood_manager
mood_manager.clear_angry_from_wakeup("global_mood")
if self.angry_chat_id:
mood_manager.clear_angry_from_wakeup(self.angry_chat_id)
self.angry_chat_id = None
else:
logger.warning("Angry state ended but no angry_chat_id was set.")
logger.info("愤怒状态结束,恢复正常")
self.context.save()
@@ -99,7 +104,7 @@ class WakeUpManager:
logger.debug(f"唤醒度衰减: {old_value:.1f} -> {self.context.wakeup_value:.1f}")
self.context.save()
def add_wakeup_value(self, is_private_chat: bool, is_mentioned: bool = False) -> bool:
def add_wakeup_value(self, is_private_chat: bool, is_mentioned: bool = False, chat_id: Optional[str] = None) -> bool:
"""
增加唤醒度值
@@ -148,23 +153,27 @@ class WakeUpManager:
# 检查是否达到唤醒阈值
if self.context.wakeup_value >= self.wakeup_threshold:
self._trigger_wakeup()
if not chat_id:
logger.error("Wakeup threshold reached, but no chat_id was provided. Cannot trigger wakeup.")
return False
self._trigger_wakeup(chat_id)
return True
self.context.save()
return False
def _trigger_wakeup(self):
def _trigger_wakeup(self, chat_id: str):
"""触发唤醒,进入愤怒状态"""
self.context.is_angry = True
self.context.angry_start_time = time.time()
self.context.wakeup_value = 0.0 # 重置唤醒度
self.angry_chat_id = chat_id
self.context.save()
# 通知情绪管理系统进入愤怒状态
from src.mood.mood_manager import mood_manager
mood_manager.set_angry_from_wakeup("global_mood")
mood_manager.set_angry_from_wakeup(chat_id)
# 通知SleepManager重置睡眠状态
self.sleep_manager.reset_sleep_state_after_wakeup()
@@ -185,7 +194,11 @@ class WakeUpManager:
self.context.is_angry = False
# 通知情绪管理系统清除愤怒状态
from src.mood.mood_manager import mood_manager
mood_manager.clear_angry_from_wakeup("global_mood")
if self.angry_chat_id:
mood_manager.clear_angry_from_wakeup(self.angry_chat_id)
self.angry_chat_id = None
else:
logger.warning("Angry state expired in check, but no angry_chat_id was set.")
logger.info("愤怒状态自动过期")
return False
return self.context.is_angry

View File

@@ -151,12 +151,31 @@ class ChatterPlanFilter:
identity_block = f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}"
schedule_block = ""
if global_config.planning_system.schedule_enable:
# 优先检查是否被吵醒
from src.chat.message_manager.message_manager import message_manager
angry_prompt_addition = ""
wakeup_mgr = message_manager.wakeup_manager
# 双重检查确保愤怒状态不会丢失
# 检查1: 直接从 wakeup_manager 获取
if wakeup_mgr.is_in_angry_state():
angry_prompt_addition = wakeup_mgr.get_angry_prompt_addition()
# 检查2: 如果上面没获取到,再从 mood_manager 确认
if not angry_prompt_addition:
chat_mood_for_check = mood_manager.get_mood_by_chat_id(plan.chat_id)
if chat_mood_for_check.is_angry_from_wakeup:
angry_prompt_addition = global_config.sleep_system.angry_prompt
if angry_prompt_addition:
schedule_block = angry_prompt_addition
elif global_config.planning_system.schedule_enable:
if current_activity := schedule_manager.get_current_activity():
schedule_block = f"你当前正在:{current_activity},但注意它与群聊的聊天无关。"
mood_block = ""
if global_config.mood.enable_mood:
# 如果被吵醒,则心情也是愤怒的,不需要另外的情绪模块
if not angry_prompt_addition and global_config.mood.enable_mood:
chat_mood = mood_manager.get_mood_by_chat_id(plan.chat_id)
mood_block = f"你现在的心情是:{chat_mood.mood_state}"