feat:启用频率控制,优化模式切换

This commit is contained in:
SengokuCola
2025-07-16 11:01:17 +08:00
parent d67cffd953
commit e5689117cf
2 changed files with 58 additions and 115 deletions

View File

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

View File

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