Files
Mofox-Core/src/manager/mood_manager.py

297 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
"""全局情绪管理器"""