feat;新的情绪系统

This commit is contained in:
SengokuCola
2025-07-09 01:50:26 +08:00
parent 0d33a6cf06
commit 50f0ddf2ce
16 changed files with 211 additions and 414 deletions

82
bot.py
View File

@@ -236,67 +236,6 @@ def raw_main():
return MainSystem() 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__": if __name__ == "__main__":
exit_code = 0 # 用于记录程序最终的退出状态 exit_code = 0 # 用于记录程序最终的退出状态
@@ -314,17 +253,7 @@ if __name__ == "__main__":
# Schedule tasks returns a future that runs forever. # Schedule tasks returns a future that runs forever.
# We can run console_input_loop concurrently. # We can run console_input_loop concurrently.
main_tasks = loop.create_task(main_system.schedule_tasks()) main_tasks = loop.create_task(main_system.schedule_tasks())
loop.run_until_complete(main_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)
except KeyboardInterrupt: except KeyboardInterrupt:
# loop.run_until_complete(get_global_api().stop()) # loop.run_until_complete(get_global_api().stop())
@@ -336,15 +265,6 @@ if __name__ == "__main__":
logger.error(f"优雅关闭时发生错误: {ge}") 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: except Exception as e:
logger.error(f"主程序发生异常: {str(e)} {str(traceback.format_exc())}") logger.error(f"主程序发生异常: {str(e)} {str(traceback.format_exc())}")

46
interested_rates.txt Normal file
View File

@@ -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

View File

@@ -1,4 +1,4 @@
from src.manager.mood_manager import mood_manager from src.mood.mood_manager import mood_manager
import enum import enum
@@ -12,6 +12,3 @@ class ChatStateInfo:
def __init__(self): def __init__(self):
self.chat_status: ChatState = ChatState.NORMAL self.chat_status: ChatState = ChatState.NORMAL
self.current_state_time = 120 self.current_state_time = 120
self.mood_manager = mood_manager
self.mood = self.mood_manager.get_mood_prompt()

View File

@@ -1,5 +1,6 @@
from src.chat.memory_system.Hippocampus import hippocampus_manager from src.chat.memory_system.Hippocampus import hippocampus_manager
from src.config.config import global_config from src.config.config import global_config
import asyncio
from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.message import MessageRecv
from src.chat.message_receive.storage import MessageStorage from src.chat.message_receive.storage import MessageStorage
from src.chat.heart_flow.heartflow import heartflow from src.chat.heart_flow.heartflow import heartflow
@@ -13,6 +14,7 @@ import traceback
from typing import Tuple from typing import Tuple
from src.person_info.relationship_manager import get_relationship_manager from src.person_info.relationship_manager import get_relationship_manager
from src.mood.mood_manager import mood_manager
logger = get_logger("chat") logger = get_logger("chat")
@@ -114,6 +116,12 @@ class HeartFCMessageReceiver:
interested_rate, is_mentioned = await _calculate_interest(message) interested_rate, is_mentioned = await _calculate_interest(message)
subheartflow.add_message_to_normal_chat_cache(message, interested_rate, is_mentioned) 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. 日志记录 # 7. 日志记录
mes_name = chat.group_info.group_name if chat.group_info else "私聊" 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)) # current_time = time.strftime("%H:%M:%S", time.localtime(message.message_info.time))

View File

@@ -26,8 +26,6 @@ class SubHeartflow:
Args: Args:
subheartflow_id: 子心流唯一标识符 subheartflow_id: 子心流唯一标识符
mai_states: 麦麦状态信息实例
hfc_no_reply_callback: HFChatting 连续不回复时触发的回调
""" """
# 基础属性,两个值是一样的 # 基础属性,两个值是一样的
self.subheartflow_id = subheartflow_id self.subheartflow_id = subheartflow_id

View File

@@ -3,7 +3,7 @@ import os
from typing import Dict, Any from typing import Dict, Any
from src.common.logger import get_logger 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.chat_stream import get_chat_manager
from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.message import MessageRecv
from src.experimental.only_message_process import MessageProcessor from src.experimental.only_message_process import MessageProcessor

View File

@@ -22,7 +22,7 @@ from src.chat.planner_actions.planner import ActionPlanner
from src.chat.planner_actions.action_modifier import ActionModifier from src.chat.planner_actions.action_modifier import ActionModifier
from src.chat.utils.utils import get_chat_type_and_target_info 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() willing_manager = get_willing_manager()

View File

@@ -18,7 +18,7 @@ from src.chat.utils.chat_message_builder import build_readable_messages, get_raw
import time import time
import asyncio import asyncio
from src.chat.express.expression_selector import expression_selector 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 from src.person_info.relationship_fetcher import relationship_fetcher_manager
import random import random
import ast import ast
@@ -55,9 +55,9 @@ def init_prompt():
{identity} {identity}
{action_descriptions} {action_descriptions}
你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},请你给出回复 你正在{chat_target_2},现在的心情是:{mood_state}
{config_expression_style} 现在请你读读之前的聊天记录,并给出回复
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,注意不要复读你说过的话 {config_expression_style}注意不要复读你说过的话
{keywords_reaction_prompt} {keywords_reaction_prompt}
请注意不要输出多余内容(包括前后缀冒号和引号at或 @等 )。只输出回复内容。 请注意不要输出多余内容(包括前后缀冒号和引号at或 @等 )。只输出回复内容。
{moderation_prompt} {moderation_prompt}
@@ -504,6 +504,9 @@ class DefaultReplyer:
reply_to = reply_data.get("reply_to", "none") reply_to = reply_data.get("reply_to", "none")
extra_info_block = reply_data.get("extra_info", "") or reply_data.get("extra_info_block", "") 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) sender, target = self._parse_reply_target(reply_to)
# 构建action描述 (如果启用planner) # 构建action描述 (如果启用planner)
@@ -639,8 +642,6 @@ class DefaultReplyer:
else: else:
reply_target_block = "" reply_target_block = ""
mood_prompt = mood_manager.get_mood_prompt()
prompt_info = await get_prompt_info(target, threshold=0.38) prompt_info = await get_prompt_info(target, threshold=0.38)
if prompt_info: if prompt_info:
prompt_info = await global_prompt_manager.format_prompt("knowledge_prompt", prompt_info=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, config_expression_style=global_config.expression.expression_style,
action_descriptions=action_descriptions, action_descriptions=action_descriptions,
chat_target_2=chat_target_2, chat_target_2=chat_target_2,
mood_prompt=mood_prompt, mood_state=mood_prompt,
) )
return prompt return prompt
@@ -774,8 +775,6 @@ class DefaultReplyer:
else: else:
reply_target_block = "" reply_target_block = ""
mood_manager.get_mood_prompt()
if is_group_chat: if is_group_chat:
chat_target_1 = await global_prompt_manager.get_prompt_async("chat_target_group1") 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") chat_target_2 = await global_prompt_manager.get_prompt_async("chat_target_group2")

View File

@@ -8,7 +8,7 @@ import numpy as np
from maim_message import UserInfo from maim_message import UserInfo
from src.common.logger import get_logger 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 ..message_receive.message import MessageRecv
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from .typo_generator import ChineseTypoGenerator from .typo_generator import ChineseTypoGenerator
@@ -412,12 +412,12 @@ def calculate_typing_time(
- 在所有输入结束后额外加上回车时间0.3秒 - 在所有输入结束后额外加上回车时间0.3秒
- 如果is_emoji为True将使用固定1秒的输入时间 - 如果is_emoji为True将使用固定1秒的输入时间
""" """
# 将0-1的唤醒度映射到-1到1 # # 将0-1的唤醒度映射到-1到1
mood_arousal = mood_manager.current_mood.arousal # mood_arousal = mood_manager.current_mood.arousal
# 映射到0.5到2倍的速度系数 # # 映射到0.5到2倍的速度系数
typing_speed_multiplier = 1.5**mood_arousal # 唤醒度为1时速度翻倍,为-1时速度减半 # typing_speed_multiplier = 1.5**mood_arousal # 唤醒度为1时速度翻倍,为-1时速度减半
chinese_time *= 1 / typing_speed_multiplier # chinese_time *= 1 / typing_speed_multiplier
english_time *= 1 / typing_speed_multiplier # english_time *= 1 / typing_speed_multiplier
# 计算中文字符数 # 计算中文字符数
chinese_chars = sum(1 for char in input_string if "\u4e00" <= char <= "\u9fff") chinese_chars = sum(1 for char in input_string if "\u4e00" <= char <= "\u9fff")

View File

@@ -6,7 +6,6 @@ from src.chat.express.exprssion_learner import get_expression_learner
from src.common.remote import TelemetryHeartBeatTask from src.common.remote import TelemetryHeartBeatTask
from src.manager.async_task_manager import async_task_manager from src.manager.async_task_manager import async_task_manager
from src.chat.utils.statistic import OnlineTimeRecordTask, StatisticOutputTask 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.emoji_system.emoji_manager import get_emoji_manager
from src.chat.normal_chat.willing.willing_manager import get_willing_manager from src.chat.normal_chat.willing.willing_manager import get_willing_manager
from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.message_receive.chat_stream import get_chat_manager
@@ -95,13 +94,6 @@ class MainSystem:
get_emoji_manager().initialize() get_emoji_manager().initialize()
logger.info("表情包管理器初始化成功") logger.info("表情包管理器初始化成功")
# 添加情绪衰减任务
await async_task_manager.add_task(MoodUpdateTask())
# 添加情绪打印任务
await async_task_manager.add_task(MoodPrintTask())
logger.info("情绪管理器初始化成功")
# 启动愿望管理器 # 启动愿望管理器
await willing_manager.async_task_starter() await willing_manager.async_task_starter()

View File

@@ -1,6 +1,5 @@
import os import os
from typing import AsyncGenerator from typing import AsyncGenerator
from src.llm_models.utils_model import LLMRequest
from src.mais4u.openai_client import AsyncOpenAIClient from src.mais4u.openai_client import AsyncOpenAIClient
from src.config.config import global_config from src.config.config import global_config
from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.message import MessageRecv

View File

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

135
src/mood/mood_manager.py Normal file
View File

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

View File

@@ -426,7 +426,7 @@ class RelationshipBuilder:
if not segments_to_process and segments: if not segments_to_process and segments:
segments.sort(key=lambda x: x["end_time"], reverse=True) segments.sort(key=lambda x: x["end_time"], reverse=True)
segments_to_process.append(segments[0]) segments_to_process.append(segments[0])
logger.debug(f"随机丢弃了所有消息段,强制保留最新的一个以进行处理。") logger.debug("随机丢弃了所有消息段,强制保留最新的一个以进行处理。")
dropped_count = original_segment_count - len(segments_to_process) dropped_count = original_segment_count - len(segments_to_process)
if dropped_count > 0: if dropped_count > 0:

View File

@@ -1,12 +1,10 @@
from src.common.logger import get_logger from src.common.logger import get_logger
import math
from src.person_info.person_info import PersonInfoManager, get_person_info_manager from src.person_info.person_info import PersonInfoManager, get_person_info_manager
import time import time
import random import random
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.config.config import global_config from src.config.config import global_config
from src.chat.utils.chat_message_builder import build_readable_messages from src.chat.utils.chat_message_builder import build_readable_messages
from src.manager.mood_manager import mood_manager
import json import json
from json_repair import repair_json from json_repair import repair_json
from datetime import datetime from datetime import datetime

View File

@@ -45,7 +45,8 @@ compress_indentity = true # 是否压缩身份,压缩后会精简身份信息
[expression] [expression]
# 表达方式 # 表达方式
enable_expression = true # 是否启用表达方式 enable_expression = true # 是否启用表达方式
expression_style = "描述麦麦说话的表达风格,表达习惯,例如:(请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景。)" # 描述麦麦说话的表达风格,表达习惯,例如:(请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景。)
expression_style = "请回复的平淡一些,简短一些,说中文,可以参考贴吧,知乎和微博的回复风格,回复不要浮夸,不要用夸张修辞,不要刻意突出自身学科背景。"
enable_expression_learning = false # 是否启用表达学习,麦麦会学习不同群里人类说话风格(群之间不互通) enable_expression_learning = false # 是否启用表达学习,麦麦会学习不同群里人类说话风格(群之间不互通)
learning_interval = 600 # 学习间隔 单位秒 learning_interval = 600 # 学习间隔 单位秒