From e5b2e2a5ee5f779ad6d5c768a83c0262373596b2 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 12:26:53 +0800 Subject: [PATCH 01/17] =?UTF-8?q?pfc=20=E5=85=B3=E7=B3=BB=E5=80=BC?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E5=8A=A0=E5=85=A5=E9=9D=9E=E7=BA=BF=E6=80=A7?= =?UTF-8?q?=E6=8D=A2=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/pfc_relationship.py | 23 +++++++++++++--------- src/plugins/PFC/pfc_utils.py | 30 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/plugins/PFC/pfc_relationship.py b/src/plugins/PFC/pfc_relationship.py index aa0586e3..fb6e96aa 100644 --- a/src/plugins/PFC/pfc_relationship.py +++ b/src/plugins/PFC/pfc_relationship.py @@ -9,7 +9,7 @@ from src.plugins.person_info.relationship_manager import ( from src.plugins.utils.chat_message_builder import build_readable_messages from src.plugins.PFC.observation_info import ObservationInfo from src.plugins.PFC.conversation_info import ConversationInfo -from src.plugins.PFC.pfc_utils import get_items_from_json +from src.plugins.PFC.pfc_utils import get_items_from_json, adjust_relationship_value_nonlinear from src.config.config import global_config # 导入全局配置 (向上两级到 src/, 再到 config) @@ -121,7 +121,7 @@ class PfcRelationshipUpdater: 请输出一个JSON对象,包含一个 "adjustment" 字段,其值为一个介于 -{self.REL_INCREMENTAL_MAX_CHANGE} 和 +{self.REL_INCREMENTAL_MAX_CHANGE} 之间的整数,代表关系值的变化。 例如:{{ "adjustment": 3 }}。如果对话内容不明确或难以判断,请倾向于输出较小的调整值(如0, 1, -1)。""" - adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE + raw_adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE try: logger.debug(f"[私聊][{self.private_name}] 增量关系评估Prompt:\n{relationship_prompt}") content, _ = await self.llm.generate_response_async(relationship_prompt) @@ -136,13 +136,15 @@ class PfcRelationshipUpdater: ) raw_adjustment = result.get("adjustment", self.REL_INCREMENTAL_DEFAULT_CHANGE) if not isinstance(raw_adjustment, (int, float)): - adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE + raw_adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE else: - adjustment_val = float(raw_adjustment) - adjustment_val = max(-self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, adjustment_val)) + raw_adjustment_val = float(raw_adjustment) + raw_adjustment_val = max(-self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, raw_adjustment_val)) except Exception as e: logger.error(f"[私聊][{self.private_name}] 增量关系评估LLM调用或解析失败: {e}") + adjustment_val = await adjust_relationship_value_nonlinear(current_relationship_value, raw_adjustment_val) + new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val)) await self.person_info_mng.update_one_field( conversation_info.person_id, "relationship_value", new_relationship_value @@ -202,7 +204,7 @@ class PfcRelationshipUpdater: 请输出一个JSON对象,包含一个 "final_adjustment" 字段,其值为一个整数,代表关系值的变化量(例如,可以是 -{self.REL_FINAL_MAX_CHANGE} 到 +{self.REL_FINAL_MAX_CHANGE} 之间的一个值)。 请大胆评估,但也要合理。""" - adjustment_val = self.REL_FINAL_DEFAULT_CHANGE + raw_adjustment_val = self.REL_FINAL_DEFAULT_CHANGE try: logger.debug(f"[私聊][{self.private_name}] 最终关系评估Prompt:\n{relationship_prompt}") content, _ = await self.llm.generate_response_async(relationship_prompt) @@ -217,13 +219,16 @@ class PfcRelationshipUpdater: ) raw_adjustment = result.get("final_adjustment", self.REL_FINAL_DEFAULT_CHANGE) if not isinstance(raw_adjustment, (int, float)): - adjustment_val = self.REL_FINAL_DEFAULT_CHANGE + raw_adjustment_val = self.REL_FINAL_DEFAULT_CHANGE else: - adjustment_val = float(raw_adjustment) - adjustment_val = max(-self.REL_FINAL_MAX_CHANGE, min(self.REL_FINAL_MAX_CHANGE, adjustment_val)) + raw_adjustment_val = float(raw_adjustment) + raw_adjustment_val = max(-self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, raw_adjustment_val)) except Exception as e: logger.error(f"[私聊][{self.private_name}] 最终关系评估LLM调用或解析失败: {e}") + + adjustment_val = await adjust_relationship_value_nonlinear(current_relationship_value, raw_adjustment_val) + new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val)) await self.person_info_mng.update_one_field( conversation_info.person_id, "relationship_value", new_relationship_value diff --git a/src/plugins/PFC/pfc_utils.py b/src/plugins/PFC/pfc_utils.py index 47430af6..7d0f6695 100644 --- a/src/plugins/PFC/pfc_utils.py +++ b/src/plugins/PFC/pfc_utils.py @@ -7,6 +7,7 @@ from src.plugins.memory_system.Hippocampus import HippocampusManager from src.plugins.heartFC_chat.heartflow_prompt_builder import prompt_builder # 确认 prompt_builder 的导入路径 from src.plugins.chat.chat_stream import ChatStream from ..person_info.person_info import person_info_manager +import math logger = get_logger("pfc_utils") @@ -308,3 +309,32 @@ async def get_person_id(private_name: str, chat_stream: ChatStream): f"[私聊][{private_name}] 未能确定私聊对象的 user_id 或 platform,无法获取 person_id。将在收到消息后尝试。" ) return None # 返回 None 表示失败 + +async def adjust_relationship_value_nonlinear(self, old_value: float, raw_adjustment: float) -> float: + # 限制 old_value 范围 + old_value = max(-1000, min(1000, old_value)) + value = raw_adjustment + + if old_value >= 0: + if value >= 0: + value = value * math.cos(math.pi * old_value / 2000) + if old_value > 500: + rdict = person_info_manager.get_specific_value_list("relationship_value", lambda x: x > 700) + high_value_count = len(rdict) + if old_value > 700: + value *= 3 / (high_value_count + 2) + else: + value *= 3 / (high_value_count + 3) + elif value < 0: + value = value * math.exp(old_value / 2000) + else: + value = 0 + else: + if value >= 0: + value = value * math.exp(old_value / 2000) + elif value < 0: + value = value * math.cos(math.pi * old_value / 2000) + else: + value = 0 + + return value From 2ab60068f856b66c2b6c0cd02ace4fd64864948a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 May 2025 04:27:16 +0000 Subject: [PATCH 02/17] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/pfc_relationship.py | 9 ++++++--- src/plugins/PFC/pfc_utils.py | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/PFC/pfc_relationship.py b/src/plugins/PFC/pfc_relationship.py index fb6e96aa..3ebc0c1b 100644 --- a/src/plugins/PFC/pfc_relationship.py +++ b/src/plugins/PFC/pfc_relationship.py @@ -139,7 +139,9 @@ class PfcRelationshipUpdater: raw_adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE else: raw_adjustment_val = float(raw_adjustment) - raw_adjustment_val = max(-self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, raw_adjustment_val)) + raw_adjustment_val = max( + -self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, raw_adjustment_val) + ) except Exception as e: logger.error(f"[私聊][{self.private_name}] 增量关系评估LLM调用或解析失败: {e}") @@ -222,11 +224,12 @@ class PfcRelationshipUpdater: raw_adjustment_val = self.REL_FINAL_DEFAULT_CHANGE else: raw_adjustment_val = float(raw_adjustment) - raw_adjustment_val = max(-self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, raw_adjustment_val)) + raw_adjustment_val = max( + -self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, raw_adjustment_val) + ) except Exception as e: logger.error(f"[私聊][{self.private_name}] 最终关系评估LLM调用或解析失败: {e}") - adjustment_val = await adjust_relationship_value_nonlinear(current_relationship_value, raw_adjustment_val) new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val)) diff --git a/src/plugins/PFC/pfc_utils.py b/src/plugins/PFC/pfc_utils.py index 7d0f6695..e4cc39e2 100644 --- a/src/plugins/PFC/pfc_utils.py +++ b/src/plugins/PFC/pfc_utils.py @@ -310,6 +310,7 @@ async def get_person_id(private_name: str, chat_stream: ChatStream): ) return None # 返回 None 表示失败 + async def adjust_relationship_value_nonlinear(self, old_value: float, raw_adjustment: float) -> float: # 限制 old_value 范围 old_value = max(-1000, min(1000, old_value)) From 997b4e4380459807fe75abb77308d1debed88d85 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 12:35:45 +0800 Subject: [PATCH 03/17] =?UTF-8?q?=E4=BF=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/pfc_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/PFC/pfc_utils.py b/src/plugins/PFC/pfc_utils.py index 7d0f6695..96aeaa7f 100644 --- a/src/plugins/PFC/pfc_utils.py +++ b/src/plugins/PFC/pfc_utils.py @@ -310,7 +310,7 @@ async def get_person_id(private_name: str, chat_stream: ChatStream): ) return None # 返回 None 表示失败 -async def adjust_relationship_value_nonlinear(self, old_value: float, raw_adjustment: float) -> float: +async def adjust_relationship_value_nonlinear(old_value: float, raw_adjustment: float) -> float: # 限制 old_value 范围 old_value = max(-1000, min(1000, old_value)) value = raw_adjustment From 5f2fac4a6d3244e8e589c9888c602c46eeecef74 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 13:13:30 +0800 Subject: [PATCH 04/17] =?UTF-8?q?=E5=8F=98=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/pfc_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/PFC/pfc_utils.py b/src/plugins/PFC/pfc_utils.py index 30333d7d..6047aa81 100644 --- a/src/plugins/PFC/pfc_utils.py +++ b/src/plugins/PFC/pfc_utils.py @@ -320,7 +320,7 @@ async def adjust_relationship_value_nonlinear(old_value: float, raw_adjustment: if value >= 0: value = value * math.cos(math.pi * old_value / 2000) if old_value > 500: - rdict = person_info_manager.get_specific_value_list("relationship_value", lambda x: x > 700) + rdict = await person_info_manager.get_specific_value_list("relationship_value", lambda x: x > 700) high_value_count = len(rdict) if old_value > 700: value *= 3 / (high_value_count + 2) From 5b1e5aa50e265a65af04e84e6399f4b789230fe2 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Thu, 8 May 2025 18:54:49 +0800 Subject: [PATCH 05/17] Update logger.py --- src/common/logger.py | 50 -------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/src/common/logger.py b/src/common/logger.py index 318d9b37..f7d6fb28 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -6,56 +6,6 @@ from types import ModuleType from pathlib import Path from dotenv import load_dotenv -""" -日志颜色说明: - -1. 主程序(Main) -浅黄色标题 | 浅黄色消息 - -2. 海马体(Memory) -浅黄色标题 | 浅黄色消息 - -3. PFC(前额叶皮质) -浅绿色标题 | 浅绿色消息 - -4. 心情(Mood) -品红色标题 | 品红色消息 - -5. 工具使用(Tool) -品红色标题 | 品红色消息 - -6. 关系(Relation) -浅品红色标题 | 浅品红色消息 - -7. 配置(Config) -浅青色标题 | 浅青色消息 - -8. 麦麦大脑袋 -浅绿色标题 | 浅绿色消息 - -9. 在干嘛 -青色标题 | 青色消息 - -10. 麦麦组织语言 -浅绿色标题 | 浅绿色消息 - -11. 见闻(Chat) -浅蓝色标题 | 绿色消息 - -12. 表情包(Emoji) -橙色标题 | 橙色消息 fg #FFD700 - -13. 子心流 - -13. 其他模块 -模块名标题 | 对应颜色消息 - - -注意: -1. 级别颜色遵循loguru默认配置 -2. 可通过环境变量修改日志级别 -""" - # 加载 .env 文件 env_path = Path(__file__).resolve().parent.parent.parent / ".env" From 38adeadc475887fffc843cde837e3f853d0783e5 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 19:32:36 +0800 Subject: [PATCH 06/17] ruff --- src/plugins/PFC/PFC_idle/idle_chat.py | 7 +++---- src/plugins/PFC/PFC_idle/idle_chat_manager.py | 2 +- src/plugins/PFC/PFC_idle/idle_conversation.py | 5 ++--- src/plugins/PFC/PFC_idle/idle_conversation_starter.py | 2 -- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/plugins/PFC/PFC_idle/idle_chat.py b/src/plugins/PFC/PFC_idle/idle_chat.py index 38c9e772..6a01e4c4 100644 --- a/src/plugins/PFC/PFC_idle/idle_chat.py +++ b/src/plugins/PFC/PFC_idle/idle_chat.py @@ -1,4 +1,4 @@ -from typing import Optional, Dict, List, Set +from typing import Optional, Dict, Set import asyncio import time import random @@ -7,11 +7,10 @@ from datetime import datetime from src.common.logger_manager import get_logger from src.config.config import global_config from src.plugins.models.utils_model import LLMRequest -from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager +from src.plugins.utils.prompt_builder import global_prompt_manager from src.plugins.person_info.person_info import person_info_manager from src.plugins.utils.chat_message_builder import build_readable_messages from ...schedule.schedule_generator import bot_schedule -from ....config.config import global_config from ..chat_observer import ChatObserver from ..message_sender import DirectMessageSender from src.plugins.chat.chat_stream import ChatStream @@ -109,7 +108,7 @@ class IdleChat: # 如果所有用户都已尝试过,重置尝试集合,从头开始 if len(cls._tried_users) >= len(all_users): cls._tried_users.clear() - logger.info(f"[私聊]所有用户都已尝试过,重置尝试列表") + logger.info("[私聊]所有用户都已尝试过,重置尝试列表") # 随机选择一个不在待回复列表中的用户 available_users = all_users - set(cls._pending_replies.keys()) if available_users: diff --git a/src/plugins/PFC/PFC_idle/idle_chat_manager.py b/src/plugins/PFC/PFC_idle/idle_chat_manager.py index 66ec37b6..1218c109 100644 --- a/src/plugins/PFC/PFC_idle/idle_chat_manager.py +++ b/src/plugins/PFC/PFC_idle/idle_chat_manager.py @@ -146,7 +146,7 @@ class IdleChatManager: # 检查是否所有对话都结束了,帮助调试 all_counts = sum(self._active_conversations_count.values()) if all_counts == 0: - logger.info(f"所有对话实例都已结束,当前总活跃计数为0") + logger.info("所有对话实例都已结束,当前总活跃计数为0") except Exception as e: logger.error(f"对话结束通知处理失败: {stream_id}, 错误: {e}") logger.error(traceback.format_exc()) diff --git a/src/plugins/PFC/PFC_idle/idle_conversation.py b/src/plugins/PFC/PFC_idle/idle_conversation.py index 3aae1853..68e5a240 100644 --- a/src/plugins/PFC/PFC_idle/idle_conversation.py +++ b/src/plugins/PFC/PFC_idle/idle_conversation.py @@ -1,5 +1,4 @@ import traceback -import logging import asyncio from typing import Optional, Dict from src.common.logger_manager import get_logger @@ -210,7 +209,7 @@ class IdleConversation: try: # 创建IdleChat实例 - idle_chat = await self._idle_chat_manager.get_or_create_idle_chat(stream_id, private_name) + _idle_chat = await self._idle_chat_manager.get_or_create_idle_chat(stream_id, private_name) logger.debug(f"[私聊][{private_name}] 已创建或获取IdleChat实例") return True except Exception as e: @@ -478,7 +477,7 @@ async def periodic_system_check(instance: IdleConversation): # 如果IdleChatManager记录的计数为0但自己的记录不为0,进行修正 if manager_count == 0 and active_streams_count > 0: - logger.warning(f"检测到可能的计数错误,尝试修正:清空IdleConversation的活跃流记录") + logger.warning("检测到可能的计数错误,尝试修正:清空IdleConversation的活跃流记录") async with instance._lock: instance._active_streams.clear() diff --git a/src/plugins/PFC/PFC_idle/idle_conversation_starter.py b/src/plugins/PFC/PFC_idle/idle_conversation_starter.py index 1ace9dbd..e6782ab1 100644 --- a/src/plugins/PFC/PFC_idle/idle_conversation_starter.py +++ b/src/plugins/PFC/PFC_idle/idle_conversation_starter.py @@ -3,7 +3,6 @@ import asyncio import random import traceback from typing import TYPE_CHECKING, Optional -from datetime import datetime from src.common.logger_manager import get_logger from src.plugins.models.utils_model import LLMRequest @@ -22,7 +21,6 @@ from rich.traceback import install # 使用TYPE_CHECKING避免循环导入 if TYPE_CHECKING: from ..conversation import Conversation - from ..pfc_manager import PFCManager install(extra_lines=3) From 19d1da1ec805a52f66a932194b18076c4fa84b9b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 May 2025 11:32:55 +0000 Subject: [PATCH 07/17] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/PFC_idle/__init__.py | 12 +- src/plugins/PFC/PFC_idle/idle_chat.py | 243 +++++++++--------- src/plugins/PFC/PFC_idle/idle_chat_manager.py | 41 +-- src/plugins/PFC/PFC_idle/idle_conversation.py | 154 +++++------ .../PFC/PFC_idle/idle_conversation_starter.py | 3 +- src/plugins/PFC/chat_observer.py | 4 +- src/plugins/PFC/conversation_initializer.py | 5 +- src/plugins/PFC/conversation_loop.py | 5 +- 8 files changed, 237 insertions(+), 230 deletions(-) diff --git a/src/plugins/PFC/PFC_idle/__init__.py b/src/plugins/PFC/PFC_idle/__init__.py index a06c1834..77d90210 100644 --- a/src/plugins/PFC/PFC_idle/__init__.py +++ b/src/plugins/PFC/PFC_idle/__init__.py @@ -12,9 +12,9 @@ from .idle_chat_manager import IdleChatManager from .idle_conversation import IdleConversation, get_idle_conversation_instance, initialize_idle_conversation __all__ = [ - 'IdleChat', - 'IdleChatManager', - 'IdleConversation', - 'get_idle_conversation_instance', - 'initialize_idle_conversation' -] \ No newline at end of file + "IdleChat", + "IdleChatManager", + "IdleConversation", + "get_idle_conversation_instance", + "initialize_idle_conversation", +] diff --git a/src/plugins/PFC/PFC_idle/idle_chat.py b/src/plugins/PFC/PFC_idle/idle_chat.py index 6a01e4c4..2962d52c 100644 --- a/src/plugins/PFC/PFC_idle/idle_chat.py +++ b/src/plugins/PFC/PFC_idle/idle_chat.py @@ -22,32 +22,33 @@ install(extra_lines=3) logger = get_logger("pfc_idle_chat") + class IdleChat: """主动聊天组件(测试中) - + 在以下条件都满足时触发主动聊天: 1. 当前没有任何活跃的对话实例 2. 在指定的活动时间内(7:00-23:00) 3. 根据关系值动态调整触发概率 4. 上次触发后已经过了足够的冷却时间 """ - + # 单例模式实现 - _instances: Dict[str, 'IdleChat'] = {} - + _instances: Dict[str, "IdleChat"] = {} + # 全局共享状态,用于跟踪未回复的用户 _pending_replies: Dict[str, float] = {} # 用户名 -> 发送时间 _tried_users: Set[str] = set() # 已尝试过的用户集合 _global_lock = asyncio.Lock() # 保护共享状态的全局锁 - + @classmethod - def get_instance(cls, stream_id: str, private_name: str) -> 'IdleChat': + def get_instance(cls, stream_id: str, private_name: str) -> "IdleChat": """获取IdleChat实例(单例模式) - + Args: stream_id: 聊天流ID private_name: 私聊用户名称 - + Returns: IdleChat: IdleChat实例 """ @@ -58,13 +59,13 @@ class IdleChat: cls._instances[key].start() logger.info(f"[私聊][{private_name}]创建新的IdleChat实例并启动") return cls._instances[key] - + @classmethod async def register_user_response(cls, private_name: str) -> None: """注册用户已回复 - + 当用户回复消息时调用此方法,将用户从待回复列表中移除 - + Args: private_name: 私聊用户名称 """ @@ -72,39 +73,39 @@ class IdleChat: if private_name in cls._pending_replies: del cls._pending_replies[private_name] logger.info(f"[私聊][{private_name}]已回复主动聊天消息,从待回复列表中移除") - + @classmethod async def get_next_available_user(cls) -> Optional[str]: """获取下一个可用于主动聊天的用户 - + 优先选择未尝试过的用户,其次是已尝试但超时未回复的用户 - + Returns: Optional[str]: 下一个可用的用户名,如果没有则返回None """ async with cls._global_lock: current_time = time.time() timeout_threshold = 7200 # 2小时未回复视为超时 - + # 清理超时未回复的用户 for user, send_time in list(cls._pending_replies.items()): if current_time - send_time > timeout_threshold: logger.info(f"[私聊][{user}]超过{timeout_threshold}秒未回复,标记为超时") del cls._pending_replies[user] - + # 获取所有实例中的用户 all_users = set() for key in cls._instances: - user = key.split(':', 1)[0] + user = key.split(":", 1)[0] all_users.add(user) - + # 优先选择未尝试过的用户 untried_users = all_users - cls._tried_users if untried_users: next_user = random.choice(list(untried_users)) cls._tried_users.add(next_user) return next_user - + # 如果所有用户都已尝试过,重置尝试集合,从头开始 if len(cls._tried_users) >= len(all_users): cls._tried_users.clear() @@ -115,9 +116,9 @@ class IdleChat: next_user = random.choice(list(available_users)) cls._tried_users.add(next_user) return next_user - + return None - + def __init__(self, stream_id: str, private_name: str): """初始化主动聊天组件 @@ -129,99 +130,95 @@ class IdleChat: self.private_name = private_name self.chat_observer = ChatObserver.get_instance(stream_id, private_name) self.message_sender = DirectMessageSender(private_name) - + # 添加异步锁,保护对共享变量的访问 self._lock: asyncio.Lock = asyncio.Lock() - + # LLM请求对象,用于生成主动对话内容 - self.llm = LLMRequest( - model=global_config.llm_normal, - temperature=0.5, - max_tokens=500, - request_type="idle_chat" - ) - + self.llm = LLMRequest(model=global_config.llm_normal, temperature=0.5, max_tokens=500, request_type="idle_chat") + # 工作状态 self.active_instances_count: int = 0 self.last_trigger_time: float = time.time() - 1500 # 初始化时减少等待时间 self._running: bool = False self._task: Optional[asyncio.Task] = None - + # 配置参数 - 从global_config加载 - self.min_cooldown = getattr(global_config,"MIN_IDLE_TIME", 7200) # 最短冷却时间(默认2小时)建议修改长一点,你也不希望你的bot一直骚扰你吧 + self.min_cooldown = getattr( + global_config, "MIN_IDLE_TIME", 7200 + ) # 最短冷却时间(默认2小时)建议修改长一点,你也不希望你的bot一直骚扰你吧 self.max_cooldown = getattr(global_config, "MAX_IDLE_TIME", 14400) # 最长冷却时间(默认4小时) - self.min_idle_time = getattr(global_config, "MIN_IDLE_TIME", 3600) + self.min_idle_time = getattr(global_config, "MIN_IDLE_TIME", 3600) self.check_interval = getattr(global_config, "IDLE_CHECK_INTERVAL", 600) # 检查间隔(默认10分钟) self.active_hours_start = 6 # 活动开始时间 - self.active_hours_end = 24 # 活动结束时间 - + self.active_hours_end = 24 # 活动结束时间 + # 关系值相关 self.base_trigger_probability = 0.3 # 基础触发概率 - self.relationship_factor = 0.0003 # 关系值影响因子 - + self.relationship_factor = 0.0003 # 关系值影响因子 + def start(self) -> None: """启动主动聊天检测""" # 检查是否启用了主动聊天功能 if not getattr(global_config, "ENABLE_IDLE_CONVERSATION", False): logger.info(f"[私聊][{self.private_name}]主动聊天功能已禁用(配置ENABLE_IDLE_CONVERSATION=False)") return - + if self._running: logger.debug(f"[私聊][{self.private_name}]主动聊天功能已在运行中") return - + self._running = True self._task = asyncio.create_task(self._check_idle_loop()) logger.info(f"[私聊][{self.private_name}]启动主动聊天检测") - + def stop(self) -> None: - """停止主动聊天检测 - """ + """停止主动聊天检测""" if not self._running: return - + self._running = False if self._task: self._task.cancel() self._task = None logger.info(f"[私聊][{self.private_name}]停止主动聊天检测") - + async def increment_active_instances(self) -> None: """增加活跃实例计数 - + 当创建新的对话实例时调用此方法 """ async with self._lock: self.active_instances_count += 1 logger.debug(f"[私聊][{self.private_name}]活跃实例数+1,当前:{self.active_instances_count}") - + async def decrement_active_instances(self) -> None: """减少活跃实例计数 - + 当对话实例结束时调用此方法 """ async with self._lock: self.active_instances_count = max(0, self.active_instances_count - 1) logger.debug(f"[私聊][{self.private_name}]活跃实例数-1,当前:{self.active_instances_count}") - + async def update_last_message_time(self, message_time: Optional[float] = None) -> None: """更新最后一条消息的时间 - + Args: message_time: 消息时间戳,如果为None则使用当前时间 """ async with self._lock: self.last_trigger_time = message_time or time.time() logger.debug(f"[私聊][{self.private_name}]更新最后消息时间: {self.last_trigger_time:.2f}") - + # 当用户发送消息时,也应该注册响应 await self.__class__.register_user_response(self.private_name) - + def _is_active_hours(self) -> bool: """检查是否在活动时间内""" current_hour = datetime.now().hour return self.active_hours_start <= current_hour < self.active_hours_end - + async def _should_trigger(self) -> bool: """检查是否应该触发主动聊天""" async with self._lock: @@ -229,25 +226,27 @@ class IdleChat: if self.active_instances_count < 0: logger.warning(f"[私聊][{self.private_name}]检测到活跃实例数为负数,重置为0") self.active_instances_count = 0 - + # 检查是否有活跃实例 if self.active_instances_count > 0: logger.debug(f"[私聊][{self.private_name}]存在活跃实例({self.active_instances_count}),不触发主动聊天") return False - + # 检查是否在活动时间内 if not self._is_active_hours(): logger.debug(f"[私聊][{self.private_name}]不在活动时间内,不触发主动聊天") return False - + # 检查冷却时间 current_time = time.time() time_since_last_trigger = current_time - self.last_trigger_time if time_since_last_trigger < self.min_cooldown: time_left = self.min_cooldown - time_since_last_trigger - logger.debug(f"[私聊][{self.private_name}]冷却时间未到(已过{time_since_last_trigger:.0f}秒/需要{self.min_cooldown}秒),还需等待{time_left:.0f}秒,不触发主动聊天") + logger.debug( + f"[私聊][{self.private_name}]冷却时间未到(已过{time_since_last_trigger:.0f}秒/需要{self.min_cooldown}秒),还需等待{time_left:.0f}秒,不触发主动聊天" + ) return False - + # 强制触发检查 - 如果超过最大冷却时间,增加触发概率 force_trigger = False if time_since_last_trigger > self.max_cooldown * 2: # 如果超过最大冷却时间的两倍 @@ -255,9 +254,11 @@ class IdleChat: random_force = random.random() force_trigger = random_force < force_probability if force_trigger: - logger.info(f"[私聊][{self.private_name}]超过最大冷却时间({time_since_last_trigger:.0f}秒),强制触发主动聊天") + logger.info( + f"[私聊][{self.private_name}]超过最大冷却时间({time_since_last_trigger:.0f}秒),强制触发主动聊天" + ) return True - + # 获取关系值 relationship_value = 0 try: @@ -270,10 +271,10 @@ class IdleChat: # 先尝试通过昵称获取person_id platform = "qq" # 默认平台 person_id = person_info_manager.get_person_id(platform, self.private_name) - + # 如果通过昵称获取失败,尝试通过stream_id解析 if not person_id: - parts = self.stream_id.split('_') + parts = self.stream_id.split("_") if len(parts) >= 2 and parts[0] == "private": user_id = parts[1] platform = parts[2] if len(parts) >= 3 else "qq" @@ -284,7 +285,7 @@ class IdleChat: person_id = person_info_manager.get_person_id(platform, user_id) except Exception as e2: logger.warning(f"[私聊][{self.private_name}]尝试获取person_id失败: {str(e2)}") - + # 获取关系值 if person_id: raw_value = await person_info_manager.get_value(person_id, "relationship_value") @@ -292,51 +293,61 @@ class IdleChat: logger.debug(f"[私聊][{self.private_name}]成功获取关系值: {relationship_value}") else: logger.warning(f"[私聊][{self.private_name}]无法获取person_id,使用默认关系值0") - + # 使用PfcRepationshipTranslator获取关系描述 relationship_translator = PfcRepationshipTranslator(self.private_name) - relationship_level = relationship_translator._calculate_relationship_level_num(relationship_value, self.private_name) - + relationship_level = relationship_translator._calculate_relationship_level_num( + relationship_value, self.private_name + ) + # 基于关系等级调整触发概率 # 关系越好,主动聊天概率越高 level_probability_factors = [0.05, 0.1, 0.2, 0.3, 0.4, 0.5] # 每个等级对应的基础概率因子 base_probability = level_probability_factors[relationship_level] - + # 基础概率因子 trigger_probability = base_probability trigger_probability = max(0.05, min(0.6, trigger_probability)) # 限制在0.05-0.6之间 - + # 最大冷却时间调整 - 随着冷却时间增加,逐渐增加触发概率 if time_since_last_trigger > self.max_cooldown: # 计算额外概率 - 每超过最大冷却时间的10%,增加1%的概率,最多增加30% - extra_time_factor = min(0.3, (time_since_last_trigger - self.max_cooldown) / (self.max_cooldown * 10)) + extra_time_factor = min( + 0.3, (time_since_last_trigger - self.max_cooldown) / (self.max_cooldown * 10) + ) trigger_probability += extra_time_factor logger.debug(f"[私聊][{self.private_name}]超过标准冷却时间,额外增加概率: +{extra_time_factor:.2f}") - + # 随机判断是否触发 random_value = random.random() should_trigger = random_value < trigger_probability - logger.debug(f"[私聊][{self.private_name}]触发概率计算: 基础({base_probability:.2f}) + 关系值({relationship_value})影响 = {trigger_probability:.2f},随机值={random_value:.2f}, 结果={should_trigger}") - + logger.debug( + f"[私聊][{self.private_name}]触发概率计算: 基础({base_probability:.2f}) + 关系值({relationship_value})影响 = {trigger_probability:.2f},随机值={random_value:.2f}, 结果={should_trigger}" + ) + # 如果决定触发,记录详细日志 if should_trigger: - logger.info(f"[私聊][{self.private_name}]决定触发主动聊天: 触发概率={trigger_probability:.2f}, 距上次已过{time_since_last_trigger:.0f}秒") - + logger.info( + f"[私聊][{self.private_name}]决定触发主动聊天: 触发概率={trigger_probability:.2f}, 距上次已过{time_since_last_trigger:.0f}秒" + ) + return should_trigger - + except Exception as e: logger.error(f"[私聊][{self.private_name}]获取关系值失败: {str(e)}") logger.error(traceback.format_exc()) - + # 即使获取关系值失败,仍有一个基础的几率触发 # 这确保即使数据库有问题,主动聊天功能仍然可用 base_fallback_probability = 0.1 # 较低的基础几率 random_fallback = random.random() fallback_trigger = random_fallback < base_fallback_probability if fallback_trigger: - logger.info(f"[私聊][{self.private_name}]获取关系值失败,使用后备触发机制: 概率={base_fallback_probability:.2f}, 决定={fallback_trigger}") + logger.info( + f"[私聊][{self.private_name}]获取关系值失败,使用后备触发机制: 概率={base_fallback_probability:.2f}, 决定={fallback_trigger}" + ) return fallback_trigger - + async def _check_idle_loop(self) -> None: """检查空闲状态的循环""" try: @@ -346,16 +357,16 @@ class IdleChat: # 如果禁用了功能,等待一段时间后再次检查配置 await asyncio.sleep(60) # 每分钟检查一次配置变更 continue - + # 检查当前用户是否应该触发主动聊天 should_trigger = await self._should_trigger() - + # 如果当前用户不触发,检查是否有其他用户已经超时未回复 if not should_trigger: async with self.__class__._global_lock: current_time = time.time() pending_timeout = 1800 # 30分钟未回复检查 - + # 检查此用户是否在等待回复列表中 if self.private_name in self.__class__._pending_replies: logger.debug(f"[私聊][{self.private_name}]当前用户在等待回复列表中,不进行额外检查") @@ -365,12 +376,12 @@ class IdleChat: for user, send_time in self.__class__._pending_replies.items(): if current_time - send_time > pending_timeout: timed_out_users.append(user) - + # 如果有超时未回复的用户,尝试找下一个用户 if timed_out_users: logger.info(f"[私聊]发现{len(timed_out_users)}个用户超过{pending_timeout}秒未回复") next_user = await self.__class__.get_next_available_user() - + if next_user and next_user != self.private_name: logger.info(f"[私聊]选择下一个用户[{next_user}]进行主动聊天") # 查找该用户的实例并触发聊天 @@ -380,7 +391,7 @@ class IdleChat: # 触发该实例的主动聊天 asyncio.create_task(instance._initiate_chat()) break - + # 如果当前用户应该触发主动聊天 if should_trigger: try: @@ -388,7 +399,7 @@ class IdleChat: # 更新上次触发时间 async with self._lock: self.last_trigger_time = time.time() - + # 将此用户添加到等待回复列表中 async with self.__class__._global_lock: self.__class__._pending_replies[self.private_name] = time.time() @@ -397,12 +408,12 @@ class IdleChat: except Exception as e: logger.error(f"[私聊][{self.private_name}]执行主动聊天过程出错: {str(e)}") logger.error(traceback.format_exc()) - + # 等待下一次检查 check_interval = self.check_interval # 使用配置的检查间隔 logger.debug(f"[私聊][{self.private_name}]等待{check_interval}秒后进行下一次主动聊天检查") await asyncio.sleep(check_interval) - + except asyncio.CancelledError: logger.debug(f"[私聊][{self.private_name}]主动聊天检测任务被取消") except Exception as e: @@ -412,24 +423,25 @@ class IdleChat: if self._running: logger.info(f"[私聊][{self.private_name}]尝试重新启动主动聊天检测") self._task = asyncio.create_task(self._check_idle_loop()) - + async def _get_chat_stream(self) -> Optional[ChatStream]: """获取聊天流实例""" try: # 尝试从全局聊天管理器获取现有的聊天流 from src.plugins.chat.chat_stream import chat_manager + existing_chat_stream = chat_manager.get_stream(self.stream_id) if existing_chat_stream: logger.debug(f"[私聊][{self.private_name}]从chat_manager找到现有聊天流") return existing_chat_stream - + # 如果没有现有聊天流,则创建新的 logger.debug(f"[私聊][{self.private_name}]未找到现有聊天流,创建新聊天流") # 创建用户信息对象 user_info = UserInfo( user_id=self.private_name, # 使用私聊用户的ID user_nickname=self.private_name, # 使用私聊用户的名称 - platform="qq" + platform="qq", ) # 创建聊天流 new_stream = ChatStream(self.stream_id, "qq", user_info) @@ -441,23 +453,19 @@ class IdleChat: logger.error(f"[私聊][{self.private_name}]创建/获取聊天流失败: {str(e)}") logger.error(traceback.format_exc()) return None - + async def _initiate_chat(self) -> None: """生成并发送主动聊天消息""" try: # 获取聊天历史记录 messages = self.chat_observer.get_cached_messages(limit=12) chat_history_text = await build_readable_messages( - messages, - replace_bot_name=True, - merge_messages=False, - timestamp_mode="relative", - read_mark=0.0 + messages, replace_bot_name=True, merge_messages=False, timestamp_mode="relative", read_mark=0.0 ) - + # 获取关系信息 from src.plugins.person_info.relationship_manager import relationship_manager - + # 获取关系值 relationship_value = 0 try: @@ -468,23 +476,25 @@ class IdleChat: relationship_value = relationship_manager.ensure_float(raw_value, person_id) except Exception as e: logger.warning(f"[私聊][{self.private_name}]获取关系值失败,使用默认值: {e}") - + # 使用PfcRepationshipTranslator获取关系描述 relationship_translator = PfcRepationshipTranslator(self.private_name) - full_relationship_text = await relationship_translator.translate_relationship_value_to_text(relationship_value) - + full_relationship_text = await relationship_translator.translate_relationship_value_to_text( + relationship_value + ) + # 提取纯关系描述(去掉"你们的关系是:"前缀) relationship_description = "普通" # 默认值 if ":" in full_relationship_text: relationship_description = full_relationship_text.split(":")[1].replace("。", "") - + if global_config.ENABLE_SCHEDULE_GEN: schedule_prompt = await global_prompt_manager.format_prompt( - "schedule_prompt", schedule_info=bot_schedule.get_current_num_task(num=1, time_info=False) - ) + "schedule_prompt", schedule_info=bot_schedule.get_current_num_task(num=1, time_info=False) + ) else: schedule_prompt = "" - + # 构建提示词 current_time = datetime.now().strftime("%H:%M") prompt = f"""你是{global_config.BOT_NICKNAME}。 @@ -500,14 +510,11 @@ class IdleChat: 请直接输出一条消息,不要有任何额外的解释或引导文字 消息内容尽量简短 """ - + # 生成回复 logger.debug(f"[私聊][{self.private_name}]开始生成主动聊天内容") try: - content, _ = await asyncio.wait_for( - self.llm.generate_response_async(prompt), - timeout=30 - ) + content, _ = await asyncio.wait_for(self.llm.generate_response_async(prompt), timeout=30) logger.debug(f"[私聊][{self.private_name}]成功生成主动聊天内容: {content}") except asyncio.TimeoutError: logger.error(f"[私聊][{self.private_name}]生成主动聊天内容超时") @@ -516,34 +523,30 @@ class IdleChat: logger.error(f"[私聊][{self.private_name}]生成主动聊天内容失败: {str(llm_err)}") logger.error(traceback.format_exc()) return - + # 清理结果 content = content.strip() content = content.strip("\"'") - + if not content: logger.error(f"[私聊][{self.private_name}]生成的主动聊天内容为空") return - + # 获取聊天流 chat_stream = await self._get_chat_stream() if not chat_stream: logger.error(f"[私聊][{self.private_name}]无法获取有效的聊天流,取消发送主动消息") return - + # 发送消息 try: logger.debug(f"[私聊][{self.private_name}]准备发送主动聊天消息: {content}") - await self.message_sender.send_message( - chat_stream=chat_stream, - content=content, - reply_to_message=None - ) + await self.message_sender.send_message(chat_stream=chat_stream, content=content, reply_to_message=None) logger.info(f"[私聊][{self.private_name}]成功主动发起聊天: {content}") except Exception as e: logger.error(f"[私聊][{self.private_name}]发送主动聊天消息失败: {str(e)}") logger.error(traceback.format_exc()) - + except Exception as e: logger.error(f"[私聊][{self.private_name}]主动发起聊天过程中发生未预期的错误: {str(e)}") - logger.error(traceback.format_exc()) \ No newline at end of file + logger.error(traceback.format_exc()) diff --git a/src/plugins/PFC/PFC_idle/idle_chat_manager.py b/src/plugins/PFC/PFC_idle/idle_chat_manager.py index 1218c109..a8708331 100644 --- a/src/plugins/PFC/PFC_idle/idle_chat_manager.py +++ b/src/plugins/PFC/PFC_idle/idle_chat_manager.py @@ -6,21 +6,22 @@ import traceback logger = get_logger("pfc_idle_chat_manager") + class IdleChatManager: """空闲聊天管理器 - + 用于管理所有私聊用户的空闲聊天实例。 采用单例模式,确保全局只有一个管理器实例。 """ - + _instance: Optional["IdleChatManager"] = None _lock: asyncio.Lock = asyncio.Lock() - + def __init__(self): """初始化空闲聊天管理器""" self._idle_chats: Dict[str, IdleChat] = {} # stream_id -> IdleChat self._active_conversations_count: Dict[str, int] = {} # stream_id -> count - + @classmethod def get_instance(cls) -> "IdleChatManager": """获取管理器单例 (同步版本) @@ -32,7 +33,7 @@ class IdleChatManager: # 在同步环境中创建实例 cls._instance = cls() return cls._instance - + @classmethod async def get_instance_async(cls) -> "IdleChatManager": """获取管理器单例 (异步版本) @@ -45,7 +46,7 @@ class IdleChatManager: if not cls._instance: cls._instance = cls() return cls._instance - + async def get_or_create_idle_chat(self, stream_id: str, private_name: str) -> IdleChat: """获取或创建空闲聊天实例 @@ -65,7 +66,7 @@ class IdleChatManager: idle_chat.start() # 启动空闲检测 logger.info(f"[私聊][{private_name}]创建并启动新的空闲聊天实例") return self._idle_chats[stream_id] - + async def remove_idle_chat(self, stream_id: str) -> None: """移除空闲聊天实例 @@ -79,7 +80,7 @@ class IdleChatManager: if stream_id in self._active_conversations_count: del self._active_conversations_count[stream_id] logger.info(f"[私聊][{idle_chat.private_name}]移除空闲聊天实例") - + async def notify_conversation_start(self, stream_id: str) -> None: """通知对话开始 @@ -96,15 +97,15 @@ class IdleChatManager: if len(parts) >= 2: private_name = parts[1] # 取第二部分作为名称 await self.get_or_create_idle_chat(stream_id, private_name) - + if stream_id not in self._active_conversations_count: self._active_conversations_count[stream_id] = 0 - + # 增加计数前记录当前值,用于日志 old_count = self._active_conversations_count[stream_id] self._active_conversations_count[stream_id] += 1 new_count = self._active_conversations_count[stream_id] - + # 确保IdleChat实例存在 idle_chat = self._idle_chats.get(stream_id) if idle_chat: @@ -115,7 +116,7 @@ class IdleChatManager: except Exception as e: logger.error(f"对话开始通知处理失败: {stream_id}, 错误: {e}") logger.error(traceback.format_exc()) - + async def notify_conversation_end(self, stream_id: str) -> None: """通知对话结束 @@ -125,16 +126,16 @@ class IdleChatManager: try: # 记录当前计数用于日志 old_count = self._active_conversations_count.get(stream_id, 0) - + # 安全减少计数,避免负数 if stream_id in self._active_conversations_count and self._active_conversations_count[stream_id] > 0: self._active_conversations_count[stream_id] -= 1 else: # 如果计数已经为0或不存在,设置为0 self._active_conversations_count[stream_id] = 0 - + new_count = self._active_conversations_count.get(stream_id, 0) - + # 确保IdleChat实例存在 idle_chat = self._idle_chats.get(stream_id) if idle_chat: @@ -142,7 +143,7 @@ class IdleChatManager: logger.debug(f"对话结束通知: {stream_id}, 计数从{old_count}减少到{new_count}") else: logger.warning(f"对话结束通知: {stream_id}, 计数减少但IdleChat不存在! 计数:{old_count}->{new_count}") - + # 检查是否所有对话都结束了,帮助调试 all_counts = sum(self._active_conversations_count.values()) if all_counts == 0: @@ -150,7 +151,7 @@ class IdleChatManager: except Exception as e: logger.error(f"对话结束通知处理失败: {stream_id}, 错误: {e}") logger.error(traceback.format_exc()) - + def get_idle_chat(self, stream_id: str) -> Optional[IdleChat]: """获取空闲聊天实例 @@ -161,7 +162,7 @@ class IdleChatManager: Optional[IdleChat]: 空闲聊天实例,如果不存在则返回None """ return self._idle_chats.get(stream_id) - + def get_active_conversations_count(self, stream_id: str) -> int: """获取指定流的活跃对话计数 @@ -172,11 +173,11 @@ class IdleChatManager: int: 活跃对话计数 """ return self._active_conversations_count.get(stream_id, 0) - + def get_all_active_conversations_count(self) -> int: """获取所有活跃对话总计数 Returns: int: 活跃对话总计数 """ - return sum(self._active_conversations_count.values()) \ No newline at end of file + return sum(self._active_conversations_count.values()) diff --git a/src/plugins/PFC/PFC_idle/idle_conversation.py b/src/plugins/PFC/PFC_idle/idle_conversation.py index 68e5a240..90036d13 100644 --- a/src/plugins/PFC/PFC_idle/idle_conversation.py +++ b/src/plugins/PFC/PFC_idle/idle_conversation.py @@ -6,6 +6,7 @@ import time logger = get_logger("pfc_idle_conversation") + class IdleConversation: """ 处理Idle聊天相关的功能,将这些功能从主Conversation类中分离出来, @@ -27,17 +28,18 @@ class IdleConversation: if self._initialization_in_progress: logger.debug("IdleConversation正在初始化中,等待完成") return False - + if self._idle_chat_manager is not None: logger.debug("IdleConversation已初始化,无需重复操作") return True - + # 标记开始初始化 self._initialization_in_progress = True - + try: # 从PFCManager获取IdleChatManager实例 from ..pfc_manager import PFCManager + pfc_manager = PFCManager.get_instance() self._idle_chat_manager = pfc_manager.get_idle_chat_manager() logger.debug("IdleConversation初始化完成,已获取IdleChatManager实例") @@ -49,19 +51,19 @@ class IdleConversation: finally: # 无论成功或失败,都清除初始化标志 self._initialization_in_progress = False - + async def start(self): """启动IdleConversation,创建后台监控任务""" if self._running: logger.debug("IdleConversation已经在运行") return False - + if not self._idle_chat_manager: success = await self.initialize() if not success: logger.error("无法启动IdleConversation:初始化失败") return False - + try: self._running = True # 创建后台监控任务,使用try-except块来捕获可能的异常 @@ -76,19 +78,19 @@ class IdleConversation: # 如果没有活跃的事件循环,记录警告但继续执行 logger.warning("没有活跃的事件循环,IdleConversation将不会启动监控任务") # 尽管没有监控任务,但仍然将running设为True表示IdleConversation已启动 - + return True except Exception as e: self._running = False logger.error(f"启动IdleConversation失败: {e}") logger.error(traceback.format_exc()) return False - + async def stop(self): """停止IdleConversation的后台任务""" if not self._running: return - + self._running = False if self._monitor_task and not self._monitor_task.done(): try: @@ -102,10 +104,10 @@ class IdleConversation: except Exception as e: logger.error(f"停止IdleConversation监控任务时出错: {e}") logger.error(traceback.format_exc()) - + self._monitor_task = None logger.info("IdleConversation已停止") - + async def _monitor_loop(self): """后台监控循环,定期检查活跃的会话并执行必要的操作""" try: @@ -114,15 +116,15 @@ class IdleConversation: # 同步活跃流计数到IdleChatManager if self._idle_chat_manager: await self._sync_active_streams_to_manager() - + # 这里可以添加定期检查逻辑,如查询空闲状态等 active_count = len(self._active_streams) logger.debug(f"IdleConversation监控中,当前活跃流数量: {active_count}") - + except Exception as e: logger.error(f"IdleConversation监控循环出错: {e}") logger.error(traceback.format_exc()) - + # 每30秒执行一次监控 await asyncio.sleep(30) except asyncio.CancelledError: @@ -131,29 +133,29 @@ class IdleConversation: logger.error(f"IdleConversation监控任务异常退出: {e}") logger.error(traceback.format_exc()) self._running = False - + async def _sync_active_streams_to_manager(self): """同步活跃流计数到IdleChatManager和IdleChat""" try: if not self._idle_chat_manager: return - + # 获取当前的活跃流列表 async with self._lock: active_streams = list(self._active_streams.keys()) - + # 对每个活跃流,确保IdleChatManager和IdleChat中的计数是正确的 for stream_id in active_streams: # 获取当前IdleChatManager中的计数 manager_count = self._idle_chat_manager.get_active_conversations_count(stream_id) - + # 由于我们的活跃流字典只记录是否活跃(值为True),所以计数应该是1 if manager_count != 1: # 修正IdleChatManager中的计数 old_count = manager_count self._idle_chat_manager._active_conversations_count[stream_id] = 1 logger.warning(f"同步调整IdleChatManager中的计数: stream_id={stream_id}, {old_count}->1") - + # 同时修正IdleChat中的计数 idle_chat = self._idle_chat_manager.get_idle_chat(stream_id) if idle_chat: @@ -161,26 +163,26 @@ class IdleConversation: old_count = getattr(idle_chat, "active_instances_count", 0) idle_chat.active_instances_count = 1 logger.warning(f"同步调整IdleChat中的计数: stream_id={stream_id}, {old_count}->1") - + # 检查IdleChatManager中有没有多余的计数(conversation中已不存在但manager中还有) for stream_id, count in list(self._idle_chat_manager._active_conversations_count.items()): if count > 0 and stream_id not in active_streams: # 重置为0 self._idle_chat_manager._active_conversations_count[stream_id] = 0 logger.warning(f"重置IdleChatManager中的多余计数: stream_id={stream_id}, {count}->0") - + # 同时修正IdleChat中的计数 idle_chat = self._idle_chat_manager.get_idle_chat(stream_id) if idle_chat and getattr(idle_chat, "active_instances_count", 0) > 0: old_count = getattr(idle_chat, "active_instances_count", 0) idle_chat.active_instances_count = 0 logger.warning(f"同步重置IdleChat中的计数: stream_id={stream_id}, {old_count}->0") - + # 日志记录同步结果 total_active = len(active_streams) total_manager = sum(self._idle_chat_manager._active_conversations_count.values()) logger.debug(f"同步后的计数: IdleConversation活跃流={total_active}, IdleChatManager总计数={total_manager}") - + except Exception as e: logger.error(f"同步活跃流计数失败: {e}") logger.error(traceback.format_exc()) @@ -188,25 +190,25 @@ class IdleConversation: async def get_or_create_idle_chat(self, stream_id: str, private_name: str): """ 获取或创建IdleChat实例 - + Args: stream_id: 聊天流ID private_name: 私聊对象名称,用于日志 - + Returns: bool: 操作是否成功 """ # 确保IdleConversation已启动 if not self._running: await self.start() - + if not self._idle_chat_manager: # 如果尚未初始化,尝试初始化 success = await self.initialize() if not success: logger.warning(f"[私聊][{private_name}] 获取或创建IdleChat失败:IdleChatManager未初始化") return False - + try: # 创建IdleChat实例 _idle_chat = await self._idle_chat_manager.get_or_create_idle_chat(stream_id, private_name) @@ -220,11 +222,11 @@ class IdleConversation: async def notify_conversation_start(self, stream_id: str, private_name: str) -> bool: """ 通知空闲聊天管理器对话开始 - + Args: stream_id: 聊天流ID private_name: 私聊对象名称,用于日志 - + Returns: bool: 通知是否成功 """ @@ -235,32 +237,32 @@ class IdleConversation: if not success: logger.warning(f"[私聊][{private_name}] 启动IdleConversation失败,无法通知对话开始") return False - + if not self._idle_chat_manager: # 如果尚未初始化,尝试初始化 success = await self.initialize() if not success: logger.warning(f"[私聊][{private_name}] 通知对话开始失败:IdleChatManager未初始化") return False - + try: # 确保IdleChat实例已创建 - 这是关键步骤,要先创建IdleChat await self.get_or_create_idle_chat(stream_id, private_name) - + # 先记录活跃状态 - 这是权威源 async with self._lock: self._active_streams[stream_id] = True - + # 然后同步到IdleChatManager if self._idle_chat_manager: await self._idle_chat_manager.notify_conversation_start(stream_id) logger.info(f"[私聊][{private_name}] 已通知空闲聊天管理器对话开始") else: logger.warning(f"[私聊][{private_name}] IdleChatManager不存在,但已记录活跃状态") - + # 立即进行一次同步,确保数据一致性 await self._sync_active_streams_to_manager() - + return True except Exception as e: logger.warning(f"[私聊][{private_name}] 通知空闲聊天管理器对话开始失败: {e}") @@ -277,11 +279,11 @@ class IdleConversation: async def notify_conversation_end(self, stream_id: str, private_name: str) -> bool: """ 通知空闲聊天管理器对话结束 - + Args: stream_id: 聊天流ID private_name: 私聊对象名称,用于日志 - + Returns: bool: 通知是否成功 """ @@ -293,30 +295,30 @@ class IdleConversation: del self._active_streams[stream_id] was_active = True logger.debug(f"[私聊][{private_name}] 已从活跃流中移除 {stream_id}") - + if not self._idle_chat_manager: # 如果尚未初始化,尝试初始化 success = await self.initialize() if not success: logger.warning(f"[私聊][{private_name}] 通知对话结束失败:IdleChatManager未初始化") return False - + try: # 然后同步到IdleChatManager if self._idle_chat_manager: # 无论如何都尝试通知 await self._idle_chat_manager.notify_conversation_end(stream_id) - + # 立即进行一次同步,确保数据一致性 await self._sync_active_streams_to_manager() - + logger.info(f"[私聊][{private_name}] 已通知空闲聊天管理器对话结束") - + # 检查当前活跃流数量 active_count = len(self._active_streams) if active_count == 0: logger.info(f"[私聊][{private_name}] 当前无活跃流,可能会触发主动聊天") - + # 额外调用:如果实例存在且只有在确实移除了活跃流的情况下才触发检查 if was_active: idle_chat = self._idle_chat_manager.get_idle_chat(stream_id) @@ -324,7 +326,7 @@ class IdleConversation: # 直接触发IdleChat检查,而不是等待下一个循环 logger.info(f"[私聊][{private_name}] 对话结束,手动触发一次主动聊天检查") asyncio.create_task(self._trigger_idle_chat_check(idle_chat, stream_id, private_name)) - + return True else: logger.warning(f"[私聊][{private_name}] IdleChatManager不存在,但已更新活跃状态") @@ -337,14 +339,14 @@ class IdleConversation: logger.error(f"[私聊][{private_name}] 处理对话结束通知时发生严重错误: {outer_e}") logger.error(traceback.format_exc()) return False - + async def _trigger_idle_chat_check(self, idle_chat, stream_id: str, private_name: str): """在对话结束后,手动触发一次IdleChat的检查""" try: # 确保活跃计数与IdleConversation一致 async with self._lock: is_active_in_conversation = stream_id in self._active_streams - + # 强制使IdleChat的计数与IdleConversation一致 if is_active_in_conversation: # 如果在IdleConversation中是活跃的,IdleChat的计数应该是1 @@ -358,17 +360,17 @@ class IdleConversation: old_count = idle_chat.active_instances_count idle_chat.active_instances_count = 0 logger.warning(f"[私聊][{private_name}] 修正IdleChat计数: {old_count}->0") - + # 等待1秒,让任何正在进行的处理完成 await asyncio.sleep(1) - + # 只有当stream不再活跃时才触发检查 if not is_active_in_conversation: # 尝试触发一次检查 if hasattr(idle_chat, "_should_trigger"): should_trigger = await idle_chat._should_trigger() logger.info(f"[私聊][{private_name}] 手动触发主动聊天检查结果: {should_trigger}") - + # 如果应该触发,直接调用_initiate_chat if should_trigger and hasattr(idle_chat, "_initiate_chat"): logger.info(f"[私聊][{private_name}] 手动触发主动聊天") @@ -384,11 +386,11 @@ class IdleConversation: def is_stream_active(self, stream_id: str) -> bool: """检查指定的stream是否活跃""" return stream_id in self._active_streams - + def get_active_streams_count(self) -> int: """获取当前活跃的stream数量""" return len(self._active_streams) - + @property def is_running(self) -> bool: """检查IdleConversation是否正在运行""" @@ -399,39 +401,41 @@ class IdleConversation: """获取IdleChatManager实例""" return self._idle_chat_manager + # 创建单例实例 _instance: Optional[IdleConversation] = None _instance_lock = asyncio.Lock() _initialization_in_progress = False # 防止并发初始化 + async def initialize_idle_conversation() -> IdleConversation: """初始化并启动IdleConversation单例实例""" global _initialization_in_progress - + # 防止并发初始化 if _initialization_in_progress: logger.debug("IdleConversation全局初始化正在进行中,等待完成") return get_idle_conversation_instance() - + # 标记正在初始化 _initialization_in_progress = True - + try: instance = get_idle_conversation_instance() - + # 如果实例已经在运行,避免重复初始化 - if getattr(instance, '_running', False): + if getattr(instance, "_running", False): logger.debug("IdleConversation已在运行状态,无需重新初始化") _initialization_in_progress = False return instance - + # 初始化实例 success = await instance.initialize() if not success: logger.error("IdleConversation初始化失败") _initialization_in_progress = False return instance - + # 启动实例 success = await instance.start() if not success: @@ -440,10 +444,10 @@ async def initialize_idle_conversation() -> IdleConversation: # 启动成功,进行初始检查 logger.info("IdleConversation启动成功,执行初始化后检查") # 这里可以添加一些启动后的检查,如果需要 - + # 创建一个异步任务,定期检查系统状态 asyncio.create_task(periodic_system_check(instance)) - + return instance except Exception as e: logger.error(f"初始化并启动IdleConversation时出错: {e}") @@ -455,40 +459,43 @@ async def initialize_idle_conversation() -> IdleConversation: # 清除初始化标志 _initialization_in_progress = False + async def periodic_system_check(instance: IdleConversation): """定期检查系统状态,确保主动聊天功能正常工作""" try: # 等待10秒,让系统完全启动 await asyncio.sleep(10) - - while getattr(instance, '_running', False): + + while getattr(instance, "_running", False): try: # 检查活跃流数量 - active_streams_count = len(getattr(instance, '_active_streams', {})) - + active_streams_count = len(getattr(instance, "_active_streams", {})) + # 如果IdleChatManager存在,检查其中的活跃对话计数 - idle_chat_manager = getattr(instance, '_idle_chat_manager', None) - if idle_chat_manager and hasattr(idle_chat_manager, 'get_all_active_conversations_count'): + idle_chat_manager = getattr(instance, "_idle_chat_manager", None) + if idle_chat_manager and hasattr(idle_chat_manager, "get_all_active_conversations_count"): manager_count = idle_chat_manager.get_all_active_conversations_count() - + # 如果两者不一致,记录警告 if active_streams_count != manager_count: - logger.warning(f"检测到计数不一致: IdleConversation记录的活跃流数量({active_streams_count}) 与 IdleChatManager记录的活跃对话数({manager_count})不匹配") - + logger.warning( + f"检测到计数不一致: IdleConversation记录的活跃流数量({active_streams_count}) 与 IdleChatManager记录的活跃对话数({manager_count})不匹配" + ) + # 如果IdleChatManager记录的计数为0但自己的记录不为0,进行修正 if manager_count == 0 and active_streams_count > 0: logger.warning("检测到可能的计数错误,尝试修正:清空IdleConversation的活跃流记录") async with instance._lock: instance._active_streams.clear() - + # 检查计数如果为0,帮助日志输出 if active_streams_count == 0: logger.debug("当前没有活跃的对话流,应该可以触发主动聊天") - + except Exception as check_err: logger.error(f"执行系统检查时出错: {check_err}") logger.error(traceback.format_exc()) - + # 每60秒检查一次 await asyncio.sleep(60) except asyncio.CancelledError: @@ -497,9 +504,10 @@ async def periodic_system_check(instance: IdleConversation): logger.error(f"系统检查任务异常退出: {e}") logger.error(traceback.format_exc()) + def get_idle_conversation_instance() -> IdleConversation: """获取IdleConversation的单例实例""" global _instance if _instance is None: _instance = IdleConversation() - return _instance \ No newline at end of file + return _instance diff --git a/src/plugins/PFC/PFC_idle/idle_conversation_starter.py b/src/plugins/PFC/PFC_idle/idle_conversation_starter.py index e6782ab1..83a58219 100644 --- a/src/plugins/PFC/PFC_idle/idle_conversation_starter.py +++ b/src/plugins/PFC/PFC_idle/idle_conversation_starter.py @@ -27,6 +27,7 @@ install(extra_lines=3) # 获取当前模块的日志记录器 logger = get_logger("idle_conversation_starter") + class IdleConversationStarter: """长时间无对话主动发起对话的组件 @@ -239,7 +240,7 @@ class IdleConversationStarter: # 在函数内部导入PFCManager,避免循环导入 from ..pfc_manager import PFCManager - + # 获取当前实例 - 注意这是同步方法,不需要await pfc_manager = PFCManager.get_instance() diff --git a/src/plugins/PFC/chat_observer.py b/src/plugins/PFC/chat_observer.py index 4fede3d2..0a4352e1 100644 --- a/src/plugins/PFC/chat_observer.py +++ b/src/plugins/PFC/chat_observer.py @@ -114,11 +114,11 @@ class ChatObserver: logger.debug( f"[私聊][{self.private_name}] 消息已添加到 ChatObserver 缓存,当前缓存大小: {len(self.message_cache)}" ) - + # 检查是否用户发送的消息(而非机器人自己) try: from .PFC_idle.idle_chat import IdleChat - + # 获取消息的发送者 user_info = message.get("user_info", {}) if user_info and str(user_info.get("user_id")) != str(global_config.BOT_QQ): diff --git a/src/plugins/PFC/conversation_initializer.py b/src/plugins/PFC/conversation_initializer.py index ec7e47a5..f5303341 100644 --- a/src/plugins/PFC/conversation_initializer.py +++ b/src/plugins/PFC/conversation_initializer.py @@ -99,10 +99,7 @@ async def load_initial_history(conversation_instance: "Conversation"): conversation_instance.chat_observer.last_message_time = ( conversation_instance.observation_info.last_message_time ) - if ( - conversation_instance.idle_chat - and conversation_instance.observation_info.last_message_time - ): + if conversation_instance.idle_chat and conversation_instance.observation_info.last_message_time: # 更新空闲计时器的起始时间 await conversation_instance.idle_chat.update_last_message_time( conversation_instance.observation_info.last_message_time diff --git a/src/plugins/PFC/conversation_loop.py b/src/plugins/PFC/conversation_loop.py index c4575735..4b65e99f 100644 --- a/src/plugins/PFC/conversation_loop.py +++ b/src/plugins/PFC/conversation_loop.py @@ -69,10 +69,7 @@ async def run_conversation_loop(conversation_instance: "Conversation"): conversation_instance.ignore_until_timestamp and loop_iter_start_time < conversation_instance.ignore_until_timestamp ): - if ( - conversation_instance.idle_chat - and conversation_instance.idle_chat._running - ): + if conversation_instance.idle_chat and conversation_instance.idle_chat._running: # 不直接停止服务,改为暂时忽略此用户 # 虽然我们仍然可以通过active_instances_count来决定是否触发主动聊天 # 但为了安全起见,我们只记录一个日志 From fd64e8516d7965ae3a11640680ad4ef9ab7f28b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 May 2025 11:33:01 +0000 Subject: [PATCH 08/17] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/PFC_idle/__init__.py | 12 +- src/plugins/PFC/PFC_idle/idle_chat.py | 243 +++++++++--------- src/plugins/PFC/PFC_idle/idle_chat_manager.py | 41 +-- src/plugins/PFC/PFC_idle/idle_conversation.py | 154 +++++------ .../PFC/PFC_idle/idle_conversation_starter.py | 3 +- src/plugins/PFC/chat_observer.py | 4 +- src/plugins/PFC/conversation_initializer.py | 5 +- src/plugins/PFC/conversation_loop.py | 5 +- 8 files changed, 237 insertions(+), 230 deletions(-) diff --git a/src/plugins/PFC/PFC_idle/__init__.py b/src/plugins/PFC/PFC_idle/__init__.py index a06c1834..77d90210 100644 --- a/src/plugins/PFC/PFC_idle/__init__.py +++ b/src/plugins/PFC/PFC_idle/__init__.py @@ -12,9 +12,9 @@ from .idle_chat_manager import IdleChatManager from .idle_conversation import IdleConversation, get_idle_conversation_instance, initialize_idle_conversation __all__ = [ - 'IdleChat', - 'IdleChatManager', - 'IdleConversation', - 'get_idle_conversation_instance', - 'initialize_idle_conversation' -] \ No newline at end of file + "IdleChat", + "IdleChatManager", + "IdleConversation", + "get_idle_conversation_instance", + "initialize_idle_conversation", +] diff --git a/src/plugins/PFC/PFC_idle/idle_chat.py b/src/plugins/PFC/PFC_idle/idle_chat.py index 6a01e4c4..2962d52c 100644 --- a/src/plugins/PFC/PFC_idle/idle_chat.py +++ b/src/plugins/PFC/PFC_idle/idle_chat.py @@ -22,32 +22,33 @@ install(extra_lines=3) logger = get_logger("pfc_idle_chat") + class IdleChat: """主动聊天组件(测试中) - + 在以下条件都满足时触发主动聊天: 1. 当前没有任何活跃的对话实例 2. 在指定的活动时间内(7:00-23:00) 3. 根据关系值动态调整触发概率 4. 上次触发后已经过了足够的冷却时间 """ - + # 单例模式实现 - _instances: Dict[str, 'IdleChat'] = {} - + _instances: Dict[str, "IdleChat"] = {} + # 全局共享状态,用于跟踪未回复的用户 _pending_replies: Dict[str, float] = {} # 用户名 -> 发送时间 _tried_users: Set[str] = set() # 已尝试过的用户集合 _global_lock = asyncio.Lock() # 保护共享状态的全局锁 - + @classmethod - def get_instance(cls, stream_id: str, private_name: str) -> 'IdleChat': + def get_instance(cls, stream_id: str, private_name: str) -> "IdleChat": """获取IdleChat实例(单例模式) - + Args: stream_id: 聊天流ID private_name: 私聊用户名称 - + Returns: IdleChat: IdleChat实例 """ @@ -58,13 +59,13 @@ class IdleChat: cls._instances[key].start() logger.info(f"[私聊][{private_name}]创建新的IdleChat实例并启动") return cls._instances[key] - + @classmethod async def register_user_response(cls, private_name: str) -> None: """注册用户已回复 - + 当用户回复消息时调用此方法,将用户从待回复列表中移除 - + Args: private_name: 私聊用户名称 """ @@ -72,39 +73,39 @@ class IdleChat: if private_name in cls._pending_replies: del cls._pending_replies[private_name] logger.info(f"[私聊][{private_name}]已回复主动聊天消息,从待回复列表中移除") - + @classmethod async def get_next_available_user(cls) -> Optional[str]: """获取下一个可用于主动聊天的用户 - + 优先选择未尝试过的用户,其次是已尝试但超时未回复的用户 - + Returns: Optional[str]: 下一个可用的用户名,如果没有则返回None """ async with cls._global_lock: current_time = time.time() timeout_threshold = 7200 # 2小时未回复视为超时 - + # 清理超时未回复的用户 for user, send_time in list(cls._pending_replies.items()): if current_time - send_time > timeout_threshold: logger.info(f"[私聊][{user}]超过{timeout_threshold}秒未回复,标记为超时") del cls._pending_replies[user] - + # 获取所有实例中的用户 all_users = set() for key in cls._instances: - user = key.split(':', 1)[0] + user = key.split(":", 1)[0] all_users.add(user) - + # 优先选择未尝试过的用户 untried_users = all_users - cls._tried_users if untried_users: next_user = random.choice(list(untried_users)) cls._tried_users.add(next_user) return next_user - + # 如果所有用户都已尝试过,重置尝试集合,从头开始 if len(cls._tried_users) >= len(all_users): cls._tried_users.clear() @@ -115,9 +116,9 @@ class IdleChat: next_user = random.choice(list(available_users)) cls._tried_users.add(next_user) return next_user - + return None - + def __init__(self, stream_id: str, private_name: str): """初始化主动聊天组件 @@ -129,99 +130,95 @@ class IdleChat: self.private_name = private_name self.chat_observer = ChatObserver.get_instance(stream_id, private_name) self.message_sender = DirectMessageSender(private_name) - + # 添加异步锁,保护对共享变量的访问 self._lock: asyncio.Lock = asyncio.Lock() - + # LLM请求对象,用于生成主动对话内容 - self.llm = LLMRequest( - model=global_config.llm_normal, - temperature=0.5, - max_tokens=500, - request_type="idle_chat" - ) - + self.llm = LLMRequest(model=global_config.llm_normal, temperature=0.5, max_tokens=500, request_type="idle_chat") + # 工作状态 self.active_instances_count: int = 0 self.last_trigger_time: float = time.time() - 1500 # 初始化时减少等待时间 self._running: bool = False self._task: Optional[asyncio.Task] = None - + # 配置参数 - 从global_config加载 - self.min_cooldown = getattr(global_config,"MIN_IDLE_TIME", 7200) # 最短冷却时间(默认2小时)建议修改长一点,你也不希望你的bot一直骚扰你吧 + self.min_cooldown = getattr( + global_config, "MIN_IDLE_TIME", 7200 + ) # 最短冷却时间(默认2小时)建议修改长一点,你也不希望你的bot一直骚扰你吧 self.max_cooldown = getattr(global_config, "MAX_IDLE_TIME", 14400) # 最长冷却时间(默认4小时) - self.min_idle_time = getattr(global_config, "MIN_IDLE_TIME", 3600) + self.min_idle_time = getattr(global_config, "MIN_IDLE_TIME", 3600) self.check_interval = getattr(global_config, "IDLE_CHECK_INTERVAL", 600) # 检查间隔(默认10分钟) self.active_hours_start = 6 # 活动开始时间 - self.active_hours_end = 24 # 活动结束时间 - + self.active_hours_end = 24 # 活动结束时间 + # 关系值相关 self.base_trigger_probability = 0.3 # 基础触发概率 - self.relationship_factor = 0.0003 # 关系值影响因子 - + self.relationship_factor = 0.0003 # 关系值影响因子 + def start(self) -> None: """启动主动聊天检测""" # 检查是否启用了主动聊天功能 if not getattr(global_config, "ENABLE_IDLE_CONVERSATION", False): logger.info(f"[私聊][{self.private_name}]主动聊天功能已禁用(配置ENABLE_IDLE_CONVERSATION=False)") return - + if self._running: logger.debug(f"[私聊][{self.private_name}]主动聊天功能已在运行中") return - + self._running = True self._task = asyncio.create_task(self._check_idle_loop()) logger.info(f"[私聊][{self.private_name}]启动主动聊天检测") - + def stop(self) -> None: - """停止主动聊天检测 - """ + """停止主动聊天检测""" if not self._running: return - + self._running = False if self._task: self._task.cancel() self._task = None logger.info(f"[私聊][{self.private_name}]停止主动聊天检测") - + async def increment_active_instances(self) -> None: """增加活跃实例计数 - + 当创建新的对话实例时调用此方法 """ async with self._lock: self.active_instances_count += 1 logger.debug(f"[私聊][{self.private_name}]活跃实例数+1,当前:{self.active_instances_count}") - + async def decrement_active_instances(self) -> None: """减少活跃实例计数 - + 当对话实例结束时调用此方法 """ async with self._lock: self.active_instances_count = max(0, self.active_instances_count - 1) logger.debug(f"[私聊][{self.private_name}]活跃实例数-1,当前:{self.active_instances_count}") - + async def update_last_message_time(self, message_time: Optional[float] = None) -> None: """更新最后一条消息的时间 - + Args: message_time: 消息时间戳,如果为None则使用当前时间 """ async with self._lock: self.last_trigger_time = message_time or time.time() logger.debug(f"[私聊][{self.private_name}]更新最后消息时间: {self.last_trigger_time:.2f}") - + # 当用户发送消息时,也应该注册响应 await self.__class__.register_user_response(self.private_name) - + def _is_active_hours(self) -> bool: """检查是否在活动时间内""" current_hour = datetime.now().hour return self.active_hours_start <= current_hour < self.active_hours_end - + async def _should_trigger(self) -> bool: """检查是否应该触发主动聊天""" async with self._lock: @@ -229,25 +226,27 @@ class IdleChat: if self.active_instances_count < 0: logger.warning(f"[私聊][{self.private_name}]检测到活跃实例数为负数,重置为0") self.active_instances_count = 0 - + # 检查是否有活跃实例 if self.active_instances_count > 0: logger.debug(f"[私聊][{self.private_name}]存在活跃实例({self.active_instances_count}),不触发主动聊天") return False - + # 检查是否在活动时间内 if not self._is_active_hours(): logger.debug(f"[私聊][{self.private_name}]不在活动时间内,不触发主动聊天") return False - + # 检查冷却时间 current_time = time.time() time_since_last_trigger = current_time - self.last_trigger_time if time_since_last_trigger < self.min_cooldown: time_left = self.min_cooldown - time_since_last_trigger - logger.debug(f"[私聊][{self.private_name}]冷却时间未到(已过{time_since_last_trigger:.0f}秒/需要{self.min_cooldown}秒),还需等待{time_left:.0f}秒,不触发主动聊天") + logger.debug( + f"[私聊][{self.private_name}]冷却时间未到(已过{time_since_last_trigger:.0f}秒/需要{self.min_cooldown}秒),还需等待{time_left:.0f}秒,不触发主动聊天" + ) return False - + # 强制触发检查 - 如果超过最大冷却时间,增加触发概率 force_trigger = False if time_since_last_trigger > self.max_cooldown * 2: # 如果超过最大冷却时间的两倍 @@ -255,9 +254,11 @@ class IdleChat: random_force = random.random() force_trigger = random_force < force_probability if force_trigger: - logger.info(f"[私聊][{self.private_name}]超过最大冷却时间({time_since_last_trigger:.0f}秒),强制触发主动聊天") + logger.info( + f"[私聊][{self.private_name}]超过最大冷却时间({time_since_last_trigger:.0f}秒),强制触发主动聊天" + ) return True - + # 获取关系值 relationship_value = 0 try: @@ -270,10 +271,10 @@ class IdleChat: # 先尝试通过昵称获取person_id platform = "qq" # 默认平台 person_id = person_info_manager.get_person_id(platform, self.private_name) - + # 如果通过昵称获取失败,尝试通过stream_id解析 if not person_id: - parts = self.stream_id.split('_') + parts = self.stream_id.split("_") if len(parts) >= 2 and parts[0] == "private": user_id = parts[1] platform = parts[2] if len(parts) >= 3 else "qq" @@ -284,7 +285,7 @@ class IdleChat: person_id = person_info_manager.get_person_id(platform, user_id) except Exception as e2: logger.warning(f"[私聊][{self.private_name}]尝试获取person_id失败: {str(e2)}") - + # 获取关系值 if person_id: raw_value = await person_info_manager.get_value(person_id, "relationship_value") @@ -292,51 +293,61 @@ class IdleChat: logger.debug(f"[私聊][{self.private_name}]成功获取关系值: {relationship_value}") else: logger.warning(f"[私聊][{self.private_name}]无法获取person_id,使用默认关系值0") - + # 使用PfcRepationshipTranslator获取关系描述 relationship_translator = PfcRepationshipTranslator(self.private_name) - relationship_level = relationship_translator._calculate_relationship_level_num(relationship_value, self.private_name) - + relationship_level = relationship_translator._calculate_relationship_level_num( + relationship_value, self.private_name + ) + # 基于关系等级调整触发概率 # 关系越好,主动聊天概率越高 level_probability_factors = [0.05, 0.1, 0.2, 0.3, 0.4, 0.5] # 每个等级对应的基础概率因子 base_probability = level_probability_factors[relationship_level] - + # 基础概率因子 trigger_probability = base_probability trigger_probability = max(0.05, min(0.6, trigger_probability)) # 限制在0.05-0.6之间 - + # 最大冷却时间调整 - 随着冷却时间增加,逐渐增加触发概率 if time_since_last_trigger > self.max_cooldown: # 计算额外概率 - 每超过最大冷却时间的10%,增加1%的概率,最多增加30% - extra_time_factor = min(0.3, (time_since_last_trigger - self.max_cooldown) / (self.max_cooldown * 10)) + extra_time_factor = min( + 0.3, (time_since_last_trigger - self.max_cooldown) / (self.max_cooldown * 10) + ) trigger_probability += extra_time_factor logger.debug(f"[私聊][{self.private_name}]超过标准冷却时间,额外增加概率: +{extra_time_factor:.2f}") - + # 随机判断是否触发 random_value = random.random() should_trigger = random_value < trigger_probability - logger.debug(f"[私聊][{self.private_name}]触发概率计算: 基础({base_probability:.2f}) + 关系值({relationship_value})影响 = {trigger_probability:.2f},随机值={random_value:.2f}, 结果={should_trigger}") - + logger.debug( + f"[私聊][{self.private_name}]触发概率计算: 基础({base_probability:.2f}) + 关系值({relationship_value})影响 = {trigger_probability:.2f},随机值={random_value:.2f}, 结果={should_trigger}" + ) + # 如果决定触发,记录详细日志 if should_trigger: - logger.info(f"[私聊][{self.private_name}]决定触发主动聊天: 触发概率={trigger_probability:.2f}, 距上次已过{time_since_last_trigger:.0f}秒") - + logger.info( + f"[私聊][{self.private_name}]决定触发主动聊天: 触发概率={trigger_probability:.2f}, 距上次已过{time_since_last_trigger:.0f}秒" + ) + return should_trigger - + except Exception as e: logger.error(f"[私聊][{self.private_name}]获取关系值失败: {str(e)}") logger.error(traceback.format_exc()) - + # 即使获取关系值失败,仍有一个基础的几率触发 # 这确保即使数据库有问题,主动聊天功能仍然可用 base_fallback_probability = 0.1 # 较低的基础几率 random_fallback = random.random() fallback_trigger = random_fallback < base_fallback_probability if fallback_trigger: - logger.info(f"[私聊][{self.private_name}]获取关系值失败,使用后备触发机制: 概率={base_fallback_probability:.2f}, 决定={fallback_trigger}") + logger.info( + f"[私聊][{self.private_name}]获取关系值失败,使用后备触发机制: 概率={base_fallback_probability:.2f}, 决定={fallback_trigger}" + ) return fallback_trigger - + async def _check_idle_loop(self) -> None: """检查空闲状态的循环""" try: @@ -346,16 +357,16 @@ class IdleChat: # 如果禁用了功能,等待一段时间后再次检查配置 await asyncio.sleep(60) # 每分钟检查一次配置变更 continue - + # 检查当前用户是否应该触发主动聊天 should_trigger = await self._should_trigger() - + # 如果当前用户不触发,检查是否有其他用户已经超时未回复 if not should_trigger: async with self.__class__._global_lock: current_time = time.time() pending_timeout = 1800 # 30分钟未回复检查 - + # 检查此用户是否在等待回复列表中 if self.private_name in self.__class__._pending_replies: logger.debug(f"[私聊][{self.private_name}]当前用户在等待回复列表中,不进行额外检查") @@ -365,12 +376,12 @@ class IdleChat: for user, send_time in self.__class__._pending_replies.items(): if current_time - send_time > pending_timeout: timed_out_users.append(user) - + # 如果有超时未回复的用户,尝试找下一个用户 if timed_out_users: logger.info(f"[私聊]发现{len(timed_out_users)}个用户超过{pending_timeout}秒未回复") next_user = await self.__class__.get_next_available_user() - + if next_user and next_user != self.private_name: logger.info(f"[私聊]选择下一个用户[{next_user}]进行主动聊天") # 查找该用户的实例并触发聊天 @@ -380,7 +391,7 @@ class IdleChat: # 触发该实例的主动聊天 asyncio.create_task(instance._initiate_chat()) break - + # 如果当前用户应该触发主动聊天 if should_trigger: try: @@ -388,7 +399,7 @@ class IdleChat: # 更新上次触发时间 async with self._lock: self.last_trigger_time = time.time() - + # 将此用户添加到等待回复列表中 async with self.__class__._global_lock: self.__class__._pending_replies[self.private_name] = time.time() @@ -397,12 +408,12 @@ class IdleChat: except Exception as e: logger.error(f"[私聊][{self.private_name}]执行主动聊天过程出错: {str(e)}") logger.error(traceback.format_exc()) - + # 等待下一次检查 check_interval = self.check_interval # 使用配置的检查间隔 logger.debug(f"[私聊][{self.private_name}]等待{check_interval}秒后进行下一次主动聊天检查") await asyncio.sleep(check_interval) - + except asyncio.CancelledError: logger.debug(f"[私聊][{self.private_name}]主动聊天检测任务被取消") except Exception as e: @@ -412,24 +423,25 @@ class IdleChat: if self._running: logger.info(f"[私聊][{self.private_name}]尝试重新启动主动聊天检测") self._task = asyncio.create_task(self._check_idle_loop()) - + async def _get_chat_stream(self) -> Optional[ChatStream]: """获取聊天流实例""" try: # 尝试从全局聊天管理器获取现有的聊天流 from src.plugins.chat.chat_stream import chat_manager + existing_chat_stream = chat_manager.get_stream(self.stream_id) if existing_chat_stream: logger.debug(f"[私聊][{self.private_name}]从chat_manager找到现有聊天流") return existing_chat_stream - + # 如果没有现有聊天流,则创建新的 logger.debug(f"[私聊][{self.private_name}]未找到现有聊天流,创建新聊天流") # 创建用户信息对象 user_info = UserInfo( user_id=self.private_name, # 使用私聊用户的ID user_nickname=self.private_name, # 使用私聊用户的名称 - platform="qq" + platform="qq", ) # 创建聊天流 new_stream = ChatStream(self.stream_id, "qq", user_info) @@ -441,23 +453,19 @@ class IdleChat: logger.error(f"[私聊][{self.private_name}]创建/获取聊天流失败: {str(e)}") logger.error(traceback.format_exc()) return None - + async def _initiate_chat(self) -> None: """生成并发送主动聊天消息""" try: # 获取聊天历史记录 messages = self.chat_observer.get_cached_messages(limit=12) chat_history_text = await build_readable_messages( - messages, - replace_bot_name=True, - merge_messages=False, - timestamp_mode="relative", - read_mark=0.0 + messages, replace_bot_name=True, merge_messages=False, timestamp_mode="relative", read_mark=0.0 ) - + # 获取关系信息 from src.plugins.person_info.relationship_manager import relationship_manager - + # 获取关系值 relationship_value = 0 try: @@ -468,23 +476,25 @@ class IdleChat: relationship_value = relationship_manager.ensure_float(raw_value, person_id) except Exception as e: logger.warning(f"[私聊][{self.private_name}]获取关系值失败,使用默认值: {e}") - + # 使用PfcRepationshipTranslator获取关系描述 relationship_translator = PfcRepationshipTranslator(self.private_name) - full_relationship_text = await relationship_translator.translate_relationship_value_to_text(relationship_value) - + full_relationship_text = await relationship_translator.translate_relationship_value_to_text( + relationship_value + ) + # 提取纯关系描述(去掉"你们的关系是:"前缀) relationship_description = "普通" # 默认值 if ":" in full_relationship_text: relationship_description = full_relationship_text.split(":")[1].replace("。", "") - + if global_config.ENABLE_SCHEDULE_GEN: schedule_prompt = await global_prompt_manager.format_prompt( - "schedule_prompt", schedule_info=bot_schedule.get_current_num_task(num=1, time_info=False) - ) + "schedule_prompt", schedule_info=bot_schedule.get_current_num_task(num=1, time_info=False) + ) else: schedule_prompt = "" - + # 构建提示词 current_time = datetime.now().strftime("%H:%M") prompt = f"""你是{global_config.BOT_NICKNAME}。 @@ -500,14 +510,11 @@ class IdleChat: 请直接输出一条消息,不要有任何额外的解释或引导文字 消息内容尽量简短 """ - + # 生成回复 logger.debug(f"[私聊][{self.private_name}]开始生成主动聊天内容") try: - content, _ = await asyncio.wait_for( - self.llm.generate_response_async(prompt), - timeout=30 - ) + content, _ = await asyncio.wait_for(self.llm.generate_response_async(prompt), timeout=30) logger.debug(f"[私聊][{self.private_name}]成功生成主动聊天内容: {content}") except asyncio.TimeoutError: logger.error(f"[私聊][{self.private_name}]生成主动聊天内容超时") @@ -516,34 +523,30 @@ class IdleChat: logger.error(f"[私聊][{self.private_name}]生成主动聊天内容失败: {str(llm_err)}") logger.error(traceback.format_exc()) return - + # 清理结果 content = content.strip() content = content.strip("\"'") - + if not content: logger.error(f"[私聊][{self.private_name}]生成的主动聊天内容为空") return - + # 获取聊天流 chat_stream = await self._get_chat_stream() if not chat_stream: logger.error(f"[私聊][{self.private_name}]无法获取有效的聊天流,取消发送主动消息") return - + # 发送消息 try: logger.debug(f"[私聊][{self.private_name}]准备发送主动聊天消息: {content}") - await self.message_sender.send_message( - chat_stream=chat_stream, - content=content, - reply_to_message=None - ) + await self.message_sender.send_message(chat_stream=chat_stream, content=content, reply_to_message=None) logger.info(f"[私聊][{self.private_name}]成功主动发起聊天: {content}") except Exception as e: logger.error(f"[私聊][{self.private_name}]发送主动聊天消息失败: {str(e)}") logger.error(traceback.format_exc()) - + except Exception as e: logger.error(f"[私聊][{self.private_name}]主动发起聊天过程中发生未预期的错误: {str(e)}") - logger.error(traceback.format_exc()) \ No newline at end of file + logger.error(traceback.format_exc()) diff --git a/src/plugins/PFC/PFC_idle/idle_chat_manager.py b/src/plugins/PFC/PFC_idle/idle_chat_manager.py index 1218c109..a8708331 100644 --- a/src/plugins/PFC/PFC_idle/idle_chat_manager.py +++ b/src/plugins/PFC/PFC_idle/idle_chat_manager.py @@ -6,21 +6,22 @@ import traceback logger = get_logger("pfc_idle_chat_manager") + class IdleChatManager: """空闲聊天管理器 - + 用于管理所有私聊用户的空闲聊天实例。 采用单例模式,确保全局只有一个管理器实例。 """ - + _instance: Optional["IdleChatManager"] = None _lock: asyncio.Lock = asyncio.Lock() - + def __init__(self): """初始化空闲聊天管理器""" self._idle_chats: Dict[str, IdleChat] = {} # stream_id -> IdleChat self._active_conversations_count: Dict[str, int] = {} # stream_id -> count - + @classmethod def get_instance(cls) -> "IdleChatManager": """获取管理器单例 (同步版本) @@ -32,7 +33,7 @@ class IdleChatManager: # 在同步环境中创建实例 cls._instance = cls() return cls._instance - + @classmethod async def get_instance_async(cls) -> "IdleChatManager": """获取管理器单例 (异步版本) @@ -45,7 +46,7 @@ class IdleChatManager: if not cls._instance: cls._instance = cls() return cls._instance - + async def get_or_create_idle_chat(self, stream_id: str, private_name: str) -> IdleChat: """获取或创建空闲聊天实例 @@ -65,7 +66,7 @@ class IdleChatManager: idle_chat.start() # 启动空闲检测 logger.info(f"[私聊][{private_name}]创建并启动新的空闲聊天实例") return self._idle_chats[stream_id] - + async def remove_idle_chat(self, stream_id: str) -> None: """移除空闲聊天实例 @@ -79,7 +80,7 @@ class IdleChatManager: if stream_id in self._active_conversations_count: del self._active_conversations_count[stream_id] logger.info(f"[私聊][{idle_chat.private_name}]移除空闲聊天实例") - + async def notify_conversation_start(self, stream_id: str) -> None: """通知对话开始 @@ -96,15 +97,15 @@ class IdleChatManager: if len(parts) >= 2: private_name = parts[1] # 取第二部分作为名称 await self.get_or_create_idle_chat(stream_id, private_name) - + if stream_id not in self._active_conversations_count: self._active_conversations_count[stream_id] = 0 - + # 增加计数前记录当前值,用于日志 old_count = self._active_conversations_count[stream_id] self._active_conversations_count[stream_id] += 1 new_count = self._active_conversations_count[stream_id] - + # 确保IdleChat实例存在 idle_chat = self._idle_chats.get(stream_id) if idle_chat: @@ -115,7 +116,7 @@ class IdleChatManager: except Exception as e: logger.error(f"对话开始通知处理失败: {stream_id}, 错误: {e}") logger.error(traceback.format_exc()) - + async def notify_conversation_end(self, stream_id: str) -> None: """通知对话结束 @@ -125,16 +126,16 @@ class IdleChatManager: try: # 记录当前计数用于日志 old_count = self._active_conversations_count.get(stream_id, 0) - + # 安全减少计数,避免负数 if stream_id in self._active_conversations_count and self._active_conversations_count[stream_id] > 0: self._active_conversations_count[stream_id] -= 1 else: # 如果计数已经为0或不存在,设置为0 self._active_conversations_count[stream_id] = 0 - + new_count = self._active_conversations_count.get(stream_id, 0) - + # 确保IdleChat实例存在 idle_chat = self._idle_chats.get(stream_id) if idle_chat: @@ -142,7 +143,7 @@ class IdleChatManager: logger.debug(f"对话结束通知: {stream_id}, 计数从{old_count}减少到{new_count}") else: logger.warning(f"对话结束通知: {stream_id}, 计数减少但IdleChat不存在! 计数:{old_count}->{new_count}") - + # 检查是否所有对话都结束了,帮助调试 all_counts = sum(self._active_conversations_count.values()) if all_counts == 0: @@ -150,7 +151,7 @@ class IdleChatManager: except Exception as e: logger.error(f"对话结束通知处理失败: {stream_id}, 错误: {e}") logger.error(traceback.format_exc()) - + def get_idle_chat(self, stream_id: str) -> Optional[IdleChat]: """获取空闲聊天实例 @@ -161,7 +162,7 @@ class IdleChatManager: Optional[IdleChat]: 空闲聊天实例,如果不存在则返回None """ return self._idle_chats.get(stream_id) - + def get_active_conversations_count(self, stream_id: str) -> int: """获取指定流的活跃对话计数 @@ -172,11 +173,11 @@ class IdleChatManager: int: 活跃对话计数 """ return self._active_conversations_count.get(stream_id, 0) - + def get_all_active_conversations_count(self) -> int: """获取所有活跃对话总计数 Returns: int: 活跃对话总计数 """ - return sum(self._active_conversations_count.values()) \ No newline at end of file + return sum(self._active_conversations_count.values()) diff --git a/src/plugins/PFC/PFC_idle/idle_conversation.py b/src/plugins/PFC/PFC_idle/idle_conversation.py index 68e5a240..90036d13 100644 --- a/src/plugins/PFC/PFC_idle/idle_conversation.py +++ b/src/plugins/PFC/PFC_idle/idle_conversation.py @@ -6,6 +6,7 @@ import time logger = get_logger("pfc_idle_conversation") + class IdleConversation: """ 处理Idle聊天相关的功能,将这些功能从主Conversation类中分离出来, @@ -27,17 +28,18 @@ class IdleConversation: if self._initialization_in_progress: logger.debug("IdleConversation正在初始化中,等待完成") return False - + if self._idle_chat_manager is not None: logger.debug("IdleConversation已初始化,无需重复操作") return True - + # 标记开始初始化 self._initialization_in_progress = True - + try: # 从PFCManager获取IdleChatManager实例 from ..pfc_manager import PFCManager + pfc_manager = PFCManager.get_instance() self._idle_chat_manager = pfc_manager.get_idle_chat_manager() logger.debug("IdleConversation初始化完成,已获取IdleChatManager实例") @@ -49,19 +51,19 @@ class IdleConversation: finally: # 无论成功或失败,都清除初始化标志 self._initialization_in_progress = False - + async def start(self): """启动IdleConversation,创建后台监控任务""" if self._running: logger.debug("IdleConversation已经在运行") return False - + if not self._idle_chat_manager: success = await self.initialize() if not success: logger.error("无法启动IdleConversation:初始化失败") return False - + try: self._running = True # 创建后台监控任务,使用try-except块来捕获可能的异常 @@ -76,19 +78,19 @@ class IdleConversation: # 如果没有活跃的事件循环,记录警告但继续执行 logger.warning("没有活跃的事件循环,IdleConversation将不会启动监控任务") # 尽管没有监控任务,但仍然将running设为True表示IdleConversation已启动 - + return True except Exception as e: self._running = False logger.error(f"启动IdleConversation失败: {e}") logger.error(traceback.format_exc()) return False - + async def stop(self): """停止IdleConversation的后台任务""" if not self._running: return - + self._running = False if self._monitor_task and not self._monitor_task.done(): try: @@ -102,10 +104,10 @@ class IdleConversation: except Exception as e: logger.error(f"停止IdleConversation监控任务时出错: {e}") logger.error(traceback.format_exc()) - + self._monitor_task = None logger.info("IdleConversation已停止") - + async def _monitor_loop(self): """后台监控循环,定期检查活跃的会话并执行必要的操作""" try: @@ -114,15 +116,15 @@ class IdleConversation: # 同步活跃流计数到IdleChatManager if self._idle_chat_manager: await self._sync_active_streams_to_manager() - + # 这里可以添加定期检查逻辑,如查询空闲状态等 active_count = len(self._active_streams) logger.debug(f"IdleConversation监控中,当前活跃流数量: {active_count}") - + except Exception as e: logger.error(f"IdleConversation监控循环出错: {e}") logger.error(traceback.format_exc()) - + # 每30秒执行一次监控 await asyncio.sleep(30) except asyncio.CancelledError: @@ -131,29 +133,29 @@ class IdleConversation: logger.error(f"IdleConversation监控任务异常退出: {e}") logger.error(traceback.format_exc()) self._running = False - + async def _sync_active_streams_to_manager(self): """同步活跃流计数到IdleChatManager和IdleChat""" try: if not self._idle_chat_manager: return - + # 获取当前的活跃流列表 async with self._lock: active_streams = list(self._active_streams.keys()) - + # 对每个活跃流,确保IdleChatManager和IdleChat中的计数是正确的 for stream_id in active_streams: # 获取当前IdleChatManager中的计数 manager_count = self._idle_chat_manager.get_active_conversations_count(stream_id) - + # 由于我们的活跃流字典只记录是否活跃(值为True),所以计数应该是1 if manager_count != 1: # 修正IdleChatManager中的计数 old_count = manager_count self._idle_chat_manager._active_conversations_count[stream_id] = 1 logger.warning(f"同步调整IdleChatManager中的计数: stream_id={stream_id}, {old_count}->1") - + # 同时修正IdleChat中的计数 idle_chat = self._idle_chat_manager.get_idle_chat(stream_id) if idle_chat: @@ -161,26 +163,26 @@ class IdleConversation: old_count = getattr(idle_chat, "active_instances_count", 0) idle_chat.active_instances_count = 1 logger.warning(f"同步调整IdleChat中的计数: stream_id={stream_id}, {old_count}->1") - + # 检查IdleChatManager中有没有多余的计数(conversation中已不存在但manager中还有) for stream_id, count in list(self._idle_chat_manager._active_conversations_count.items()): if count > 0 and stream_id not in active_streams: # 重置为0 self._idle_chat_manager._active_conversations_count[stream_id] = 0 logger.warning(f"重置IdleChatManager中的多余计数: stream_id={stream_id}, {count}->0") - + # 同时修正IdleChat中的计数 idle_chat = self._idle_chat_manager.get_idle_chat(stream_id) if idle_chat and getattr(idle_chat, "active_instances_count", 0) > 0: old_count = getattr(idle_chat, "active_instances_count", 0) idle_chat.active_instances_count = 0 logger.warning(f"同步重置IdleChat中的计数: stream_id={stream_id}, {old_count}->0") - + # 日志记录同步结果 total_active = len(active_streams) total_manager = sum(self._idle_chat_manager._active_conversations_count.values()) logger.debug(f"同步后的计数: IdleConversation活跃流={total_active}, IdleChatManager总计数={total_manager}") - + except Exception as e: logger.error(f"同步活跃流计数失败: {e}") logger.error(traceback.format_exc()) @@ -188,25 +190,25 @@ class IdleConversation: async def get_or_create_idle_chat(self, stream_id: str, private_name: str): """ 获取或创建IdleChat实例 - + Args: stream_id: 聊天流ID private_name: 私聊对象名称,用于日志 - + Returns: bool: 操作是否成功 """ # 确保IdleConversation已启动 if not self._running: await self.start() - + if not self._idle_chat_manager: # 如果尚未初始化,尝试初始化 success = await self.initialize() if not success: logger.warning(f"[私聊][{private_name}] 获取或创建IdleChat失败:IdleChatManager未初始化") return False - + try: # 创建IdleChat实例 _idle_chat = await self._idle_chat_manager.get_or_create_idle_chat(stream_id, private_name) @@ -220,11 +222,11 @@ class IdleConversation: async def notify_conversation_start(self, stream_id: str, private_name: str) -> bool: """ 通知空闲聊天管理器对话开始 - + Args: stream_id: 聊天流ID private_name: 私聊对象名称,用于日志 - + Returns: bool: 通知是否成功 """ @@ -235,32 +237,32 @@ class IdleConversation: if not success: logger.warning(f"[私聊][{private_name}] 启动IdleConversation失败,无法通知对话开始") return False - + if not self._idle_chat_manager: # 如果尚未初始化,尝试初始化 success = await self.initialize() if not success: logger.warning(f"[私聊][{private_name}] 通知对话开始失败:IdleChatManager未初始化") return False - + try: # 确保IdleChat实例已创建 - 这是关键步骤,要先创建IdleChat await self.get_or_create_idle_chat(stream_id, private_name) - + # 先记录活跃状态 - 这是权威源 async with self._lock: self._active_streams[stream_id] = True - + # 然后同步到IdleChatManager if self._idle_chat_manager: await self._idle_chat_manager.notify_conversation_start(stream_id) logger.info(f"[私聊][{private_name}] 已通知空闲聊天管理器对话开始") else: logger.warning(f"[私聊][{private_name}] IdleChatManager不存在,但已记录活跃状态") - + # 立即进行一次同步,确保数据一致性 await self._sync_active_streams_to_manager() - + return True except Exception as e: logger.warning(f"[私聊][{private_name}] 通知空闲聊天管理器对话开始失败: {e}") @@ -277,11 +279,11 @@ class IdleConversation: async def notify_conversation_end(self, stream_id: str, private_name: str) -> bool: """ 通知空闲聊天管理器对话结束 - + Args: stream_id: 聊天流ID private_name: 私聊对象名称,用于日志 - + Returns: bool: 通知是否成功 """ @@ -293,30 +295,30 @@ class IdleConversation: del self._active_streams[stream_id] was_active = True logger.debug(f"[私聊][{private_name}] 已从活跃流中移除 {stream_id}") - + if not self._idle_chat_manager: # 如果尚未初始化,尝试初始化 success = await self.initialize() if not success: logger.warning(f"[私聊][{private_name}] 通知对话结束失败:IdleChatManager未初始化") return False - + try: # 然后同步到IdleChatManager if self._idle_chat_manager: # 无论如何都尝试通知 await self._idle_chat_manager.notify_conversation_end(stream_id) - + # 立即进行一次同步,确保数据一致性 await self._sync_active_streams_to_manager() - + logger.info(f"[私聊][{private_name}] 已通知空闲聊天管理器对话结束") - + # 检查当前活跃流数量 active_count = len(self._active_streams) if active_count == 0: logger.info(f"[私聊][{private_name}] 当前无活跃流,可能会触发主动聊天") - + # 额外调用:如果实例存在且只有在确实移除了活跃流的情况下才触发检查 if was_active: idle_chat = self._idle_chat_manager.get_idle_chat(stream_id) @@ -324,7 +326,7 @@ class IdleConversation: # 直接触发IdleChat检查,而不是等待下一个循环 logger.info(f"[私聊][{private_name}] 对话结束,手动触发一次主动聊天检查") asyncio.create_task(self._trigger_idle_chat_check(idle_chat, stream_id, private_name)) - + return True else: logger.warning(f"[私聊][{private_name}] IdleChatManager不存在,但已更新活跃状态") @@ -337,14 +339,14 @@ class IdleConversation: logger.error(f"[私聊][{private_name}] 处理对话结束通知时发生严重错误: {outer_e}") logger.error(traceback.format_exc()) return False - + async def _trigger_idle_chat_check(self, idle_chat, stream_id: str, private_name: str): """在对话结束后,手动触发一次IdleChat的检查""" try: # 确保活跃计数与IdleConversation一致 async with self._lock: is_active_in_conversation = stream_id in self._active_streams - + # 强制使IdleChat的计数与IdleConversation一致 if is_active_in_conversation: # 如果在IdleConversation中是活跃的,IdleChat的计数应该是1 @@ -358,17 +360,17 @@ class IdleConversation: old_count = idle_chat.active_instances_count idle_chat.active_instances_count = 0 logger.warning(f"[私聊][{private_name}] 修正IdleChat计数: {old_count}->0") - + # 等待1秒,让任何正在进行的处理完成 await asyncio.sleep(1) - + # 只有当stream不再活跃时才触发检查 if not is_active_in_conversation: # 尝试触发一次检查 if hasattr(idle_chat, "_should_trigger"): should_trigger = await idle_chat._should_trigger() logger.info(f"[私聊][{private_name}] 手动触发主动聊天检查结果: {should_trigger}") - + # 如果应该触发,直接调用_initiate_chat if should_trigger and hasattr(idle_chat, "_initiate_chat"): logger.info(f"[私聊][{private_name}] 手动触发主动聊天") @@ -384,11 +386,11 @@ class IdleConversation: def is_stream_active(self, stream_id: str) -> bool: """检查指定的stream是否活跃""" return stream_id in self._active_streams - + def get_active_streams_count(self) -> int: """获取当前活跃的stream数量""" return len(self._active_streams) - + @property def is_running(self) -> bool: """检查IdleConversation是否正在运行""" @@ -399,39 +401,41 @@ class IdleConversation: """获取IdleChatManager实例""" return self._idle_chat_manager + # 创建单例实例 _instance: Optional[IdleConversation] = None _instance_lock = asyncio.Lock() _initialization_in_progress = False # 防止并发初始化 + async def initialize_idle_conversation() -> IdleConversation: """初始化并启动IdleConversation单例实例""" global _initialization_in_progress - + # 防止并发初始化 if _initialization_in_progress: logger.debug("IdleConversation全局初始化正在进行中,等待完成") return get_idle_conversation_instance() - + # 标记正在初始化 _initialization_in_progress = True - + try: instance = get_idle_conversation_instance() - + # 如果实例已经在运行,避免重复初始化 - if getattr(instance, '_running', False): + if getattr(instance, "_running", False): logger.debug("IdleConversation已在运行状态,无需重新初始化") _initialization_in_progress = False return instance - + # 初始化实例 success = await instance.initialize() if not success: logger.error("IdleConversation初始化失败") _initialization_in_progress = False return instance - + # 启动实例 success = await instance.start() if not success: @@ -440,10 +444,10 @@ async def initialize_idle_conversation() -> IdleConversation: # 启动成功,进行初始检查 logger.info("IdleConversation启动成功,执行初始化后检查") # 这里可以添加一些启动后的检查,如果需要 - + # 创建一个异步任务,定期检查系统状态 asyncio.create_task(periodic_system_check(instance)) - + return instance except Exception as e: logger.error(f"初始化并启动IdleConversation时出错: {e}") @@ -455,40 +459,43 @@ async def initialize_idle_conversation() -> IdleConversation: # 清除初始化标志 _initialization_in_progress = False + async def periodic_system_check(instance: IdleConversation): """定期检查系统状态,确保主动聊天功能正常工作""" try: # 等待10秒,让系统完全启动 await asyncio.sleep(10) - - while getattr(instance, '_running', False): + + while getattr(instance, "_running", False): try: # 检查活跃流数量 - active_streams_count = len(getattr(instance, '_active_streams', {})) - + active_streams_count = len(getattr(instance, "_active_streams", {})) + # 如果IdleChatManager存在,检查其中的活跃对话计数 - idle_chat_manager = getattr(instance, '_idle_chat_manager', None) - if idle_chat_manager and hasattr(idle_chat_manager, 'get_all_active_conversations_count'): + idle_chat_manager = getattr(instance, "_idle_chat_manager", None) + if idle_chat_manager and hasattr(idle_chat_manager, "get_all_active_conversations_count"): manager_count = idle_chat_manager.get_all_active_conversations_count() - + # 如果两者不一致,记录警告 if active_streams_count != manager_count: - logger.warning(f"检测到计数不一致: IdleConversation记录的活跃流数量({active_streams_count}) 与 IdleChatManager记录的活跃对话数({manager_count})不匹配") - + logger.warning( + f"检测到计数不一致: IdleConversation记录的活跃流数量({active_streams_count}) 与 IdleChatManager记录的活跃对话数({manager_count})不匹配" + ) + # 如果IdleChatManager记录的计数为0但自己的记录不为0,进行修正 if manager_count == 0 and active_streams_count > 0: logger.warning("检测到可能的计数错误,尝试修正:清空IdleConversation的活跃流记录") async with instance._lock: instance._active_streams.clear() - + # 检查计数如果为0,帮助日志输出 if active_streams_count == 0: logger.debug("当前没有活跃的对话流,应该可以触发主动聊天") - + except Exception as check_err: logger.error(f"执行系统检查时出错: {check_err}") logger.error(traceback.format_exc()) - + # 每60秒检查一次 await asyncio.sleep(60) except asyncio.CancelledError: @@ -497,9 +504,10 @@ async def periodic_system_check(instance: IdleConversation): logger.error(f"系统检查任务异常退出: {e}") logger.error(traceback.format_exc()) + def get_idle_conversation_instance() -> IdleConversation: """获取IdleConversation的单例实例""" global _instance if _instance is None: _instance = IdleConversation() - return _instance \ No newline at end of file + return _instance diff --git a/src/plugins/PFC/PFC_idle/idle_conversation_starter.py b/src/plugins/PFC/PFC_idle/idle_conversation_starter.py index e6782ab1..83a58219 100644 --- a/src/plugins/PFC/PFC_idle/idle_conversation_starter.py +++ b/src/plugins/PFC/PFC_idle/idle_conversation_starter.py @@ -27,6 +27,7 @@ install(extra_lines=3) # 获取当前模块的日志记录器 logger = get_logger("idle_conversation_starter") + class IdleConversationStarter: """长时间无对话主动发起对话的组件 @@ -239,7 +240,7 @@ class IdleConversationStarter: # 在函数内部导入PFCManager,避免循环导入 from ..pfc_manager import PFCManager - + # 获取当前实例 - 注意这是同步方法,不需要await pfc_manager = PFCManager.get_instance() diff --git a/src/plugins/PFC/chat_observer.py b/src/plugins/PFC/chat_observer.py index 4fede3d2..0a4352e1 100644 --- a/src/plugins/PFC/chat_observer.py +++ b/src/plugins/PFC/chat_observer.py @@ -114,11 +114,11 @@ class ChatObserver: logger.debug( f"[私聊][{self.private_name}] 消息已添加到 ChatObserver 缓存,当前缓存大小: {len(self.message_cache)}" ) - + # 检查是否用户发送的消息(而非机器人自己) try: from .PFC_idle.idle_chat import IdleChat - + # 获取消息的发送者 user_info = message.get("user_info", {}) if user_info and str(user_info.get("user_id")) != str(global_config.BOT_QQ): diff --git a/src/plugins/PFC/conversation_initializer.py b/src/plugins/PFC/conversation_initializer.py index ec7e47a5..f5303341 100644 --- a/src/plugins/PFC/conversation_initializer.py +++ b/src/plugins/PFC/conversation_initializer.py @@ -99,10 +99,7 @@ async def load_initial_history(conversation_instance: "Conversation"): conversation_instance.chat_observer.last_message_time = ( conversation_instance.observation_info.last_message_time ) - if ( - conversation_instance.idle_chat - and conversation_instance.observation_info.last_message_time - ): + if conversation_instance.idle_chat and conversation_instance.observation_info.last_message_time: # 更新空闲计时器的起始时间 await conversation_instance.idle_chat.update_last_message_time( conversation_instance.observation_info.last_message_time diff --git a/src/plugins/PFC/conversation_loop.py b/src/plugins/PFC/conversation_loop.py index c4575735..4b65e99f 100644 --- a/src/plugins/PFC/conversation_loop.py +++ b/src/plugins/PFC/conversation_loop.py @@ -69,10 +69,7 @@ async def run_conversation_loop(conversation_instance: "Conversation"): conversation_instance.ignore_until_timestamp and loop_iter_start_time < conversation_instance.ignore_until_timestamp ): - if ( - conversation_instance.idle_chat - and conversation_instance.idle_chat._running - ): + if conversation_instance.idle_chat and conversation_instance.idle_chat._running: # 不直接停止服务,改为暂时忽略此用户 # 虽然我们仍然可以通过active_instances_count来决定是否触发主动聊天 # 但为了安全起见,我们只记录一个日志 From b2dd8717419e77142e007794a79825ecfb7aaf6d Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 19:48:29 +0800 Subject: [PATCH 09/17] =?UTF-8?q?config=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/config.py | 9 ++++----- template/bot_config_template.toml | 11 ++--------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/config/config.py b/src/config/config.py index b0d20ab6..da5b3c2e 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -276,7 +276,7 @@ class BotConfig: enable_pfc_reply_checker: bool = True # 是否开启PFC回复检查 # idle_conversation - enable_idle_conversation: bool = False # 是否启用 pfc 主动发言(未完善) + enable_idle_conversation: bool = False # 是否启用 pfc 主动发言 idle_check_interval: int = 10 # 检查间隔,10分钟检查一次 min_idle_time: int = 7200 # 最短无活动时间,2小时 (7200秒) max_idle_time: int = 18000 # 最长无活动时间,5小时 (18000秒) @@ -493,7 +493,6 @@ class BotConfig: "llm_heartflow", "llm_PFC_action_planner", "llm_PFC_chat", - "llm_PFC_reply_checker", "llm_PFC_relationship_eval", ] @@ -666,7 +665,7 @@ class BotConfig: config.talk_allowed_private = set(str(user) for user in experimental_config.get("talk_allowed_private", [])) if config.INNER_VERSION in SpecifierSet(">=1.1.0"): config.enable_pfc_chatting = experimental_config.get("pfc_chatting", config.enable_pfc_chatting) - if config.INNER_VERSION in SpecifierSet(">=1.1.0"): + if config.INNER_VERSION in SpecifierSet(">=1.6.2"): config.enable_pfc_reply_checker = experimental_config.get( "enable_pfc_reply_checker", config.enable_pfc_reply_checker ) @@ -674,7 +673,7 @@ class BotConfig: def idle_conversation(parent: dict): idle_conversation_config = parent["idle_conversation"] - if config.INNER_VERSION in SpecifierSet(">=1.6.3"): + if config.INNER_VERSION in SpecifierSet(">=1.6.2"): config.enable_idle_conversation = idle_conversation_config.get( "enable_idle_conversation", config.enable_idle_conversation ) @@ -717,7 +716,7 @@ class BotConfig: "chat": {"func": chat, "support": ">=1.6.0", "necessary": False}, "normal_chat": {"func": normal_chat, "support": ">=1.6.0", "necessary": False}, "focus_chat": {"func": focus_chat, "support": ">=1.6.0", "necessary": False}, - "idle_conversation": {"func": idle_conversation, "support": ">=1.6.3", "necessary": False}, + "idle_conversation": {"func": idle_conversation, "support": ">=1.6.2", "necessary": False}, } # 原地修改,将 字符串版本表达式 转换成 版本对象 diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 95282ed5..0415e75d 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "1.6.5" +version = "1.6.2" #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- #如果你想要修改配置文件,请在修改后将version的值进行变更 @@ -191,7 +191,7 @@ pfc_chatting = false # 是否启用PFC聊天,该功能仅作用于私聊,与 enable_pfc_reply_checker = true # 是否启用 PFC 的回复检查器 [idle_conversation] -enable_idle_conversation = false +enable_idle_conversation = false # 是否启用 pfc 主动发言 idle_check_interval = 10 # 检查间隔,10分钟检查一次 min_idle_time = 7200 # 最短无活动时间,2小时 (7200秒) max_idle_time = 18000 # 最长无活动时间,5小时 (18000秒) @@ -291,13 +291,6 @@ temp = 0.3 pri_in = 2 pri_out = 8 -#PFC检查模型 -[model.llm_PFC_reply_checker] -name = "Pro/deepseek-ai/DeepSeek-V3" -provider = "SILICONFLOW" -pri_in = 2 -pri_out = 8 - # PFC 关系评估LLM [model.llm_PFC_relationship_eval] name = "Pro/deepseek-ai/DeepSeek-V3" # 或者其他你认为适合判断任务的模型 From 70afc68ba69245cd017ad90524637cd13b3e7eb2 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 19:58:10 +0800 Subject: [PATCH 10/17] =?UTF-8?q?config=20template=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/bot_config_template.toml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 0415e75d..55b22cad 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -295,10 +295,9 @@ pri_out = 8 [model.llm_PFC_relationship_eval] name = "Pro/deepseek-ai/DeepSeek-V3" # 或者其他你认为适合判断任务的模型 provider = "SILICONFLOW" -temp = 0.4 # 判断任务建议温度稍低 -max_tokens = 512 # 根据Prompt长度和期望输出来调整 -pri_in = 2 # 价格信息(可选) -pri_out = 8 # 价格信息(可选) +temp = 0.4 +pri_in = 2 +pri_out = 8 #以下模型暂时没有使用!! #以下模型暂时没有使用!! From c45120161df4f29c25d7728b3418df1eab584ae2 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 20:30:12 +0800 Subject: [PATCH 11/17] =?UTF-8?q?=E5=B0=86=E9=83=A8=E5=88=86=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E8=BD=AC=E4=B8=BAdebug=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/actions.py | 6 +++--- src/plugins/PFC/conversation.py | 6 +++--- src/plugins/PFC/conversation_initializer.py | 22 ++++++++++----------- src/plugins/PFC/observation_info.py | 4 ++-- src/plugins/PFC/pfc_emotion.py | 2 +- src/plugins/PFC/pfc_utils.py | 2 +- src/plugins/PFC/reply_generator.py | 4 ++-- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/plugins/PFC/actions.py b/src/plugins/PFC/actions.py index 7b15cdfb..e4a2cb77 100644 --- a/src/plugins/PFC/actions.py +++ b/src/plugins/PFC/actions.py @@ -246,7 +246,7 @@ async def handle_action( is_suitable = True check_reason = "ReplyChecker 已通过配置关闭" need_replan_from_checker = False - logger.info(f"{log_prefix} [配置关闭] ReplyChecker 已跳过,默认回复为合适。") + logger.debug(f"{log_prefix} [配置关闭] ReplyChecker 已跳过,默认回复为合适。") # 处理检查结果 if not is_suitable: @@ -398,13 +398,13 @@ async def handle_action( # 如果是 direct_reply 且规划期间有他人新消息,则下次不追问 if other_new_msg_count_during_planning > 0 and action == "direct_reply": - logger.info( + logger.debug( f"[私聊][{conversation_instance.private_name}] 因规划期间收到 {other_new_msg_count_during_planning} 条他人新消息,下一轮强制使用【初始回复】逻辑。" ) conversation_info.last_successful_reply_action = None # conversation_info.my_message_count 不在此处重置,因为它刚发了一条 elif action == "direct_reply" or action == "send_new_message": # 成功发送后 - logger.info( + logger.debug( f"[私聊][{conversation_instance.private_name}] 成功执行 '{action}', 下一轮【允许】使用追问逻辑。" ) conversation_info.last_successful_reply_action = action diff --git a/src/plugins/PFC/conversation.py b/src/plugins/PFC/conversation.py index d74f5096..da613317 100644 --- a/src/plugins/PFC/conversation.py +++ b/src/plugins/PFC/conversation.py @@ -134,7 +134,7 @@ class Conversation: observation_info=self.observation_info, chat_observer_for_history=self.chat_observer, ) - logger.info(f"[私聊][{self.private_name}] 最终关系评估已调用。") + logger.debug(f"[私聊][{self.private_name}] 最终关系评估已调用。") except Exception as e_final_rel: logger.error(f"[私聊][{self.private_name}] 调用最终关系评估时出错: {e_final_rel}") logger.error(traceback.format_exc()) @@ -145,12 +145,12 @@ class Conversation: if self.idle_chat: # 减少活跃实例计数,而不是停止IdleChat await self.idle_chat.decrement_active_instances() - logger.info(f"[私聊][{self.private_name}] 已减少IdleChat活跃实例计数") + logger.debug(f"[私聊][{self.private_name}] 已减少IdleChat活跃实例计数") if self.observation_info and self.chat_observer: self.observation_info.unbind_from_chat_observer() if self.mood_mng and hasattr(self.mood_mng, "stop_mood_update") and self.mood_mng._running: # type: ignore self.mood_mng.stop_mood_update() # type: ignore - logger.info(f"[私聊][{self.private_name}] MoodManager 后台更新已停止。") + logger.debug(f"[私聊][{self.private_name}] MoodManager 后台更新已停止。") self._initialized = False # 标记为未初始化 logger.info(f"[私聊][{self.private_name}] 对话实例 {self.stream_id} 已停止。") diff --git a/src/plugins/PFC/conversation_initializer.py b/src/plugins/PFC/conversation_initializer.py index f5303341..932f74f5 100644 --- a/src/plugins/PFC/conversation_initializer.py +++ b/src/plugins/PFC/conversation_initializer.py @@ -42,7 +42,7 @@ async def load_initial_history(conversation_instance: "Conversation"): return try: - logger.info( + logger.debug( f"[私聊][{conversation_instance.private_name}] 为 {conversation_instance.stream_id} 加载初始聊天记录..." ) # 从聊天核心获取原始消息列表 @@ -132,7 +132,7 @@ async def initialize_core_components(conversation_instance: "Conversation"): # return # conversation_instance._initializing_flag_from_manager = True # 标记开始初始化 - logger.info( + logger.debug( f"[私聊][{conversation_instance.private_name}] (Initializer) 开始初始化对话实例核心组件: {conversation_instance.stream_id}" ) @@ -149,12 +149,12 @@ async def initialize_core_components(conversation_instance: "Conversation"): conversation_instance.relationship_translator = PfcRepationshipTranslator( private_name=conversation_instance.private_name ) - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcRelationship 初始化完成。") + logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcRelationship 初始化完成。") conversation_instance.emotion_updater = PfcEmotionUpdater( private_name=conversation_instance.private_name, bot_name=global_config.BOT_NICKNAME ) - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcEmotion 初始化完成。") + logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcEmotion 初始化完成。") logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 GoalAnalyzer...") conversation_instance.goal_analyzer = GoalAnalyzer( @@ -194,7 +194,7 @@ async def initialize_core_components(conversation_instance: "Conversation"): conversation_instance.stream_id, conversation_instance.private_name ) await conversation_instance.idle_chat.increment_active_instances() - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) IdleChat实例已获取并增加活跃计数") + logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) IdleChat实例已获取并增加活跃计数") # 2. 初始化信息存储和观察组件 logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 获取 ChatObserver 实例...") @@ -235,7 +235,7 @@ async def initialize_core_components(conversation_instance: "Conversation"): conversation_instance.conversation_info.person_id = person_id_tuple[0] # 第一个元素是 person_id private_platform_str = person_id_tuple[1] private_user_id_str = person_id_tuple[2] - logger.info( + logger.debug( f"[私聊][{conversation_instance.private_name}] (Initializer) 获取到 person_id: {conversation_instance.conversation_info.person_id} for {private_platform_str}:{private_user_id_str}" ) else: @@ -251,7 +251,7 @@ async def initialize_core_components(conversation_instance: "Conversation"): if conversation_instance.idle_chat: logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 启动 IdleChat...") # 不需要再次启动,只需确保已初始化 - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) IdleChat实例已初始化") + logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) IdleChat实例已初始化") if ( conversation_instance.mood_mng @@ -259,11 +259,11 @@ async def initialize_core_components(conversation_instance: "Conversation"): and not conversation_instance.mood_mng._running ): # type: ignore conversation_instance.mood_mng.start_mood_update(update_interval=global_config.mood_update_interval) # type: ignore - logger.info( + logger.debug( f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已启动后台更新,间隔: {global_config.mood_update_interval} 秒。" ) elif conversation_instance.mood_mng and conversation_instance.mood_mng._running: # type: ignore - logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已在运行中。") + logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已在运行中。") else: logger.warning( f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 未能启动,相关功能可能受限。" @@ -291,7 +291,7 @@ async def initialize_core_components(conversation_instance: "Conversation"): numeric_relationship_value ) ) - logger.info( + logger.debug( f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载关系文本: {conversation_instance.conversation_info.relationship_text}" ) except Exception as e_init_rel: @@ -305,7 +305,7 @@ async def initialize_core_components(conversation_instance: "Conversation"): conversation_instance.conversation_info.current_emotion_text = ( conversation_instance.mood_mng.get_prompt() ) # type: ignore - logger.info( + logger.debug( f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载情绪文本: {conversation_instance.conversation_info.current_emotion_text}" ) except Exception as e_init_emo: diff --git a/src/plugins/PFC/observation_info.py b/src/plugins/PFC/observation_info.py index 2d2a0f27..7b3d6279 100644 --- a/src/plugins/PFC/observation_info.py +++ b/src/plugins/PFC/observation_info.py @@ -297,7 +297,7 @@ class ObservationInfo: if new_count < original_count: self.new_messages_count = new_count - logger.info( + logger.debug( f"[私聊][{self.private_name}] 移除了未处理的消息 (ID: {message_id_to_delete}), 当前未处理数: {self.new_messages_count}" ) self.update_changed() @@ -384,7 +384,7 @@ class ObservationInfo: self.new_messages_count = len(self.unprocessed_messages) self.chat_history_count = len(self.chat_history) - logger.info( + logger.debug( f"[私聊][{self.private_name}] 已清理 {cleared_count} 条消息 (IDs: {message_ids_to_clear}),剩余未处理 {self.new_messages_count} 条,当前历史记录 {self.chat_history_count} 条。" ) diff --git a/src/plugins/PFC/pfc_emotion.py b/src/plugins/PFC/pfc_emotion.py index d88344f8..5237d7b2 100644 --- a/src/plugins/PFC/pfc_emotion.py +++ b/src/plugins/PFC/pfc_emotion.py @@ -24,7 +24,7 @@ class PfcEmotionUpdater: # LLM 实例 (根据 global_config.llm_summary 配置) llm_config_summary = getattr(global_config, "llm_summary", None) if llm_config_summary and isinstance(llm_config_summary, dict): - logger.info(f"[私聊][{self.private_name}] 使用 llm_summary 配置初始化情绪判断LLM。") + logger.debug(f"[私聊][{self.private_name}] 使用 llm_summary 配置初始化情绪判断LLM。") self.llm = LLMRequest( model=llm_config_summary, temperature=llm_config_summary.get( diff --git a/src/plugins/PFC/pfc_utils.py b/src/plugins/PFC/pfc_utils.py index 6047aa81..38ead120 100644 --- a/src/plugins/PFC/pfc_utils.py +++ b/src/plugins/PFC/pfc_utils.py @@ -274,7 +274,7 @@ async def get_person_id(private_name: str, chat_stream: ChatStream): if chat_stream.user_info: private_user_id_str = str(chat_stream.user_info.user_id) private_platform_str = chat_stream.user_info.platform - logger.info( + logger.debug( f"[私聊][{private_name}] 从 ChatStream 获取到私聊对象信息: ID={private_user_id_str}, Platform={private_platform_str}, Name={private_nickname_str}" ) elif chat_stream.group_info is None and private_name: diff --git a/src/plugins/PFC/reply_generator.py b/src/plugins/PFC/reply_generator.py index 79942b09..5f3b3591 100644 --- a/src/plugins/PFC/reply_generator.py +++ b/src/plugins/PFC/reply_generator.py @@ -260,7 +260,7 @@ class ReplyGenerator: f"请根据此提示调整你的新回复,确保内容新颖,不要重复你已经说过的话。\n" f"------\n" ) - logger.info( + logger.debug( f"[私聊][{self.private_name}] (ReplyGenerator) 检测到自身复读,将加入特定警告到 Prompt:\n" f" 内容: {last_content}" ) @@ -273,7 +273,7 @@ class ReplyGenerator: f"请根据【消息内容】和【失败原因】调整你的新回复,避免重复之前的错误。\n" f"------\n" ) - logger.info( + logger.debug( f"[私聊][{self.private_name}] (ReplyGenerator) 检测到上次回复失败信息,将加入 Prompt:\n" f" 内容: {last_content}\n" f" 原因: {last_reason}" From a9c2b7956531da323f42e27a4ba391f3250af7b8 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 21:07:39 +0800 Subject: [PATCH 12/17] =?UTF-8?q?logger=20=E8=BE=93=E5=87=BA=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E4=B8=BA=20bot=5Fnickname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/actions.py | 4 ++-- src/plugins/PFC/reply_checker.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/PFC/actions.py b/src/plugins/PFC/actions.py index e4a2cb77..80ea2d1d 100644 --- a/src/plugins/PFC/actions.py +++ b/src/plugins/PFC/actions.py @@ -334,7 +334,7 @@ async def handle_action( observation_info.chat_history.append(bot_message_dict) observation_info.chat_history_count = len(observation_info.chat_history) logger.debug( - f"[私聊][{conversation_instance.private_name}] 机器人发送的消息已添加到 chat_history。当前历史数: {observation_info.chat_history_count}" + f"[私聊][{conversation_instance.private_name}] {global_config.BOT_NICKNAME}发送的消息已添加到 chat_history。当前历史数: {observation_info.chat_history_count}" ) # 可选:如果 chat_history 过长,进行修剪 (例如,保留最近N条) @@ -413,7 +413,7 @@ async def handle_action( if conversation_info: # 再次确认 conversation_info.current_instance_message_count += 1 logger.debug( - f"[私聊][{conversation_instance.private_name}] 实例消息计数(机器人发送后)增加到: {conversation_info.current_instance_message_count}" + f"[私聊][{conversation_instance.private_name}] 实例消息计数({global_config.BOT_NICKNAME}发送后)增加到: {conversation_info.current_instance_message_count}" ) if conversation_instance.relationship_updater: # 确保存在 diff --git a/src/plugins/PFC/reply_checker.py b/src/plugins/PFC/reply_checker.py index aa00cd96..c155212f 100644 --- a/src/plugins/PFC/reply_checker.py +++ b/src/plugins/PFC/reply_checker.py @@ -42,7 +42,7 @@ class ReplyChecker: 对于非重复消息: (True, "消息内容未与机器人历史发言重复。", False) """ if not self.bot_qq_str: - logger.error(f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查机器人自身消息。") + logger.error(f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查{global_config.BOT_NICKNAME}自身消息。") return True, "BOT_QQ未配置,跳过重复检查。", False # 无法检查则默认通过 if len(reply) <= 4: @@ -64,11 +64,11 @@ class ReplyChecker: historical_message_text = msg_dict.get("processed_plain_text", "") # <--- 新增详细对比日志 --- START ---> logger.debug( - f"[私聊][{self.private_name}] ReplyChecker: 历史记录 #{i} (机器人): '{historical_message_text}' (长度 {len(historical_message_text)})" + f"[私聊][{self.private_name}] ReplyChecker: 历史记录 #{i} ({global_config.BOT_NICKNAME}): '{historical_message_text}' (长度 {len(historical_message_text)})" ) if reply == historical_message_text: logger.warning(f"[私聊][{self.private_name}] ReplyChecker: !!! 精确匹配成功 !!!") - logger.warning(f"[私聊][{self.private_name}] ReplyChecker 检测到机器人自身重复消息: '{reply}'") + logger.warning(f"[私聊][{self.private_name}] ReplyChecker 检测到{global_config.BOT_NICKNAME}自身重复消息: '{reply}'") match_found = True # <--- 标记找到 return (False, "机器人尝试发送重复消息", False) # <--- 新增详细对比日志 --- END ---> From 118501b0cfb044a7c65f1530e0ee66163cfa3afc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 May 2025 13:07:57 +0000 Subject: [PATCH 13/17] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/reply_checker.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugins/PFC/reply_checker.py b/src/plugins/PFC/reply_checker.py index c155212f..4f4030e6 100644 --- a/src/plugins/PFC/reply_checker.py +++ b/src/plugins/PFC/reply_checker.py @@ -42,7 +42,9 @@ class ReplyChecker: 对于非重复消息: (True, "消息内容未与机器人历史发言重复。", False) """ if not self.bot_qq_str: - logger.error(f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查{global_config.BOT_NICKNAME}自身消息。") + logger.error( + f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查{global_config.BOT_NICKNAME}自身消息。" + ) return True, "BOT_QQ未配置,跳过重复检查。", False # 无法检查则默认通过 if len(reply) <= 4: @@ -68,7 +70,9 @@ class ReplyChecker: ) if reply == historical_message_text: logger.warning(f"[私聊][{self.private_name}] ReplyChecker: !!! 精确匹配成功 !!!") - logger.warning(f"[私聊][{self.private_name}] ReplyChecker 检测到{global_config.BOT_NICKNAME}自身重复消息: '{reply}'") + logger.warning( + f"[私聊][{self.private_name}] ReplyChecker 检测到{global_config.BOT_NICKNAME}自身重复消息: '{reply}'" + ) match_found = True # <--- 标记找到 return (False, "机器人尝试发送重复消息", False) # <--- 新增详细对比日志 --- END ---> From be0cf8c61d7a99acea171ef5c195303e5871af04 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 8 May 2025 13:08:06 +0000 Subject: [PATCH 14/17] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/reply_checker.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugins/PFC/reply_checker.py b/src/plugins/PFC/reply_checker.py index c155212f..4f4030e6 100644 --- a/src/plugins/PFC/reply_checker.py +++ b/src/plugins/PFC/reply_checker.py @@ -42,7 +42,9 @@ class ReplyChecker: 对于非重复消息: (True, "消息内容未与机器人历史发言重复。", False) """ if not self.bot_qq_str: - logger.error(f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查{global_config.BOT_NICKNAME}自身消息。") + logger.error( + f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查{global_config.BOT_NICKNAME}自身消息。" + ) return True, "BOT_QQ未配置,跳过重复检查。", False # 无法检查则默认通过 if len(reply) <= 4: @@ -68,7 +70,9 @@ class ReplyChecker: ) if reply == historical_message_text: logger.warning(f"[私聊][{self.private_name}] ReplyChecker: !!! 精确匹配成功 !!!") - logger.warning(f"[私聊][{self.private_name}] ReplyChecker 检测到{global_config.BOT_NICKNAME}自身重复消息: '{reply}'") + logger.warning( + f"[私聊][{self.private_name}] ReplyChecker 检测到{global_config.BOT_NICKNAME}自身重复消息: '{reply}'" + ) match_found = True # <--- 标记找到 return (False, "机器人尝试发送重复消息", False) # <--- 新增详细对比日志 --- END ---> From 949447fd21a126ecc7a6a32e9a30968ac09627f2 Mon Sep 17 00:00:00 2001 From: 114514 <2514624910@qq.com> Date: Thu, 8 May 2025 21:31:10 +0800 Subject: [PATCH 15/17] =?UTF-8?q?=E5=8E=BB=E9=99=A4planner=E7=9A=84?= =?UTF-8?q?=E4=BA=BA=E8=AE=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/action_planner.py | 110 ++++++++++++------------------ 1 file changed, 45 insertions(+), 65 deletions(-) diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 3d7743a7..6d78fec1 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -3,7 +3,7 @@ import traceback from typing import Tuple, Optional, Dict, Any, List from src.common.logger_manager import get_logger -from src.individuality.individuality import Individuality +# from src.individuality.individuality import Individuality from src.plugins.utils.chat_message_builder import build_readable_messages from ..models.utils_model import LLMRequest from ...config.config import global_config @@ -22,26 +22,21 @@ logger = get_logger("pfc_action_planner") # Prompt(1): 首次回复或非连续回复时的决策 Prompt PROMPT_INITIAL_REPLY = """ 当前时间:{current_time_str} -{persona_text} -现在你正在和{sender_name}在QQ上私聊 -你和对方的关系是:{relationship_text} -你现在的心情是:{current_emotion_text} -请根据以下【所有信息】审慎且灵活的决策下一步行动,可以回复,可以倾听,可以调取知识,甚至可以屏蔽对方: +现在{persona_text}正在与{sender_name}在qq上私聊 +他们的关系是:{relationship_text} +{persona_text}现在的心情是是:{current_emotion_text} +你现在需要操控{persona_text},根据以下【所有信息】灵活,合理的决策{persona_text}的下一步行动,需要符合正常人的社交流程,可以回复,可以倾听,甚至可以屏蔽对方: 【当前对话目标】 {goals_str} 【最近行动历史概要】 {action_history_summary} -【你想起来的相关知识】 -{retrieved_knowledge_str} 【上一次行动的详细情况和结果】 {last_action_context} 【时间和超时提示】 {time_since_last_bot_message_info}{timeout_context} 【最近的对话记录】(包括你已成功发送的消息 和 新收到的消息) {chat_history_text} -【你的回忆】 -{retrieved_memory_str} {spam_warning_info} @@ -56,7 +51,7 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在 请以JSON格式输出你的决策: {{ "action": "选择的行动类型 (必须是上面列表中的一个)", - "reason": "选择该行动的详细原因 (必须有解释你是如何根据“上一次行动结果”、“对话记录”和自身设定人设做出合理判断的)" + "reason": "选择该行动的原因 " }} 注意:请严格按照JSON格式输出,不要包含任何其他内容。""" @@ -64,26 +59,21 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在 # Prompt(2): 上一次成功回复后,决定继续发言时的决策 Prompt PROMPT_FOLLOW_UP = """ 当前时间:{current_time_str} -{persona_text} -现在你正在和{sender_name}在QQ上私聊,**并且刚刚你已经回复了对方** -你与对方的关系是:{relationship_text} -你现在的心情是:{current_emotion_text} -请根据以下【所有信息】审慎且灵活的决策下一步行动,可以继续发送新消息,可以等待,可以倾听,可以调取知识,甚至可以屏蔽对方: +现在{persona_text}正在与{sender_name}在qq上私聊,**并且刚刚{persona_text}已经回复了对方** +他们的关系是:{relationship_text} +{persona_text}现在的心情是是:{current_emotion_text} +你现在需要操控{persona_text},根据以下【所有信息】灵活,合理的决策{persona_text}的下一步行动,需要符合正常人的社交流程,可以发送新消息,可以等待,可以倾听,可以结束对话,甚至可以屏蔽对方: 【当前对话目标】 {goals_str} 【最近行动历史概要】 {action_history_summary} -【你想起来的相关知识】 -{retrieved_knowledge_str} 【上一次行动的详细情况和结果】 {last_action_context} 【时间和超时提示】 {time_since_last_bot_message_info}{timeout_context} 【最近的对话记录】(包括你已成功发送的消息 和 新收到的消息) {chat_history_text} -【你的回忆】 -{retrieved_memory_str} {spam_warning_info} @@ -99,7 +89,7 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在 请以JSON格式输出你的决策: {{ "action": "选择的行动类型 (必须是上面列表中的一个)", - "reason": "选择该行动的详细原因 (必须有解释你是如何根据“上一次行动结果”、“对话记录”和自身设定人设做出合理判断的。)" + "reason": "选择该行动的原因" }} 注意:请严格按照JSON格式输出,不要包含任何其他内容。""" @@ -107,19 +97,22 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在 # 新增:Prompt(3): 决定是否在结束对话前发送告别语 PROMPT_END_DECISION = """ 当前时间:{current_time_str} -{persona_text}。刚刚你决定结束一场 QQ 私聊。 +现在{persona_text}与{sender_name}刚刚结束了一场qq私聊 +他们的关系是:{relationship_text} +你现在需要操控{persona_text},根据以下【所有信息】灵活,合理的决策{persona_text}的下一步行动,需要符合正常人的社交流程: -【你们之前的聊天记录】 + +【他们之前的聊天记录】 {chat_history_text} -你觉得你们的对话已经完整结束了吗?有时候,在对话自然结束后再说点什么可能会有点奇怪,但有时也可能需要一条简短的消息来圆满结束。 -如果觉得确实有必要再发一条简短、自然、符合你人设的告别消息(比如 "好,下次再聊~" 或 "嗯,先这样吧"),就输出 "yes"。 +你觉得他们的对话已经完整结束了吗?有时候,在对话自然结束后再说点什么可能会有点奇怪,但有时也可能需要一条简短的消息来圆满结束。 +如果觉得确实有必要再发一条简短、自然的告别消息(比如 "好,下次再聊~" 或 "嗯,先这样吧"),就输出 "yes"。 如果觉得当前状态下直接结束对话更好,没有必要再发消息,就输出 "no"。 请以 JSON 格式输出你的选择: {{ "say_bye": "yes/no", - "reason": "选择 yes 或 no 的原因和内心想法 (简要说明)" + "reason": "选择 yes 或 no 的原因和 (简要说明)" }} 注意:请严格按照 JSON 格式输出,不要包含任何其他内容。""" @@ -127,27 +120,21 @@ PROMPT_END_DECISION = """ # Prompt(4): 当 reply_generator 决定不发送消息后的反思决策 Prompt PROMPT_REFLECT_AND_ACT = """ 当前时间:{current_time_str} -{persona_text} -现在你正在和{sender_name}在QQ上私聊 -你与对方的关系是:{relationship_text} -你现在的心情是:{current_emotion_text} -刚刚你本来想发一条新消息,但是想了想,你决定不发了。 -请根据以下【所有信息】审慎且灵活的决策下一步行动,可以等待,可以倾听,可以结束对话,甚至可以屏蔽对方: +现在{persona_text}正在与{sender_name}在qq上私聊,刚刚{persona_text}打算发一条新消息,想了想还是不发了 +他们的关系是:{relationship_text} +{persona_text}现在的心情是是:{current_emotion_text} +你现在需要操控{persona_text},根据以下【所有信息】灵活,合理的决策{persona_text}的下一步行动,需要符合正常人的社交流程,可以等待,可以倾听,可以结束对话,甚至可以屏蔽对方: 【当前对话目标】 {goals_str} 【最近行动历史概要】 {action_history_summary} -【你想起来的相关知识】 -{retrieved_knowledge_str} 【上一次行动的详细情况和结果】 {last_action_context} 【时间和超时提示】 {time_since_last_bot_message_info}{timeout_context} 【最近的对话记录】(包括你已成功发送的消息 和 新收到的消息) {chat_history_text} -【你的回忆】 -{retrieved_memory_str} {spam_warning_info} @@ -162,12 +149,11 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在 请以JSON格式输出你的决策: {{ "action": "选择的行动类型 (必须是上面列表中的一个)", - "reason": "选择该行动的详细原因 (必须有解释你是如何根据“上一次行动结果”、“对话记录”和自身设定人设做出合理判断的。)" + "reason": "选择该行动的原因" }} 注意:请严格按照JSON格式输出,不要包含任何其他内容。""" - class ActionPlanner: """行动规划器""" @@ -195,7 +181,7 @@ class ActionPlanner: raise # 获取个性化信息和机器人名称 - self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3) + # self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3) self.name = global_config.BOT_NICKNAME # 获取 ChatObserver 实例 (单例模式) self.chat_observer = ChatObserver.get_instance(stream_id, private_name) @@ -205,7 +191,7 @@ class ActionPlanner: observation_info: ObservationInfo, conversation_info: ConversationInfo, last_successful_reply_action: Optional[str], - use_reflect_prompt: bool = False, # 新增参数,用于指示是否使用PROMPT_REFLECT_AND_ACT + use_reflect_prompt: bool = False # 新增参数,用于指示是否使用PROMPT_REFLECT_AND_ACT ) -> Tuple[str, str]: """ 规划下一步行动。 @@ -228,14 +214,14 @@ class ActionPlanner: goals_str = self._build_goals_string(conversation_info) chat_history_text = await self._build_chat_history_text(observation_info) # 获取 sender_name, relationship_text, current_emotion_text - sender_name_str = getattr(observation_info, "sender_name", "对方") # 从 observation_info 获取 - if not sender_name_str: - sender_name_str = "对方" # 再次确保有默认值 + sender_name_str = getattr(observation_info, 'sender_name', '对方') # 从 observation_info 获取 + if not sender_name_str: sender_name_str = '对方' # 再次确保有默认值 - relationship_text_str = getattr(conversation_info, "relationship_text", "你们还不熟悉。") - current_emotion_text_str = getattr(conversation_info, "current_emotion_text", "心情平静。") + relationship_text_str = getattr(conversation_info, 'relationship_text', '你们还不熟悉。') + current_emotion_text_str = getattr(conversation_info, 'current_emotion_text', '心情平静。') - persona_text = f"你的名字是{self.name},{self.personality_info}。" + + persona_text = f"{self.name}。" action_history_summary, last_action_context = self._build_action_history_context(conversation_info) retrieved_memory_str, retrieved_knowledge_str = await retrieve_contextual_info( chat_history_text, self.private_name @@ -250,16 +236,14 @@ class ActionPlanner: # --- 2. 选择并格式化 Prompt --- try: - if use_reflect_prompt: # 新增的判断 + if use_reflect_prompt: # 新增的判断 prompt_template = PROMPT_REFLECT_AND_ACT log_msg = "使用 PROMPT_REFLECT_AND_ACT (反思决策)" # 对于 PROMPT_REFLECT_AND_ACT,它不包含 send_new_message 选项,所以 spam_warning_message 中的相关提示可以调整或省略 # 但为了保持占位符填充的一致性,我们仍然计算它 spam_warning_message = "" - if conversation_info.my_message_count > 5: # 这里的 my_message_count 仍有意义,表示之前连续发送了多少 - spam_warning_message = ( - f"⚠️【警告】**你之前已连续发送{str(conversation_info.my_message_count)}条消息!请谨慎决策。**" - ) + if conversation_info.my_message_count > 5: # 这里的 my_message_count 仍有意义,表示之前连续发送了多少 + spam_warning_message = f"⚠️【警告】**你之前已连续发送{str(conversation_info.my_message_count)}条消息!请谨慎决策。**" elif conversation_info.my_message_count > 2: spam_warning_message = f"💬【提示】**你之前已连续发送{str(conversation_info.my_message_count)}条消息。请注意保持对话平衡。**" @@ -275,12 +259,12 @@ class ActionPlanner: else: prompt_template = PROMPT_INITIAL_REPLY log_msg = "使用 PROMPT_INITIAL_REPLY (首次/非连续回复决策)" - spam_warning_message = "" # 初始回复时通常不需要刷屏警告 + spam_warning_message = "" # 初始回复时通常不需要刷屏警告 logger.debug(f"[私聊][{self.private_name}] {log_msg}") current_time_value = "获取时间失败" - if observation_info and hasattr(observation_info, "current_time_str") and observation_info.current_time_str: + if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str: current_time_value = observation_info.current_time_str if spam_warning_message: @@ -300,7 +284,7 @@ class ActionPlanner: spam_warning_info=spam_warning_message, sender_name=sender_name_str, relationship_text=relationship_text_str, - current_emotion_text=current_emotion_text_str, + current_emotion_text=current_emotion_text_str ) logger.debug(f"[私聊][{self.private_name}] 发送到LLM的最终提示词:\n------\n{prompt}\n------") except KeyError as fmt_key_err: @@ -340,14 +324,10 @@ class ActionPlanner: if initial_action == "end_conversation": try: time_str_for_end_decision = "获取时间失败" - if ( - observation_info - and hasattr(observation_info, "current_time_str") - and observation_info.current_time_str - ): + if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str: time_str_for_end_decision = observation_info.current_time_str final_action, final_reason = await self._handle_end_conversation_decision( - persona_text, chat_history_text, initial_reason, time_str_for_end_decision + persona_text, chat_history_text, initial_reason,time_str_for_end_decision ) except Exception as end_dec_err: logger.error(f"[私聊][{self.private_name}] 处理结束对话决策时出错: {end_dec_err}") @@ -372,7 +352,7 @@ class ActionPlanner: "block_and_ignore", "say_goodbye", ] - valid_actions_reflect = [ # PROMPT_REFLECT_AND_ACT 的动作 + valid_actions_reflect = [ # PROMPT_REFLECT_AND_ACT 的动作 "wait", "listening", "rethink_goal", @@ -513,7 +493,9 @@ class ActionPlanner: ) logger.debug(f"[私聊][{self.private_name}] 向 LLM 追加了 {other_unread_count} 条未读消息。") else: - chat_history_text += "\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n" + chat_history_text += ( + f"\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n" + ) except AttributeError as e: logger.warning(f"[私聊][{self.private_name}] 构建聊天记录文本时属性错误: {e}") chat_history_text = "[获取聊天记录时出错]\n" @@ -575,9 +557,7 @@ class ActionPlanner: ) -> Tuple[str, str]: """处理结束对话前的告别决策""" logger.info(f"[私聊][{self.private_name}] 初步规划结束对话,进入告别决策...") - end_decision_prompt = PROMPT_END_DECISION.format( - persona_text=persona_text, chat_history_text=chat_history_text, current_time_str=current_time_str - ) + end_decision_prompt = PROMPT_END_DECISION.format(persona_text=persona_text, chat_history_text=chat_history_text,current_time_str=current_time_str) logger.debug(f"[私聊][{self.private_name}] 发送到LLM的结束决策提示词:\n------\n{end_decision_prompt}\n------") llm_start_time = time.time() end_content, _ = await self.llm.generate_response_async(end_decision_prompt) From 01236be285fb523845c73f7c86aa5fe4271447a2 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 21:34:26 +0800 Subject: [PATCH 16/17] =?UTF-8?q?=E7=BB=93=E6=9D=9F=E7=8E=AF=E8=8A=82nonet?= =?UTF-8?q?ype=20bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/action_planner.py | 38 +++++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 6d78fec1..905ee68e 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -6,10 +6,10 @@ from src.common.logger_manager import get_logger # from src.individuality.individuality import Individuality from src.plugins.utils.chat_message_builder import build_readable_messages from ..models.utils_model import LLMRequest -from ...config.config import global_config +from src.config.config import global_config # 确保导入路径正确 -from .pfc_utils import get_items_from_json, retrieve_contextual_info +from .pfc_utils import get_items_from_json from .chat_observer import ChatObserver from .observation_info import ObservationInfo from .conversation_info import ConversationInfo @@ -191,7 +191,7 @@ class ActionPlanner: observation_info: ObservationInfo, conversation_info: ConversationInfo, last_successful_reply_action: Optional[str], - use_reflect_prompt: bool = False # 新增参数,用于指示是否使用PROMPT_REFLECT_AND_ACT + use_reflect_prompt: bool = False, # 新增参数,用于指示是否使用PROMPT_REFLECT_AND_ACT ) -> Tuple[str, str]: """ 规划下一步行动。 @@ -223,12 +223,12 @@ class ActionPlanner: persona_text = f"{self.name}。" action_history_summary, last_action_context = self._build_action_history_context(conversation_info) - retrieved_memory_str, retrieved_knowledge_str = await retrieve_contextual_info( - chat_history_text, self.private_name - ) - logger.info( - f"[私聊][{self.private_name}] (ActionPlanner) 检索完成。记忆: {'有' if '回忆起' in retrieved_memory_str else '无'} / 知识: {'有' if retrieved_knowledge_str and '无相关知识' not in retrieved_knowledge_str and '出错' not in retrieved_knowledge_str else '无'}" - ) + # retrieved_memory_str, retrieved_knowledge_str = await retrieve_contextual_info( + # chat_history_text, self.private_name + # ) + # logger.info( + # f"[私聊][{self.private_name}] (ActionPlanner) 检索完成。记忆: {'有' if '回忆起' in retrieved_memory_str else '无'} / 知识: {'有' if retrieved_knowledge_str and '无相关知识' not in retrieved_knowledge_str and '出错' not in retrieved_knowledge_str else '无'}" + # ) except Exception as prep_err: logger.error(f"[私聊][{self.private_name}] 准备 Prompt 输入时出错: {prep_err}") logger.error(traceback.format_exc()) @@ -278,8 +278,8 @@ class ActionPlanner: time_since_last_bot_message_info=time_since_last_bot_message_info, timeout_context=timeout_context, chat_history_text=chat_history_text if chat_history_text.strip() else "还没有聊天记录。", - retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。", - retrieved_knowledge_str=retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。", + # retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。", + # retrieved_knowledge_str=retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。", current_time_str=current_time_value, spam_warning_info=spam_warning_message, sender_name=sender_name_str, @@ -324,10 +324,18 @@ class ActionPlanner: if initial_action == "end_conversation": try: time_str_for_end_decision = "获取时间失败" - if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str: + if ( + observation_info + and hasattr(observation_info, "current_time_str") + and observation_info.current_time_str + ): time_str_for_end_decision = observation_info.current_time_str final_action, final_reason = await self._handle_end_conversation_decision( - persona_text, chat_history_text, initial_reason,time_str_for_end_decision + persona_text, + chat_history_text, initial_reason, + time_str_for_end_decision, + sender_name_str=sender_name_str, + relationship_text_str=relationship_text_str ) except Exception as end_dec_err: logger.error(f"[私聊][{self.private_name}] 处理结束对话决策时出错: {end_dec_err}") @@ -553,11 +561,11 @@ class ActionPlanner: # --- Helper method for handling end_conversation decision --- async def _handle_end_conversation_decision( - self, persona_text: str, chat_history_text: str, initial_reason: str, current_time_str: str + self, persona_text: str, chat_history_text: str, initial_reason: str, current_time_str: str, sender_name_str: str, relationship_text_str: str ) -> Tuple[str, str]: """处理结束对话前的告别决策""" logger.info(f"[私聊][{self.private_name}] 初步规划结束对话,进入告别决策...") - end_decision_prompt = PROMPT_END_DECISION.format(persona_text=persona_text, chat_history_text=chat_history_text,current_time_str=current_time_str) + end_decision_prompt = PROMPT_END_DECISION.format(persona_text=persona_text, chat_history_text=chat_history_text,current_time_str=current_time_str,sender_name = sender_name_str, relationship_text = relationship_text_str) logger.debug(f"[私聊][{self.private_name}] 发送到LLM的结束决策提示词:\n------\n{end_decision_prompt}\n------") llm_start_time = time.time() end_content, _ = await self.llm.generate_response_async(end_decision_prompt) From a48d91739a20743fe4a0df8d34b51c72b80cc532 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Thu, 8 May 2025 21:42:40 +0800 Subject: [PATCH 17/17] =?UTF-8?q?=E6=9D=80=E6=8E=89=E5=A4=9A=E4=BD=99logge?= =?UTF-8?q?rinfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/actions.py | 4 ++-- src/plugins/PFC/conversation_loop.py | 2 +- src/plugins/PFC/pfc_emotion.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/PFC/actions.py b/src/plugins/PFC/actions.py index 80ea2d1d..8d8a9e0f 100644 --- a/src/plugins/PFC/actions.py +++ b/src/plugins/PFC/actions.py @@ -295,7 +295,7 @@ async def handle_action( # 后续的 plan 循环会检测到这个 "done_no_reply" 状态并使用反思 prompt elif is_suitable: # 适用于 direct_reply 或 (send_new_message 且 RG决定发送并通过检查) - logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 找到合适的回复,准备发送。") + logger.debug(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 找到合适的回复,准备发送。") # conversation_info.last_reply_rejection_reason = None # 已在循环内清除 # conversation_info.last_rejected_reply_content = None conversation_instance.generated_reply = generated_content_for_check_or_send # 使用检查通过的内容 @@ -311,7 +311,7 @@ async def handle_action( action_successful = True final_status = "done" # 明确设置 final_status final_reason = "成功发送" # 明确设置 final_reason - logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 成功发送回复.") + logger.debug(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 成功发送回复.") # --- 新增:将机器人发送的消息添加到 ObservationInfo 的 chat_history --- if ( diff --git a/src/plugins/PFC/conversation_loop.py b/src/plugins/PFC/conversation_loop.py index 4b65e99f..d0cd287d 100644 --- a/src/plugins/PFC/conversation_loop.py +++ b/src/plugins/PFC/conversation_loop.py @@ -28,7 +28,7 @@ async def run_conversation_loop(conversation_instance: "Conversation"): 核心的规划与行动循环 (PFC Loop)。 之前是 Conversation 类中的 _plan_and_action_loop 方法。 """ - logger.info(f"[私聊][{conversation_instance.private_name}] 进入 run_conversation_loop 循环。") + logger.debug(f"[私聊][{conversation_instance.private_name}] 进入 run_conversation_loop 循环。") if not conversation_instance._initialized: logger.error(f"[私聊][{conversation_instance.private_name}] 尝试在未初始化状态下运行规划循环,退出。") diff --git a/src/plugins/PFC/pfc_emotion.py b/src/plugins/PFC/pfc_emotion.py index 5237d7b2..d2496520 100644 --- a/src/plugins/PFC/pfc_emotion.py +++ b/src/plugins/PFC/pfc_emotion.py @@ -102,11 +102,11 @@ class PfcEmotionUpdater: and detected_emotion_word in self.mood_mng.emotion_map ): self.mood_mng.update_mood_from_emotion(detected_emotion_word, intensity=self.EMOTION_UPDATE_INTENSITY) - logger.info( + logger.debug( f"[私聊][{self.private_name}] 基于事件 '{event_description}',情绪已更新为倾向于 '{detected_emotion_word}'。当前心情: {self.mood_mng.current_mood.text}" ) elif detected_emotion_word == "无变化": - logger.info(f"[私聊][{self.private_name}] 基于事件 '{event_description}',LLM判断情绪无显著变化。") + logger.debug(f"[私聊][{self.private_name}] 基于事件 '{event_description}',LLM判断情绪无显著变化。") else: logger.warning( f"[私聊][{self.private_name}] LLM返回了未知的情绪词 '{detected_emotion_word}' 或未返回有效词,情绪未主动更新。"