From 50f0ddf2cec5adcf4008e392e686883fd14f905a Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Wed, 9 Jul 2025 01:50:26 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9B=E6=96=B0=E7=9A=84=E6=83=85?= =?UTF-8?q?=E7=BB=AA=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 82 +---- interested_rates.txt | 46 +++ src/chat/heart_flow/chat_state_info.py | 5 +- .../heart_flow/heartflow_message_processor.py | 8 + src/chat/heart_flow/sub_heartflow.py | 2 - src/chat/message_receive/bot.py | 2 +- src/chat/normal_chat/normal_chat.py | 2 +- src/chat/replyer/default_generator.py | 17 +- src/chat/utils/utils.py | 14 +- src/main.py | 8 - .../mais4u_chat/s4u_stream_generator.py | 1 - src/manager/mood_manager.py | 296 ------------------ src/mood/mood_manager.py | 135 ++++++++ src/person_info/relationship_builder.py | 2 +- src/person_info/relationship_manager.py | 2 - template/bot_config_template.toml | 3 +- 16 files changed, 211 insertions(+), 414 deletions(-) create mode 100644 interested_rates.txt delete mode 100644 src/manager/mood_manager.py create mode 100644 src/mood/mood_manager.py diff --git a/bot.py b/bot.py index a3e49fceb..f19afbfd3 100644 --- a/bot.py +++ b/bot.py @@ -236,67 +236,6 @@ def raw_main(): return MainSystem() -async def _create_console_message_dict(text: str) -> dict: - """使用配置创建消息字典""" - timestamp = time.time() - - # --- User & Group Info (hardcoded for console) --- - user_info = UserInfo( - platform="console", - user_id="console_user", - user_nickname="ConsoleUser", - user_cardname="", - ) - # Console input is private chat - group_info = None - - # --- Base Message Info --- - message_info = BaseMessageInfo( - platform="console", - message_id=f"console_{int(timestamp * 1000)}_{hash(text) % 10000}", - time=timestamp, - user_info=user_info, - group_info=group_info, - # Other infos can be added here if needed, e.g., FormatInfo - ) - - # --- Message Segment --- - message_segment = Seg(type="text", data=text) - - # --- Final MessageBase object to convert to dict --- - message = MessageBase(message_info=message_info, message_segment=message_segment, raw_message=text) - - return message.to_dict() - - -async def console_input_loop(main_system: MainSystem): - """异步循环以读取控制台输入并模拟接收消息""" - logger.info("控制台输入已准备就绪 (模拟接收消息)。输入 'exit()' 来停止。") - loop = asyncio.get_event_loop() - while True: - try: - line = await loop.run_in_executor(None, sys.stdin.readline) - text = line.strip() - - if not text: - continue - if text.lower() == "exit()": - logger.info("收到 'exit()' 命令,正在停止...") - break - - # Create message dict and pass to the processor - message_dict = await _create_console_message_dict(text) - await chat_bot.message_process(message_dict) - logger.info(f"已将控制台消息 '{text}' 作为接收消息处理。") - - except asyncio.CancelledError: - logger.info("控制台输入循环被取消。") - break - except Exception as e: - logger.error(f"控制台输入循环出错: {e}", exc_info=True) - await asyncio.sleep(1) - logger.info("控制台输入循环结束。") - if __name__ == "__main__": exit_code = 0 # 用于记录程序最终的退出状态 @@ -314,17 +253,7 @@ if __name__ == "__main__": # Schedule tasks returns a future that runs forever. # We can run console_input_loop concurrently. main_tasks = loop.create_task(main_system.schedule_tasks()) - - # 仅在 TTY 中启用 console_input_loop - if sys.stdin.isatty(): - logger.info("检测到终端环境,启用控制台输入循环") - console_task = loop.create_task(console_input_loop(main_system)) - # Wait for all tasks to complete (which they won't, normally) - loop.run_until_complete(asyncio.gather(main_tasks, console_task)) - else: - logger.info("非终端环境,跳过控制台输入循环") - # Wait for all tasks to complete (which they won't, normally) - loop.run_until_complete(main_tasks) + loop.run_until_complete(main_tasks) except KeyboardInterrupt: # loop.run_until_complete(get_global_api().stop()) @@ -336,15 +265,6 @@ if __name__ == "__main__": logger.error(f"优雅关闭时发生错误: {ge}") # 新增:检测外部请求关闭 - # except Exception as e: # 将主异常捕获移到外层 try...except - # logger.error(f"事件循环内发生错误: {str(e)} {str(traceback.format_exc())}") - # exit_code = 1 - # finally: # finally 块移到最外层,确保 loop 关闭和暂停总是执行 - # if loop and not loop.is_closed(): - # loop.close() - # # 在这里添加 input() 来暂停 - # input("按 Enter 键退出...") # <--- 添加这行 - # sys.exit(exit_code) # <--- 使用记录的退出码 except Exception as e: logger.error(f"主程序发生异常: {str(e)} {str(traceback.format_exc())}") diff --git a/interested_rates.txt b/interested_rates.txt new file mode 100644 index 000000000..1dd65f819 --- /dev/null +++ b/interested_rates.txt @@ -0,0 +1,46 @@ +0.02388322700338219 +0.02789637960584667 +6.1002656551513885 +6.1002656551513885 +6.1171064375469255 +6.106626351535966 +6.112541462320276 +0.04230527065567247 +9.04004621778353 +6.104278807753853 +6.106626351535966 +6.198517524266092 +0.020373848987042205 +6.106626351535966 +6.104278807753853 +0.03203964454588806 +6.104278807753853 +6.104278807753853 +6.104278807753853 +6.104278807753853 +6.1002656551513885 +6.1002656551513885 +6.1002656551513885 +0.02605261040985793 +1.0273445569816615 +0.02203945780739345 +0.03203964454588806 +0.014013152602464482 +0.03203964454588806 +1.018026305204929 +4.183876948487736 +0.020373848987042205 +0.19241219083184483 +6.103223210543543 +6.1002656551513885 +6.103223210543543 +6.103223210543543 +1.021266343711497 +6.103223210543543 +0.018026305204928966 +0.020373848987042205 +6.106626351535966 +6.089034714923968 +0.03203964454588806 +6.089034714923968 +0.027344556981661584 diff --git a/src/chat/heart_flow/chat_state_info.py b/src/chat/heart_flow/chat_state_info.py index db4c2d5c7..5abc76dbe 100644 --- a/src/chat/heart_flow/chat_state_info.py +++ b/src/chat/heart_flow/chat_state_info.py @@ -1,4 +1,4 @@ -from src.manager.mood_manager import mood_manager +from src.mood.mood_manager import mood_manager import enum @@ -12,6 +12,3 @@ class ChatStateInfo: def __init__(self): self.chat_status: ChatState = ChatState.NORMAL self.current_state_time = 120 - - self.mood_manager = mood_manager - self.mood = self.mood_manager.get_mood_prompt() diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index 5de0d7015..301013465 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -1,5 +1,6 @@ from src.chat.memory_system.Hippocampus import hippocampus_manager from src.config.config import global_config +import asyncio from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.storage import MessageStorage from src.chat.heart_flow.heartflow import heartflow @@ -13,6 +14,7 @@ import traceback from typing import Tuple from src.person_info.relationship_manager import get_relationship_manager +from src.mood.mood_manager import mood_manager logger = get_logger("chat") @@ -113,6 +115,12 @@ class HeartFCMessageReceiver: # 6. 兴趣度计算与更新 interested_rate, is_mentioned = await _calculate_interest(message) subheartflow.add_message_to_normal_chat_cache(message, interested_rate, is_mentioned) + + chat_mood = mood_manager.get_mood_by_chat_id(subheartflow.chat_id) + asyncio.create_task(chat_mood.update_mood_by_message(message, interested_rate)) + + with open("interested_rates.txt", "a", encoding="utf-8") as f: + f.write(f"{interested_rate}\n") # 7. 日志记录 mes_name = chat.group_info.group_name if chat.group_info else "私聊" diff --git a/src/chat/heart_flow/sub_heartflow.py b/src/chat/heart_flow/sub_heartflow.py index 51b663dfe..9ef357379 100644 --- a/src/chat/heart_flow/sub_heartflow.py +++ b/src/chat/heart_flow/sub_heartflow.py @@ -26,8 +26,6 @@ class SubHeartflow: Args: subheartflow_id: 子心流唯一标识符 - mai_states: 麦麦状态信息实例 - hfc_no_reply_callback: HFChatting 连续不回复时触发的回调 """ # 基础属性,两个值是一样的 self.subheartflow_id = subheartflow_id diff --git a/src/chat/message_receive/bot.py b/src/chat/message_receive/bot.py index 0e94991b6..b460ad99b 100644 --- a/src/chat/message_receive/bot.py +++ b/src/chat/message_receive/bot.py @@ -3,7 +3,7 @@ import os from typing import Dict, Any from src.common.logger import get_logger -from src.manager.mood_manager import mood_manager # 导入情绪管理器 +from src.mood.mood_manager import mood_manager # 导入情绪管理器 from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.message_receive.message import MessageRecv from src.experimental.only_message_process import MessageProcessor diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 43ab5803e..6571f1abc 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -22,7 +22,7 @@ from src.chat.planner_actions.planner import ActionPlanner from src.chat.planner_actions.action_modifier import ActionModifier from src.chat.utils.utils import get_chat_type_and_target_info -from src.manager.mood_manager import mood_manager +from src.mood.mood_manager import mood_manager willing_manager = get_willing_manager() diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index 0b3c25f14..1e17c3cb7 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -18,7 +18,7 @@ from src.chat.utils.chat_message_builder import build_readable_messages, get_raw import time import asyncio from src.chat.express.expression_selector import expression_selector -from src.manager.mood_manager import mood_manager +from src.mood.mood_manager import mood_manager from src.person_info.relationship_fetcher import relationship_fetcher_manager import random import ast @@ -55,9 +55,9 @@ def init_prompt(): {identity} {action_descriptions} -你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},请你给出回复 -{config_expression_style}。 -请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,注意不要复读你说过的话。 +你正在{chat_target_2},你现在的心情是:{mood_state} +现在请你读读之前的聊天记录,并给出回复 +{config_expression_style}。注意不要复读你说过的话 {keywords_reaction_prompt} 请注意不要输出多余内容(包括前后缀,冒号和引号,at或 @等 )。只输出回复内容。 {moderation_prompt} @@ -503,6 +503,9 @@ class DefaultReplyer: is_group_chat = bool(chat_stream.group_info) reply_to = reply_data.get("reply_to", "none") extra_info_block = reply_data.get("extra_info", "") or reply_data.get("extra_info_block", "") + + chat_mood = mood_manager.get_mood_by_chat_id(chat_id) + mood_prompt = chat_mood.mood_state sender, target = self._parse_reply_target(reply_to) @@ -639,8 +642,6 @@ class DefaultReplyer: else: reply_target_block = "" - mood_prompt = mood_manager.get_mood_prompt() - prompt_info = await get_prompt_info(target, threshold=0.38) if prompt_info: prompt_info = await global_prompt_manager.format_prompt("knowledge_prompt", prompt_info=prompt_info) @@ -682,7 +683,7 @@ class DefaultReplyer: config_expression_style=global_config.expression.expression_style, action_descriptions=action_descriptions, chat_target_2=chat_target_2, - mood_prompt=mood_prompt, + mood_state=mood_prompt, ) return prompt @@ -774,8 +775,6 @@ class DefaultReplyer: else: reply_target_block = "" - mood_manager.get_mood_prompt() - if is_group_chat: chat_target_1 = await global_prompt_manager.get_prompt_async("chat_target_group1") chat_target_2 = await global_prompt_manager.get_prompt_async("chat_target_group2") diff --git a/src/chat/utils/utils.py b/src/chat/utils/utils.py index 6bf776202..144af9c65 100644 --- a/src/chat/utils/utils.py +++ b/src/chat/utils/utils.py @@ -8,7 +8,7 @@ import numpy as np from maim_message import UserInfo from src.common.logger import get_logger -from src.manager.mood_manager import mood_manager +# from src.mood.mood_manager import mood_manager from ..message_receive.message import MessageRecv from src.llm_models.utils_model import LLMRequest from .typo_generator import ChineseTypoGenerator @@ -412,12 +412,12 @@ def calculate_typing_time( - 在所有输入结束后,额外加上回车时间0.3秒 - 如果is_emoji为True,将使用固定1秒的输入时间 """ - # 将0-1的唤醒度映射到-1到1 - mood_arousal = mood_manager.current_mood.arousal - # 映射到0.5到2倍的速度系数 - typing_speed_multiplier = 1.5**mood_arousal # 唤醒度为1时速度翻倍,为-1时速度减半 - chinese_time *= 1 / typing_speed_multiplier - english_time *= 1 / typing_speed_multiplier + # # 将0-1的唤醒度映射到-1到1 + # mood_arousal = mood_manager.current_mood.arousal + # # 映射到0.5到2倍的速度系数 + # typing_speed_multiplier = 1.5**mood_arousal # 唤醒度为1时速度翻倍,为-1时速度减半 + # chinese_time *= 1 / typing_speed_multiplier + # english_time *= 1 / typing_speed_multiplier # 计算中文字符数 chinese_chars = sum(1 for char in input_string if "\u4e00" <= char <= "\u9fff") diff --git a/src/main.py b/src/main.py index fae064773..733c706f9 100644 --- a/src/main.py +++ b/src/main.py @@ -6,7 +6,6 @@ from src.chat.express.exprssion_learner import get_expression_learner from src.common.remote import TelemetryHeartBeatTask from src.manager.async_task_manager import async_task_manager from src.chat.utils.statistic import OnlineTimeRecordTask, StatisticOutputTask -from src.manager.mood_manager import MoodPrintTask, MoodUpdateTask from src.chat.emoji_system.emoji_manager import get_emoji_manager from src.chat.normal_chat.willing.willing_manager import get_willing_manager from src.chat.message_receive.chat_stream import get_chat_manager @@ -95,13 +94,6 @@ class MainSystem: get_emoji_manager().initialize() logger.info("表情包管理器初始化成功") - # 添加情绪衰减任务 - await async_task_manager.add_task(MoodUpdateTask()) - # 添加情绪打印任务 - await async_task_manager.add_task(MoodPrintTask()) - - logger.info("情绪管理器初始化成功") - # 启动愿望管理器 await willing_manager.async_task_starter() diff --git a/src/mais4u/mais4u_chat/s4u_stream_generator.py b/src/mais4u/mais4u_chat/s4u_stream_generator.py index 235952bb7..0f3ef1944 100644 --- a/src/mais4u/mais4u_chat/s4u_stream_generator.py +++ b/src/mais4u/mais4u_chat/s4u_stream_generator.py @@ -1,6 +1,5 @@ import os from typing import AsyncGenerator -from src.llm_models.utils_model import LLMRequest from src.mais4u.openai_client import AsyncOpenAIClient from src.config.config import global_config from src.chat.message_receive.message import MessageRecv diff --git a/src/manager/mood_manager.py b/src/manager/mood_manager.py deleted file mode 100644 index a62a64fcb..000000000 --- a/src/manager/mood_manager.py +++ /dev/null @@ -1,296 +0,0 @@ -import asyncio -import math -import time -from dataclasses import dataclass -from typing import Dict, Tuple - -from ..config.config import global_config -from ..common.logger import get_logger -from ..manager.async_task_manager import AsyncTask -from ..individuality.individuality import get_individuality - -logger = get_logger("mood") - - -@dataclass -class MoodState: - valence: float - """愉悦度 (-1.0 到 1.0),-1表示极度负面,1表示极度正面""" - arousal: float - """唤醒度 (-1.0 到 1.0),-1表示抑制,1表示兴奋""" - text: str - """心情的文本描述""" - - -@dataclass -class MoodChangeHistory: - valence_direction_factor: int - """愉悦度变化的系数(正为增益,负为抑制)""" - arousal_direction_factor: int - """唤醒度变化的系数(正为增益,负为抑制)""" - - -class MoodUpdateTask(AsyncTask): - def __init__(self): - super().__init__( - task_name="Mood Update Task", - wait_before_start=global_config.mood.mood_update_interval, - run_interval=global_config.mood.mood_update_interval, - ) - - # 从配置文件获取衰减率 - self.decay_rate_valence: float = 1 - global_config.mood.mood_decay_rate - """愉悦度衰减率""" - self.decay_rate_arousal: float = 1 - global_config.mood.mood_decay_rate - """唤醒度衰减率""" - - self.last_update = time.time() - """上次更新时间""" - - async def run(self): - current_time = time.time() - time_diff = current_time - self.last_update - agreeableness_factor = 1 # 宜人性系数 - agreeableness_bias = 0 # 宜人性偏置 - neuroticism_factor = 0.5 # 神经质系数 - # 获取人格特质 - personality = get_individuality().personality - if personality: - # 神经质:影响情绪变化速度 - neuroticism_factor = 1 + (personality.neuroticism - 0.5) * 0.4 - agreeableness_factor = 1 + (personality.agreeableness - 0.5) * 0.4 - - # 宜人性:影响情绪基准线 - if personality.agreeableness < 0.2: - agreeableness_bias = (personality.agreeableness - 0.2) * 0.5 - elif personality.agreeableness > 0.8: - agreeableness_bias = (personality.agreeableness - 0.8) * 0.5 - else: - agreeableness_bias = 0 - - # 分别计算正向和负向的衰减率 - if mood_manager.current_mood.valence >= 0: - # 正向情绪衰减 - decay_rate_positive = self.decay_rate_valence * (1 / agreeableness_factor) - valence_target = 0 + agreeableness_bias - new_valence = valence_target + (mood_manager.current_mood.valence - valence_target) * math.exp( - -decay_rate_positive * time_diff * neuroticism_factor - ) - else: - # 负向情绪衰减 - decay_rate_negative = self.decay_rate_valence * agreeableness_factor - valence_target = 0 + agreeableness_bias - new_valence = valence_target + (mood_manager.current_mood.valence - valence_target) * math.exp( - -decay_rate_negative * time_diff * neuroticism_factor - ) - - # Arousal 向中性(0)回归 - arousal_target = 0 - new_arousal = arousal_target + (mood_manager.current_mood.arousal - arousal_target) * math.exp( - -self.decay_rate_arousal * time_diff * neuroticism_factor - ) - - mood_manager.set_current_mood(new_valence, new_arousal) - - self.last_update = current_time - - -class MoodPrintTask(AsyncTask): - def __init__(self): - super().__init__( - task_name="Mood Print Task", - wait_before_start=60, - run_interval=60, - ) - - async def run(self): - # 打印当前心情 - logger.info( - f"愉悦度: {mood_manager.current_mood.valence:.2f}, " - f"唤醒度: {mood_manager.current_mood.arousal:.2f}, " - f"心情: {mood_manager.current_mood.text}" - ) - - -class MoodManager: - # TODO: 改进,使用具有实验支持的新情绪模型 - - EMOTION_FACTOR_MAP: Dict[str, Tuple[float, float]] = { - "开心": (0.21, 0.6), - "害羞": (0.15, 0.2), - "愤怒": (-0.24, 0.8), - "恐惧": (-0.21, 0.7), - "悲伤": (-0.21, 0.3), - "厌恶": (-0.12, 0.4), - "惊讶": (0.06, 0.7), - "困惑": (0.0, 0.6), - "平静": (0.03, 0.5), - } - """ - 情绪词映射表 {mood: (valence, arousal)} - 将情绪描述词映射到愉悦度和唤醒度的元组 - """ - - EMOTION_POINT_MAP: Dict[Tuple[float, float], str] = { - # 第一象限:高唤醒,正愉悦 - (0.5, 0.4): "兴奋", - (0.3, 0.6): "快乐", - (0.2, 0.3): "满足", - # 第二象限:高唤醒,负愉悦 - (-0.5, 0.4): "愤怒", - (-0.3, 0.6): "焦虑", - (-0.2, 0.3): "烦躁", - # 第三象限:低唤醒,负愉悦 - (-0.5, -0.4): "悲伤", - (-0.3, -0.3): "疲倦", - (-0.4, -0.7): "疲倦", - # 第四象限:低唤醒,正愉悦 - (0.2, -0.1): "平静", - (0.3, -0.2): "安宁", - (0.5, -0.4): "放松", - } - """ - 情绪文本映射表 {(valence, arousal): mood} - 将量化的情绪状态元组映射到文本描述 - """ - - def __init__(self): - self.current_mood = MoodState( - valence=0.0, - arousal=0.0, - text="平静", - ) - """当前情绪状态""" - - self.mood_change_history: MoodChangeHistory = MoodChangeHistory( - valence_direction_factor=0, - arousal_direction_factor=0, - ) - """情绪变化历史""" - - self._lock = asyncio.Lock() - """异步锁,用于保护线程安全""" - - def set_current_mood(self, new_valence: float, new_arousal: float): - """ - 设置当前情绪状态 - :param new_valence: 新的愉悦度 - :param new_arousal: 新的唤醒度 - """ - # 限制范围 - self.current_mood.valence = max(-1.0, min(new_valence, 1.0)) - self.current_mood.arousal = max(-1.0, min(new_arousal, 1.0)) - - closest_mood = None - min_distance = float("inf") - - for (v, a), text in self.EMOTION_POINT_MAP.items(): - # 计算当前情绪状态与每个情绪文本的欧氏距离 - distance = math.sqrt((self.current_mood.valence - v) ** 2 + (self.current_mood.arousal - a) ** 2) - if distance < min_distance: - min_distance = distance - closest_mood = text - - if closest_mood: - self.current_mood.text = closest_mood - - def update_current_mood(self, valence_delta: float, arousal_delta: float): - """ - 根据愉悦度和唤醒度变化量更新当前情绪状态 - :param valence_delta: 愉悦度变化量 - :param arousal_delta: 唤醒度变化量 - """ - # 计算连续增益/抑制 - # 规则:多次相同方向的变化会有更大的影响系数,反方向的变化会清零影响系数(系数的正负号由变化方向决定) - if valence_delta * self.mood_change_history.valence_direction_factor > 0: - # 如果方向相同,则根据变化方向改变系数 - if valence_delta > 0: - self.mood_change_history.valence_direction_factor += 1 # 若为正向,则增加 - else: - self.mood_change_history.valence_direction_factor -= 1 # 若为负向,则减少 - else: - # 如果方向不同,则重置计数 - self.mood_change_history.valence_direction_factor = 0 - - if arousal_delta * self.mood_change_history.arousal_direction_factor > 0: - # 如果方向相同,则根据变化方向改变系数 - if arousal_delta > 0: - self.mood_change_history.arousal_direction_factor += 1 # 若为正向,则增加计数 - else: - self.mood_change_history.arousal_direction_factor -= 1 # 若为负向,则减少计数 - else: - # 如果方向不同,则重置计数 - self.mood_change_history.arousal_direction_factor = 0 - - # 计算增益/抑制的结果 - # 规则:如果当前情绪状态与变化方向相同,则增益;否则抑制 - if self.current_mood.valence * self.mood_change_history.valence_direction_factor > 0: - valence_delta = valence_delta * (1.01 ** abs(self.mood_change_history.valence_direction_factor)) - else: - valence_delta = valence_delta * (0.99 ** abs(self.mood_change_history.valence_direction_factor)) - - if self.current_mood.arousal * self.mood_change_history.arousal_direction_factor > 0: - arousal_delta = arousal_delta * (1.01 ** abs(self.mood_change_history.arousal_direction_factor)) - else: - arousal_delta = arousal_delta * (0.99 ** abs(self.mood_change_history.arousal_direction_factor)) - - self.set_current_mood( - new_valence=self.current_mood.valence + valence_delta, - new_arousal=self.current_mood.arousal + arousal_delta, - ) - - def get_mood_prompt(self) -> str: - """ - 根据当前情绪状态生成提示词 - """ - base_prompt = f"当前心情:{self.current_mood.text}。" - - # 根据情绪状态添加额外的提示信息 - if self.current_mood.valence > 0.5: - base_prompt += "你现在心情很好," - elif self.current_mood.valence < -0.5: - base_prompt += "你现在心情不太好," - - if self.current_mood.arousal > 0.4: - base_prompt += "情绪比较激动。" - elif self.current_mood.arousal < -0.4: - base_prompt += "情绪比较平静。" - - return base_prompt - - def get_arousal_multiplier(self) -> float: - """ - 根据当前情绪状态返回唤醒度乘数 - """ - if self.current_mood.arousal > 0.4: - multiplier = 1 + min(0.15, (self.current_mood.arousal - 0.4) / 3) - return multiplier - elif self.current_mood.arousal < -0.4: - multiplier = 1 - min(0.15, ((0 - self.current_mood.arousal) - 0.4) / 3) - return multiplier - return 1.0 - - def update_mood_from_emotion(self, emotion: str, intensity: float = 1.0) -> None: - """ - 根据情绪词更新心情状态 - :param emotion: 情绪词(如'开心', '悲伤'等位于self.EMOTION_FACTOR_MAP中的键) - :param intensity: 情绪强度(0.0-1.0) - """ - if emotion not in self.EMOTION_FACTOR_MAP: - logger.error(f"[情绪更新] 未知情绪词: {emotion}") - return - - valence_change, arousal_change = self.EMOTION_FACTOR_MAP[emotion] - old_valence = self.current_mood.valence - old_arousal = self.current_mood.arousal - old_mood = self.current_mood.text - - self.update_current_mood(valence_change, arousal_change) # 更新当前情绪状态 - - logger.info( - f"[情绪变化] {emotion}(强度:{intensity:.2f}) | 愉悦度:{old_valence:.2f}->{self.current_mood.valence:.2f}, 唤醒度:{old_arousal:.2f}->{self.current_mood.arousal:.2f} | 心情:{old_mood}->{self.current_mood.text}" - ) - - -mood_manager = MoodManager() -"""全局情绪管理器""" diff --git a/src/mood/mood_manager.py b/src/mood/mood_manager.py new file mode 100644 index 000000000..bdb867b09 --- /dev/null +++ b/src/mood/mood_manager.py @@ -0,0 +1,135 @@ +import math +import random + +from src.chat.message_receive.message import MessageRecv +from src.llm_models.utils_model import LLMRequest +from ..common.logger import get_logger +from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_by_timestamp_with_chat_inclusive +from src.config.config import global_config +from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +logger = get_logger("mood") + +def init_prompt(): + Prompt( + """ +{chat_talking_prompt} +以上是群里正在进行的聊天记录 + +{indentify_block} +你刚刚的情绪状态是:{mood_state} + +现在,发送了消息,引起了你的注意,你对其进行了阅读和思考,请你输出一句话描述你新的情绪状态 +请只输出情绪状态,不要输出其他内容: +""", + "change_mood_prompt", + ) + +class ChatMood: + def __init__(self,chat_id:str): + self.chat_id:str = chat_id + self.mood_state:str = "感觉很平静" + + + self.mood_model = LLMRequest( + model=global_config.model.utils, + temperature=0.7, + request_type="mood", + ) + + self.last_change_time = 0 + + async def update_mood_by_message(self,message:MessageRecv,interested_rate:float): + + during_last_time = message.message_info.time - self.last_change_time + + base_probability = 0.05 + time_multiplier = 4 * (1 - math.exp(-0.01 * during_last_time)) + + if interested_rate <= 0: + interest_multiplier = 0 + else: + interest_multiplier = 3 * math.pow(interested_rate, 0.25) + + logger.info(f"base_probability: {base_probability}, time_multiplier: {time_multiplier}, interest_multiplier: {interest_multiplier}") + update_probability = min(1.0, base_probability * time_multiplier * interest_multiplier) + + if random.random() > update_probability: + return + + + + message_time = message.message_info.time + message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive( + chat_id=self.chat_id, + timestamp_start=self.last_change_time, + timestamp_end=message_time, + limit=15, + limit_mode="last", + ) + chat_talking_prompt = build_readable_messages( + message_list_before_now, + replace_bot_name=True, + merge_messages=False, + timestamp_mode="normal_no_YMD", + read_mark=0.0, + truncate=True, + show_actions=True, + ) + + + bot_name = global_config.bot.nickname + if global_config.bot.alias_names: + bot_nickname = f",也有人叫你{','.join(global_config.bot.alias_names)}" + else: + bot_nickname = "" + + prompt_personality = global_config.personality.personality_core + indentify_block = f"你的名字是{bot_name}{bot_nickname},你{prompt_personality}:" + + prompt = await global_prompt_manager.format_prompt( + "change_mood_prompt", + chat_talking_prompt=chat_talking_prompt, + indentify_block=indentify_block, + mood_state=self.mood_state, + ) + + logger.info(f"prompt: {prompt}") + response, (reasoning_content, model_name) = await self.mood_model.generate_response_async(prompt=prompt) + logger.info(f"response: {response}") + logger.info(f"reasoning_content: {reasoning_content}") + + + self.mood_state = response + + + self.last_change_time = message_time + + +class MoodManager: + + def __init__(self): + self.mood_list:list[ChatMood] = [] + """当前情绪状态""" + + def get_mood_by_chat_id(self, chat_id:str) -> ChatMood: + for mood in self.mood_list: + if mood.chat_id == chat_id: + return mood + + new_mood = ChatMood(chat_id) + self.mood_list.append(new_mood) + return new_mood + + def reset_mood_by_chat_id(self, chat_id:str): + for mood in self.mood_list: + if mood.chat_id == chat_id: + mood.mood_state = "感觉很平静" + return + self.mood_list.append(ChatMood(chat_id)) + + + +init_prompt() + +mood_manager = MoodManager() +"""全局情绪管理器""" diff --git a/src/person_info/relationship_builder.py b/src/person_info/relationship_builder.py index f1bf98625..0b443850f 100644 --- a/src/person_info/relationship_builder.py +++ b/src/person_info/relationship_builder.py @@ -426,7 +426,7 @@ class RelationshipBuilder: if not segments_to_process and segments: segments.sort(key=lambda x: x["end_time"], reverse=True) segments_to_process.append(segments[0]) - logger.debug(f"随机丢弃了所有消息段,强制保留最新的一个以进行处理。") + logger.debug("随机丢弃了所有消息段,强制保留最新的一个以进行处理。") dropped_count = original_segment_count - len(segments_to_process) if dropped_count > 0: diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index be46d754e..d5dd94df6 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -1,12 +1,10 @@ from src.common.logger import get_logger -import math from src.person_info.person_info import PersonInfoManager, get_person_info_manager import time import random from src.llm_models.utils_model import LLMRequest from src.config.config import global_config from src.chat.utils.chat_message_builder import build_readable_messages -from src.manager.mood_manager import mood_manager import json from json_repair import repair_json from datetime import datetime diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 47526d52e..49e0674ef 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -45,7 +45,8 @@ compress_indentity = true # 是否压缩身份,压缩后会精简身份信息 [expression] # 表达方式 enable_expression = true # 是否启用表达方式 -expression_style = "描述麦麦说话的表达风格,表达习惯,例如:(请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景。)" +# 描述麦麦说话的表达风格,表达习惯,例如:(请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景。) +expression_style = "请回复的平淡一些,简短一些,说中文,可以参考贴吧,知乎和微博的回复风格,回复不要浮夸,不要用夸张修辞,不要刻意突出自身学科背景。" enable_expression_learning = false # 是否启用表达学习,麦麦会学习不同群里人类说话风格(群之间不互通) learning_interval = 600 # 学习间隔 单位秒