refactor(proactive_thinker): 优化唤醒逻辑并增加防打扰机制

重构了日常唤醒任务(ProactiveThinkingTask)的逻辑,将其拆分为私聊和群聊的独立处理流程。
- 私聊现在直接遍历白名单,确保能覆盖到所有配置的用户,即使他们当前不在内存中。
- 群聊则继续遍历内存中的活跃流。
这个改动修复了之前版本中,只有当用户发送消息后,bot才有可能对其进行主动唤醒的问题。

同时,在决策模块中引入了防打扰机制:
- 在决策提示词中加入了最近的决策历史记录作为上下文。
- 增加了新的决策原则,明确指示模型在近期已主动发起过对话的情况下,应倾向于保持沉默,以避免过度打扰用户。

此外,对冷启动任务(ColdStartTask)进行了微调,将初始的等待时间移至循环的开始,以确保插件加载后能先等待一段时间再开始工作。
This commit is contained in:
minecraft1024a
2025-10-03 19:39:37 +08:00
parent 4089e714b7
commit 135909449c
2 changed files with 50 additions and 38 deletions

View File

@@ -38,6 +38,8 @@ class ColdStartTask(AsyncTask):
while True: while True:
try: try:
#开始就先暂停一小时,等bot聊一会再说()
await asyncio.sleep(3600)
logger.info("【冷启动】开始扫描白名单,寻找从未聊过的用户...") logger.info("【冷启动】开始扫描白名单,寻找从未聊过的用户...")
# 从全局配置中获取私聊白名单 # 从全局配置中获取私聊白名单
@@ -83,9 +85,6 @@ class ColdStartTask(AsyncTask):
except Exception as e: except Exception as e:
logger.error(f"【冷启动】处理用户 {chat_id} 时发生未知错误: {e}", exc_info=True) logger.error(f"【冷启动】处理用户 {chat_id} 时发生未知错误: {e}", exc_info=True)
# 完成一轮检查后,进入长时休眠
await asyncio.sleep(3600)
except asyncio.CancelledError: except asyncio.CancelledError:
logger.info("冷启动任务被正常取消。") logger.info("冷启动任务被正常取消。")
break break
@@ -157,44 +156,50 @@ class ProactiveThinkingTask(AsyncTask):
enabled_private = set(global_config.proactive_thinking.enabled_private_chats) enabled_private = set(global_config.proactive_thinking.enabled_private_chats)
enabled_groups = set(global_config.proactive_thinking.enabled_group_chats) enabled_groups = set(global_config.proactive_thinking.enabled_group_chats)
# 获取当前所有聊天流的快照 # 分别处理私聊和群聊
all_streams = list(self.chat_manager.streams.values()) # 1. 处理私聊:直接遍历白名单,确保能覆盖到所有(包括本次运行尚未活跃的)用户
for chat_id in enabled_private:
for stream in all_streams: try:
# 1. 检查该聊天是否在白名单内(或白名单为空时默认允许) platform, user_id_str = chat_id.split(":")
is_whitelisted = False # 【核心逻辑】检查聊天流是否存在。不存在则跳过交由ColdStartTask处理。
if stream.group_info: # 群聊 stream = chat_api.get_stream_by_user_id(user_id_str, platform)
if not enabled_groups or f"qq:{stream.group_info.group_id}" in enabled_groups: if not stream:
is_whitelisted = True
else: # 私聊
if not enabled_private or f"qq:{stream.user_info.user_id}" in enabled_private:
is_whitelisted = True
if not is_whitelisted:
continue # 不在白名单内,跳过
# 2. 【核心逻辑】检查聊天冷却时间是否足够长
time_since_last_active = time.time() - stream.last_active_time
if time_since_last_active > next_interval:
logger.info(
f"【日常唤醒】聊天流 {stream.stream_id} 已冷却 {time_since_last_active:.2f} 秒,触发主动对话。"
)
# 构建符合 executor 期望的 stream_id 格式
if stream.group_info and stream.group_info.group_id:
formatted_stream_id = f"{stream.user_info.platform}:{stream.group_info.group_id}:group"
elif stream.user_info and stream.user_info.user_id:
formatted_stream_id = f"{stream.user_info.platform}:{stream.user_info.user_id}:private"
else:
logger.warning(f"【日常唤醒】跳过 stream {stream.stream_id},因为它缺少有效的用户信息或群组信息。")
continue continue
await self.executor.execute(stream_id=formatted_stream_id, start_mode="wake_up") # 检查冷却时间
time_since_last_active = time.time() - stream.last_active_time
if time_since_last_active > next_interval:
logger.info(
f"【日常唤醒-私聊】聊天流 {stream.stream_id} 已冷却 {time_since_last_active:.2f} 秒,触发主动对话。"
)
formatted_stream_id = f"{stream.user_info.platform}:{stream.user_info.user_id}:private"
await self.executor.execute(stream_id=formatted_stream_id, start_mode="wake_up")
stream.update_active_time()
await self.chat_manager._save_stream(stream)
# 【关键步骤】在触发后,立刻更新活跃时间并保存。 except ValueError:
# 这可以防止在同一个检查周期内,对同一个目标因为意外的延迟而发送多条消息。 logger.warning(f"【日常唤醒】私聊白名单条目格式错误,已跳过: {chat_id}")
stream.update_active_time() except Exception as e:
await self.chat_manager._save_stream(stream) logger.error(f"【日常唤醒】处理私聊用户 {chat_id} 时发生未知错误: {e}", exc_info=True)
# 2. 处理群聊:遍历内存中的活跃流(群聊不存在冷启动问题)
all_streams = list(self.chat_manager.streams.values())
for stream in all_streams:
if not stream.group_info:
continue # 只处理群聊
# 检查群聊是否在白名单内
if not enabled_groups or f"qq:{stream.group_info.group_id}" in enabled_groups:
# 检查冷却时间
time_since_last_active = time.time() - stream.last_active_time
if time_since_last_active > next_interval:
logger.info(
f"【日常唤醒-群聊】聊天流 {stream.stream_id} 已冷却 {time_since_last_active:.2f} 秒,触发主动对话。"
)
formatted_stream_id = f"{stream.user_info.platform}:{stream.group_info.group_id}:group"
await self.executor.execute(stream_id=formatted_stream_id, start_mode="wake_up")
stream.update_active_time()
await self.chat_manager._save_stream(stream)
except asyncio.CancelledError: except asyncio.CancelledError:
logger.info("日常唤醒任务被正常取消。") logger.info("日常唤醒任务被正常取消。")

View File

@@ -240,6 +240,9 @@ class ProactiveThinkerExecutor:
- 身份: {persona["identity"]} - 身份: {persona["identity"]}
你的当前情绪状态是: {context["mood_state"]} 你的当前情绪状态是: {context["mood_state"]}
# 你最近的相关决策历史 (供参考)
{context["action_history_context"]}
""" """
# 根据聊天类型构建任务和情境 # 根据聊天类型构建任务和情境
if chat_type == "private": if chat_type == "private":
@@ -283,6 +286,10 @@ class ProactiveThinkerExecutor:
- `topic`: str, 如果 `should_reply` 为 true你打算聊什么话题(例如:问候一下今天的日程、关心一下昨天的某件事、分享一个你自己的趣事等) - `topic`: str, 如果 `should_reply` 为 true你打算聊什么话题(例如:问候一下今天的日程、关心一下昨天的某件事、分享一个你自己的趣事等)
- `reason`: str, 做出此决策的简要理由。 - `reason`: str, 做出此决策的简要理由。
# 决策原则
- **避免打扰**: 如果你最近(尤其是在最近的几次决策中)已经主动发起过对话,请倾向于选择“不回复”,除非有非常重要和紧急的事情。
--- ---
示例1 (应该回复): 示例1 (应该回复):
{{ {{