diff --git a/src/chat/emoji_system/emoji_manager.py b/src/chat/emoji_system/emoji_manager.py index b10d8b0bf..3511d938b 100644 --- a/src/chat/emoji_system/emoji_manager.py +++ b/src/chat/emoji_system/emoji_manager.py @@ -324,8 +324,6 @@ async def clear_temp_emoji() -> None: os.remove(file_path) logger.debug(f"[清理] 删除: {filename}") - logger.info("[清理] 完成") - async def clean_unused_emojis(emoji_dir: str, emoji_objects: List["MaiEmoji"], removed_count: int) -> int: """清理指定目录中未被 emoji_objects 追踪的表情包文件""" @@ -590,7 +588,7 @@ class EmojiManager: """定期检查表情包完整性和数量""" await self.get_all_emoji_from_db() while True: - logger.info("[扫描] 开始检查表情包完整性...") + # logger.info("[扫描] 开始检查表情包完整性...") await self.check_emoji_file_integrity() await clear_temp_emoji() logger.info("[扫描] 开始扫描新表情包...") diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index 66ddf362e..5de0d7015 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -119,15 +119,15 @@ class HeartFCMessageReceiver: # current_time = time.strftime("%H:%M:%S", time.localtime(message.message_info.time)) current_talk_frequency = global_config.chat.get_current_talk_frequency(chat.stream_id) - # 如果消息中包含图片标识,则日志展示为图片 + # 如果消息中包含图片标识,则将 [picid:...] 替换为 [图片] + picid_pattern = r"\[picid:([^\]]+)\]" + processed_plain_text = re.sub(picid_pattern, "[图片]", message.processed_plain_text) - picid_match = re.search(r"\[picid:([^\]]+)\]", message.processed_plain_text) - if picid_match: - logger.info(f"[{mes_name}]{userinfo.user_nickname}: [图片] [当前回复频率: {current_talk_frequency}]") - else: - logger.info( - f"[{mes_name}]{userinfo.user_nickname}:{message.processed_plain_text}[当前回复频率: {current_talk_frequency}]" - ) + logger.info( + f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}" + ) + + logger.debug(f"[{mes_name}][当前时段回复频率: {current_talk_frequency}]") # 8. 关系处理 if global_config.relationship.enable_relationship: diff --git a/src/chat/message_receive/storage.py b/src/chat/message_receive/storage.py index 146a4372a..c40c4eb75 100644 --- a/src/chat/message_receive/storage.py +++ b/src/chat/message_receive/storage.py @@ -131,7 +131,7 @@ class MessageStorage: if matched_message: # 更新找到的消息记录 Messages.update(message_id=qq_message_id).where(Messages.id == matched_message.id).execute() - logger.info(f"更新消息ID成功: {matched_message.message_id} -> {qq_message_id}") + logger.debug(f"更新消息ID成功: {matched_message.message_id} -> {qq_message_id}") else: logger.debug("未找到匹配的消息") diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 48e8f4191..43ab5803e 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -585,10 +585,19 @@ class NormalChat: ) response_set, plan_result = results except asyncio.TimeoutError: - logger.warning( - f"[{self.stream_name}] 并行执行回复生成和动作规划超时 ({gather_timeout}秒),正在取消相关任务..." - ) - print(f"111{self.timeout_count}") + gen_timed_out = not gen_task.done() + plan_timed_out = not plan_task.done() + + timeout_details = [] + if gen_timed_out: + timeout_details.append("回复生成(gen)") + if plan_timed_out: + timeout_details.append("动作规划(plan)") + + timeout_source = " 和 ".join(timeout_details) + + logger.warning(f"[{self.stream_name}] {timeout_source} 任务超时 ({global_config.chat.thinking_timeout}秒),正在取消相关任务...") + # print(f"111{self.timeout_count}") self.timeout_count += 1 if self.timeout_count > 5: logger.warning( diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index 2359abf30..ab97f395b 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -551,6 +551,9 @@ def build_readable_messages( show_actions: 是否显示动作记录 """ # 创建messages的深拷贝,避免修改原始列表 + if not messages: + return "" + copy_messages = [msg.copy() for msg in messages] if show_actions and copy_messages: diff --git a/src/common/database/database_model.py b/src/common/database/database_model.py index 500852d00..8c2bf423b 100644 --- a/src/common/database/database_model.py +++ b/src/common/database/database_model.py @@ -252,8 +252,7 @@ class PersonInfo(BaseModel): know_times = FloatField(null=True) # 认识时间 (时间戳) know_since = FloatField(null=True) # 首次印象总结时间 last_know = FloatField(null=True) # 最后一次印象总结时间 - familiarity_value = IntegerField(null=True, default=0) # 熟悉度,0-100,从完全陌生到非常熟悉 - liking_value = IntegerField(null=True, default=50) # 好感度,0-100,从非常厌恶到十分喜欢 + attitude = IntegerField(null=True, default=50) # 态度,0-100,从非常厌恶到十分喜欢 class Meta: # database = db # 继承自 BaseModel diff --git a/src/common/logger.py b/src/common/logger.py index 7202b993f..40fd15070 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -321,14 +321,13 @@ MODULE_COLORS = { # 核心模块 "main": "\033[1;97m", # 亮白色+粗体 (主程序) "api": "\033[92m", # 亮绿色 - "emoji": "\033[92m", # 亮绿色 + "emoji": "\033[33m", # 亮绿色 "chat": "\033[92m", # 亮蓝色 "config": "\033[93m", # 亮黄色 "common": "\033[95m", # 亮紫色 "tools": "\033[96m", # 亮青色 "lpmm": "\033[96m", "plugin_system": "\033[91m", # 亮红色 - "experimental": "\033[97m", # 亮白色 "person_info": "\033[32m", # 绿色 "individuality": "\033[34m", # 蓝色 "manager": "\033[35m", # 紫色 @@ -339,8 +338,7 @@ MODULE_COLORS = { "planner": "\033[36m", "memory": "\033[34m", "hfc": "\033[96m", - "base_action": "\033[96m", - "action_manager": "\033[32m", + "action_manager": "\033[38;5;166m", # 关系系统 "relation": "\033[38;5;201m", # 深粉色 # 聊天相关模块 @@ -356,11 +354,9 @@ MODULE_COLORS = { "message_storage": "\033[38;5;33m", # 深蓝色 # 专注聊天模块 "replyer": "\033[38;5;166m", # 橙色 - "expressor": "\033[38;5;172m", # 黄橙色 - "processor": "\033[38;5;184m", # 黄绿色 "base_processor": "\033[38;5;190m", # 绿黄色 "working_memory": "\033[38;5;22m", # 深绿色 - "memory_activator": "\033[38;5;28m", # 绿色 + "memory_activator": "\033[34m", # 绿色 # 插件系统 "plugin_manager": "\033[38;5;208m", # 红色 "base_plugin": "\033[38;5;202m", # 橙红色 @@ -386,11 +382,9 @@ MODULE_COLORS = { "tool_executor": "\033[38;5;64m", # 深绿色 "base_tool": "\033[38;5;70m", # 绿色 # 工具和实用模块 - "prompt": "\033[38;5;99m", # 紫色 "prompt_build": "\033[38;5;105m", # 紫色 "chat_utils": "\033[38;5;111m", # 蓝色 "chat_image": "\033[38;5;117m", # 浅蓝色 - "typo_gen": "\033[38;5;123m", # 青绿色 "maibot_statistic": "\033[38;5;129m", # 紫色 # 特殊功能插件 "mute_plugin": "\033[38;5;240m", # 灰色 @@ -402,16 +396,13 @@ MODULE_COLORS = { # 数据库和消息 "database_model": "\033[38;5;94m", # 橙褐色 "maim_message": "\033[38;5;100m", # 绿褐色 - # 实验性模块 - "pfc": "\033[38;5;252m", # 浅灰色 # 日志系统 "logger": "\033[38;5;8m", # 深灰色 - "demo": "\033[38;5;15m", # 白色 "confirm": "\033[1;93m", # 黄色+粗体 # 模型相关 "model_utils": "\033[38;5;164m", # 紫红色 "relationship_fetcher": "\033[38;5;170m", # 浅紫色 - "relationship_builder": "\033[38;5;117m", # 浅蓝色 + "relationship_builder": "\033[38;5;93m", # 浅蓝色 } RESET_COLOR = "\033[0m" diff --git a/src/person_info/person_info.py b/src/person_info/person_info.py index 86e3b6fcd..7f22fc2d4 100644 --- a/src/person_info/person_info.py +++ b/src/person_info/person_info.py @@ -48,6 +48,7 @@ person_info_default = { "points": None, "forgotten_points": None, "relation_value": None, + "attitude": 50, } diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index 2d37bcda8..813036c69 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -21,66 +21,11 @@ logger = get_logger("relation") class RelationshipManager: def __init__(self): - self.positive_feedback_value = 0 # 正反馈系统 - self.gain_coefficient = [1.0, 1.0, 1.1, 1.2, 1.4, 1.7, 1.9, 2.0] - self._mood_manager = None - self.relationship_llm = LLMRequest( model=global_config.model.relation, request_type="relationship", # 用于动作规划 ) - @property - def mood_manager(self): - if self._mood_manager is None: - self._mood_manager = mood_manager - return self._mood_manager - - def positive_feedback_sys(self, label: str, stance: str): - """正反馈系统,通过正反馈系数增益情绪变化,根据情绪再影响关系变更""" - - positive_list = [ - "开心", - "惊讶", - "害羞", - ] - - negative_list = [ - "愤怒", - "悲伤", - "恐惧", - "厌恶", - ] - - if label in positive_list: - if 7 > self.positive_feedback_value >= 0: - self.positive_feedback_value += 1 - elif self.positive_feedback_value < 0: - self.positive_feedback_value = 0 - elif label in negative_list: - if -7 < self.positive_feedback_value <= 0: - self.positive_feedback_value -= 1 - elif self.positive_feedback_value > 0: - self.positive_feedback_value = 0 - - if abs(self.positive_feedback_value) > 1: - logger.debug(f"触发mood变更增益,当前增益系数:{self.gain_coefficient[abs(self.positive_feedback_value)]}") - - def mood_feedback(self, value): - """情绪反馈""" - mood_manager = self.mood_manager - mood_gain = mood_manager.current_mood.valence**2 * math.copysign(1, value * mood_manager.current_mood.valence) - value += value * mood_gain - logger.debug(f"当前relationship增益系数:{mood_gain:.3f}") - return value - - def feedback_to_mood(self, mood_value): - """对情绪的反馈""" - coefficient = self.gain_coefficient[abs(self.positive_feedback_value)] - if mood_value > 0 and self.positive_feedback_value > 0 or mood_value < 0 and self.positive_feedback_value < 0: - return mood_value * coefficient - else: - return mood_value / coefficient @staticmethod async def is_known_some_one(platform, user_id): @@ -168,18 +113,6 @@ class RelationshipManager: return relation_prompt - async def _update_list_field(self, person_id: str, field_name: str, new_items: list) -> None: - """更新列表类型的字段,将新项目添加到现有列表中 - - Args: - person_id: 用户ID - field_name: 字段名称 - new_items: 新的项目列表 - """ - person_info_manager = get_person_info_manager() - old_items = await person_info_manager.get_value(person_id, field_name) or [] - updated_items = list(set(old_items + [item for item in new_items if isinstance(item, str) and item])) - await person_info_manager.update_one_field(person_id, field_name, updated_items) async def update_person_impression(self, person_id, timestamp, bot_engaged_messages=None): """更新用户印象 @@ -194,6 +127,7 @@ class RelationshipManager: person_info_manager = get_person_info_manager() person_name = await person_info_manager.get_value(person_id, "person_name") nickname = await person_info_manager.get_value(person_id, "nickname") + know_times = await person_info_manager.get_value(person_id, "know_times") or 0 alias_str = ", ".join(global_config.bot.alias_names) # personality_block =get_individuality().get_personality_prompt(x_person=2, level=2) @@ -239,8 +173,10 @@ class RelationshipManager: user_count += 1 name_mapping[replace_person_name] = f"用户{current_user}{user_count if user_count > 1 else ''}" current_user = chr(ord(current_user) + 1) - - readable_messages = self.build_focus_readable_messages(messages=user_messages, target_person_id=person_id) + + readable_messages = build_readable_messages( + messages=user_messages, replace_bot_name=True, timestamp_mode="normal_no_YMD", truncate=True + ) if not readable_messages: return @@ -385,73 +321,121 @@ class RelationshipManager: # 如果points超过10条,按权重随机选择多余的条目移动到forgotten_points if len(current_points) > 10: - # 获取现有forgotten_points - forgotten_points = await person_info_manager.get_value(person_id, "forgotten_points") or [] - if isinstance(forgotten_points, str): - try: - forgotten_points = json.loads(forgotten_points) - except json.JSONDecodeError: - logger.error(f"解析forgotten_points JSON失败: {forgotten_points}") - forgotten_points = [] - elif not isinstance(forgotten_points, list): + current_points = await self._update_impression(person_id, current_points, timestamp) + + # 更新数据库 + await person_info_manager.update_one_field( + person_id, "points", json.dumps(current_points, ensure_ascii=False, indent=None) + ) + + await person_info_manager.update_one_field(person_id, "know_times", know_times + 1) + know_since = await person_info_manager.get_value(person_id, "know_since") or 0 + if know_since == 0: + await person_info_manager.update_one_field(person_id, "know_since", timestamp) + await person_info_manager.update_one_field(person_id, "last_know", timestamp) + + logger.debug(f"{person_name} 的印象更新完成") + + async def _update_impression(self, person_id, current_points, timestamp): + # 获取现有forgotten_points + person_info_manager = get_person_info_manager() + + + person_name = await person_info_manager.get_value(person_id, "person_name") + nickname = await person_info_manager.get_value(person_id, "nickname") + know_times = await person_info_manager.get_value(person_id, "know_times") or 0 + attitude = await person_info_manager.get_value(person_id, "attitude") or 50 + + # 根据熟悉度,调整印象和简短印象的最大长度 + if know_times > 300: + max_impression_length = 2000 + max_short_impression_length = 800 + elif know_times > 100: + max_impression_length = 1000 + max_short_impression_length = 500 + elif know_times > 50: + max_impression_length = 500 + max_short_impression_length = 300 + elif know_times > 10: + max_impression_length = 200 + max_short_impression_length = 100 + else: + max_impression_length = 100 + max_short_impression_length = 50 + + # 根据好感度,调整印象和简短印象的最大长度 + attitude_multiplier = (abs(100-attitude) / 100) + 1 + max_impression_length = max_impression_length * attitude_multiplier + max_short_impression_length = max_short_impression_length * attitude_multiplier + + + + forgotten_points = await person_info_manager.get_value(person_id, "forgotten_points") or [] + if isinstance(forgotten_points, str): + try: + forgotten_points = json.loads(forgotten_points) + except json.JSONDecodeError: + logger.error(f"解析forgotten_points JSON失败: {forgotten_points}") forgotten_points = [] + elif not isinstance(forgotten_points, list): + forgotten_points = [] - # 计算当前时间 - current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") + # 计算当前时间 + current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") - # 计算每个点的最终权重(原始权重 * 时间权重) - weighted_points = [] - for point in current_points: - time_weight = self.calculate_time_weight(point[2], current_time) - final_weight = point[1] * time_weight - weighted_points.append((point, final_weight)) + # 计算每个点的最终权重(原始权重 * 时间权重) + weighted_points = [] + for point in current_points: + time_weight = self.calculate_time_weight(point[2], current_time) + final_weight = point[1] * time_weight + weighted_points.append((point, final_weight)) - # 计算总权重 - total_weight = sum(w for _, w in weighted_points) + # 计算总权重 + total_weight = sum(w for _, w in weighted_points) - # 按权重随机选择要保留的点 - remaining_points = [] - points_to_move = [] + # 按权重随机选择要保留的点 + remaining_points = [] + points_to_move = [] - # 对每个点进行随机选择 - for point, weight in weighted_points: - # 计算保留概率(权重越高越可能保留) - keep_probability = weight / total_weight + # 对每个点进行随机选择 + for point, weight in weighted_points: + # 计算保留概率(权重越高越可能保留) + keep_probability = weight / total_weight - if len(remaining_points) < 10: - # 如果还没达到30条,直接保留 - remaining_points.append(point) + if len(remaining_points) < 10: + # 如果还没达到30条,直接保留 + remaining_points.append(point) + else: + # 随机决定是否保留 + if random.random() < keep_probability: + # 保留这个点,随机移除一个已保留的点 + idx_to_remove = random.randrange(len(remaining_points)) + points_to_move.append(remaining_points[idx_to_remove]) + remaining_points[idx_to_remove] = point else: - # 随机决定是否保留 - if random.random() < keep_probability: - # 保留这个点,随机移除一个已保留的点 - idx_to_remove = random.randrange(len(remaining_points)) - points_to_move.append(remaining_points[idx_to_remove]) - remaining_points[idx_to_remove] = point - else: - # 不保留这个点 - points_to_move.append(point) + # 不保留这个点 + points_to_move.append(point) - # 更新points和forgotten_points - current_points = remaining_points - forgotten_points.extend(points_to_move) + # 更新points和forgotten_points + current_points = remaining_points + forgotten_points.extend(points_to_move) - # 检查forgotten_points是否达到5条 - if len(forgotten_points) >= 10: - # 构建压缩总结提示词 - alias_str = ", ".join(global_config.bot.alias_names) + # 检查forgotten_points是否达到10条 + if len(forgotten_points) >= 10: + # 构建压缩总结提示词 + alias_str = ", ".join(global_config.bot.alias_names) - # 按时间排序forgotten_points - forgotten_points.sort(key=lambda x: x[2]) + # 按时间排序forgotten_points + forgotten_points.sort(key=lambda x: x[2]) - # 构建points文本 - points_text = "\n".join( - [f"时间:{point[2]}\n权重:{point[1]}\n内容:{point[0]}" for point in forgotten_points] - ) + # 构建points文本 + points_text = "\n".join( + [f"时间:{point[2]}\n权重:{point[1]}\n内容:{point[0]}" for point in forgotten_points] + ) - impression = await person_info_manager.get_value(person_id, "impression") or "" + impression = await person_info_manager.get_value(person_id, "impression") or "" - compress_prompt = f""" + compress_prompt = f""" 你的名字是{global_config.bot.nickname},{global_config.bot.nickname}的别名是{alias_str}。 请不要混淆你自己和{global_config.bot.nickname}和{person_name}。 @@ -466,17 +450,17 @@ class RelationshipManager: 你记得ta最近做的事: {points_text} -请输出一段平文本,以陈诉自白的语气,输出你对{person_name}的了解,不要输出任何其他内容。 +请输出一段{max_impression_length}字左右的平文本,以陈诉自白的语气,输出你对{person_name}的了解,不要输出任何其他内容。 """ - # 调用LLM生成压缩总结 - compressed_summary, _ = await self.relationship_llm.generate_response_async(prompt=compress_prompt) + # 调用LLM生成压缩总结 + compressed_summary, _ = await self.relationship_llm.generate_response_async(prompt=compress_prompt) - current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") - compressed_summary = f"截至{current_time},你对{person_name}的了解:{compressed_summary}" + current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") + compressed_summary = f"截至{current_time},你对{person_name}的了解:{compressed_summary}" - await person_info_manager.update_one_field(person_id, "impression", compressed_summary) + await person_info_manager.update_one_field(person_id, "impression", compressed_summary) - compress_short_prompt = f""" + compress_short_prompt = f""" 你的名字是{global_config.bot.nickname},{global_config.bot.nickname}的别名是{alias_str}。 请不要混淆你自己和{global_config.bot.nickname}和{person_name}。 @@ -487,107 +471,77 @@ class RelationshipManager: 1.对{person_name}的直观印象 2.{global_config.bot.nickname}与{person_name}的关系 3.{person_name}的关键信息 -请输出一段平文本,以陈诉自白的语气,输出你对{person_name}的概括,不要输出任何其他内容。 +请输出一段{max_short_impression_length}字左右的平文本,以陈诉自白的语气,输出你对{person_name}的概括,不要输出任何其他内容。 """ - compressed_short_summary, _ = await self.relationship_llm.generate_response_async( - prompt=compress_short_prompt - ) + compressed_short_summary, _ = await self.relationship_llm.generate_response_async( + prompt=compress_short_prompt + ) - # current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") - # compressed_short_summary = f"截至{current_time},你对{person_name}的了解:{compressed_short_summary}" + # current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") + # compressed_short_summary = f"截至{current_time},你对{person_name}的了解:{compressed_short_summary}" - await person_info_manager.update_one_field(person_id, "short_impression", compressed_short_summary) + await person_info_manager.update_one_field(person_id, "short_impression", compressed_short_summary) - relation_value_prompt = f""" + relation_value_prompt = f""" 你的名字是{global_config.bot.nickname}。 你最近对{person_name}的了解如下: {points_text} -请根据以上信息,评估你和{person_name}的关系,给出两个维度的值:熟悉度和好感度。 -1. 了解度 (familiarity_value): 0-100的整数,表示这些信息让你对ta的了解增进程度。 - - 0: 没有任何进一步了解 - - 25: 有点进一步了解 - - 50: 有进一步了解 - - 75: 有更多了解 - - 100: 有了更多重要的了解 +请根据以上信息,评估你和{person_name}的关系,给出你对ta的态度。 -2. **好感度 (liking_value)**: 0-100的整数,表示这些信息让你对ta的喜。 - - 0: 非常厌恶 - - 25: 有点反感 - - 50: 中立/无感(或者文本中无法明显看出) - - 75: 喜欢这个人 - - 100: 非常喜欢/开心对这个人 +态度: 0-100的整数,表示这些信息让你对ta的态度。 +- 0: 非常厌恶 +- 25: 有点反感 +- 50: 中立/无感(或者文本中无法明显看出) +- 75: 喜欢这个人 +- 100: 非常喜欢/开心对这个人 请严格按照json格式输出,不要有其他多余内容: {{ - "familiarity_value": <0-100之间的整数>, - "liking_value": <0-100之间的整数> +"attitude": <0-100之间的整数>, }} """ - try: - relation_value_response, _ = await self.relationship_llm.generate_response_async( - prompt=relation_value_prompt - ) - relation_value_json = json.loads(repair_json(relation_value_response)) - - # 从LLM获取新生成的值 - new_familiarity_value = int(relation_value_json.get("familiarity_value", 0)) - new_liking_value = int(relation_value_json.get("liking_value", 50)) - - # 获取当前的关系值 - old_familiarity_value = await person_info_manager.get_value(person_id, "familiarity_value") or 0 - liking_value = await person_info_manager.get_value(person_id, "liking_value") or 50 - - # 更新熟悉度 - if new_familiarity_value > 25: - familiarity_value = old_familiarity_value + (new_familiarity_value - 25) / 75 - else: - familiarity_value = old_familiarity_value - - # 更新好感度 - if new_liking_value > 50: - liking_value += (new_liking_value - 50) / 50 - elif new_liking_value < 50: - liking_value -= (50 - new_liking_value) / 50 * 1.5 - - await person_info_manager.update_one_field(person_id, "familiarity_value", familiarity_value) - await person_info_manager.update_one_field(person_id, "liking_value", liking_value) - logger.info(f"更新了与 {person_name} 的关系值: 熟悉度={familiarity_value}, 好感度={liking_value}") - except (json.JSONDecodeError, ValueError, TypeError) as e: - logger.error(f"解析relation_value JSON失败或值无效: {e}, 响应: {relation_value_response}") - - forgotten_points = [] - info_list = [] - await person_info_manager.update_one_field( - person_id, "info_list", json.dumps(info_list, ensure_ascii=False, indent=None) + try: + relation_value_response, _ = await self.relationship_llm.generate_response_async( + prompt=relation_value_prompt ) + relation_value_json = json.loads(repair_json(relation_value_response)) + # 从LLM获取新生成的值 + new_attitude = int(relation_value_json.get("attitude", 50)) + + # 获取当前的关系值 + old_attitude = await person_info_manager.get_value(person_id, "attitude") or 50 + + # 更新熟悉度 + if new_attitude > 25: + attitude = old_attitude + (new_attitude - 25) / 75 + else: + attitude = old_attitude + + # 更新好感度 + if new_attitude > 50: + attitude += (new_attitude - 50) / 50 + elif new_attitude < 50: + attitude -= (50 - new_attitude) / 50 * 1.5 + + await person_info_manager.update_one_field(person_id, "attitude", attitude) + logger.info(f"更新了与 {person_name} 的态度: {attitude}") + except (json.JSONDecodeError, ValueError, TypeError) as e: + logger.error(f"解析relation_value JSON失败或值无效: {e}, 响应: {relation_value_response}") + + forgotten_points = [] + info_list = [] await person_info_manager.update_one_field( - person_id, "forgotten_points", json.dumps(forgotten_points, ensure_ascii=False, indent=None) + person_id, "info_list", json.dumps(info_list, ensure_ascii=False, indent=None) ) - # 更新数据库 await person_info_manager.update_one_field( - person_id, "points", json.dumps(current_points, ensure_ascii=False, indent=None) + person_id, "forgotten_points", json.dumps(forgotten_points, ensure_ascii=False, indent=None) ) - know_times = await person_info_manager.get_value(person_id, "know_times") or 0 - await person_info_manager.update_one_field(person_id, "know_times", know_times + 1) - know_since = await person_info_manager.get_value(person_id, "know_since") or 0 - if know_since == 0: - await person_info_manager.update_one_field(person_id, "know_since", timestamp) - await person_info_manager.update_one_field(person_id, "last_know", timestamp) + + return current_points - logger.info(f"{person_name} 的印象更新完成") - - def build_focus_readable_messages(self, messages: list, target_person_id: str = None) -> str: - """格式化消息,处理所有消息内容""" - if not messages: - return "" - - # 直接处理所有消息,不进行过滤 - return build_readable_messages( - messages=messages, replace_bot_name=True, timestamp_mode="normal_no_YMD", truncate=True - ) def calculate_time_weight(self, point_time: str, current_time: str) -> float: """计算基于时间的权重系数""" diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index c3cf0201c..a5c8d637b 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -103,6 +103,8 @@ class NoReplyAction(BaseAction): logger.info(f"{self.log_prefix} 选择不回复(第{count}次),开始摸鱼,原因: {reason}") + + # 进入等待状态 while True: current_time = time.time() elapsed_time = current_time - start_time @@ -141,19 +143,9 @@ class NoReplyAction(BaseAction): # 判定条件:累计3条消息或等待超过5秒且有新消息 time_since_last_judge = current_time - last_judge_time - should_judge = ( - new_message_count >= 3 # 累计3条消息 - or (new_message_count > 0 and time_since_last_judge >= 15.0) # 等待超过5秒且有新消息 - ) + should_judge, trigger_reason = self._should_trigger_judge(new_message_count, time_since_last_judge) if should_judge and time_since_last_judge >= min_judge_interval: - # 判断触发原因 - trigger_reason = "" - if new_message_count >= 3: - trigger_reason = f"累计{new_message_count}条消息" - elif time_since_last_judge >= 10.0: - trigger_reason = f"等待{time_since_last_judge:.1f}秒且有新消息" - logger.info(f"{self.log_prefix} 触发判定({trigger_reason}),进行智能判断...") # 获取最近的消息内容用于判断 @@ -166,7 +158,10 @@ class NoReplyAction(BaseAction): if recent_messages: # 使用message_api构建可读的消息字符串 messages_text = message_api.build_readable_messages( - messages=recent_messages, timestamp_mode="normal_no_YMD", truncate=False, show_actions=False + messages=recent_messages, + timestamp_mode="normal_no_YMD", + truncate=False, + show_actions=False, ) # 获取身份信息 @@ -189,81 +184,13 @@ class NoReplyAction(BaseAction): history_block += "\n" # 检查过去10分钟的发言频率 - frequency_block = "" - should_skip_llm_judge = False # 是否跳过LLM判断 - - try: - # 获取过去10分钟的所有消息 - past_10min_time = current_time - 600 # 10分钟前 - all_messages_10min = message_api.get_messages_by_time_in_chat( - chat_id=self.chat_id, - start_time=past_10min_time, - end_time=current_time, - ) - - # 手动过滤bot自己的消息 - bot_message_count = 0 - if all_messages_10min: - user_id = global_config.bot.qq_account - - for message in all_messages_10min: - # 检查消息发送者是否是bot - sender_id = message.get("user_id", "") - - if sender_id == user_id: - bot_message_count += 1 - - talk_frequency_threshold = global_config.chat.get_current_talk_frequency(self.chat_id) * 10 - - if bot_message_count > talk_frequency_threshold: - over_count = bot_message_count - talk_frequency_threshold - - # 根据超过的数量设置不同的提示词和跳过概率 - skip_probability = 0 - if over_count <= 3: - frequency_block = "你感觉稍微有些累,回复的有点多了。\n" - elif over_count <= 5: - frequency_block = "你今天说话比较多,感觉有点疲惫,想要稍微休息一下。\n" - elif over_count <= 8: - frequency_block = "你发现自己说话太多了,感觉很累,想要安静一会儿,除非有重要的事情否则不想回复。\n" - skip_probability = self._skip_probability - else: - frequency_block = "你感觉非常累,想要安静一会儿。\n" - skip_probability = 1 - - # 根据配置和概率决定是否跳过LLM判断 - if self._skip_judge_when_tired and random.random() < skip_probability: - should_skip_llm_judge = True - logger.info( - f"{self.log_prefix} 发言过多(超过{over_count}条),随机决定跳过此次LLM判断(概率{skip_probability * 100:.0f}%)" - ) - - logger.info( - f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示" - ) - else: - # 回复次数少时的正向提示 - under_count = talk_frequency_threshold - bot_message_count - - if under_count >= talk_frequency_threshold * 0.8: # 回复很少(少于20%) - frequency_block = "你感觉精力充沛,状态很好,积极参与聊天。\n" - elif under_count >= talk_frequency_threshold * 0.5: # 回复较少(少于50%) - frequency_block = "你感觉状态不错。\n" - else: # 刚好达到阈值 - frequency_block = "" - - logger.info( - f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,未超过阈值{talk_frequency_threshold},添加正向提示" - ) - - except Exception as e: - logger.warning(f"{self.log_prefix} 检查发言频率时出错: {e}") - frequency_block = "" + frequency_block, should_skip_llm_judge = self._get_fatigue_status(current_time) # 如果决定跳过LLM判断,直接更新时间并继续等待 - if should_skip_llm_judge: + logger.info(f"{self.log_prefix} 疲劳,继续等待。") last_judge_time = time.time() # 更新判断时间,避免立即重新判断 + start_time = current_time # 更新消息检查的起始时间,以避免重复判断 continue # 跳过本次LLM判断,继续循环等待 # 构建判断上下文 @@ -379,6 +306,105 @@ class NoReplyAction(BaseAction): ) return False, f"不回复动作执行失败: {e}" + def _should_trigger_judge(self, new_message_count: int, time_since_last_judge: float) -> Tuple[bool, str]: + """判断是否应该触发智能判断,并返回触发原因。 + + Args: + new_message_count: 新消息的数量。 + time_since_last_judge: 距离上次判断的时间。 + + Returns: + 一个元组 (should_judge, reason)。 + - should_judge: 一个布尔值,指示是否应该触发判断。 + - reason: 触发判断的原因字符串。 + """ + # 判定条件:累计3条消息或等待超过15秒且有新消息 + should_judge_flag = new_message_count >= 3 or (new_message_count > 0 and time_since_last_judge >= 15.0) + + if not should_judge_flag: + return False, "" + + # 判断触发原因 + if new_message_count >= 3: + return True, f"累计{new_message_count}条消息" + elif new_message_count > 0 and time_since_last_judge >= 15.0: + return True, f"等待{time_since_last_judge:.1f}秒且有新消息" + + return False, "" + + def _get_fatigue_status(self, current_time: float) -> Tuple[str, bool]: + """ + 根据最近的发言频率生成疲劳提示,并决定是否跳过判断。 + + Args: + current_time: 当前时间戳。 + + Returns: + 一个元组 (frequency_block, should_skip_judge)。 + - frequency_block: 疲劳度相关的提示字符串。 + - should_skip_judge: 是否应该跳过LLM判断的布尔值。 + """ + try: + # 获取过去10分钟的所有消息 + past_10min_time = current_time - 600 # 10分钟前 + all_messages_10min = message_api.get_messages_by_time_in_chat( + chat_id=self.chat_id, + start_time=past_10min_time, + end_time=current_time, + ) + + # 手动过滤bot自己的消息 + bot_message_count = 0 + if all_messages_10min: + user_id = global_config.bot.qq_account + for message in all_messages_10min: + sender_id = message.get("user_id", "") + if sender_id == user_id: + bot_message_count += 1 + + talk_frequency_threshold = global_config.chat.get_current_talk_frequency(self.chat_id) * 10 + + if bot_message_count > talk_frequency_threshold: + over_count = bot_message_count - talk_frequency_threshold + skip_probability = 0 + frequency_block = "" + + if over_count <= 3: + frequency_block = "你感觉稍微有些累,回复的有点多了。\n" + elif over_count <= 5: + frequency_block = "你今天说话比较多,感觉有点疲惫,想要稍微休息一下。\n" + elif over_count <= 8: + frequency_block = "你发现自己说话太多了,感觉很累,想要安静一会儿,除非有重要的事情否则不想回复。\n" + skip_probability = self._skip_probability + else: + frequency_block = "你感觉非常累,想要安静一会儿。\n" + skip_probability = 1 + + should_skip_judge = self._skip_judge_when_tired and random.random() < skip_probability + + if should_skip_judge: + logger.info( + f"{self.log_prefix} 发言过多(超过{over_count}条),随机决定跳过此次LLM判断(概率{skip_probability * 100:.0f}%)" + ) + + logger.info(f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示") + return frequency_block, should_skip_judge + else: + # 回复次数少时的正向提示 + under_count = talk_frequency_threshold - bot_message_count + frequency_block = "" + if under_count >= talk_frequency_threshold * 0.8: + frequency_block = "你感觉精力充沛,状态很好,积极参与聊天。\n" + elif under_count >= talk_frequency_threshold * 0.5: + frequency_block = "你感觉状态不错。\n" + + logger.info(f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,未超过阈值{talk_frequency_threshold},添加正向提示") + return frequency_block, False + + except Exception as e: + logger.warning(f"{self.log_prefix} 检查发言频率时出错: {e}") + return "", False + def _check_no_activity_and_exit_focus(self, current_time: float) -> bool: """检查过去10分钟是否完全没有发言,决定是否退出专注模式