feat:启用频率控制,优化模式切换
This commit is contained in:
@@ -100,6 +100,7 @@ class HeartFChatting:
|
|||||||
# 循环控制内部状态
|
# 循环控制内部状态
|
||||||
self.running: bool = False
|
self.running: bool = False
|
||||||
self._loop_task: Optional[asyncio.Task] = None # 主循环任务
|
self._loop_task: Optional[asyncio.Task] = None # 主循环任务
|
||||||
|
self._energy_task: Optional[asyncio.Task] = None
|
||||||
|
|
||||||
# 添加循环信息管理相关的属性
|
# 添加循环信息管理相关的属性
|
||||||
self.history_loop: List[CycleDetail] = []
|
self.history_loop: List[CycleDetail] = []
|
||||||
@@ -139,6 +140,9 @@ class HeartFChatting:
|
|||||||
# 标记为活动状态,防止重复启动
|
# 标记为活动状态,防止重复启动
|
||||||
self.running = True
|
self.running = True
|
||||||
|
|
||||||
|
self._energy_task = asyncio.create_task(self._energy_loop())
|
||||||
|
self._energy_task.add_done_callback(self._handle_energy_completion)
|
||||||
|
|
||||||
self._loop_task = asyncio.create_task(self._main_chat_loop())
|
self._loop_task = asyncio.create_task(self._main_chat_loop())
|
||||||
self._loop_task.add_done_callback(self._handle_loop_completion)
|
self._loop_task.add_done_callback(self._handle_loop_completion)
|
||||||
logger.info(f"{self.log_prefix} HeartFChatting 启动完成")
|
logger.info(f"{self.log_prefix} HeartFChatting 启动完成")
|
||||||
@@ -173,6 +177,22 @@ class HeartFChatting:
|
|||||||
self.history_loop.append(self._current_cycle_detail)
|
self.history_loop.append(self._current_cycle_detail)
|
||||||
self._current_cycle_detail.timers = cycle_timers
|
self._current_cycle_detail.timers = cycle_timers
|
||||||
self._current_cycle_detail.end_time = time.time()
|
self._current_cycle_detail.end_time = time.time()
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_energy_completion(self, task: asyncio.Task):
|
||||||
|
if exception := task.exception():
|
||||||
|
logger.error(f"{self.log_prefix} HeartFChatting: 能量循环异常: {exception}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
else:
|
||||||
|
logger.info(f"{self.log_prefix} HeartFChatting: 能量循环完成")
|
||||||
|
|
||||||
|
async def _energy_loop(self):
|
||||||
|
while self.running:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
if self.loop_mode == ChatMode.NORMAL:
|
||||||
|
self.energy_value -= 1
|
||||||
|
if self.energy_value <= 0:
|
||||||
|
self.energy_value = 0
|
||||||
|
|
||||||
def print_cycle_info(self, cycle_timers):
|
def print_cycle_info(self, cycle_timers):
|
||||||
# 记录循环信息和计时器结果
|
# 记录循环信息和计时器结果
|
||||||
@@ -190,12 +210,16 @@ class HeartFChatting:
|
|||||||
|
|
||||||
async def _loopbody(self):
|
async def _loopbody(self):
|
||||||
if self.loop_mode == ChatMode.FOCUS:
|
if self.loop_mode == ChatMode.FOCUS:
|
||||||
self.energy_value -= 5 * global_config.chat.focus_value
|
if await self._observe():
|
||||||
|
self.energy_value -= 1 * global_config.chat.focus_value
|
||||||
|
else:
|
||||||
|
self.energy_value -= 3 * global_config.chat.focus_value
|
||||||
if self.energy_value <= 0:
|
if self.energy_value <= 0:
|
||||||
|
self.energy_value = 0
|
||||||
self.loop_mode = ChatMode.NORMAL
|
self.loop_mode = ChatMode.NORMAL
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return await self._observe()
|
return False
|
||||||
elif self.loop_mode == ChatMode.NORMAL:
|
elif self.loop_mode == ChatMode.NORMAL:
|
||||||
new_messages_data = get_raw_msg_by_timestamp_with_chat(
|
new_messages_data = get_raw_msg_by_timestamp_with_chat(
|
||||||
chat_id=self.stream_id,
|
chat_id=self.stream_id,
|
||||||
@@ -206,17 +230,24 @@ class HeartFChatting:
|
|||||||
filter_bot=True,
|
filter_bot=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(new_messages_data) > 4 * global_config.chat.focus_value:
|
if len(new_messages_data) > 3 * global_config.chat.focus_value:
|
||||||
|
self.loop_mode = ChatMode.FOCUS
|
||||||
|
self.energy_value = 10 + (new_messages_data / 3) * 10
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self.energy_value >= 30 * global_config.chat.focus_value:
|
||||||
self.loop_mode = ChatMode.FOCUS
|
self.loop_mode = ChatMode.FOCUS
|
||||||
self.energy_value = 100
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if new_messages_data:
|
if new_messages_data:
|
||||||
earliest_messages_data = new_messages_data[0]
|
earliest_messages_data = new_messages_data[0]
|
||||||
self.last_read_time = earliest_messages_data.get("time")
|
self.last_read_time = earliest_messages_data.get("time")
|
||||||
|
|
||||||
await self.normal_response(earliest_messages_data)
|
if_think = await self.normal_response(earliest_messages_data)
|
||||||
return True
|
if if_think:
|
||||||
|
self.energy_value *= 1.1
|
||||||
|
logger.info(f"{self.log_prefix} 麦麦进行了思考,能量值增加1,当前能量值:{self.energy_value}")
|
||||||
|
return False
|
||||||
|
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
@@ -234,6 +265,7 @@ class HeartFChatting:
|
|||||||
async def _observe(self, message_data: Optional[Dict[str, Any]] = None):
|
async def _observe(self, message_data: Optional[Dict[str, Any]] = None):
|
||||||
if not message_data:
|
if not message_data:
|
||||||
message_data = {}
|
message_data = {}
|
||||||
|
action_type = "no_action"
|
||||||
# 创建新的循环信息
|
# 创建新的循环信息
|
||||||
cycle_timers, thinking_id = self.start_cycle()
|
cycle_timers, thinking_id = self.start_cycle()
|
||||||
|
|
||||||
@@ -322,6 +354,8 @@ class HeartFChatting:
|
|||||||
success, reply_text, command = await self._handle_action(
|
success, reply_text, command = await self._handle_action(
|
||||||
action_type, reasoning, action_data, cycle_timers, thinking_id, action_message
|
action_type, reasoning, action_data, cycle_timers, thinking_id, action_message
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
loop_info = {
|
loop_info = {
|
||||||
"loop_plan_info": {
|
"loop_plan_info": {
|
||||||
@@ -346,6 +380,9 @@ class HeartFChatting:
|
|||||||
if self.loop_mode == ChatMode.NORMAL:
|
if self.loop_mode == ChatMode.NORMAL:
|
||||||
await self.willing_manager.after_generate_reply_handle(message_data.get("message_id", ""))
|
await self.willing_manager.after_generate_reply_handle(message_data.get("message_id", ""))
|
||||||
|
|
||||||
|
if action_type != "no_reply" and action_type != "no_action":
|
||||||
|
return True
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _main_chat_loop(self):
|
async def _main_chat_loop(self):
|
||||||
@@ -432,110 +469,6 @@ class HeartFChatting:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False, "", ""
|
return False, "", ""
|
||||||
|
|
||||||
# async def shutdown(self):
|
|
||||||
# """优雅关闭HeartFChatting实例,取消活动循环任务"""
|
|
||||||
# logger.info(f"{self.log_prefix} 正在关闭HeartFChatting...")
|
|
||||||
# self.running = False # <-- 在开始关闭时设置标志位
|
|
||||||
|
|
||||||
# # 记录最终的消息统计
|
|
||||||
# if self._message_count > 0:
|
|
||||||
# logger.info(f"{self.log_prefix} 本次focus会话共发送了 {self._message_count} 条消息")
|
|
||||||
# if self._fatigue_triggered:
|
|
||||||
# logger.info(f"{self.log_prefix} 因疲惫而退出focus模式")
|
|
||||||
|
|
||||||
# # 取消循环任务
|
|
||||||
# if self._loop_task and not self._loop_task.done():
|
|
||||||
# logger.info(f"{self.log_prefix} 正在取消HeartFChatting循环任务")
|
|
||||||
# self._loop_task.cancel()
|
|
||||||
# try:
|
|
||||||
# await asyncio.wait_for(self._loop_task, timeout=1.0)
|
|
||||||
# logger.info(f"{self.log_prefix} HeartFChatting循环任务已取消")
|
|
||||||
# except (asyncio.CancelledError, asyncio.TimeoutError):
|
|
||||||
# pass
|
|
||||||
# except Exception as e:
|
|
||||||
# logger.error(f"{self.log_prefix} 取消循环任务出错: {e}")
|
|
||||||
# else:
|
|
||||||
# logger.info(f"{self.log_prefix} 没有活动的HeartFChatting循环任务")
|
|
||||||
|
|
||||||
# # 清理状态
|
|
||||||
# self.running = False
|
|
||||||
# self._loop_task = None
|
|
||||||
|
|
||||||
# # 重置消息计数器,为下次启动做准备
|
|
||||||
# self.reset_message_count()
|
|
||||||
|
|
||||||
# logger.info(f"{self.log_prefix} HeartFChatting关闭完成")
|
|
||||||
|
|
||||||
def adjust_reply_frequency(self):
|
|
||||||
"""
|
|
||||||
根据预设规则动态调整回复意愿(willing_amplifier)。
|
|
||||||
- 评估周期:10分钟
|
|
||||||
- 目标频率:由 global_config.chat.talk_frequency 定义(例如 1条/分钟)
|
|
||||||
- 调整逻辑:
|
|
||||||
- 0条回复 -> 5.0x 意愿
|
|
||||||
- 达到目标回复数 -> 1.0x 意愿(基准)
|
|
||||||
- 达到目标2倍回复数 -> 0.2x 意愿
|
|
||||||
- 中间值线性变化
|
|
||||||
- 增益抑制:如果最近5分钟回复过快,则不增加意愿。
|
|
||||||
"""
|
|
||||||
# --- 1. 定义参数 ---
|
|
||||||
evaluation_minutes = 10.0
|
|
||||||
target_replies_per_min = global_config.chat.get_current_talk_frequency(
|
|
||||||
self.stream_id
|
|
||||||
) # 目标频率:e.g. 1条/分钟
|
|
||||||
target_replies_in_window = target_replies_per_min * evaluation_minutes # 10分钟内的目标回复数
|
|
||||||
|
|
||||||
if target_replies_in_window <= 0:
|
|
||||||
logger.debug(f"[{self.log_prefix}] 目标回复频率为0或负数,不调整意愿放大器。")
|
|
||||||
return
|
|
||||||
|
|
||||||
# --- 2. 获取近期统计数据 ---
|
|
||||||
stats_10_min = get_recent_message_stats(minutes=evaluation_minutes, chat_id=self.stream_id)
|
|
||||||
bot_reply_count_10_min = stats_10_min["bot_reply_count"]
|
|
||||||
|
|
||||||
# --- 3. 计算新的意愿放大器 (willing_amplifier) ---
|
|
||||||
# 基于回复数在 [0, target*2] 区间内进行分段线性映射
|
|
||||||
if bot_reply_count_10_min <= target_replies_in_window:
|
|
||||||
# 在 [0, 目标数] 区间,意愿从 5.0 线性下降到 1.0
|
|
||||||
new_amplifier = 5.0 + (bot_reply_count_10_min - 0) * (1.0 - 5.0) / (target_replies_in_window - 0)
|
|
||||||
elif bot_reply_count_10_min <= target_replies_in_window * 2:
|
|
||||||
# 在 [目标数, 目标数*2] 区间,意愿从 1.0 线性下降到 0.2
|
|
||||||
over_target_cap = target_replies_in_window * 2
|
|
||||||
new_amplifier = 1.0 + (bot_reply_count_10_min - target_replies_in_window) * (0.2 - 1.0) / (
|
|
||||||
over_target_cap - target_replies_in_window
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# 超过目标数2倍,直接设为最小值
|
|
||||||
new_amplifier = 0.2
|
|
||||||
|
|
||||||
# --- 4. 检查是否需要抑制增益 ---
|
|
||||||
# "如果邻近5分钟内,回复数量 > 频率/2,就不再进行增益"
|
|
||||||
suppress_gain = False
|
|
||||||
if new_amplifier > self.willing_amplifier: # 仅在计算结果为增益时检查
|
|
||||||
suppression_minutes = 5.0
|
|
||||||
# 5分钟内目标回复数的一半
|
|
||||||
suppression_threshold = (target_replies_per_min / 2) * suppression_minutes # e.g., (1/2)*5 = 2.5
|
|
||||||
stats_5_min = get_recent_message_stats(minutes=suppression_minutes, chat_id=self.stream_id)
|
|
||||||
bot_reply_count_5_min = stats_5_min["bot_reply_count"]
|
|
||||||
|
|
||||||
if bot_reply_count_5_min > suppression_threshold:
|
|
||||||
suppress_gain = True
|
|
||||||
|
|
||||||
# --- 5. 更新意愿放大器 ---
|
|
||||||
if suppress_gain:
|
|
||||||
logger.debug(
|
|
||||||
f"[{self.log_prefix}] 回复增益被抑制。最近5分钟内回复数 ({bot_reply_count_5_min}) "
|
|
||||||
f"> 阈值 ({suppression_threshold:.1f})。意愿放大器保持在 {self.willing_amplifier:.2f}"
|
|
||||||
)
|
|
||||||
# 不做任何改动
|
|
||||||
else:
|
|
||||||
# 限制最终值在 [0.2, 5.0] 范围内
|
|
||||||
self.willing_amplifier = max(0.2, min(5.0, new_amplifier))
|
|
||||||
logger.debug(
|
|
||||||
f"[{self.log_prefix}] 调整回复意愿。10分钟内回复: {bot_reply_count_10_min} (目标: {target_replies_in_window:.0f}) -> "
|
|
||||||
f"意愿放大器更新为: {self.willing_amplifier:.2f}"
|
|
||||||
)
|
|
||||||
|
|
||||||
async def normal_response(self, message_data: dict) -> None:
|
async def normal_response(self, message_data: dict) -> None:
|
||||||
"""
|
"""
|
||||||
处理接收到的消息。
|
处理接收到的消息。
|
||||||
@@ -575,12 +508,18 @@ class HeartFChatting:
|
|||||||
f"{message_data.get('user_nickname')}:"
|
f"{message_data.get('user_nickname')}:"
|
||||||
f"{message_data.get('processed_plain_text')}[兴趣:{interested_rate:.2f}][回复概率:{reply_probability * 100:.1f}%]"
|
f"{message_data.get('processed_plain_text')}[兴趣:{interested_rate:.2f}][回复概率:{reply_probability * 100:.1f}%]"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
talk_frequency = global_config.chat.get_current_talk_frequency(self.stream_id)
|
||||||
|
reply_probability = talk_frequency * reply_probability
|
||||||
|
|
||||||
if random.random() < reply_probability:
|
if random.random() < reply_probability:
|
||||||
await self.willing_manager.before_generate_reply_handle(message_data.get("message_id", ""))
|
await self.willing_manager.before_generate_reply_handle(message_data.get("message_id", ""))
|
||||||
await self._observe(message_data=message_data)
|
await self._observe(message_data=message_data)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# 意愿管理器:注销当前message信息 (无论是否回复,只要处理过就删除)
|
# 意愿管理器:注销当前message信息 (无论是否回复,只要处理过就删除)
|
||||||
|
return False
|
||||||
self.willing_manager.delete(message_data.get("message_id", ""))
|
self.willing_manager.delete(message_data.get("message_id", ""))
|
||||||
|
|
||||||
async def _generate_response(
|
async def _generate_response(
|
||||||
|
|||||||
@@ -88,9 +88,10 @@ class NoReplyAction(BaseAction):
|
|||||||
new_message_count = len(recent_messages_dict)
|
new_message_count = len(recent_messages_dict)
|
||||||
|
|
||||||
# 2. 检查消息数量是否达到阈值
|
# 2. 检查消息数量是否达到阈值
|
||||||
if new_message_count >= exit_message_count_threshold:
|
talk_frequency = global_config.chat.get_current_talk_frequency(self.stream_id)
|
||||||
|
if new_message_count >= exit_message_count_threshold / talk_frequency:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 累计消息数量达到{new_message_count}条(>{exit_message_count_threshold}),结束等待"
|
f"{self.log_prefix} 累计消息数量达到{new_message_count}条(>{exit_message_count_threshold / talk_frequency}),结束等待"
|
||||||
)
|
)
|
||||||
exit_reason = f"{global_config.bot.nickname}(你)看到了{new_message_count}条新消息,可以考虑一下是否要进行回复"
|
exit_reason = f"{global_config.bot.nickname}(你)看到了{new_message_count}条新消息,可以考虑一下是否要进行回复"
|
||||||
await self.store_action_info(
|
await self.store_action_info(
|
||||||
@@ -108,10 +109,13 @@ class NoReplyAction(BaseAction):
|
|||||||
interest_value = msg_dict.get("interest_value", 0.0)
|
interest_value = msg_dict.get("interest_value", 0.0)
|
||||||
if text:
|
if text:
|
||||||
accumulated_interest += interest_value
|
accumulated_interest += interest_value
|
||||||
logger.info(f"{self.log_prefix} 当前累计兴趣值: {accumulated_interest:.2f}")
|
|
||||||
if accumulated_interest >= self._interest_exit_threshold:
|
talk_frequency = global_config.chat.get_current_talk_frequency(self.stream_id)
|
||||||
|
logger.info(f"{self.log_prefix} 当前累计兴趣值: {accumulated_interest:.2f}, 当前聊天频率: {talk_frequency:.2f}")
|
||||||
|
|
||||||
|
if accumulated_interest >= self._interest_exit_threshold / talk_frequency:
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{self.log_prefix} 累计兴趣值达到{accumulated_interest:.2f}(>{self._interest_exit_threshold}),结束等待"
|
f"{self.log_prefix} 累计兴趣值达到{accumulated_interest:.2f}(>{self._interest_exit_threshold / talk_frequency}),结束等待"
|
||||||
)
|
)
|
||||||
exit_reason = f"{global_config.bot.nickname}(你)感觉到了大家浓厚的兴趣(兴趣值{accumulated_interest:.1f}),决定重新加入讨论"
|
exit_reason = f"{global_config.bot.nickname}(你)感觉到了大家浓厚的兴趣(兴趣值{accumulated_interest:.1f}),决定重新加入讨论"
|
||||||
await self.store_action_info(
|
await self.store_action_info(
|
||||||
|
|||||||
Reference in New Issue
Block a user