From d76bd95159cf4fc9760e104521915ec837bd0332 Mon Sep 17 00:00:00 2001 From: Bakadax Date: Wed, 30 Apr 2025 21:30:30 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E4=BA=86=E5=90=97=EF=BC=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/heart_flow/mai_state_manager.py | 2 +- src/plugins/group_nickname/nickname_mapper.py | 14 +- .../group_nickname/nickname_processor.py | 141 ++++--- src/plugins/heartFC_chat/heartFC_chat.py | 2 +- src/plugins/person_info/person_info.py | 1 + .../person_info/relationship_manager.py | 353 +++++++++++------- 6 files changed, 332 insertions(+), 181 deletions(-) diff --git a/src/heart_flow/mai_state_manager.py b/src/heart_flow/mai_state_manager.py index cd739344..dfba2cc2 100644 --- a/src/heart_flow/mai_state_manager.py +++ b/src/heart_flow/mai_state_manager.py @@ -11,7 +11,7 @@ logger = get_logger("mai_state") # -- 状态相关的可配置参数 (可以从 glocal_config 加载) -- # enable_unlimited_hfc_chat = True # 调试用:无限专注聊天 -enable_unlimited_hfc_chat = False +enable_unlimited_hfc_chat = True prevent_offline_state = True # 目前默认不启用OFFLINE状态 diff --git a/src/plugins/group_nickname/nickname_mapper.py b/src/plugins/group_nickname/nickname_mapper.py index 95803f9d..b5e258a4 100644 --- a/src/plugins/group_nickname/nickname_mapper.py +++ b/src/plugins/group_nickname/nickname_mapper.py @@ -30,9 +30,9 @@ if global_config.ENABLE_NICKNAME_MAPPING: # 使用全局开关 def _build_mapping_prompt(chat_history_str: str, bot_reply: str, user_name_map: Dict[str, str]) -> str: user_list_str = "\n".join([f"- {uid}: {name}" for uid, name in user_name_map.items()]) - + print(f"\n\n\n{user_list_str}\n\n\n\n") prompt = f""" -任务:分析以下聊天记录和 Bot 的最新回复,判断其中是否包含用户绰号,并确定绰号与用户 ID 之间是否存在明确的一一对应关系。 +任务:分析以下聊天记录和你的最新回复,判断其中是否包含用户绰号,并确定绰号与用户 ID 之间是否存在明确的一一对应关系。 已知用户信息: {user_list_str} @@ -42,18 +42,18 @@ def _build_mapping_prompt(chat_history_str: str, bot_reply: str, user_name_map: {chat_history_str} --- -Bot 最新回复: +你的最新回复: {bot_reply} 分析要求: -1. 识别聊天记录和 Bot 回复中出现的可能是用户绰号的词语。 +1. 识别聊天记录和你发言中出现的可能是用户绰号的词语。 2. 判断这些绰号是否能明确地指向某个特定的用户 ID。一个绰号必须在上下文中清晰地与某个发言人或被提及的人关联起来。 3. 如果能建立可靠的一一映射关系,请输出一个 JSON 对象,格式如下: {{ "is_exist": true, "data": {{ - "用户ID_A": "绰号_A", - "用户ID_B": "绰号_B" + "用户A数字id": "绰号_A", + "用户B数字id": "绰号_B" }} }} 其中 "data" 字段的键是用户的 ID,值是对应的绰号。只包含你能确认映射关系的绰号。 @@ -61,7 +61,7 @@ Bot 最新回复: {{ "is_exist": false }} -5. 不需要输出 Bot 自身的绰号。 +5. 你的昵称后面包含"(你)",不需要输出你自身的绰号。 6. 请严格按照 JSON 格式输出,不要包含任何额外的解释或文本。 输出: diff --git a/src/plugins/group_nickname/nickname_processor.py b/src/plugins/group_nickname/nickname_processor.py index a0b7d9e2..ae75851c 100644 --- a/src/plugins/group_nickname/nickname_processor.py +++ b/src/plugins/group_nickname/nickname_processor.py @@ -20,85 +20,132 @@ _stop_event = Event() mongo_client: Optional[MongoClient] = None # MongoDB 客户端实例 person_info_collection = None # 用户信息集合对象 - -# --- 数据库更新逻辑 --- +# --- 数据库更新逻辑 (使用推荐的新结构) --- async def update_nickname_counts(group_id: str, nickname_map: Dict[str, str]): """ 更新数据库中用户的群组绰号计数。 + 使用新的数据结构: + { + "user_id": 12345, + "group_nicknames": [ # <--- 字段名统一为 group_nicknames + { + "group_id": "群号1", + "nicknames": [ { "name": "绰号A", "count": 5 }, ... ] + }, ... + ] + } """ - person_info_collection = db.person_info + person_info_collection = db.person_info # 获取集合对象 - if not person_info_collection: # 理论上 db 对象总是可用,但保留检查 - logger.error("无法访问数据库集合 'person_info'。无法更新绰号计数。") - return if not nickname_map: logger.debug("提供的用于更新的绰号映射为空。") return - logger.info(f"尝试更新群组 '{group_id}' 的绰号计数,映射为: {nickname_map}") + logger.info(f"尝试更新群组 '{group_id}' 的绰号计数 (新结构),映射为: {nickname_map}") - for user_id, nickname in nickname_map.items(): - if not user_id or not nickname: - logger.warning(f"跳过绰号映射中的无效条目: user_id='{user_id}', nickname='{nickname}'") + for user_id_str, nickname in nickname_map.items(): # user_id 从 map 中取出是 str + if not user_id_str or not nickname: + logger.warning(f"跳过绰号映射中的无效条目: user_id='{user_id_str}', nickname='{nickname}'") continue - group_id_str = str(group_id) + group_id_str = str(group_id) # 确保 group_id 是字符串 + try: + # 假设数据库中存储的用户ID是整数类型,如果不是请移除 int() + user_id_int = int(user_id_str) + except ValueError: + logger.warning(f"无效的用户ID格式: '{user_id_str}',跳过。") + continue try: - # a. 确保用户文档存在 group_nickname 字段且为 list + # 步骤 1: 确保用户文档存在,且有 group_nicknames 字段 (如果不存在则添加空数组) + # 注意:这里不再使用 $setOnInsert 添加 group_nicknames,因为 $addToSet 或 $push 在字段不存在时会自动创建。 + # upsert=True 确保用户文档存在。 person_info_collection.update_one( - {"person_id": user_id}, - {"$setOnInsert": {"group_nickname": []}}, + {"user_id": user_id_int}, + {"$setOnInsert": {"user_id": user_id_int}}, # 确保 upsert 时 user_id 被正确设置 upsert=True ) - - # b. 确保特定 group_id 的条目存在 - update_result = person_info_collection.update_one( - {"person_id": user_id, f"group_nickname.{group_id_str}": {"$exists": False}}, - {"$push": {"group_nickname": {group_id_str: []}}} + # 确保 group_nicknames 字段存在且为数组 (如果不存在则创建) + person_info_collection.update_one( + {"user_id": user_id_int, "group_nicknames": {"$exists": False}}, + {"$set": {"group_nicknames": []}} ) - if update_result.modified_count > 0: - logger.debug(f"为用户 '{user_id}' 添加了群组 '{group_id_str}' 的条目。") - # c. 确保特定 nickname 存在于 group_id 的数组中,并增加计数 + + # 步骤 2: 尝试直接增加现有绰号的计数 + # 条件:用户存在,且 group_nicknames 数组中存在一个元素其 group_id 匹配,且该元素的 nicknames 数组中存在一个元素的 name 匹配 update_result = person_info_collection.update_one( { - "person_id": user_id, - "group_nickname": { - "$elemMatch": { - group_id_str: {"$elemMatch": {nickname: {"$exists": True}}} - } + "user_id": user_id_int, + "group_nicknames": { # <--- 确保使用 group_nicknames + "$elemMatch": {"group_id": group_id_str, "nicknames.name": nickname} } }, - {"$inc": {f"group_nickname.$[group].$[nick].{nickname}": 1}}, + { # <--- 确保使用 group_nicknames + "$inc": {"group_nicknames.$[group].nicknames.$[nick].count": 1} + }, array_filters=[ - {f"group.{group_id_str}": {"$exists": True}}, - {f"nick.{nickname}": {"$exists": True}} + {"group.group_id": group_id_str}, + {"nick.name": nickname} ] ) - if update_result.matched_count == 0: - # nickname 不存在,添加 nickname 并设置次数为 1 - add_nick_result = person_info_collection.update_one( - {"person_id": user_id, f"group_nickname.{group_id_str}": {"$exists": True}}, - {"$push": {f"group_nickname.$[group].{group_id_str}": {nickname: 1}}}, - array_filters=[{f"group.{group_id_str}": {"$exists": True}}] - ) - if add_nick_result.modified_count > 0: - logger.debug(f"为用户 '{user_id}' 在群组 '{group_id_str}' 中添加了绰号 '{nickname}',计数为 1。") - else: - logger.warning(f"未能为用户 '{user_id}' 在群组 '{group_id_str}' 中添加绰号 '{nickname}'。更新结果: {add_nick_result.raw_result}") + if update_result.modified_count > 0: + logger.debug(f"用户 '{user_id_str}' 在群组 '{group_id_str}' 中的绰号 '{nickname}' 计数已增加。") + continue # 处理完成,进行下一次循环 - elif update_result.modified_count > 0: - logger.debug(f"用户 '{user_id}' 在群组 '{group_id_str}' 中的绰号 '{nickname}' 计数已增加。") + # 步骤 3: 如果步骤 2 未修改任何内容,尝试将新绰号添加到现有群组的 nicknames 数组中 + # 条件:用户存在,且 group_nicknames 数组中存在一个元素其 group_id 匹配 + update_result = person_info_collection.update_one( + { + "user_id": user_id_int, + "group_nicknames.group_id": group_id_str # <--- 确保使用 group_nicknames + }, + { # <--- 确保使用 group_nicknames + "$push": {"group_nicknames.$[group].nicknames": {"name": nickname, "count": 1}} + }, + array_filters=[ + {"group.group_id": group_id_str} + ] + ) + + if update_result.modified_count > 0: + logger.debug(f"为用户 '{user_id_str}' 在群组 '{group_id_str}' 中添加了新绰号 '{nickname}',计数为 1。") + continue # 处理完成,进行下一次循环 + + # 步骤 4: 如果步骤 2 和 3 都未修改任何内容,说明群组条目本身可能不存在于 group_nicknames 数组中,尝试添加新的群组条目 + # 条件:用户存在,且 group_nicknames 数组中 *不包含* 指定 group_id 的元素 + update_result = person_info_collection.update_one( + { + "user_id": user_id_int, + "group_nicknames.group_id": {"$ne": group_id_str} # <--- 检查 group_id 是否不存在 + }, + { + "$push": { # <--- 确保使用 group_nicknames + "group_nicknames": { + "group_id": group_id_str, + "nicknames": [{"name": nickname, "count": 1}] + } + } + } + # 注意:这里不需要 upsert=True,因为步骤1已确保用户存在。 + # 如果字段 group_nicknames 不存在,$push 会自动创建它。 + ) + + # 记录日志(无论修改与否,因为可能是因为组已存在但无匹配导致没修改) + if update_result.modified_count > 0: + logger.debug(f"为用户 '{user_id_str}' 添加了新群组 '{group_id_str}' 条目和绰号 '{nickname}'。") else: - logger.warning(f"绰号增加操作匹配但未修改用户 '{user_id}' 的绰号 '{nickname}'。更新结果: {update_result.raw_result}") + # 到这里还没成功,可能意味着群组已存在但之前的步骤意外失败,或者有并发问题 + logger.warning(f"未能为用户 '{user_id_str}' 更新或添加群组 '{group_id_str}' 的绰号 '{nickname}'。可能群组已存在但前面的步骤未成功修改。UpdateResult: {update_result.raw_result}") + except OperationFailure as op_err: - logger.error(f"数据库操作失败: 用户 {user_id}, 群组 {group_id_str}, 绰号 {nickname}: {op_err}", exc_info=True) + # 使用 logger.exception 来记录数据库操作错误,自动包含 traceback + logger.exception(f"数据库操作失败: 用户 {user_id_str}, 群组 {group_id_str}, 绰号 {nickname}") # <--- 修改了日志记录方式 except Exception as e: - logger.error(f"更新用户 {user_id} 的绰号 {nickname} 时发生意外错误: {e}", exc_info=True) - + # 记录其他意外错误 + logger.exception(f"更新用户 {user_id_str} 的绰号 {nickname} 时发生意外错误") # <--- 修改了日志记录方式 # --- 队列和进程 --- nickname_queue: mpQueue = mpQueue(maxsize=global_config.NICKNAME_QUEUE_MAX_SIZE) diff --git a/src/plugins/heartFC_chat/heartFC_chat.py b/src/plugins/heartFC_chat/heartFC_chat.py index ca56601e..58586c21 100644 --- a/src/plugins/heartFC_chat/heartFC_chat.py +++ b/src/plugins/heartFC_chat/heartFC_chat.py @@ -582,7 +582,7 @@ class HeartFChatting: response_set=reply, send_emoji=emoji_query, ) - + print("消息发送成功,准备进入绰号分析") # --- [新增] 触发绰号分析 --- # 在发送成功后(或至少尝试发送后)触发 await self._trigger_nickname_analysis(anchor_message, reply) diff --git a/src/plugins/person_info/person_info.py b/src/plugins/person_info/person_info.py index 8bafe5eb..6114f181 100644 --- a/src/plugins/person_info/person_info.py +++ b/src/plugins/person_info/person_info.py @@ -51,6 +51,7 @@ person_info_default = { "konw_time": 0, "msg_interval": 2000, "msg_interval_list": [], + "group_nicknames": [], } # 个人信息的各项与默认值在此定义,以下处理会自动创建/补全每一项 diff --git a/src/plugins/person_info/relationship_manager.py b/src/plugins/person_info/relationship_manager.py index 7bdc02be..c8bda8f3 100644 --- a/src/plugins/person_info/relationship_manager.py +++ b/src/plugins/person_info/relationship_manager.py @@ -5,7 +5,10 @@ from bson.decimal128 import Decimal128 from .person_info import person_info_manager import time import random -from typing import List, Dict, Any, Optional, Tuple # 确保导入了 List, Dict, Optional, Tuple +from typing import List, Dict +# import re +# import traceback + logger = get_logger("relation") @@ -20,13 +23,25 @@ class RelationshipManager: def mood_manager(self): if self._mood_manager is None: from ..moods.moods import MoodManager # 延迟导入 + self._mood_manager = MoodManager.get_instance() return self._mood_manager def positive_feedback_sys(self, label: str, stance: str): """正反馈系统,通过正反馈系数增益情绪变化,根据情绪再影响关系变更""" - positive_list = ["开心", "惊讶", "害羞"] - negative_list = ["愤怒", "悲伤", "恐惧", "厌恶"] + + positive_list = [ + "开心", + "惊讶", + "害羞", + ] + + negative_list = [ + "愤怒", + "悲伤", + "恐惧", + "厌恶", + ] if label in positive_list: if 7 > self.positive_feedback_value >= 0: @@ -66,21 +81,6 @@ class RelationshipManager: is_known = person_info_manager.is_person_known(platform, user_id) return is_known - @staticmethod - async def is_qved_name(platform, user_id): - """判断是否已经命名""" - person_id = person_info_manager.get_person_id(platform, user_id) - # 优化:直接检查 person_name 字段是否存在且不为 None 或空字符串 - person_name = await person_info_manager.get_value(person_id, "person_name") - return bool(person_name) # 如果 person_name 非空则返回 True - - @staticmethod - async def get_person_name(platform: str, user_id: str) -> Optional[str]: - """获取单个用户的 person_name""" - person_id = person_info_manager.get_person_id(platform, str(user_id)) # 确保 user_id 是字符串 - return await person_info_manager.get_value(person_id, "person_name") - - # --- [新增] 批量获取用户名称 --- @staticmethod async def get_person_names_batch(platform: str, user_ids: List[str]) -> Dict[str, str]: """ @@ -186,169 +186,272 @@ class RelationshipManager: logger.error(f"Error during batch get group nicknames: {e}", exc_info=True) return nicknames_data - # --- 结束新增 --- + @staticmethod + async def is_qved_name(platform, user_id): + """判断是否认识某人""" + person_id = person_info_manager.get_person_id(platform, user_id) + is_qved = await person_info_manager.has_one_field(person_id, "person_name") + old_name = await person_info_manager.get_value(person_id, "person_name") + # print(f"old_name: {old_name}") + # print(f"is_qved: {is_qved}") + if is_qved and old_name is not None: + return True + else: + return False @staticmethod async def first_knowing_some_one(platform, user_id, user_nickname, user_cardname, user_avatar): - """初次认识某人或更新信息""" + """判断是否认识某人""" person_id = person_info_manager.get_person_id(platform, user_id) - # 首次认识时,除了更新 nickname,也应该设置初始关系值等 - initial_data = { - "platform": platform, - "user_id": user_id, - "nickname": user_nickname, - "konw_time": int(time.time()), - "relationship_value": 0.0, # 设置初始关系值为 0 - "msg_interval": -1, # 初始消息间隔设为 -1 或其他标记 - "msg_interval_list": [], - "group_nickname": [] # 初始化为空列表 - } - # 使用 update_one 并结合 $setOnInsert 来避免覆盖已有数据 - await person_info_manager.collection.update_one( - {"person_id": person_id}, - { - "$set": {"nickname": user_nickname}, # 总是更新 nickname - "$setOnInsert": initial_data # 仅在插入新文档时设置这些初始值 - }, - upsert=True - ) - # 尝试获取或生成 person_name + await person_info_manager.update_one_field(person_id, "nickname", user_nickname) + # await person_info_manager.update_one_field(person_id, "user_cardname", user_cardname) + # await person_info_manager.update_one_field(person_id, "user_avatar", user_avatar) await person_info_manager.qv_person_name(person_id, user_nickname, user_cardname, user_avatar) - async def calculate_update_relationship_value(self, chat_stream: ChatStream, label: str, stance: str) -> tuple: - """计算并变更关系值""" - stancedict = {"支持": 0, "中立": 1, "反对": 2} + """计算并变更关系值 + 新的关系值变更计算方式: + 将关系值限定在-1000到1000 + 对于关系值的变更,期望: + 1.向两端逼近时会逐渐减缓 + 2.关系越差,改善越难,关系越好,恶化越容易 + 3.人维护关系的精力往往有限,所以当高关系值用户越多,对于中高关系值用户增长越慢 + 4.连续正面或负面情感会正反馈 + + 返回: + 用户昵称,变更值,变更后关系等级 + + """ + stancedict = { + "支持": 0, + "中立": 1, + "反对": 2, + } + valuedict = { - "开心": 1.5, "愤怒": -2.0, "悲伤": -0.5, "惊讶": 0.6, "害羞": 2.0, - "平静": 0.3, "恐惧": -1.5, "厌恶": -1.0, "困惑": 0.5, + "开心": 1.5, + "愤怒": -2.0, + "悲伤": -0.5, + "惊讶": 0.6, + "害羞": 2.0, + "平静": 0.3, + "恐惧": -1.5, + "厌恶": -1.0, + "困惑": 0.5, } person_id = person_info_manager.get_person_id(chat_stream.user_info.platform, chat_stream.user_info.user_id) - data = { # 这个 data 似乎是用于 setOnInsert 的,应该在 first_knowing 时处理 + data = { "platform": chat_stream.user_info.platform, "user_id": chat_stream.user_info.user_id, "nickname": chat_stream.user_info.user_nickname, "konw_time": int(time.time()), } old_value = await person_info_manager.get_value(person_id, "relationship_value") - old_value = self.ensure_float(old_value, person_id) # 确保是 float + old_value = self.ensure_float(old_value, person_id) - # 限制旧值范围 - old_value = max(min(old_value, 1000), -1000) + if old_value > 1000: + old_value = 1000 + elif old_value < -1000: + old_value = -1000 - value_change = 0.0 # 初始化变化量 - base_value = valuedict.get(label, 0.0) # 获取基础情绪值 + value = valuedict[label] + if old_value >= 0: + if valuedict[label] >= 0 and stancedict[stance] != 2: + value = value * math.cos(math.pi * old_value / 2000) + if old_value > 500: + 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) # 排除自己 + else: + value *= 3 / (high_value_count + 3) + elif valuedict[label] < 0 and stancedict[stance] != 0: + value = value * math.exp(old_value / 2000) + else: + value = 0 + elif old_value < 0: + if valuedict[label] >= 0 and stancedict[stance] != 2: + value = value * math.exp(old_value / 2000) + elif valuedict[label] < 0 and stancedict[stance] != 0: + value = value * math.cos(math.pi * old_value / 2000) + else: + value = 0 - # 应用立场影响和关系值衰减/增强逻辑 - if base_value > 0 and stancedict.get(stance, 1) != 2: # 正面情绪且非反对 - value_change = base_value * math.cos(math.pi * old_value / 2000) - if old_value > 500: # 高关系值增长减缓 - rdict = await person_info_manager.get_specific_value_list("relationship_value", lambda x: x > 700) - high_value_count = len(rdict) - # 注意:这里的减缓因子可能需要调整 - value_change *= 3 / (high_value_count + (2 if old_value > 700 else 3)) - elif base_value < 0 and stancedict.get(stance, 1) != 0: # 负面情绪且非支持 - # 关系好时负面影响更大,关系差时负面影响减弱 - value_change = base_value * math.exp(old_value / 2000) if old_value >= 0 else base_value * math.cos(math.pi * old_value / 2000) - # else: 立场冲突或情绪平静,基础变化为 0 - - # 应用正反馈系统和情绪反馈 self.positive_feedback_sys(label, stance) - value_change = self.mood_feedback(value_change) # 应用当前情绪对关系变化的影响 - value_change = self.feedback_to_mood(value_change) # 应用连续反馈对关系变化的影响 + value = self.mood_feedback(value) - new_value = old_value + value_change - # 再次限制新值范围 - new_value = max(min(new_value, 1000), -1000) - actual_change = new_value - old_value # 记录实际变化量 - - level_num = self.calculate_level_num(new_value) + level_num = self.calculate_level_num(old_value + value) relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"] logger.info( - f"用户: {chat_stream.user_info.user_nickname} " + f"用户: {chat_stream.user_info.user_nickname}" f"当前关系: {relationship_level[level_num]}, " f"关系值: {old_value:.2f}, " - f"立场情感: {stance}-{label}, " - f"变更: {actual_change:+.5f}, " - f"新值: {new_value:.2f}" + f"当前立场情感: {stance}-{label}, " + f"变更: {value:+.5f}" ) - # 更新数据库,只更新 relationship_value - await person_info_manager.update_one_field(person_id, "relationship_value", new_value) + await person_info_manager.update_one_field(person_id, "relationship_value", old_value + value, data) - return chat_stream.user_info.user_nickname, actual_change, relationship_level[level_num] + return chat_stream.user_info.user_nickname, value, relationship_level[level_num] + async def calculate_update_relationship_value_with_reason( + self, chat_stream: ChatStream, label: str, stance: str, reason: str + ) -> tuple: + """计算并变更关系值 + 新的关系值变更计算方式: + 将关系值限定在-1000到1000 + 对于关系值的变更,期望: + 1.向两端逼近时会逐渐减缓 + 2.关系越差,改善越难,关系越好,恶化越容易 + 3.人维护关系的精力往往有限,所以当高关系值用户越多,对于中高关系值用户增长越慢 + 4.连续正面或负面情感会正反馈 + + 返回: + 用户昵称,变更值,变更后关系等级 + + """ + stancedict = { + "支持": 0, + "中立": 1, + "反对": 2, + } + + valuedict = { + "开心": 1.5, + "愤怒": -2.0, + "悲伤": -0.5, + "惊讶": 0.6, + "害羞": 2.0, + "平静": 0.3, + "恐惧": -1.5, + "厌恶": -1.0, + "困惑": 0.5, + } + + person_id = person_info_manager.get_person_id(chat_stream.user_info.platform, chat_stream.user_info.user_id) + data = { + "platform": chat_stream.user_info.platform, + "user_id": chat_stream.user_info.user_id, + "nickname": chat_stream.user_info.user_nickname, + "konw_time": int(time.time()), + } + old_value = await person_info_manager.get_value(person_id, "relationship_value") + old_value = self.ensure_float(old_value, person_id) + + if old_value > 1000: + old_value = 1000 + elif old_value < -1000: + old_value = -1000 + + value = valuedict[label] + if old_value >= 0: + if valuedict[label] >= 0 and stancedict[stance] != 2: + value = value * math.cos(math.pi * old_value / 2000) + if old_value > 500: + 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) # 排除自己 + else: + value *= 3 / (high_value_count + 3) + elif valuedict[label] < 0 and stancedict[stance] != 0: + value = value * math.exp(old_value / 2000) + else: + value = 0 + elif old_value < 0: + if valuedict[label] >= 0 and stancedict[stance] != 2: + value = value * math.exp(old_value / 2000) + elif valuedict[label] < 0 and stancedict[stance] != 0: + value = value * math.cos(math.pi * old_value / 2000) + else: + value = 0 + + self.positive_feedback_sys(label, stance) + value = self.mood_feedback(value) + + level_num = self.calculate_level_num(old_value + value) + relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"] + logger.info( + f"用户: {chat_stream.user_info.user_nickname}" + f"当前关系: {relationship_level[level_num]}, " + f"关系值: {old_value:.2f}, " + f"当前立场情感: {stance}-{label}, " + f"变更: {value:+.5f}" + ) + + await person_info_manager.update_one_field(person_id, "relationship_value", old_value + value, data) + + return chat_stream.user_info.user_nickname, value, relationship_level[level_num] async def build_relationship_info(self, person, is_id: bool = False) -> str: - """构建用于 Prompt 的关系信息字符串""" if is_id: person_id = person - # 如果只有 person_id,需要反查 platform 和 user_id 来获取 person_name - # 这依赖于 person_id 的格式,假设是 platform_userid - try: - platform, user_id_str = person_id.split("_", 1) - person_name = await self.get_person_name(platform, user_id_str) - except ValueError: - logger.warning(f"Invalid person_id format for prompt building: {person_id}") - person_name = None else: # print(f"person: {person}") person_id = person_info_manager.get_person_id(person[0], person[1]) person_name = await person_info_manager.get_value(person_id, "person_name") # print(f"person_name: {person_name}") relationship_value = await person_info_manager.get_value(person_id, "relationship_value") - relationship_value = self.ensure_float(relationship_value, person_id) # 确保是 float level_num = self.calculate_level_num(relationship_value) - # 定义关系等级和对应的行为描述 - relationship_levels = ["厌恶", "冷漠以对", "认识", "友好对待", "喜欢", "暧昧"] - relation_prompt_list = ["忽视的回应", "冷淡回复", "保持理性", "愿意回复", "积极回复", "友善和包容的回复"] - - # 根据等级和随机性决定是否输出及输出内容 - if level_num == 2: # "一般"关系不特别提示 + if level_num == 0 or level_num == 5: + relationship_level = ["厌恶", "冷漠以对", "认识", "友好对待", "喜欢", "暧昧"] + relation_prompt2_list = [ + "忽视的回应", + "冷淡回复", + "保持理性", + "愿意回复", + "积极回复", + "友善和包容的回复", + ] + return f"你{relationship_level[level_num]}{person_name},打算{relation_prompt2_list[level_num]}。\n" + elif level_num == 2: return "" - elif level_num in [0, 5] or random.random() < 0.6: # 极好/极差 或 60% 概率 - # 修正索引,确保在列表范围内 - level_idx = max(0, min(level_num, len(relationship_levels) - 1)) - prompt_idx = max(0, min(level_num, len(relation_prompt_list) - 1)) - return f"你{relationship_levels[level_idx]}{person_name},打算{relation_prompt_list[prompt_idx]}。\n" else: - return "" + if random.random() < 0.6: + relationship_level = ["厌恶", "冷漠以对", "认识", "友好对待", "喜欢", "暧昧"] + relation_prompt2_list = [ + "忽视的回应", + "冷淡回复", + "保持理性", + "愿意回复", + "积极回复", + "友善和包容的回复", + ] + return f"你{relationship_level[level_num]}{person_name},打算{relation_prompt2_list[level_num]}。\n" + else: + return "" @staticmethod def calculate_level_num(relationship_value) -> int: """关系等级计算""" - # 确保 value 是 float - try: - value = float(relationship_value.to_decimal() if isinstance(relationship_value, Decimal128) else relationship_value) - except (ValueError, TypeError, AttributeError): - value = 0.0 # 转换失败默认为 0 - - # 阈值判断 - if value < -227: return 0 - elif value < -73: return 1 - elif value < 227: return 2 - elif value < 587: return 3 - elif value < 900: return 4 - else: return 5 # >= 900 + if -1000 <= relationship_value < -227: + level_num = 0 + elif -227 <= relationship_value < -73: + level_num = 1 + elif -73 <= relationship_value < 227: + level_num = 2 + elif 227 <= relationship_value < 587: + level_num = 3 + elif 587 <= relationship_value < 900: + level_num = 4 + elif 900 <= relationship_value <= 1000: + level_num = 5 + else: + level_num = 5 if relationship_value > 1000 else 0 + return level_num @staticmethod def ensure_float(value, person_id): """确保返回浮点数,转换失败返回0.0""" - if isinstance(value, (float, int)): # 直接处理 float 和 int - return float(value) + if isinstance(value, float): + return value try: - # 尝试处理 Decimal128 或其他可转换为 float 的类型 return float(value.to_decimal() if isinstance(value, Decimal128) else value) except (ValueError, TypeError, AttributeError): - logger.warning(f"[关系管理] {person_id} 值转换失败(原始值:{value}),已重置为0") - # 在转换失败时,尝试在数据库中将该字段重置为 0.0 - try: - person_info_manager.update_one_field(person_id, "relationship_value", 0.0) - except Exception as db_err: - logger.error(f"Failed to reset relationship_value for {person_id} in DB: {db_err}") + logger.warning(f"[关系管理] {person_id}值转换失败(原始值:{value}),已重置为0") return 0.0