feat:将no_reply功能移动到主循环中

This commit is contained in:
SengokuCola
2025-08-09 22:45:00 +08:00
parent b57671b639
commit 685c7598b3
2 changed files with 130 additions and 216 deletions

View File

@@ -92,6 +92,8 @@ class HeartFChatting:
self.loop_mode = ChatMode.NORMAL # 初始循环模式为普通模式
self.last_action = "no_action"
self.action_manager = ActionManager()
self.action_planner = ActionPlanner(chat_id=self.stream_id, action_manager=self.action_manager)
self.action_modifier = ActionModifier(action_manager=self.action_manager, chat_id=self.stream_id)
@@ -117,6 +119,9 @@ class HeartFChatting:
self.energy_value = 5
self.focus_energy = 1
self.no_reply_consecutive = 0
async def start(self):
"""检查是否需要启动主循环,如果未激活则启动。"""
@@ -198,12 +203,112 @@ class HeartFChatting:
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
)
async def _loopbody(self):
if self.loop_mode == ChatMode.FOCUS:
if await self._observe():
self.energy_value -= 1 / global_config.chat.focus_value
def _determine_form_type(self) -> str:
"""判断使用哪种形式的no_reply"""
# 如果连续no_reply次数少于3次使用waiting形式
if self.no_reply_consecutive <= 3:
self.focus_energy = 1
else:
self.energy_value -= 3 / global_config.chat.focus_value
# 计算最近三次记录的兴趣度总和
total_recent_interest = sum(NoReplyAction._recent_interest_records)
# 获取当前聊天频率和意愿系数
talk_frequency = global_config.chat.get_current_talk_frequency(self.stream_id)
# 计算调整后的阈值
adjusted_threshold = 3 / talk_frequency
logger.info(f"{self.log_prefix} 最近三次兴趣度总和: {total_recent_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}")
# 如果兴趣度总和小于阈值进入breaking形式
if total_recent_interest < adjusted_threshold:
logger.info(f"{self.log_prefix} 兴趣度不足进入breaking形式")
self.focus_energy = random.randint(3, 6)
else:
logger.info(f"{self.log_prefix} 兴趣度充足")
self.focus_energy = 1
async def _execute_no_reply(self, new_message:List[Dict[str, Any]]) -> Tuple[bool, str]:
"""执行breaking形式的no_reply原有逻辑"""
new_message_count = len(new_message)
# 检查消息数量是否达到阈值
talk_frequency = global_config.chat.get_current_talk_frequency(self.stream_id)
modified_exit_count_threshold = self.focus_energy / talk_frequency
if new_message_count >= modified_exit_count_threshold:
# 记录兴趣度到列表
total_interest = 0.0
for msg_dict in new_message:
interest_value = msg_dict.get("interest_value", 0.0)
if msg_dict.get("processed_plain_text", ""):
total_interest += interest_value
NoReplyAction._recent_interest_records.append(total_interest)
logger.info(
f"{self.log_prefix} 累计消息数量达到{new_message_count}条(>{modified_exit_count_threshold}),结束等待"
)
return True
# 检查累计兴趣值
if new_message_count > 0:
accumulated_interest = 0.0
for msg_dict in new_message:
text = msg_dict.get("processed_plain_text", "")
interest_value = msg_dict.get("interest_value", 0.0)
if text:
accumulated_interest += interest_value
# 只在兴趣值变化时输出log
if not hasattr(self, "_last_accumulated_interest") or accumulated_interest != self._last_accumulated_interest:
logger.info(f"{self.log_prefix} breaking形式当前累计兴趣值: {accumulated_interest:.2f}, 当前聊天频率: {talk_frequency:.2f}")
self._last_accumulated_interest = accumulated_interest
if accumulated_interest >= 3 / talk_frequency:
# 记录兴趣度到列表
NoReplyAction._recent_interest_records.append(accumulated_interest)
logger.info(
f"{self.log_prefix} 累计兴趣值达到{accumulated_interest:.2f}(>{5 / talk_frequency}),结束等待"
)
return True
# 每10秒输出一次等待状态
if int(time.time() - self.last_read_time) > 0 and int(time.time() - self.last_read_time) % 10 == 0:
logger.info(
f"{self.log_prefix} 已等待{time.time() - self.last_read_time:.0f}秒,累计{new_message_count}条消息,继续等待..."
)
async def _loopbody(self):
recent_messages_dict = message_api.get_messages_by_time_in_chat(
chat_id=self.stream_id,
start_time=self.last_read_time,
end_time=time.time(),
limit = 10,
limit_mode="latest",
filter_mai=True,
filter_command=True,
)
new_message_count = len(recent_messages_dict)
if self.loop_mode == ChatMode.FOCUS:
if self.last_action == "no_reply":
if not await self._execute_no_reply(recent_messages_dict):
self.energy_value -= 0.3 / global_config.chat.focus_value
logger.info(f"{self.log_prefix} 能量值减少,当前能量值:{self.energy_value:.1f}")
await asyncio.sleep(0.5)
return True
self.last_read_time = time.time()
if await self._observe():
self.energy_value += 1 / global_config.chat.focus_value
logger.info(f"{self.log_prefix} 能量值增加,当前能量值:{self.energy_value:.1f}")
if self.energy_value <= 1:
self.energy_value = 1
self.loop_mode = ChatMode.NORMAL
@@ -211,19 +316,11 @@ class HeartFChatting:
return True
elif self.loop_mode == ChatMode.NORMAL:
new_messages_data = get_raw_msg_by_timestamp_with_chat(
chat_id=self.stream_id,
timestamp_start=self.last_read_time,
timestamp_end=time.time(),
limit=10,
limit_mode="earliest",
filter_bot=True,
)
if global_config.chat.focus_value != 0:
if len(new_messages_data) > 3 / pow(global_config.chat.focus_value, 0.5):
if new_message_count > 3 / pow(global_config.chat.focus_value, 0.5):
self.loop_mode = ChatMode.FOCUS
self.energy_value = (
10 + (len(new_messages_data) / (3 / pow(global_config.chat.focus_value, 0.5))) * 10
10 + (new_message_count / (3 / pow(global_config.chat.focus_value, 0.5))) * 10
)
return True
@@ -231,8 +328,8 @@ class HeartFChatting:
self.loop_mode = ChatMode.FOCUS
return True
if new_messages_data:
earliest_messages_data = new_messages_data[0]
if new_message_count >= self.focus_energy:
earliest_messages_data = recent_messages_dict[0]
self.last_read_time = earliest_messages_data.get("time")
if_think = await self.normal_response(earliest_messages_data)
@@ -247,7 +344,7 @@ class HeartFChatting:
logger.debug(f"{self.log_prefix} 当前能量值:{self.energy_value:.1f}")
return True
await asyncio.sleep(1)
await asyncio.sleep(0.5)
return True
@@ -593,6 +690,8 @@ class HeartFChatting:
}
reply_text = action_reply_text
self.last_action = action_type
if ENABLE_S4U:
await stop_typing()
await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text)
@@ -607,13 +706,19 @@ class HeartFChatting:
if action_type != "no_reply" and action_type != "no_action":
# 导入NoReplyAction并重置计数器
NoReplyAction.reset_consecutive_count()
self.no_reply_consecutive = 0
logger.info(f"{self.log_prefix} 执行了{action_type}动作重置no_reply计数器")
return True
elif action_type == "no_action":
# 当执行回复动作时也重置no_reply计数器s
# 当执行回复动作时也重置no_reply计数
NoReplyAction.reset_consecutive_count()
self.no_reply_consecutive = 0
logger.info(f"{self.log_prefix} 执行了回复动作重置no_reply计数器")
if action_type == "no_reply":
self.no_reply_consecutive += 1
self._determine_form_type()
return True
async def _main_chat_loop(self):

View File

@@ -1,5 +1,3 @@
import random
import time
from typing import Tuple, List
from collections import deque
@@ -9,10 +7,6 @@ from src.plugin_system import BaseAction, ActionActivationType, ChatMode
# 导入依赖的系统组件
from src.common.logger import get_logger
# 导入API模块 - 标准Python包方式
from src.plugin_system.apis import message_api
from src.config.config import global_config
logger = get_logger("no_reply_action")
@@ -38,9 +32,6 @@ class NoReplyAction(BaseAction):
action_name = "no_reply"
action_description = "暂时不回复消息"
# 连续no_reply计数器
_consecutive_count = 0
# 最近三次no_reply的新消息兴趣度记录
_recent_interest_records: deque = deque(maxlen=3)
@@ -64,21 +55,15 @@ class NoReplyAction(BaseAction):
try:
reason = self.action_data.get("reason", "")
start_time = self.action_data.get("loop_start_time", time.time())
check_interval = 0.6
# 判断使用哪种形式
form_type = self._determine_form_type()
logger.info(f"{self.log_prefix} 选择不回复,原因: {reason}")
logger.info(f"{self.log_prefix} 选择不回复(第{NoReplyAction._consecutive_count + 1}次),使用{form_type}形式,原因: {reason}")
# 增加连续计数在确定要执行no_reply时才增加
NoReplyAction._consecutive_count += 1
if form_type == "waiting":
return await self._execute_waiting_form(start_time, check_interval)
else:
return await self._execute_breaking_form(start_time, check_interval)
await self.store_action_info(
action_build_into_prompt=False,
action_prompt_display=reason,
action_done=True,
)
return True, reason
except Exception as e:
logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}")
@@ -91,181 +76,9 @@ class NoReplyAction(BaseAction):
)
return False, f"不回复动作执行失败: {e}"
def _determine_form_type(self) -> str:
"""判断使用哪种形式的no_reply"""
# 如果连续no_reply次数少于3次使用waiting形式
if NoReplyAction._consecutive_count < 3:
return "waiting"
# 如果最近三次记录不足使用waiting形式
if len(NoReplyAction._recent_interest_records) < 3:
return "waiting"
# 计算最近三次记录的兴趣度总和
total_recent_interest = sum(NoReplyAction._recent_interest_records)
# 获取当前聊天频率和意愿系数
talk_frequency = global_config.chat.get_current_talk_frequency(self.chat_id)
# 计算调整后的阈值
adjusted_threshold = self._interest_exit_threshold / talk_frequency
logger.info(f"{self.log_prefix} 最近三次兴趣度总和: {total_recent_interest:.2f}, 调整后阈值: {adjusted_threshold:.2f}")
# 如果兴趣度总和小于阈值进入breaking形式
if total_recent_interest < adjusted_threshold:
logger.info(f"{self.log_prefix} 兴趣度不足进入breaking形式")
return "breaking"
else:
logger.info(f"{self.log_prefix} 兴趣度充足继续使用waiting形式")
return "waiting"
async def _execute_waiting_form(self, start_time: float, check_interval: float) -> Tuple[bool, str]:
"""执行waiting形式的no_reply"""
import asyncio
logger.info(f"{self.log_prefix} 进入waiting形式等待任何新消息")
while True:
current_time = time.time()
elapsed_time = current_time - start_time
# 检查新消息
recent_messages_dict = message_api.get_messages_by_time_in_chat(
chat_id=self.chat_id,
start_time=start_time,
end_time=current_time,
filter_mai=True,
filter_command=True,
)
new_message_count = len(recent_messages_dict)
# waiting形式只要有新消息就结束
if new_message_count > 0:
# 计算新消息的总兴趣度
total_interest = 0.0
for msg_dict in recent_messages_dict:
interest_value = msg_dict.get("interest_value", 0.0)
if msg_dict.get("processed_plain_text", ""):
total_interest += interest_value
# 记录到最近兴趣度列表
NoReplyAction._recent_interest_records.append(total_interest)
logger.info(
f"{self.log_prefix} waiting形式检测到{new_message_count}条新消息,总兴趣度: {total_interest:.2f},结束等待"
)
exit_reason = f"{global_config.bot.nickname}(你)看到了{new_message_count}条新消息,可以考虑一下是否要进行回复"
await self.store_action_info(
action_build_into_prompt=False,
action_prompt_display=exit_reason,
action_done=True,
)
return True, f"waiting形式检测到{new_message_count}条新消息,结束等待 (等待时间: {elapsed_time:.1f}秒)"
# 每10秒输出一次等待状态
if int(elapsed_time) > 0 and int(elapsed_time) % 10 == 0:
logger.debug(f"{self.log_prefix} waiting形式已等待{elapsed_time:.0f}秒,继续等待新消息...")
await asyncio.sleep(1)
# 短暂等待后继续检查
await asyncio.sleep(check_interval)
async def _execute_breaking_form(self, start_time: float, check_interval: float) -> Tuple[bool, str]:
"""执行breaking形式的no_reply原有逻辑"""
import asyncio
# 随机生成本次等待需要的新消息数量阈值
exit_message_count_threshold = random.randint(self._min_exit_message_count, self._max_exit_message_count)
logger.info(f"{self.log_prefix} 进入breaking形式需要{exit_message_count_threshold}条消息或足够兴趣度")
while True:
current_time = time.time()
elapsed_time = current_time - start_time
# 检查新消息
recent_messages_dict = message_api.get_messages_by_time_in_chat(
chat_id=self.chat_id,
start_time=start_time,
end_time=current_time,
filter_mai=True,
filter_command=True,
)
new_message_count = len(recent_messages_dict)
# 检查消息数量是否达到阈值
talk_frequency = global_config.chat.get_current_talk_frequency(self.chat_id)
modified_exit_count_threshold = exit_message_count_threshold / talk_frequency
if new_message_count >= modified_exit_count_threshold:
# 记录兴趣度到列表
total_interest = 0.0
for msg_dict in recent_messages_dict:
interest_value = msg_dict.get("interest_value", 0.0)
if msg_dict.get("processed_plain_text", ""):
total_interest += interest_value
NoReplyAction._recent_interest_records.append(total_interest)
logger.info(
f"{self.log_prefix} breaking形式累计消息数量达到{new_message_count}条(>{modified_exit_count_threshold}),结束等待"
)
exit_reason = f"{global_config.bot.nickname}(你)看到了{new_message_count}条新消息,可以考虑一下是否要进行回复"
await self.store_action_info(
action_build_into_prompt=False,
action_prompt_display=exit_reason,
action_done=True,
)
return True, f"breaking形式累计消息数量达到{new_message_count}条,结束等待 (等待时间: {elapsed_time:.1f}秒)"
# 检查累计兴趣值
if new_message_count > 0:
accumulated_interest = 0.0
for msg_dict in recent_messages_dict:
text = msg_dict.get("processed_plain_text", "")
interest_value = msg_dict.get("interest_value", 0.0)
if text:
accumulated_interest += interest_value
# 只在兴趣值变化时输出log
if not hasattr(self, "_last_accumulated_interest") or accumulated_interest != self._last_accumulated_interest:
logger.info(f"{self.log_prefix} breaking形式当前累计兴趣值: {accumulated_interest:.2f}, 当前聊天频率: {talk_frequency:.2f}")
self._last_accumulated_interest = accumulated_interest
if accumulated_interest >= self._interest_exit_threshold / talk_frequency:
# 记录兴趣度到列表
NoReplyAction._recent_interest_records.append(accumulated_interest)
logger.info(
f"{self.log_prefix} breaking形式累计兴趣值达到{accumulated_interest:.2f}(>{self._interest_exit_threshold / talk_frequency}),结束等待"
)
exit_reason = f"{global_config.bot.nickname}(你)感觉到了大家浓厚的兴趣(兴趣值{accumulated_interest:.1f}),决定重新加入讨论"
await self.store_action_info(
action_build_into_prompt=False,
action_prompt_display=exit_reason,
action_done=True,
)
return (
True,
f"breaking形式累计兴趣值达到{accumulated_interest:.2f},结束等待 (等待时间: {elapsed_time:.1f}秒)",
)
# 每10秒输出一次等待状态
if int(elapsed_time) > 0 and int(elapsed_time) % 10 == 0:
logger.debug(
f"{self.log_prefix} breaking形式已等待{elapsed_time:.0f}秒,累计{new_message_count}条消息,继续等待..."
)
await asyncio.sleep(1)
# 短暂等待后继续检查
await asyncio.sleep(check_interval)
@classmethod
def reset_consecutive_count(cls):
"""重置连续计数器和兴趣度记录"""
cls._consecutive_count = 0
cls._recent_interest_records.clear()
logger.debug("NoReplyAction连续计数器和兴趣度记录已重置")
@@ -274,7 +87,3 @@ class NoReplyAction(BaseAction):
"""获取最近的兴趣度记录"""
return list(cls._recent_interest_records)
@classmethod
def get_consecutive_count(cls) -> int:
"""获取连续计数"""
return cls._consecutive_count