feat:s4u模式现在可以操控表情
This commit is contained in:
244
src/mais4u/mais4u_chat/body_emotion_action_manager.py
Normal file
244
src/mais4u/mais4u_chat/body_emotion_action_manager.py
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import json
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
from src.chat.message_receive.message import MessageRecv
|
||||||
|
from src.llm_models.utils_model import LLMRequest
|
||||||
|
from src.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
|
||||||
|
from src.manager.async_task_manager import AsyncTask, async_task_manager
|
||||||
|
from json_repair import repair_json
|
||||||
|
|
||||||
|
logger = get_logger("action")
|
||||||
|
|
||||||
|
|
||||||
|
def init_prompt():
|
||||||
|
Prompt(
|
||||||
|
"""
|
||||||
|
{chat_talking_prompt}
|
||||||
|
以上是群里正在进行的聊天记录
|
||||||
|
|
||||||
|
{indentify_block}
|
||||||
|
你现在的动作状态是:
|
||||||
|
- 手部:{hand_action}
|
||||||
|
- 上半身:{upper_body_action}
|
||||||
|
- 头部:{head_action}
|
||||||
|
|
||||||
|
现在,因为你发送了消息,或者群里其他人发送了消息,引起了你的注意,你对其进行了阅读和思考,请你更新你的动作状态。
|
||||||
|
请只按照以下json格式输出,描述你新的动作状态,每个动作一到三个中文词,确保每个字段都存在:
|
||||||
|
{{
|
||||||
|
"hand_action": "...",
|
||||||
|
"upper_body_action": "...",
|
||||||
|
"head_action": "..."
|
||||||
|
}}
|
||||||
|
""",
|
||||||
|
"change_action_prompt",
|
||||||
|
)
|
||||||
|
Prompt(
|
||||||
|
"""
|
||||||
|
{chat_talking_prompt}
|
||||||
|
以上是群里最近的聊天记录
|
||||||
|
|
||||||
|
{indentify_block}
|
||||||
|
你之前的动作状态是:
|
||||||
|
- 手部:{hand_action}
|
||||||
|
- 上半身:{upper_body_action}
|
||||||
|
- 头部:{head_action}
|
||||||
|
|
||||||
|
距离你上次关注群里消息已经过去了一段时间,你冷静了下来,你的动作会趋于平缓或静止,请你输出你现在新的动作状态,用中文。
|
||||||
|
请只按照以下json格式输出,描述你新的动作状态,每个动作一到三个词,确保每个字段都存在:
|
||||||
|
{{
|
||||||
|
"hand_action": "...",
|
||||||
|
"upper_body_action": "...",
|
||||||
|
"head_action": "..."
|
||||||
|
}}
|
||||||
|
""",
|
||||||
|
"regress_action_prompt",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ChatAction:
|
||||||
|
def __init__(self, chat_id: str):
|
||||||
|
self.chat_id: str = chat_id
|
||||||
|
self.hand_action: str = "双手放在桌面"
|
||||||
|
self.upper_body_action: str = "坐着"
|
||||||
|
self.head_action: str = "注视摄像机"
|
||||||
|
|
||||||
|
self.regression_count: int = 0
|
||||||
|
|
||||||
|
self.action_model = LLMRequest(
|
||||||
|
model=global_config.model.emotion,
|
||||||
|
temperature=0.7,
|
||||||
|
request_type="action",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.last_change_time = 0
|
||||||
|
|
||||||
|
async def update_action_by_message(self, message: MessageRecv):
|
||||||
|
self.regression_count = 0
|
||||||
|
|
||||||
|
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_action_prompt",
|
||||||
|
chat_talking_prompt=chat_talking_prompt,
|
||||||
|
indentify_block=indentify_block,
|
||||||
|
hand_action=self.hand_action,
|
||||||
|
upper_body_action=self.upper_body_action,
|
||||||
|
head_action=self.head_action,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"prompt: {prompt}")
|
||||||
|
response, (reasoning_content, model_name) = await self.action_model.generate_response_async(prompt=prompt)
|
||||||
|
logger.info(f"response: {response}")
|
||||||
|
logger.info(f"reasoning_content: {reasoning_content}")
|
||||||
|
|
||||||
|
action_data = json.loads(repair_json(response))
|
||||||
|
|
||||||
|
if action_data:
|
||||||
|
self.hand_action = action_data.get("hand_action", self.hand_action)
|
||||||
|
self.upper_body_action = action_data.get("upper_body_action", self.upper_body_action)
|
||||||
|
self.head_action = action_data.get("head_action", self.head_action)
|
||||||
|
|
||||||
|
self.last_change_time = message_time
|
||||||
|
|
||||||
|
async def regress_action(self):
|
||||||
|
message_time = time.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(
|
||||||
|
"regress_action_prompt",
|
||||||
|
chat_talking_prompt=chat_talking_prompt,
|
||||||
|
indentify_block=indentify_block,
|
||||||
|
hand_action=self.hand_action,
|
||||||
|
upper_body_action=self.upper_body_action,
|
||||||
|
head_action=self.head_action,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(f"prompt: {prompt}")
|
||||||
|
response, (reasoning_content, model_name) = await self.action_model.generate_response_async(prompt=prompt)
|
||||||
|
logger.info(f"response: {response}")
|
||||||
|
logger.info(f"reasoning_content: {reasoning_content}")
|
||||||
|
|
||||||
|
action_data = json.loads(repair_json(response))
|
||||||
|
if action_data:
|
||||||
|
self.hand_action = action_data.get("hand_action", self.hand_action)
|
||||||
|
self.upper_body_action = action_data.get("upper_body_action", self.upper_body_action)
|
||||||
|
self.head_action = action_data.get("head_action", self.head_action)
|
||||||
|
|
||||||
|
self.regression_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
class ActionRegressionTask(AsyncTask):
|
||||||
|
def __init__(self, action_manager: "ActionManager"):
|
||||||
|
super().__init__(task_name="ActionRegressionTask", run_interval=30)
|
||||||
|
self.action_manager = action_manager
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
logger.debug("Running action regression task...")
|
||||||
|
now = time.time()
|
||||||
|
for action_state in self.action_manager.action_state_list:
|
||||||
|
if action_state.last_change_time == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if now - action_state.last_change_time > 180:
|
||||||
|
if action_state.regression_count >= 3:
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.info(f"chat {action_state.chat_id} 开始动作回归, 这是第 {action_state.regression_count + 1} 次")
|
||||||
|
await action_state.regress_action()
|
||||||
|
|
||||||
|
|
||||||
|
class ActionManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.action_state_list: list[ChatAction] = []
|
||||||
|
"""当前动作状态"""
|
||||||
|
self.task_started: bool = False
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
"""启动动作回归后台任务"""
|
||||||
|
if self.task_started:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info("启动动作回归任务...")
|
||||||
|
task = ActionRegressionTask(self)
|
||||||
|
await async_task_manager.add_task(task)
|
||||||
|
self.task_started = True
|
||||||
|
logger.info("动作回归任务已启动")
|
||||||
|
|
||||||
|
def get_action_state_by_chat_id(self, chat_id: str) -> ChatAction:
|
||||||
|
for action_state in self.action_state_list:
|
||||||
|
if action_state.chat_id == chat_id:
|
||||||
|
return action_state
|
||||||
|
|
||||||
|
new_action_state = ChatAction(chat_id)
|
||||||
|
self.action_state_list.append(new_action_state)
|
||||||
|
return new_action_state
|
||||||
|
|
||||||
|
def reset_action_state_by_chat_id(self, chat_id: str):
|
||||||
|
for action_state in self.action_state_list:
|
||||||
|
if action_state.chat_id == chat_id:
|
||||||
|
action_state.hand_action = "双手放在桌面"
|
||||||
|
action_state.upper_body_action = "坐着"
|
||||||
|
action_state.head_action = "注视摄像机"
|
||||||
|
action_state.regression_count = 0
|
||||||
|
return
|
||||||
|
self.action_state_list.append(ChatAction(chat_id))
|
||||||
|
|
||||||
|
|
||||||
|
init_prompt()
|
||||||
|
|
||||||
|
action_manager = ActionManager()
|
||||||
|
"""全局动作管理器"""
|
||||||
365
src/mais4u/mais4u_chat/s4u_mood_manager.py
Normal file
365
src/mais4u/mais4u_chat/s4u_mood_manager.py
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
from src.chat.message_receive.message import MessageRecv
|
||||||
|
from src.llm_models.utils_model import LLMRequest
|
||||||
|
from src.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
|
||||||
|
from src.manager.async_task_manager import AsyncTask, async_task_manager
|
||||||
|
from src.plugin_system.apis import send_api
|
||||||
|
|
||||||
|
logger = get_logger("mood")
|
||||||
|
|
||||||
|
|
||||||
|
async def send_joy_action(chat_id: str):
|
||||||
|
action_content = {"action": "Joy_eye", "data": 1.0}
|
||||||
|
await send_api.custom_to_stream(message_type="face_emotion", content=action_content, stream_id=chat_id)
|
||||||
|
logger.info(f"[{chat_id}] 已发送 Joy 动作: {action_content}")
|
||||||
|
|
||||||
|
await asyncio.sleep(5.0)
|
||||||
|
|
||||||
|
end_action_content = {"action": "Joy_eye", "data": 0.0}
|
||||||
|
await send_api.custom_to_stream(message_type="face_emotion", content=end_action_content, stream_id=chat_id)
|
||||||
|
logger.info(f"[{chat_id}] 已发送 Joy 结束动作: {end_action_content}")
|
||||||
|
|
||||||
|
|
||||||
|
def init_prompt():
|
||||||
|
Prompt(
|
||||||
|
"""
|
||||||
|
{chat_talking_prompt}
|
||||||
|
以上是直播间里正在进行的对话
|
||||||
|
|
||||||
|
{indentify_block}
|
||||||
|
你刚刚的情绪状态是:{mood_state}
|
||||||
|
|
||||||
|
现在,发送了消息,引起了你的注意,你对其进行了阅读和思考,请你输出一句话描述你新的情绪状态,不要输出任何其他内容
|
||||||
|
请只输出情绪状态,不要输出其他内容:
|
||||||
|
""",
|
||||||
|
"change_mood_prompt",
|
||||||
|
)
|
||||||
|
Prompt(
|
||||||
|
"""
|
||||||
|
{chat_talking_prompt}
|
||||||
|
以上是直播间里最近的对话
|
||||||
|
|
||||||
|
{indentify_block}
|
||||||
|
你之前的情绪状态是:{mood_state}
|
||||||
|
|
||||||
|
距离你上次关注直播间消息已经过去了一段时间,你冷静了下来,请你输出一句话描述你现在的情绪状态
|
||||||
|
请只输出情绪状态,不要输出其他内容:
|
||||||
|
""",
|
||||||
|
"regress_mood_prompt",
|
||||||
|
)
|
||||||
|
Prompt(
|
||||||
|
"""
|
||||||
|
{chat_talking_prompt}
|
||||||
|
以上是直播间里正在进行的对话
|
||||||
|
|
||||||
|
{indentify_block}
|
||||||
|
你刚刚的情绪状态是:{mood_state}
|
||||||
|
具体来说,从1-10分,你的情绪状态是:
|
||||||
|
喜(Joy): {joy}
|
||||||
|
怒(Anger): {anger}
|
||||||
|
哀(Sorrow): {sorrow}
|
||||||
|
乐(Pleasure): {pleasure}
|
||||||
|
惧(Fear): {fear}
|
||||||
|
|
||||||
|
现在,发送了消息,引起了你的注意,你对其进行了阅读和思考。请基于对话内容,评估你新的情绪状态。
|
||||||
|
请以JSON格式输出你新的情绪状态,包含“喜怒哀乐惧”五个维度,每个维度的取值范围为1-10。
|
||||||
|
键值请使用英文: "joy", "anger", "sorrow", "pleasure", "fear".
|
||||||
|
例如: {{"joy": 5, "anger": 1, "sorrow": 1, "pleasure": 5, "fear": 1}}
|
||||||
|
不要输出任何其他内容,只输出JSON。
|
||||||
|
""",
|
||||||
|
"change_mood_numerical_prompt",
|
||||||
|
)
|
||||||
|
Prompt(
|
||||||
|
"""
|
||||||
|
{chat_talking_prompt}
|
||||||
|
以上是直播间里最近的对话
|
||||||
|
|
||||||
|
{indentify_block}
|
||||||
|
你之前的情绪状态是:{mood_state}
|
||||||
|
具体来说,从1-10分,你的情绪状态是:
|
||||||
|
喜(Joy): {joy}
|
||||||
|
怒(Anger): {anger}
|
||||||
|
哀(Sorrow): {sorrow}
|
||||||
|
乐(Pleasure): {pleasure}
|
||||||
|
惧(Fear): {fear}
|
||||||
|
|
||||||
|
距离你上次关注直播间消息已经过去了一段时间,你冷静了下来。请基于此,评估你现在的情绪状态。
|
||||||
|
请以JSON格式输出你新的情绪状态,包含“喜怒哀乐惧”五个维度,每个维度的取值范围为1-10。
|
||||||
|
键值请使用英文: "joy", "anger", "sorrow", "pleasure", "fear".
|
||||||
|
例如: {{"joy": 5, "anger": 1, "sorrow": 1, "pleasure": 5, "fear": 1}}
|
||||||
|
不要输出任何其他内容,只输出JSON。
|
||||||
|
""",
|
||||||
|
"regress_mood_numerical_prompt",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ChatMood:
|
||||||
|
def __init__(self, chat_id: str):
|
||||||
|
self.chat_id: str = chat_id
|
||||||
|
self.mood_state: str = "感觉很平静"
|
||||||
|
self.mood_values: dict[str, int] = {"joy": 5, "anger": 1, "sorrow": 1, "pleasure": 5, "fear": 1}
|
||||||
|
|
||||||
|
self.regression_count: int = 0
|
||||||
|
|
||||||
|
self.mood_model = LLMRequest(
|
||||||
|
model=global_config.model.emotion,
|
||||||
|
temperature=0.7,
|
||||||
|
request_type="mood_text",
|
||||||
|
)
|
||||||
|
self.mood_model_numerical = LLMRequest(
|
||||||
|
model=global_config.model.emotion,
|
||||||
|
temperature=0.4,
|
||||||
|
request_type="mood_numerical",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.last_change_time = 0
|
||||||
|
|
||||||
|
def _parse_numerical_mood(self, response: str) -> dict[str, int] | None:
|
||||||
|
try:
|
||||||
|
# The LLM might output markdown with json inside
|
||||||
|
if "```json" in response:
|
||||||
|
response = response.split("```json")[1].split("```")[0]
|
||||||
|
elif "```" in response:
|
||||||
|
response = response.split("```")[1].split("```")[0]
|
||||||
|
|
||||||
|
data = json.loads(response)
|
||||||
|
|
||||||
|
# Validate
|
||||||
|
required_keys = {"joy", "anger", "sorrow", "pleasure", "fear"}
|
||||||
|
if not required_keys.issubset(data.keys()):
|
||||||
|
logger.warning(f"Numerical mood response missing keys: {response}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
for key in required_keys:
|
||||||
|
value = data[key]
|
||||||
|
if not isinstance(value, int) or not (1 <= value <= 10):
|
||||||
|
logger.warning(f"Numerical mood response invalid value for {key}: {value} in {response}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return {key: data[key] for key in required_keys}
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
logger.warning(f"Failed to parse numerical mood JSON: {response}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error parsing numerical mood: {e}, response: {response}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def update_mood_by_message(self, message: MessageRecv):
|
||||||
|
self.regression_count = 0
|
||||||
|
|
||||||
|
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}:"
|
||||||
|
|
||||||
|
async def _update_text_mood():
|
||||||
|
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.debug(f"text mood prompt: {prompt}")
|
||||||
|
response, (reasoning_content, model_name) = await self.mood_model.generate_response_async(prompt=prompt)
|
||||||
|
logger.info(f"text mood response: {response}")
|
||||||
|
logger.debug(f"text mood reasoning_content: {reasoning_content}")
|
||||||
|
return response
|
||||||
|
|
||||||
|
async def _update_numerical_mood():
|
||||||
|
prompt = await global_prompt_manager.format_prompt(
|
||||||
|
"change_mood_numerical_prompt",
|
||||||
|
chat_talking_prompt=chat_talking_prompt,
|
||||||
|
indentify_block=indentify_block,
|
||||||
|
mood_state=self.mood_state,
|
||||||
|
joy=self.mood_values["joy"],
|
||||||
|
anger=self.mood_values["anger"],
|
||||||
|
sorrow=self.mood_values["sorrow"],
|
||||||
|
pleasure=self.mood_values["pleasure"],
|
||||||
|
fear=self.mood_values["fear"],
|
||||||
|
)
|
||||||
|
logger.info(f"numerical mood prompt: {prompt}")
|
||||||
|
response, (reasoning_content, model_name) = await self.mood_model_numerical.generate_response_async(
|
||||||
|
prompt=prompt
|
||||||
|
)
|
||||||
|
logger.info(f"numerical mood response: {response}")
|
||||||
|
logger.debug(f"numerical mood reasoning_content: {reasoning_content}")
|
||||||
|
return self._parse_numerical_mood(response)
|
||||||
|
|
||||||
|
results = await asyncio.gather(_update_text_mood(), _update_numerical_mood())
|
||||||
|
text_mood_response, numerical_mood_response = results
|
||||||
|
|
||||||
|
if text_mood_response:
|
||||||
|
self.mood_state = text_mood_response
|
||||||
|
|
||||||
|
if numerical_mood_response:
|
||||||
|
self.mood_values = numerical_mood_response
|
||||||
|
if self.mood_values.get("joy", 0) > 5:
|
||||||
|
asyncio.create_task(send_joy_action(self.chat_id))
|
||||||
|
|
||||||
|
self.last_change_time = message_time
|
||||||
|
|
||||||
|
async def regress_mood(self):
|
||||||
|
message_time = time.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}:"
|
||||||
|
|
||||||
|
async def _regress_text_mood():
|
||||||
|
prompt = await global_prompt_manager.format_prompt(
|
||||||
|
"regress_mood_prompt",
|
||||||
|
chat_talking_prompt=chat_talking_prompt,
|
||||||
|
indentify_block=indentify_block,
|
||||||
|
mood_state=self.mood_state,
|
||||||
|
)
|
||||||
|
logger.debug(f"text regress prompt: {prompt}")
|
||||||
|
response, (reasoning_content, model_name) = await self.mood_model.generate_response_async(prompt=prompt)
|
||||||
|
logger.info(f"text regress response: {response}")
|
||||||
|
logger.debug(f"text regress reasoning_content: {reasoning_content}")
|
||||||
|
return response
|
||||||
|
|
||||||
|
async def _regress_numerical_mood():
|
||||||
|
prompt = await global_prompt_manager.format_prompt(
|
||||||
|
"regress_mood_numerical_prompt",
|
||||||
|
chat_talking_prompt=chat_talking_prompt,
|
||||||
|
indentify_block=indentify_block,
|
||||||
|
mood_state=self.mood_state,
|
||||||
|
joy=self.mood_values["joy"],
|
||||||
|
anger=self.mood_values["anger"],
|
||||||
|
sorrow=self.mood_values["sorrow"],
|
||||||
|
pleasure=self.mood_values["pleasure"],
|
||||||
|
fear=self.mood_values["fear"],
|
||||||
|
)
|
||||||
|
logger.debug(f"numerical regress prompt: {prompt}")
|
||||||
|
response, (reasoning_content, model_name) = await self.mood_model_numerical.generate_response_async(
|
||||||
|
prompt=prompt
|
||||||
|
)
|
||||||
|
logger.info(f"numerical regress response: {response}")
|
||||||
|
logger.debug(f"numerical regress reasoning_content: {reasoning_content}")
|
||||||
|
return self._parse_numerical_mood(response)
|
||||||
|
|
||||||
|
results = await asyncio.gather(_regress_text_mood(), _regress_numerical_mood())
|
||||||
|
text_mood_response, numerical_mood_response = results
|
||||||
|
|
||||||
|
if text_mood_response:
|
||||||
|
self.mood_state = text_mood_response
|
||||||
|
|
||||||
|
if numerical_mood_response:
|
||||||
|
self.mood_values = numerical_mood_response
|
||||||
|
if self.mood_values.get("joy", 0) > 5:
|
||||||
|
asyncio.create_task(send_joy_action(self.chat_id))
|
||||||
|
|
||||||
|
self.regression_count += 1
|
||||||
|
|
||||||
|
|
||||||
|
class MoodRegressionTask(AsyncTask):
|
||||||
|
def __init__(self, mood_manager: "MoodManager"):
|
||||||
|
super().__init__(task_name="MoodRegressionTask", run_interval=30)
|
||||||
|
self.mood_manager = mood_manager
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
logger.debug("Running mood regression task...")
|
||||||
|
now = time.time()
|
||||||
|
for mood in self.mood_manager.mood_list:
|
||||||
|
if mood.last_change_time == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if now - mood.last_change_time > 180:
|
||||||
|
if mood.regression_count >= 3:
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.info(f"chat {mood.chat_id} 开始情绪回归, 这是第 {mood.regression_count + 1} 次")
|
||||||
|
await mood.regress_mood()
|
||||||
|
|
||||||
|
|
||||||
|
class MoodManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.mood_list: list[ChatMood] = []
|
||||||
|
"""当前情绪状态"""
|
||||||
|
self.task_started: bool = False
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
"""启动情绪回归后台任务"""
|
||||||
|
if self.task_started:
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info("启动情绪回归任务...")
|
||||||
|
task = MoodRegressionTask(self)
|
||||||
|
await async_task_manager.add_task(task)
|
||||||
|
self.task_started = True
|
||||||
|
logger.info("情绪回归任务已启动")
|
||||||
|
|
||||||
|
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 = "感觉很平静"
|
||||||
|
mood.regression_count = 0
|
||||||
|
return
|
||||||
|
self.mood_list.append(ChatMood(chat_id))
|
||||||
|
|
||||||
|
|
||||||
|
init_prompt()
|
||||||
|
|
||||||
|
mood_manager = MoodManager()
|
||||||
|
"""全局情绪管理器"""
|
||||||
@@ -1,7 +1,18 @@
|
|||||||
|
import asyncio
|
||||||
|
import math
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||||
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.message_receive.chat_stream import get_chat_manager
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
|
from src.chat.utils.timer_calculator import Timer
|
||||||
|
from src.chat.utils.utils import is_mentioned_bot_in_message
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
from src.config.config import global_config
|
||||||
|
from src.mais4u.mais4u_chat.body_emotion_action_manager import action_manager
|
||||||
|
from src.mais4u.mais4u_chat.s4u_mood_manager import mood_manager
|
||||||
|
|
||||||
from .s4u_chat import get_s4u_chat_manager
|
from .s4u_chat import get_s4u_chat_manager
|
||||||
|
|
||||||
|
|
||||||
@@ -10,6 +21,42 @@ from .s4u_chat import get_s4u_chat_manager
|
|||||||
logger = get_logger("chat")
|
logger = get_logger("chat")
|
||||||
|
|
||||||
|
|
||||||
|
async def _calculate_interest(message: MessageRecv) -> Tuple[float, bool]:
|
||||||
|
"""计算消息的兴趣度
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message: 待处理的消息对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple[float, bool]: (兴趣度, 是否被提及)
|
||||||
|
"""
|
||||||
|
is_mentioned, _ = is_mentioned_bot_in_message(message)
|
||||||
|
interested_rate = 0.0
|
||||||
|
|
||||||
|
if global_config.memory.enable_memory:
|
||||||
|
with Timer("记忆激活"):
|
||||||
|
interested_rate = await hippocampus_manager.get_activate_from_text(
|
||||||
|
message.processed_plain_text,
|
||||||
|
fast_retrieval=True,
|
||||||
|
)
|
||||||
|
logger.debug(f"记忆激活率: {interested_rate:.2f}")
|
||||||
|
|
||||||
|
text_len = len(message.processed_plain_text)
|
||||||
|
# 根据文本长度调整兴趣度,长度越大兴趣度越高,但增长率递减,最低0.01,最高0.05
|
||||||
|
# 采用对数函数实现递减增长
|
||||||
|
|
||||||
|
base_interest = 0.01 + (0.05 - 0.01) * (math.log10(text_len + 1) / math.log10(1000 + 1))
|
||||||
|
base_interest = min(max(base_interest, 0.01), 0.05)
|
||||||
|
|
||||||
|
interested_rate += base_interest
|
||||||
|
|
||||||
|
if is_mentioned:
|
||||||
|
interest_increase_on_mention = 1
|
||||||
|
interested_rate += interest_increase_on_mention
|
||||||
|
|
||||||
|
return interested_rate, is_mentioned
|
||||||
|
|
||||||
|
|
||||||
class S4UMessageProcessor:
|
class S4UMessageProcessor:
|
||||||
"""心流处理器,负责处理接收到的消息并计算兴趣度"""
|
"""心流处理器,负责处理接收到的消息并计算兴趣度"""
|
||||||
|
|
||||||
@@ -53,5 +100,13 @@ class S4UMessageProcessor:
|
|||||||
else:
|
else:
|
||||||
await s4u_chat.add_message(message)
|
await s4u_chat.add_message(message)
|
||||||
|
|
||||||
|
interested_rate, _ = await _calculate_interest(message)
|
||||||
|
|
||||||
|
chat_mood = mood_manager.get_mood_by_chat_id(chat.stream_id)
|
||||||
|
asyncio.create_task(chat_mood.update_mood_by_message(message))
|
||||||
|
chat_action = action_manager.get_action_state_by_chat_id(chat.stream_id)
|
||||||
|
asyncio.create_task(chat_action.update_action_by_message(message))
|
||||||
|
# asyncio.create_task(chat_action.update_facial_expression_by_message(message, interested_rate))
|
||||||
|
|
||||||
# 7. 日志记录
|
# 7. 日志记录
|
||||||
logger.info(f"[S4U]{userinfo.user_nickname}:{message.processed_plain_text}")
|
logger.info(f"[S4U]{userinfo.user_nickname}:{message.processed_plain_text}")
|
||||||
|
|||||||
@@ -17,11 +17,6 @@ logger = get_logger("prompt")
|
|||||||
|
|
||||||
|
|
||||||
def init_prompt():
|
def init_prompt():
|
||||||
Prompt("你正在qq群里聊天,下面是群里在聊的内容:", "chat_target_group1")
|
|
||||||
Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1")
|
|
||||||
Prompt("在群里聊天", "chat_target_group2")
|
|
||||||
Prompt("和{sender_name}私聊", "chat_target_private2")
|
|
||||||
|
|
||||||
Prompt("\n你有以下这些**知识**:\n{prompt_info}\n请你**记住上面的知识**,之后可能会用到。\n", "knowledge_prompt")
|
Prompt("\n你有以下这些**知识**:\n{prompt_info}\n请你**记住上面的知识**,之后可能会用到。\n", "knowledge_prompt")
|
||||||
Prompt("\n关于你们的关系,你需要知道:\n{relation_info}\n", "relation_prompt")
|
Prompt("\n关于你们的关系,你需要知道:\n{relation_info}\n", "relation_prompt")
|
||||||
Prompt("你回想起了一些事情:\n{memory_info}\n", "memory_prompt")
|
Prompt("你回想起了一些事情:\n{memory_info}\n", "memory_prompt")
|
||||||
|
|||||||
Reference in New Issue
Block a user