diff --git a/src/chat/message_manager/message_manager.py b/src/chat/message_manager/message_manager.py index 7461b4461..e1ad6308c 100644 --- a/src/chat/message_manager/message_manager.py +++ b/src/chat/message_manager/message_manager.py @@ -380,11 +380,11 @@ class MessageManager: else: logger.warning(f"消息打断未能取消任何任务: {chat_stream.stream_id}") - # 增加打断计数并应用afc阈值降低 + # 增加打断计数 await chat_stream.context_manager.context.increment_interruption_count() - chat_stream.context_manager.context.apply_interruption_afc_reduction( - global_config.chat.interruption_afc_reduction - ) + + # 🚀 新增:打断后立即重新进入聊天流程 + await self._trigger_immediate_reprocess(chat_stream) # 检查是否已达到最大次数 if chat_stream.context_manager.context.interruption_count >= global_config.chat.interruption_max_limit: @@ -393,11 +393,64 @@ class MessageManager: ) else: logger.info( - f"聊天流 {chat_stream.stream_id} 已打断,当前打断次数: {chat_stream.context_manager.context.interruption_count}/{global_config.chat.interruption_max_limit}, afc阈值调整: {chat_stream.context_manager.context.get_afc_threshold_adjustment()}" + f"聊天流 {chat_stream.stream_id} 已打断并重新进入处理流程,当前打断次数: {chat_stream.context_manager.context.interruption_count}/{global_config.chat.interruption_max_limit}" ) else: logger.debug(f"聊天流 {chat_stream.stream_id} 未触发打断,打断概率: {interruption_probability:.2f},检测到 {len(all_processing_tasks)} 个任务") + async def _trigger_immediate_reprocess(self, chat_stream: ChatStream): + """打断后立即重新进入聊天流程""" + try: + stream_id = chat_stream.stream_id + + logger.info(f"🚀 打断后立即重新处理聊天流: {stream_id}") + + # 等待一小段时间确保当前消息已经添加到未读消息中 + await asyncio.sleep(0.1) + + # 获取当前的stream context + context = chat_stream.stream_context + + # 确保有未读消息需要处理 + unread_messages = context.get_unread_messages() + if not unread_messages: + logger.debug(f"💭 聊天流 {stream_id} 没有未读消息,跳过重新处理") + return + + logger.info(f"💬 开始重新处理 {len(unread_messages)} 条未读消息: {stream_id}") + + # 创建新的处理任务 + task = asyncio.create_task( + self.chatter_manager.process_stream_context(stream_id, context), + name=f"reprocess_{stream_id}_{int(time.time())}" + ) + + # 设置处理任务 + self.chatter_manager.set_processing_task(stream_id, task) + + # 等待处理完成(使用超时防止无限等待) + try: + result = await asyncio.wait_for(task, timeout=30.0) + success = result.get("success", False) + actions_count = result.get("actions_count", 0) + + if success: + logger.info(f"✅ 聊天流 {stream_id} 重新处理成功: 执行了 {actions_count} 个动作") + else: + logger.warning(f"❌ 聊天流 {stream_id} 重新处理失败") + + except asyncio.TimeoutError: + logger.warning(f"⏰ 聊天流 {stream_id} 重新处理超时") + if not task.done(): + task.cancel() + except Exception as e: + logger.error(f"💥 聊天流 {stream_id} 重新处理出错: {e}") + if not task.done(): + task.cancel() + + except Exception as e: + logger.error(f"🚨 触发重新处理时出错: {e}") + async def clear_all_unread_messages(self, stream_id: str): """清除指定上下文中的所有未读消息,在消息处理完成后调用""" try: diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index bc6ffa54b..ccabddead 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -903,9 +903,21 @@ class DefaultReplyer: person_id = PersonInfoManager.get_person_id(platform, user_id) person_info_manager = get_person_info_manager() sender_name = await person_info_manager.get_value(person_id, "person_name") or "未知用户" + + # 检查是否是机器人自己,如果是则显示为(你) + if user_id == str(global_config.bot.qq_account): + sender_name = f"{global_config.bot.nickname}(你)" else: sender_name = "未知用户" + # 处理消息内容中的用户引用,确保bot回复在消息内容中也正确显示 + from src.chat.utils.chat_message_builder import replace_user_references_sync + msg_content = replace_user_references_sync( + msg_content, + platform, + replace_bot_name=True + ) + # 添加兴趣度信息 interest_score = interest_scores.get(msg_id, 0.0) interest_text = f" [兴趣度: {interest_score:.3f}]" if interest_score > 0 else "" @@ -1002,9 +1014,21 @@ class DefaultReplyer: person_id = PersonInfoManager.get_person_id(platform, user_id) person_info_manager = get_person_info_manager() sender_name = await person_info_manager.get_value(person_id, "person_name") or "未知用户" + + # 检查是否是机器人自己,如果是则显示为(你) + if user_id == str(global_config.bot.qq_account): + sender_name = f"{global_config.bot.nickname}(你)" else: sender_name = "未知用户" + # 处理消息内容中的用户引用,确保bot回复在消息内容中也正确显示 + from src.chat.utils.chat_message_builder import replace_user_references_sync + msg_content = replace_user_references_sync( + msg_content, + platform, + replace_bot_name=True + ) + # 添加兴趣度信息 interest_score = interest_scores.get(msg_id, 0.0) interest_text = f" [兴趣度: {interest_score:.3f}]" if interest_score > 0 else "" @@ -1678,7 +1702,7 @@ class DefaultReplyer: return "" async def build_relation_info(self, sender: str, target: str): - if not global_config.relationship.enable_relationship: + if not global_config.affinity_flow.enable_relationship_tracking: return "" # 获取用户ID diff --git a/src/chat/utils/prompt.py b/src/chat/utils/prompt.py index f39f69b81..d8a6a4e24 100644 --- a/src/chat/utils/prompt.py +++ b/src/chat/utils/prompt.py @@ -979,7 +979,7 @@ class Prompt: Returns: str: 关系信息字符串 """ - if not global_config.relationship.enable_relationship: + if not global_config.affinity_flow.enable_relationship_tracking: return "" from src.person_info.relationship_fetcher import relationship_fetcher_manager diff --git a/src/common/data_models/message_manager_data_model.py b/src/common/data_models/message_manager_data_model.py index 656895648..7a967c55c 100644 --- a/src/common/data_models/message_manager_data_model.py +++ b/src/common/data_models/message_manager_data_model.py @@ -42,7 +42,6 @@ class StreamContext(BaseDataModel): processing_task: asyncio.Task | None = None interruption_count: int = 0 # 打断计数器 last_interruption_time: float = 0.0 # 上次打断时间 - afc_threshold_adjustment: float = 0.0 # afc阈值调整量 # 独立分发周期字段 next_check_time: float = field(default_factory=time.time) # 下次检查时间 @@ -140,23 +139,14 @@ class StreamContext(BaseDataModel): await self._sync_interruption_count_to_stream() async def reset_interruption_count(self): - """重置打断计数和afc阈值调整""" + """重置打断计数""" self.interruption_count = 0 self.last_interruption_time = 0.0 - self.afc_threshold_adjustment = 0.0 # 同步打断计数到ChatStream await self._sync_interruption_count_to_stream() - def apply_interruption_afc_reduction(self, reduction_value: float): - """应用打断导致的afc阈值降低""" - self.afc_threshold_adjustment += reduction_value - logger.debug(f"应用afc阈值降低: {reduction_value}, 总调整量: {self.afc_threshold_adjustment}") - - def get_afc_threshold_adjustment(self) -> float: - """获取当前的afc阈值调整量""" - return self.afc_threshold_adjustment - + async def _sync_interruption_count_to_stream(self): """同步打断计数到ChatStream""" try: diff --git a/src/config/config.py b/src/config/config.py index 83d880389..53cd4d8ed 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -37,7 +37,6 @@ from src.config.official_configs import ( PersonalityConfig, PlanningSystemConfig, ProactiveThinkingConfig, - RelationshipConfig, ResponsePostProcessConfig, ResponseSplitterConfig, SleepSystemConfig, @@ -377,7 +376,6 @@ class Config(ValidatedConfigBase): database: DatabaseConfig = Field(..., description="数据库配置") bot: BotConfig = Field(..., description="机器人基本配置") personality: PersonalityConfig = Field(..., description="个性配置") - relationship: RelationshipConfig = Field(..., description="关系配置") chat: ChatConfig = Field(..., description="聊天配置") message_receive: MessageReceiveConfig = Field(..., description="消息接收配置") normal_chat: NormalChatConfig = Field(..., description="普通聊天配置") diff --git a/src/config/official_configs.py b/src/config/official_configs.py index dada1cd86..86d116242 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -99,12 +99,6 @@ class PersonalityConfig(ValidatedConfigBase): ) -class RelationshipConfig(ValidatedConfigBase): - """关系配置类""" - - enable_relationship: bool = Field(default=True, description="是否启用关系") - relation_frequency: float = Field(default=1.0, description="关系频率") - class ChatConfig(ValidatedConfigBase): """聊天配置类""" @@ -132,8 +126,7 @@ class ChatConfig(ValidatedConfigBase): interruption_min_probability: float = Field( default=0.1, ge=0.0, le=1.0, description="最低打断概率(即使达到较高打断次数,也保证有此概率的打断机会)" ) - interruption_afc_reduction: float = Field(default=0.05, ge=0.0, le=1.0, description="每次连续打断降低的afc阈值数值") - + # DEPRECATED: interruption_probability_factor (已废弃的配置项) # 新的线性概率模型不再需要复杂的概率因子 # 保留此字段是为了向后兼容,现有配置文件不会报错 @@ -698,6 +691,12 @@ class AffinityFlowConfig(ValidatedConfigBase): mention_bot_interest_score: float = Field(default=0.6, description="提及bot的兴趣分") base_relationship_score: float = Field(default=0.5, description="基础人物关系分") + # 关系追踪系统参数 + enable_relationship_tracking: bool = Field(default=True, description="是否启用关系追踪系统") + relationship_tracking_probability: float = Field(default=0.7, description="关系追踪执行概率 (0.0-1.0),用于减少API调用压力") + relationship_tracking_interval_min: int = Field(default=300, description="关系追踪最小间隔时间(秒)") + relationship_tracking_cooldown_hours: float = Field(default=1.0, description="同一用户关系追踪冷却时间(小时)") + class ProactiveThinkingConfig(ValidatedConfigBase): """主动思考(主动发起对话)功能配置""" diff --git a/src/mais4u/mais4u_chat/s4u_prompt.py b/src/mais4u/mais4u_chat/s4u_prompt.py index b53a8b3f6..7b0dca370 100644 --- a/src/mais4u/mais4u_chat/s4u_prompt.py +++ b/src/mais4u/mais4u_chat/s4u_prompt.py @@ -154,7 +154,7 @@ class PromptBuilder: ) relation_prompt = "" - if global_config.relationship.enable_relationship and who_chat_in_group: + if global_config.affinity_flow.enable_relationship_tracking and who_chat_in_group: relationship_fetcher = relationship_fetcher_manager.get_fetcher(chat_stream.stream_id) # 将 (platform, user_id, nickname) 转换为 person_id diff --git a/src/person_info/relationship_builder.py b/src/person_info/relationship_builder.py index 4e258a8fd..45899b89f 100644 --- a/src/person_info/relationship_builder.py +++ b/src/person_info/relationship_builder.py @@ -26,7 +26,7 @@ SEGMENT_CLEANUP_CONFIG = { "cleanup_interval_hours": 0.5, # 清理间隔(小时) } -MAX_MESSAGE_COUNT = int(80 / global_config.relationship.relation_frequency) +MAX_MESSAGE_COUNT = 80 # 默认消息计数,旧的relation_frequency配置已移除 class RelationshipBuilder: diff --git a/src/plugins/built_in/affinity_flow_chatter/affinity_interest_calculator.py b/src/plugins/built_in/affinity_flow_chatter/affinity_interest_calculator.py index ba8beba7f..87ff99444 100644 --- a/src/plugins/built_in/affinity_flow_chatter/affinity_interest_calculator.py +++ b/src/plugins/built_in/affinity_flow_chatter/affinity_interest_calculator.py @@ -228,10 +228,18 @@ class AffinityInterestCalculator(BaseInterestCalculator): is_at = getattr(message, "is_at", False) processed_plain_text = getattr(message, "processed_plain_text", "") + # 判断是否为私聊 + chat_info_group_id = getattr(message, "chat_info_group_id", None) + is_private_chat = not chat_info_group_id # 如果没有group_id则是私聊 + + logger.debug(f"[提及分计算] is_mentioned={is_mentioned}, is_at={is_at}, is_private_chat={is_private_chat}") + if is_mentioned: if is_at: + logger.debug(f"[提及分计算] 直接@机器人,返回1.0") return 1.0 # 直接@机器人,最高分 else: + logger.debug(f"[提及分计算] 提及机器人,返回0.8") return 0.8 # 提及机器人名字,高分 else: # 检查是否被提及(文本匹配) @@ -239,9 +247,14 @@ class AffinityInterestCalculator(BaseInterestCalculator): is_text_mentioned = any(alias in processed_plain_text for alias in bot_aliases if alias) # 如果被提及或是私聊,都视为提及了bot - if is_text_mentioned or not hasattr(message, "chat_info_group_id"): + if is_text_mentioned: + logger.debug(f"[提及分计算] 文本提及机器人,返回提及分") + return global_config.affinity_flow.mention_bot_interest_score + elif is_private_chat: + logger.debug(f"[提及分计算] 私聊消息,返回提及分") return global_config.affinity_flow.mention_bot_interest_score else: + logger.debug(f"[提及分计算] 未提及机器人,返回0.0") return 0.0 # 未提及机器人 def _apply_no_reply_boost(self, base_score: float) -> float: diff --git a/src/plugins/built_in/affinity_flow_chatter/plan_executor.py b/src/plugins/built_in/affinity_flow_chatter/plan_executor.py index 8764feb3c..8a3fc017d 100644 --- a/src/plugins/built_in/affinity_flow_chatter/plan_executor.py +++ b/src/plugins/built_in/affinity_flow_chatter/plan_executor.py @@ -184,9 +184,16 @@ class ChatterPlanExecutor: # 获取用户ID - 兼容对象和字典 if hasattr(action_info.action_message, "user_info"): + # DatabaseMessages对象情况 user_id = action_info.action_message.user_info.user_id else: - user_id = action_info.action_message.get("user_info", {}).get("user_id") + # 字典情况(向后兼容)- 适配扁平化消息字典结构 + # 首先尝试从扁平化结构直接获取用户信息 + user_id = action_info.action_message.get("user_id") + + # 如果扁平化结构中没有用户信息,再尝试从嵌套的user_info获取 + if not user_id: + user_id = action_info.action_message.get("user_info", {}).get("user_id") if user_id == str(global_config.bot.qq_account): logger.warning("尝试回复自己,跳过此动作以防止死循环。") @@ -231,11 +238,19 @@ class ChatterPlanExecutor: except Exception as e: error_message = str(e) logger.error(f"执行回复动作失败: {action_info.action_type}, 错误: {error_message}") - """ - # 记录用户关系追踪 + # 记录用户关系追踪 - 使用后台异步执行,防止阻塞主流程 if success and action_info.action_message: - await self._track_user_interaction(action_info, plan, reply_content) - """ + logger.debug(f"准备执行关系追踪: success={success}, action_message存在={bool(action_info.action_message)}") + logger.debug(f"关系追踪器状态: {self.relationship_tracker is not None}") + + # 直接使用后台异步任务执行关系追踪,避免阻塞主回复流程 + import asyncio + asyncio.create_task(self._track_user_interaction(action_info, plan, reply_content)) + logger.debug("关系追踪已启动为后台异步任务") + else: + logger.debug(f"跳过关系追踪: success={success}, action_message存在={bool(action_info.action_message)}") + # 将机器人回复添加到已读消息中 + await self._add_bot_reply_to_read_messages(action_info, plan, reply_content) execution_time = time.time() - start_time self.execution_stats["execution_times"].append(execution_time) @@ -344,29 +359,54 @@ class ChatterPlanExecutor: async def _track_user_interaction(self, action_info: ActionPlannerInfo, plan: Plan, reply_content: str): """追踪用户交互 - 集成回复后关系追踪""" try: + logger.debug("🔍 开始执行用户交互追踪") + if not action_info.action_message: + logger.debug("❌ 跳过追踪:action_message为空") return - # 获取用户信息 - 处理对象和字典两种情况 - if hasattr(action_info.action_message, "user_info"): - # 对象情况 - user_info = action_info.action_message.user_info - user_id = user_info.user_id - user_name = user_info.user_nickname or user_id - user_message = action_info.action_message.content + # 获取用户信息 - 处理DatabaseMessages对象 + if hasattr(action_info.action_message, "user_id"): + # DatabaseMessages对象情况 + user_id = action_info.action_message.user_id + user_name = action_info.action_message.user_nickname or user_id + # 使用processed_plain_text作为消息内容,如果没有则使用display_message + user_message = ( + action_info.action_message.processed_plain_text + or action_info.action_message.display_message + or "" + ) + logger.debug(f"📝 从DatabaseMessages获取用户信息: user_id={user_id}, user_name={user_name}") else: - # 字典情况 - user_info = action_info.action_message.get("user_info", {}) - user_id = user_info.get("user_id") - user_name = user_info.get("user_nickname") or user_id - user_message = action_info.action_message.get("content", "") + # 字典情况(向后兼容)- 适配扁平化消息字典结构 + # 首先尝试从扁平化结构直接获取用户信息 + user_id = action_info.action_message.get("user_id") + user_name = action_info.action_message.get("user_nickname") or user_id + + # 如果扁平化结构中没有用户信息,再尝试从嵌套的user_info获取 + if not user_id: + user_info = action_info.action_message.get("user_info", {}) + user_id = user_info.get("user_id") + user_name = user_info.get("user_nickname") or user_id + logger.debug(f"📝 从嵌套user_info获取用户信息: user_id={user_id}, user_name={user_name}") + else: + logger.debug(f"📝 从扁平化结构获取用户信息: user_id={user_id}, user_name={user_name}") + + # 获取消息内容,优先使用processed_plain_text + user_message = ( + action_info.action_message.get("processed_plain_text", "") + or action_info.action_message.get("display_message", "") + or action_info.action_message.get("content", "") + ) if not user_id: - logger.debug("跳过追踪:缺少用户ID") + logger.debug("❌ 跳过追踪:缺少用户ID") return # 如果有设置关系追踪器,执行回复后关系追踪 if self.relationship_tracker: + logger.debug(f"✅ 关系追踪器存在,开始为用户 {user_id} 执行追踪") + # 记录基础交互信息(保持向后兼容) self.relationship_tracker.add_interaction( user_id=user_id, @@ -375,19 +415,102 @@ class ChatterPlanExecutor: bot_reply=reply_content, reply_timestamp=time.time(), ) + logger.debug(f"📊 已添加基础交互信息: {user_name}({user_id})") # 执行新的回复后关系追踪 await self.relationship_tracker.track_reply_relationship( user_id=user_id, user_name=user_name, bot_reply_content=reply_content, reply_timestamp=time.time() ) + logger.debug(f"🎯 已执行回复后关系追踪: {user_id}") - logger.debug(f"已执行用户交互追踪: {user_id}") + else: + logger.debug("❌ 关系追踪器不存在,跳过追踪") except Exception as e: logger.error(f"追踪用户交互时出错: {e}") logger.debug(f"action_message类型: {type(action_info.action_message)}") logger.debug(f"action_message内容: {action_info.action_message}") + async def _add_bot_reply_to_read_messages(self, action_info: ActionPlannerInfo, plan: Plan, reply_content: str): + """将机器人回复添加到已读消息中""" + try: + if not reply_content or not plan.chat_id: + logger.debug("跳过添加已读消息:回复内容为空或缺少chat_id") + return + + # 获取chat_stream对象 + from src.plugin_system.apis.chat_api import get_chat_manager + + chat_manager = get_chat_manager() + chat_stream = await chat_manager.get_stream(plan.chat_id) + + if not chat_stream: + logger.warning(f"无法获取chat_stream: {plan.chat_id}") + return + + # 构建机器人回复的DatabaseMessages对象 + from src.common.data_models.database_data_model import DatabaseMessages + + current_time = time.time() + + # 构建用户信息 + bot_user_id = str(global_config.bot.qq_account) + bot_nickname = global_config.bot.nickname + + # 创建机器人回复消息 + bot_message = DatabaseMessages( + message_id=f"bot_reply_{int(current_time * 1000)}", # 生成唯一ID + time=current_time, + chat_id=plan.chat_id, + reply_to=None, # 不是回复消息 + interest_value=None, # 机器人回复不需要兴趣值 + processed_plain_text=reply_content, + display_message=reply_content, + is_read=True, # 标记为已读 + is_emoji=False, + is_picid=False, + is_command=False, + is_notify=False, + + # 用户信息 + user_id=bot_user_id, + user_nickname=bot_nickname, + user_cardname=bot_nickname, + user_platform="qq", + + # 聊天上下文信息 + chat_info_user_id=chat_stream.user_info.user_id if chat_stream.user_info else bot_user_id, + chat_info_user_nickname=chat_stream.user_info.user_nickname if chat_stream.user_info else bot_nickname, + chat_info_user_cardname=chat_stream.user_info.user_cardname if chat_stream.user_info else bot_nickname, + chat_info_user_platform=chat_stream.platform, + chat_info_stream_id=chat_stream.stream_id, + chat_info_platform=chat_stream.platform, + chat_info_create_time=chat_stream.create_time, + chat_info_last_active_time=chat_stream.last_active_time, + + # 群组信息(如果是群聊) + chat_info_group_id=chat_stream.group_info.group_id if chat_stream.group_info else None, + chat_info_group_name=chat_stream.group_info.group_name if chat_stream.group_info else None, + chat_info_group_platform=chat_stream.group_info.group_platform if chat_stream.group_info else None, + + # 动作信息 + actions=["bot_reply"], + should_reply=False, + should_act=False + ) + + # 添加到chat_stream的已读消息中 + if hasattr(chat_stream, 'stream_context') and chat_stream.stream_context: + chat_stream.stream_context.history_messages.append(bot_message) + logger.debug(f"机器人回复已添加到已读消息: {reply_content[:50]}...") + else: + logger.warning("chat_stream没有stream_context,无法添加已读消息") + + except Exception as e: + logger.error(f"添加机器人回复到已读消息时出错: {e}") + logger.debug(f"plan.chat_id: {plan.chat_id}") + logger.debug(f"reply_content: {reply_content[:100] if reply_content else 'None'}") + def get_execution_stats(self) -> dict[str, Any]: """获取执行统计信息""" stats = self.execution_stats.copy() diff --git a/src/plugins/built_in/affinity_flow_chatter/planner.py b/src/plugins/built_in/affinity_flow_chatter/planner.py index 5d0bed659..703141d70 100644 --- a/src/plugins/built_in/affinity_flow_chatter/planner.py +++ b/src/plugins/built_in/affinity_flow_chatter/planner.py @@ -50,6 +50,16 @@ class ChatterActionPlanner: self.generator = ChatterPlanGenerator(chat_id) self.executor = ChatterPlanExecutor(action_manager) + # 初始化关系追踪器 + if global_config.affinity_flow.enable_relationship_tracking: + from .relationship_tracker import ChatterRelationshipTracker + self.relationship_tracker = ChatterRelationshipTracker() + self.executor.set_relationship_tracker(self.relationship_tracker) + logger.info(f"关系追踪器已初始化 (chat_id: {chat_id})") + else: + self.relationship_tracker = None + logger.info(f"关系系统已禁用,跳过关系追踪器初始化 (chat_id: {chat_id})") + # 使用新的统一兴趣度管理系统 # 规划器统计 diff --git a/src/plugins/built_in/affinity_flow_chatter/relationship_tracker.py b/src/plugins/built_in/affinity_flow_chatter/relationship_tracker.py index 68c2fb73a..db8c533b6 100644 --- a/src/plugins/built_in/affinity_flow_chatter/relationship_tracker.py +++ b/src/plugins/built_in/affinity_flow_chatter/relationship_tracker.py @@ -4,6 +4,7 @@ 支持数据库持久化存储和回复后自动关系更新 """ +import random import time from sqlalchemy import desc, select @@ -142,26 +143,40 @@ class ChatterRelationshipTracker: 1. 加分必须符合现实关系发展逻辑 - 不能因为对方态度好就盲目加分到不符合当前关系档次的分数 2. 关系提升需要足够的互动积累和时间验证 3. 即使是朋友关系,单次互动加分通常不超过0.05-0.1 -4. 关系描述要详细具体,包括: - - 用户性格特点观察 - - 印象深刻的互动记忆 - - 你们关系的具体状态描述 +4. 人物印象描述应该是泛化的、整体的理解,从你的视角对用户整体性格特质的描述: + - 描述用户的整体性格特点(如:温柔、幽默、理性、感性等) + - 用户给你的整体感觉和印象 + - 你们关系的整体状态和氛围 + - 避免描述具体事件或对话内容,而是基于这些事件形成的整体认知 根据你的人设性格,思考: -1. 以你的性格,你会如何看待这次互动? -2. 用户的行为是否符合你性格的喜好? -3. 这次互动是否真的让你们的关系提升了一个档次?为什么? -4. 有什么特别值得记住的互动细节? +1. 从你的性格视角,这个用户给你什么样的整体印象? +2. 用户的性格特质和行为模式是否符合你的喜好? +3. 基于这次互动,你对用户的整体认知有什么变化? +4. 这个用户在你心中的整体形象是怎样的? 请以JSON格式返回更新结果: {{ "new_relationship_score": 0.0~1.0的数值(必须符合现实逻辑), "reasoning": "从你的性格角度说明更新理由,重点说明是否符合现实关系发展逻辑", - "interaction_summary": "基于你性格的交互总结,包含印象深刻的互动记忆" + "interaction_summary": "基于你性格的用户整体印象描述,包含用户的整体性格特质、给你的整体感觉,避免具体事件描述" }} """ - llm_response, _ = await self.relationship_llm.generate_response_async(prompt=prompt) + # 调用LLM进行分析 - 添加超时保护 + import asyncio + try: + llm_response, _ = await asyncio.wait_for( + self.relationship_llm.generate_response_async(prompt=prompt), + timeout=30.0 # 30秒超时 + ) + except asyncio.TimeoutError: + logger.warning(f"初次见面LLM调用超时: user_id={user_id}, 跳过此次追踪") + return + except Exception as e: + logger.error(f"初次见面LLM调用失败: user_id={user_id}, 错误: {e}") + return + if llm_response: import json @@ -382,15 +397,35 @@ class ChatterRelationshipTracker: ): """回复后关系追踪 - 主要入口点""" try: - logger.info(f"🔄 [RelationshipTracker] 开始回复后关系追踪: {user_id}") + # 首先检查是否启用关系追踪 + if not global_config.affinity_flow.enable_relationship_tracking: + logger.debug(f"🚫 [RelationshipTracker] 关系追踪系统已禁用,跳过用户 {user_id}") + return - # 检查上次追踪时间 - last_tracked_time = self._get_last_tracked_time(user_id) + # 概率筛选 - 减少API调用压力 + tracking_probability = global_config.affinity_flow.relationship_tracking_probability + if random.random() > tracking_probability: + logger.debug( + f"🎲 [RelationshipTracker] 概率筛选未通过 ({tracking_probability:.2f}),跳过用户 {user_id} 的关系追踪" + ) + return + + logger.info(f"🔄 [RelationshipTracker] 开始回复后关系追踪: {user_id} (概率通过: {tracking_probability:.2f})") + + # 检查上次追踪时间 - 使用配置的冷却时间 + last_tracked_time = await self._get_last_tracked_time(user_id) + cooldown_hours = global_config.affinity_flow.relationship_tracking_cooldown_hours + cooldown_seconds = cooldown_hours * 3600 time_diff = reply_timestamp - last_tracked_time - if time_diff < 5 * 60: # 5分钟内不重复追踪 + # 使用配置的最小间隔时间 + min_interval = global_config.affinity_flow.relationship_tracking_interval_min + required_interval = max(min_interval, cooldown_seconds) + + if time_diff < required_interval: logger.debug( - f"⏱️ [RelationshipTracker] 用户 {user_id} 距离上次追踪时间不足5分钟 ({time_diff:.2f}s),跳过" + f"⏱️ [RelationshipTracker] 用户 {user_id} 距离上次追踪时间不足 {required_interval/60:.1f} 分钟 " + f"(实际: {time_diff/60:.1f} 分钟),跳过" ) return @@ -563,30 +598,42 @@ class ChatterRelationshipTracker: 1. 加分必须符合现实关系发展逻辑 - 不能因为用户反应好就盲目加分 2. 关系提升需要足够的互动积累和时间验证,单次互动加分通常不超过0.05-0.1 3. 必须考虑当前关系档次,不能跳跃式提升(比如从0.3直接到0.7) -4. 关系印象描述要详细具体(100-200字),包括: - - 用户性格特点和交流风格观察 - - 印象深刻的互动记忆和对话片段 - - 你们关系的具体状态描述和发展阶段 - - 根据你的性格,你对用户的真实感受 +4. 人物印象描述应该是泛化的、整体的理解(100-200字),从你的视角对用户整体性格特质的描述: + - 描述用户的整体性格特点和行为模式(如:温柔体贴、幽默风趣、理性稳重等) + - 用户给你的整体感觉和印象氛围 + - 你们关系的整体状态和发展阶段 + - 基于所有互动形成的用户整体形象认知 + - 避免提及具体事件或对话内容,而是总结形成的整体印象 性格视角深度分析: -1. 以你的性格特点,用户这次的反应给你什么感受? -2. 用户的情绪和行为符合你性格的喜好吗?具体哪些方面? +1. 从你的性格视角,基于这次互动,你对用户的整体印象有什么新的认识? +2. 用户的整体性格特质和行为模式符合你的喜好吗? 3. 从现实角度看,这次互动是否足以让关系提升到下一个档次?为什么? -4. 有什么特别值得记住的互动细节或对话内容? -5. 基于你们的互动历史,用户给你留下了哪些深刻印象? +4. 基于你们的互动历史,用户在你心中的整体形象是怎样的? +5. 这个用户给你带来的整体感受和情绪体验是怎样的? 请以JSON格式返回更新结果: {{ - "relationship_text": "详细的关系印象描述(100-200字),包含用户性格观察、印象深刻记忆、关系状态描述", + "relationship_text": "泛化的用户整体印象描述(100-200字),包含用户的整体性格特质、给你的整体感觉和印象氛围,避免具体事件描述", "relationship_score": 0.0~1.0的新分数(必须严格符合现实逻辑), "analysis_reasoning": "从你性格角度的深度分析,重点说明分数调整的现实合理性", "interaction_quality": "high/medium/low" }} """ - # 调用LLM进行分析 - llm_response, _ = await self.relationship_llm.generate_response_async(prompt=prompt) + # 调用LLM进行分析 - 添加超时保护 + import asyncio + try: + llm_response, _ = await asyncio.wait_for( + self.relationship_llm.generate_response_async(prompt=prompt), + timeout=30.0 # 30秒超时 + ) + except asyncio.TimeoutError: + logger.warning(f"关系追踪LLM调用超时: user_id={user_id}, 跳过此次追踪") + return + except Exception as e: + logger.error(f"关系追踪LLM调用失败: user_id={user_id}, 错误: {e}") + return if llm_response: import json @@ -650,7 +697,15 @@ class ChatterRelationshipTracker: async def _handle_first_interaction(self, user_id: str, user_name: str, bot_reply_content: str): """处理与用户的初次交互""" try: - logger.info(f"✨ [RelationshipTracker] 正在处理与用户 {user_id} 的初次交互") + # 初次交互也进行概率检查,但使用更高的通过率 + first_interaction_probability = min(1.0, global_config.affinity_flow.relationship_tracking_probability * 1.5) + if random.random() > first_interaction_probability: + logger.debug( + f"🎲 [RelationshipTracker] 初次交互概率筛选未通过 ({first_interaction_probability:.2f}),跳过用户 {user_id}" + ) + return + + logger.info(f"✨ [RelationshipTracker] 正在处理与用户 {user_id} 的初次交互 (概率通过: {first_interaction_probability:.2f})") # 获取bot人设信息 from src.individuality.individuality import Individuality @@ -671,14 +726,15 @@ class ChatterRelationshipTracker: 【严格要求】: 1. 建立一个初始关系分数,通常在0.2-0.4之间(普通网友)。 -2. 关系印象描述要简洁地记录你对用户的初步看法(50-100字)。 - - 用户名给你的感觉? - - 你的回复是基于什么考虑? - - 你对接下来与TA的互动有什么期待? +2. 初始关系印象描述要简洁地记录你对用户的整体初步看法(50-100字): + - 基于用户名和初次互动,用户给你的整体感觉 + - 你感受到的用户整体性格特质倾向 + - 你对与这个用户建立关系的整体期待和感觉 + - 避免描述具体的事件细节,而是整体的直觉印象 请以JSON格式返回结果: {{ - "relationship_text": "简洁的初始关系印象描述(50-100字)", + "relationship_text": "简洁的用户整体初始印象描述(50-100字),基于第一印象的整体性格感觉", "relationship_score": 0.2~0.4的新分数, "analysis_reasoning": "从你性格角度说明建立此初始印象的理由" }} diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 85349f68b..75cd84da3 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "7.2.4" +version = "7.2.6" #----以下是给开发人员阅读的,如果你只是部署了MoFox-Bot,不需要阅读---- #如果你想要修改配置文件,请递增version的值 @@ -126,13 +126,6 @@ thinking_timeout = 40 # MoFox-Bot一次回复最长思考规划时间,超过 interruption_enabled = true # 是否启用消息打断系统 interruption_max_limit = 5 # 每个聊天流的最大打断次数 interruption_min_probability = 0.05 # 最低打断概率(反比例函数趋近的下限值) -interruption_afc_reduction = 0.05 # 每次连续打断降低的afc阈值数值 - -# DEPRECATED: interruption_probability_factor (已废弃的配置项) -# 新的反比例函数概率模型不再需要复杂的概率因子 -# 公式:打断概率 = 1.4 / (当前打断次数 + 2.0) + 最低概率 -# 特性:第1次80% → 第2次35% → 第3次15% → 趋近于最低概率 -# interruption_probability_factor = 0.8 # 此配置已废弃,请删除 # 动态消息分发系统配置 dynamic_distribution_enabled = true # 是否启用动态消息分发周期调整 @@ -143,10 +136,6 @@ dynamic_distribution_jitter_factor = 0.2 # 分发间隔随机扰动因子 max_concurrent_distributions = 10 # 最大并发处理的消息流数量,可以根据API性能和服务器负载调整 -[relationship] -enable_relationship = true # 是否启用关系系统 -relation_frequency = 1 # 关系频率,MoFox-Bot构建关系的频率 - [message_receive] # 以下是消息过滤,可以根据规则过滤特定消息,将不会读取这些消息 @@ -554,21 +543,21 @@ chat_ids = [ [affinity_flow] # 兴趣评分系统参数 -reply_action_interest_threshold = 0.62 # 回复动作兴趣阈值 -non_reply_action_interest_threshold = 0.48 # 非回复动作兴趣阈值 -high_match_interest_threshold = 0.6 # 高匹配兴趣阈值 +reply_action_interest_threshold = 1.1 # 回复动作兴趣阈值 +non_reply_action_interest_threshold = 0.9 # 非回复动作兴趣阈值 +high_match_interest_threshold = 0.7 # 高匹配兴趣阈值 medium_match_interest_threshold = 0.4 # 中匹配兴趣阈值 low_match_interest_threshold = 0.2 # 低匹配兴趣阈值 -high_match_keyword_multiplier = 4.5 # 高匹配关键词兴趣倍率 -medium_match_keyword_multiplier = 2.75 # 中匹配关键词兴趣倍率 -low_match_keyword_multiplier = 1.15 # 低匹配关键词兴趣倍率 +high_match_keyword_multiplier = 5 # 高匹配关键词兴趣倍率 +medium_match_keyword_multiplier = 3.75 # 中匹配关键词兴趣倍率 +low_match_keyword_multiplier = 1.3 # 低匹配关键词兴趣倍率 match_count_bonus = 0.02 # 匹配数关键词加成值 max_match_bonus = 0.25 # 最大匹配数加成值 # 回复决策系统参数 -no_reply_threshold_adjustment = 0.03 # 不回复兴趣阈值调整值 -reply_cooldown_reduction = 3 # 回复后减少的不回复计数 -max_no_reply_count = 5 # 最大不回复计数次数 +no_reply_threshold_adjustment = 0.01 # 不回复兴趣阈值调整值 +reply_cooldown_reduction = 5 # 回复后减少的不回复计数 +max_no_reply_count = 20 # 最大不回复计数次数 # 综合评分权重 keyword_match_weight = 0.4 # 兴趣关键词匹配度权重 @@ -576,10 +565,16 @@ mention_bot_weight = 0.3 # 提及bot分数权重 relationship_weight = 0.3 # 人物关系分数权重 # 提及bot相关参数 -mention_bot_adjustment_threshold = 0.3 # 提及bot后的调整阈值 -mention_bot_interest_score = 0.6 # 提及bot的兴趣分 +mention_bot_adjustment_threshold = 0.5 # 提及bot后的调整阈值 +mention_bot_interest_score = 2.5 # 提及bot的兴趣分 base_relationship_score = 0.3 # 基础人物关系分 +# 关系追踪系统参数 +enable_relationship_tracking = true # 是否启用关系追踪系统 +relationship_tracking_probability = 0.7 # 关系追踪执行概率 (0.0-1.0),用于减少API调用压力 +relationship_tracking_interval_min = 300 # 关系追踪最小间隔时间(秒) +relationship_tracking_cooldown_hours = 1.0 # 同一用户关系追踪冷却时间(小时) + [proactive_thinking] # 主动思考(主动发起对话)功能配置 # --- 总开关 --- enable = true # 是否启用主动发起对话功能