From 7d4e6870178df20ceb0165097c9afb1634198b0f Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 00:11:16 +0800 Subject: [PATCH 01/24] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E4=B8=AA=E4=BA=BA=E4=BF=A1=E6=81=AF=E7=9A=84person=5Finfo?= =?UTF-8?q?=EF=BC=8C=E5=B0=86=E5=85=B3=E7=B3=BB=E5=80=BC=E5=B9=B6=E5=85=A5?= =?UTF-8?q?=E5=85=B6=E4=B8=AD=EF=BC=8C=E9=80=9A=E8=BF=87person=5Finfo?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=AE=A1=E7=90=86=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.py | 7 +- src/plugins/chat/bot.py | 21 +-- src/plugins/chat/llm_generator.py | 5 +- src/plugins/chat/person_info.py | 214 ++++++++++++++++++++++++++++++ 4 files changed, 231 insertions(+), 16 deletions(-) create mode 100644 src/plugins/chat/person_info.py diff --git a/src/main.py b/src/main.py index f58fc0d9d..72e982ffc 100644 --- a/src/main.py +++ b/src/main.py @@ -4,7 +4,7 @@ from .plugins.utils.statistic import LLMStatistics from .plugins.moods.moods import MoodManager from .plugins.schedule.schedule_generator import bot_schedule from .plugins.chat.emoji_manager import emoji_manager -from .plugins.chat.relationship_manager import relationship_manager +from .plugins.chat.person_info import person_info_manager from .plugins.willing.willing_manager import willing_manager from .plugins.chat.chat_stream import chat_manager from .heart_flow.heartflow import heartflow @@ -55,9 +55,8 @@ class MainSystem: self.mood_manager.start_mood_update(update_interval=global_config.mood_update_interval) logger.success("情绪管理器启动成功") - # 加载用户关系 - await relationship_manager.load_all_relationships() - asyncio.create_task(relationship_manager._start_relationship_manager()) + # 检查并清除person_info冗余字段 + await person_info_manager.del_all_undefined_field() # 启动愿望管理器 await willing_manager.ensure_started() diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index ac6d4d2c9..9e01f8e3e 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -158,6 +158,10 @@ class ChatBot: else: mes_name = "私聊" + if message.message_info.additional_config: + if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): + reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] + # 打印收到的信息的信息 current_time = time.strftime("%H:%M:%S", time.localtime(messageinfo.time)) logger.info( @@ -166,10 +170,6 @@ class ChatBot: f"{message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" ) - if message.message_info.additional_config: - if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): - reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] - do_reply = False # 开始组织语言 if random() < reply_probability: @@ -191,7 +191,7 @@ class ChatBot: timing_results["思考前脑内状态"] = timer2 - timer1 timer1 = time.time() - response_set = await self.gpt.generate_response(message) + response_set, undivided_response = await self.gpt.generate_response(message) timer2 = time.time() timing_results["生成回复"] = timer2 - timer1 @@ -223,6 +223,9 @@ class ChatBot: response_msg = " ".join(response_set) if response_set else "无回复" logger.info(f"触发消息: {trigger_msg[:20]}... | 生成消息: {response_msg[:20]}... | 性能计时: {timing_str}") + # 更新情绪和关系 + await self._update_emotion_and_relationship(message, chat, undivided_response) + async def _update_using_response(self, message, response_set): # 更新心流状态 stream_id = message.chat_stream.stream_id @@ -310,17 +313,15 @@ class ChatBot: ) message_manager.add_message(bot_message) - async def _update_emotion_and_relationship(self, message, chat, response, raw_content): + async def _update_emotion_and_relationship(self, message, chat, undivided_response): """更新情绪和关系 Args: message: 接收到的消息 chat: 聊天流对象 - response: 生成的回复 - raw_content: 原始内容 + undivided_response: 生成的未分割回复 """ - stance, emotion = await self.gpt._get_emotion_tags(raw_content, message.processed_plain_text) - logger.debug(f"为 '{response}' 立场为:{stance} 获取到的情感标签为:{emotion}") + stance, emotion = await self.gpt._get_emotion_tags(undivided_response, message.processed_plain_text) await relationship_manager.calculate_update_relationship_value(chat_stream=chat, label=emotion, stance=stance) self.mood_manager.update_mood_from_emotion(emotion, global_config.mood_intensity_factor) diff --git a/src/plugins/chat/llm_generator.py b/src/plugins/chat/llm_generator.py index b0c9a59e2..44681bee3 100644 --- a/src/plugins/chat/llm_generator.py +++ b/src/plugins/chat/llm_generator.py @@ -59,6 +59,7 @@ class ResponseGenerator: current_model = self.model_normal model_response = await self._generate_response_with_model(message, current_model) + undivided_response = model_response # print(f"raw_content: {model_response}") @@ -66,10 +67,10 @@ class ResponseGenerator: logger.info(f"{global_config.BOT_NICKNAME}的回复是:{model_response}") model_response = await self._process_response(model_response) - return model_response + return model_response, undivided_response else: logger.info(f"{self.current_model_type}思考,失败") - return None + return None, None async def _generate_response_with_model(self, message: MessageThinking, model: LLM_request): sender_name = "" diff --git a/src/plugins/chat/person_info.py b/src/plugins/chat/person_info.py new file mode 100644 index 000000000..28e515971 --- /dev/null +++ b/src/plugins/chat/person_info.py @@ -0,0 +1,214 @@ +from src.common.logger import get_module_logger +from ...common.database import db +import copy +import hashlib +from typing import Any, Callable, Dict, TypeVar +T = TypeVar('T') # 泛型类型 + +""" +PersonInfoManager 类方法功能摘要: +1. get_person_id - 根据平台和用户ID生成MD5哈希的唯一person_id +2. create_person_info - 创建新个人信息文档(自动合并默认值) +3. update_one_field - 更新单个字段值(若文档不存在则创建) +4. del_one_document - 删除指定person_id的文档 +5. get_value - 获取单个字段值(返回实际值或默认值) +6. get_values - 批量获取字段值(任一字段无效则返回空字典) +7. del_all_undefined_field - 清理全集合中未定义的字段 +8. get_specific_value_list - 根据指定条件,返回person_id,value字典 +""" + +logger = get_module_logger("person_info") + +person_info_default = { + "person_id" : None, + "platform" : None, + "user_id" : None, + "nickname" : None, + # "age" : 0, + "relationship_value" : 0, + # "saved" : True, + # "impression" : None, + # "gender" : Unkown, + "konw_time" : 0, +} # 个人信息的各项与默认值在此定义,以下处理会自动创建/补全每一项 + +class PersonInfoManager: + def __init__(self): + if "person_info" not in db.list_collection_names(): + db.create_collection("person_info") + db.person_info.create_index("person_id", unique=True) + + def get_person_id(self, platform:str, user_id:int): + """获取唯一id""" + components = [platform, str(user_id)] + key = "_".join(components) + return hashlib.md5(key.encode()).hexdigest() + + async def create_person_info(self, person_id:str, data:dict = {}): + """创建一个项""" + if not person_id: + logger.debug("创建失败,personid不存在") + return + + _person_info_default = copy.deepcopy(person_info_default) + _person_info_default["person_id"] = person_id + + if data: + for key in _person_info_default: + if key != "person_id" and key in data: + _person_info_default[key] = data[key] + + db.person_info.insert_one(_person_info_default) + + async def update_one_field(self, person_id:str, field_name:str, value, Data:dict = {}): + """更新某一个字段,会补全""" + if not field_name in person_info_default.keys(): + logger.debug(f"更新'{field_name}'失败,未定义的字段") + return + + document = db.person_info.find_one({"person_id": person_id}) + + if document: + db.person_info.update_one( + {"person_id": person_id}, + {"$set": {field_name: value}} + ) + else: + Data[field_name] = value + logger.debug(f"更新时{person_id}不存在,已新建") + await self.create_person_info(person_id, Data) + + async def del_one_document(self, person_id: str): + """删除指定 person_id 的文档""" + if not person_id: + logger.debug("删除失败:person_id 不能为空") + return + + result = db.person_info.delete_one({"person_id": person_id}) + if result.deleted_count > 0: + logger.debug(f"删除成功:person_id={person_id}") + else: + logger.debug(f"删除失败:未找到 person_id={person_id}") + + async def get_value(self, person_id: str, field_name: str): + """获取指定person_id文档的字段值,若不存在该字段,则返回该字段的全局默认值""" + if not person_id: + logger.debug("get_value获取失败:person_id不能为空") + return None + + if field_name not in person_info_default: + logger.debug(f"get_value获取失败:字段'{field_name}'未定义") + return None + + document = db.person_info.find_one( + {"person_id": person_id}, + {field_name: 1} + ) + + if document and field_name in document: + return document[field_name] + else: + logger.debug(f"获取{person_id}的{field_name}失败,已返回默认值{person_info_default[field_name]}") + return person_info_default[field_name] + + async def get_values(self, person_id: str, field_names: list) -> dict: + """获取指定person_id文档的多个字段值,若不存在该字段,则返回该字段的全局默认值""" + if not person_id: + logger.debug("get_values获取失败:person_id不能为空") + return {} + + # 检查所有字段是否有效 + for field in field_names: + if field not in person_info_default: + logger.debug(f"get_values获取失败:字段'{field}'未定义") + return {} + + # 构建查询投影(所有字段都有效才会执行到这里) + projection = {field: 1 for field in field_names} + + document = db.person_info.find_one( + {"person_id": person_id}, + projection + ) + + result = {} + for field in field_names: + result[field] = document.get(field, person_info_default[field]) if document else person_info_default[field] + + return result + + async def del_all_undefined_field(self): + """删除所有项里的未定义字段""" + # 获取所有已定义的字段名 + defined_fields = set(person_info_default.keys()) + + try: + # 遍历集合中的所有文档 + for document in db.person_info.find({}): + # 找出文档中未定义的字段 + undefined_fields = set(document.keys()) - defined_fields - {'_id'} + + if undefined_fields: + # 构建更新操作,使用$unset删除未定义字段 + update_result = db.person_info.update_one( + {'_id': document['_id']}, + {'$unset': {field: 1 for field in undefined_fields}} + ) + + if update_result.modified_count > 0: + logger.debug(f"已清理文档 {document['_id']} 的未定义字段: {undefined_fields}") + + return + + except Exception as e: + logger.error(f"清理未定义字段时出错: {e}") + return + + async def get_specific_value_list( + self, + field_name: str, + way: Callable[[Any], bool], # 接受任意类型值 +) ->Dict[str, Any]: + """ + 获取满足条件的字段值字典 + + Args: + field_name: 目标字段名 + way: 判断函数 (value: Any) -> bool + convert_type: 强制类型转换(如float/int等) + + Returns: + {person_id: value} | {} + + Example: + # 查找所有nickname包含"admin"的用户 + result = manager.specific_value_list( + "nickname", + lambda x: "admin" in x.lower() + ) + """ + if field_name not in person_info_default: + logger.error(f"字段检查失败:'{field_name}'未定义") + return {} + + try: + result = {} + for doc in db.person_info.find( + {field_name: {"$exists": True}}, + {"person_id": 1, field_name: 1, "_id": 0} + ): + try: + value = doc[field_name] + if way(value): + result[doc["person_id"]] = value + except (KeyError, TypeError, ValueError) as e: + logger.debug(f"记录{doc.get('person_id')}处理失败: {str(e)}") + continue + + return result + + except Exception as e: + logger.error(f"数据库查询失败: {str(e)}", exc_info=True) + return {} + +person_info_manager = PersonInfoManager() \ No newline at end of file From 8f22612d38dc2eb0c6a7ea61de386c2b4571bb20 Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 00:32:22 +0800 Subject: [PATCH 02/24] =?UTF-8?q?=E5=A4=8D=E6=B4=BB=E5=90=A7=EF=BC=81?= =?UTF-8?q?=E6=88=91=E7=9A=84=E5=85=B3=E7=B3=BB=E7=B3=BB=E7=BB=9F=EF=BC=8C?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=AD=A3=E5=8F=8D=E9=A6=88=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/relationship_manager.py | 343 +++++------------------ 1 file changed, 76 insertions(+), 267 deletions(-) diff --git a/src/plugins/chat/relationship_manager.py b/src/plugins/chat/relationship_manager.py index 9221817c3..bb9c78353 100644 --- a/src/plugins/chat/relationship_manager.py +++ b/src/plugins/chat/relationship_manager.py @@ -1,12 +1,9 @@ -import asyncio -from typing import Optional from src.common.logger import get_module_logger, LogConfig, RELATION_STYLE_CONFIG - -from ...common.database import db -from ..message.message_base import UserInfo from .chat_stream import ChatStream import math from bson.decimal128 import Decimal128 +from .person_info import person_info_manager +import time relationship_config = LogConfig( # 使用关系专用样式 @@ -15,287 +12,89 @@ relationship_config = LogConfig( ) logger = get_module_logger("rel_manager", config=relationship_config) - -class Impression: - traits: str = None - called: str = None - know_time: float = None - - relationship_value: float = None - - -class Relationship: - user_id: int = None - platform: str = None - gender: str = None - age: int = None - nickname: str = None - relationship_value: float = None - saved = False - - def __init__(self, chat: ChatStream = None, data: dict = None): - self.user_id = chat.user_info.user_id if chat else data.get("user_id", 0) - self.platform = chat.platform if chat else data.get("platform", "") - self.nickname = chat.user_info.user_nickname if chat else data.get("nickname", "") - self.relationship_value = data.get("relationship_value", 0) if data else 0 - self.age = data.get("age", 0) if data else 0 - self.gender = data.get("gender", "") if data else "" - - class RelationshipManager: def __init__(self): - self.relationships: dict[tuple[int, str], Relationship] = {} # 修改为使用(user_id, platform)作为键 + self.positive_feedback_dict = {} # 正反馈系统 - async def update_relationship(self, chat_stream: ChatStream, data: dict = None, **kwargs) -> Optional[Relationship]: - """更新或创建关系 - Args: - chat_stream: 聊天流对象 - data: 字典格式的数据(可选) - **kwargs: 其他参数 - Returns: - Relationship: 关系对象 - """ - # 确定user_id和platform - if chat_stream.user_info is not None: - user_id = chat_stream.user_info.user_id - platform = chat_stream.user_info.platform or "qq" + def positive_feedback_sys(self, person_id, value, label: str, stance: str): + """正反馈系统""" + + positive_list = [ + "开心", + "惊讶", + "害羞", + "困惑", + ] + + negative_list = [ + "愤怒", + "悲伤", + "恐惧", + "厌恶", + ] + + if person_id not in self.positive_feedback_dict: + self.positive_feedback_dict[person_id] = 0 + + if label in positive_list and stance != "反对": + if 6 > self.positive_feedback_dict[person_id] >= 0: + self.positive_feedback_dict[person_id] += 1 + elif self.positive_feedback_dict[person_id] < 0: + self.positive_feedback_dict[person_id] = 0 + return value + elif label in negative_list and stance != "支持": + if -6 < self.positive_feedback_dict[person_id] <= 0: + self.positive_feedback_dict[person_id] -= 1 + elif self.positive_feedback_dict[person_id] > 0: + self.positive_feedback_dict[person_id] = 0 + return value else: - platform = platform or "qq" + return value - if user_id is None: - raise ValueError("必须提供user_id或user_info") + gain_coefficient = [1.1, 1.2, 1.4, 1.7, 1.9, 2.0] + value *= gain_coefficient[abs(self.positive_feedback_dict[person_id])-1] + logger.info(f"触发增益,当前增益系数:{gain_coefficient[abs(self.positive_feedback_dict[person_id])-1]}") - # 使用(user_id, platform)作为键 - key = (user_id, platform) + return value - # 检查是否在内存中已存在 - relationship = self.relationships.get(key) - if relationship: - # 如果存在,更新现有对象 - if isinstance(data, dict): - for k, value in data.items(): - if hasattr(relationship, k) and value is not None: - setattr(relationship, k, value) - else: - # 如果不存在,创建新对象 - if chat_stream.user_info is not None: - relationship = Relationship(chat=chat_stream, **kwargs) - else: - raise ValueError("必须提供user_id或user_info") - self.relationships[key] = relationship - - # 保存到数据库 - await self.storage_relationship(relationship) - relationship.saved = True - - return relationship - - async def update_relationship_value(self, chat_stream: ChatStream, **kwargs) -> Optional[Relationship]: - """更新关系值 - Args: - user_id: 用户ID(可选,如果提供user_info则不需要) - platform: 平台(可选,如果提供user_info则不需要) - user_info: 用户信息对象(可选) - **kwargs: 其他参数 - Returns: - Relationship: 关系对象 - """ - # 确定user_id和platform - user_info = chat_stream.user_info - if user_info is not None: - user_id = user_info.user_id - platform = user_info.platform or "qq" - else: - platform = platform or "qq" - - if user_id is None: - raise ValueError("必须提供user_id或user_info") - - # 使用(user_id, platform)作为键 - key = (user_id, platform) - - # 检查是否在内存中已存在 - relationship = self.relationships.get(key) - if relationship: - for k, value in kwargs.items(): - if k == "relationship_value": - # 检查relationship.relationship_value是否为double类型 - if not isinstance(relationship.relationship_value, float): - try: - # 处理 Decimal128 类型 - if isinstance(relationship.relationship_value, Decimal128): - relationship.relationship_value = float(relationship.relationship_value.to_decimal()) - else: - relationship.relationship_value = float(relationship.relationship_value) - logger.info( - f"[关系管理] 用户 {user_id}({platform}) 的关系值已转换为double类型: {relationship.relationship_value}" - ) # noqa: E501 - except (ValueError, TypeError): - # 如果不能解析/强转则将relationship.relationship_value设置为double类型的0 - relationship.relationship_value = 0.0 - logger.warning(f"[关系管理] 用户 {user_id}({platform}) 的无法转换为double类型,已设置为0") - relationship.relationship_value += value - await self.storage_relationship(relationship) - relationship.saved = True - return relationship - else: - # 如果不存在且提供了user_info,则创建新的关系 - if user_info is not None: - return await self.update_relationship(chat_stream=chat_stream, **kwargs) - logger.warning(f"[关系管理] 用户 {user_id}({platform}) 不存在,无法更新") - return None - - def get_relationship(self, chat_stream: ChatStream) -> Optional[Relationship]: - """获取用户关系对象 - Args: - user_id: 用户ID(可选,如果提供user_info则不需要) - platform: 平台(可选,如果提供user_info则不需要) - user_info: 用户信息对象(可选) - Returns: - Relationship: 关系对象 - """ - # 确定user_id和platform - user_info = chat_stream.user_info - platform = chat_stream.user_info.platform or "qq" - if user_info is not None: - user_id = user_info.user_id - platform = user_info.platform or "qq" - else: - platform = platform or "qq" - - if user_id is None: - raise ValueError("必须提供user_id或user_info") - - key = (user_id, platform) - if key in self.relationships: - return self.relationships[key] - else: - return None - - async def load_relationship(self, data: dict) -> Relationship: - """从数据库加载或创建新的关系对象""" - # 确保data中有platform字段,如果没有则默认为'qq' - if "platform" not in data: - data["platform"] = "qq" - - rela = Relationship(data=data) - rela.saved = True - key = (rela.user_id, rela.platform) - self.relationships[key] = rela - return rela - - async def load_all_relationships(self): - """加载所有关系对象""" - all_relationships = db.relationships.find({}) - for data in all_relationships: - await self.load_relationship(data) - - async def _start_relationship_manager(self): - """每5分钟自动保存一次关系数据""" - # 获取所有关系记录 - all_relationships = db.relationships.find({}) - # 依次加载每条记录 - for data in all_relationships: - await self.load_relationship(data) - logger.debug(f"[关系管理] 已加载 {len(self.relationships)} 条关系记录") - - while True: - logger.debug("正在自动保存关系") - await asyncio.sleep(300) # 等待300秒(5分钟) - await self._save_all_relationships() - - async def _save_all_relationships(self): - """将所有关系数据保存到数据库""" - # 保存所有关系数据 - for _, relationship in self.relationships.items(): - if not relationship.saved: - relationship.saved = True - await self.storage_relationship(relationship) - - async def storage_relationship(self, relationship: Relationship): - """将关系记录存储到数据库中""" - user_id = relationship.user_id - platform = relationship.platform - nickname = relationship.nickname - relationship_value = relationship.relationship_value - gender = relationship.gender - age = relationship.age - saved = relationship.saved - - db.relationships.update_one( - {"user_id": user_id, "platform": platform}, - { - "$set": { - "platform": platform, - "nickname": nickname, - "relationship_value": relationship_value, - "gender": gender, - "age": age, - "saved": saved, - } - }, - upsert=True, - ) - - def get_name(self, user_id: int = None, platform: str = None, user_info: UserInfo = None) -> str: - """获取用户昵称 - Args: - user_id: 用户ID(可选,如果提供user_info则不需要) - platform: 平台(可选,如果提供user_info则不需要) - user_info: 用户信息对象(可选) - Returns: - str: 用户昵称 - """ - # 确定user_id和platform - if user_info is not None: - user_id = user_info.user_id - platform = user_info.platform or "qq" - else: - platform = platform or "qq" - - if user_id is None: - raise ValueError("必须提供user_id或user_info") - - # 确保user_id是整数类型 - user_id = int(user_id) - key = (user_id, platform) - if key in self.relationships: - return self.relationships[key].nickname - elif user_info is not None: - return user_info.user_nickname or user_info.user_cardname or "某人" - else: - return "某人" async def calculate_update_relationship_value(self, chat_stream: ChatStream, label: str, stance: str) -> None: - """计算变更关系值 + """计算并变更关系值 新的关系值变更计算方式: 将关系值限定在-1000到1000 对于关系值的变更,期望: 1.向两端逼近时会逐渐减缓 2.关系越差,改善越难,关系越好,恶化越容易 3.人维护关系的精力往往有限,所以当高关系值用户越多,对于中高关系值用户增长越慢 + 4.连续正面或负面情感会正反馈 """ stancedict = { "支持": 0, "中立": 1, "反对": 2, } - + valuedict = { "开心": 1.5, - "愤怒": -3.5, - "悲伤": -1.5, + "愤怒": -2.0, + "悲伤": -0.5, "惊讶": 0.6, "害羞": 2.0, "平静": 0.3, - "恐惧": -2, - "厌恶": -2.5, + "恐惧": -1.5, + "厌恶": -1.0, "困惑": 0.5, } - if self.get_relationship(chat_stream): - old_value = self.get_relationship(chat_stream).relationship_value - else: - return + + 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 @@ -307,26 +106,26 @@ class RelationshipManager: if valuedict[label] >= 0 and stancedict[stance] != 2: value = value * math.cos(math.pi * old_value / 2000) if old_value > 500: - high_value_count = 0 - for _, relationship in self.relationships.items(): - if relationship.relationship_value >= 700: - high_value_count += 1 - if old_value >= 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) # 排除自己 else: value *= 3 / (high_value_count + 3) elif valuedict[label] < 0 and stancedict[stance] != 0: - value = value * math.exp(old_value / 1000) + 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 / 1000) + 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 + value = self.positive_feedback_sys(person_id, value, label, stance) + level_num = self.calculate_level_num(old_value + value) relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"] logger.info( @@ -336,10 +135,11 @@ class RelationshipManager: f"变更: {value:+.5f}" ) - await self.update_relationship_value(chat_stream=chat_stream, relationship_value=value) + await person_info_manager.update_one_field(person_id, "relationship_value", old_value + value, data) def build_relationship_info(self, person) -> str: - relationship_value = relationship_manager.get_relationship(person).relationship_value + person_id = person_info_manager.get_person_id(person.user_info.platform, person.user_info.user_id) + relationship_value = person_info_manager.get_value(person_id, "relationship_value") level_num = self.calculate_level_num(relationship_value) relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"] relation_prompt2_list = [ @@ -379,5 +179,14 @@ class RelationshipManager: level_num = 5 if relationship_value > 1000 else 0 return level_num + def ensure_float(elsf, value, person_id): + """确保返回浮点数,转换失败返回0.0""" + if isinstance(value, float): + return value + try: + return float(value.to_decimal() if isinstance(value, Decimal128) else value) + except (ValueError, TypeError, AttributeError): + logger.warning(f"[关系管理] {person_id}值转换失败(原始值:{value}),已重置为0") + return 0.0 relationship_manager = RelationshipManager() From af6f23615ebf5da1029a896ee260f7ce61da1c13 Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 01:09:05 +0800 Subject: [PATCH 03/24] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=A2=9E=E7=9B=8A?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/relationship_manager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/chat/relationship_manager.py b/src/plugins/chat/relationship_manager.py index bb9c78353..2e58cd4b9 100644 --- a/src/plugins/chat/relationship_manager.py +++ b/src/plugins/chat/relationship_manager.py @@ -23,7 +23,6 @@ class RelationshipManager: "开心", "惊讶", "害羞", - "困惑", ] negative_list = [ @@ -37,13 +36,13 @@ class RelationshipManager: self.positive_feedback_dict[person_id] = 0 if label in positive_list and stance != "反对": - if 6 > self.positive_feedback_dict[person_id] >= 0: + if 7 > self.positive_feedback_dict[person_id] >= 0: self.positive_feedback_dict[person_id] += 1 elif self.positive_feedback_dict[person_id] < 0: self.positive_feedback_dict[person_id] = 0 return value elif label in negative_list and stance != "支持": - if -6 < self.positive_feedback_dict[person_id] <= 0: + if -7 < self.positive_feedback_dict[person_id] <= 0: self.positive_feedback_dict[person_id] -= 1 elif self.positive_feedback_dict[person_id] > 0: self.positive_feedback_dict[person_id] = 0 @@ -51,9 +50,10 @@ class RelationshipManager: else: return value - gain_coefficient = [1.1, 1.2, 1.4, 1.7, 1.9, 2.0] + gain_coefficient = [1.0, 1.1, 1.2, 1.4, 1.7, 1.9, 2.0] value *= gain_coefficient[abs(self.positive_feedback_dict[person_id])-1] - logger.info(f"触发增益,当前增益系数:{gain_coefficient[abs(self.positive_feedback_dict[person_id])-1]}") + if abs(self.positive_feedback_dict[person_id]) - 1: + logger.info(f"触发增益,当前增益系数:{gain_coefficient[abs(self.positive_feedback_dict[person_id])-1]}") return value From 4cce44bcedcfe76a659ec77a6478e56a2f111419 Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 02:50:48 +0800 Subject: [PATCH 04/24] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=85=B3=E7=B3=BB?= =?UTF-8?q?=E7=9A=84prompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/prompt_builder.py | 26 +++++++++++++++++++++--- src/plugins/chat/relationship_manager.py | 23 ++++++++------------- src/plugins/chat/utils.py | 18 +++++++--------- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/plugins/chat/prompt_builder.py b/src/plugins/chat/prompt_builder.py index cc048fc70..37da5dd4f 100644 --- a/src/plugins/chat/prompt_builder.py +++ b/src/plugins/chat/prompt_builder.py @@ -7,9 +7,10 @@ from ..memory_system.Hippocampus import HippocampusManager from ..moods.moods import MoodManager from ..schedule.schedule_generator import bot_schedule from ..config.config import global_config -from .utils import get_embedding, get_recent_group_detailed_plain_text +from .utils import get_embedding, get_recent_group_detailed_plain_text, get_recent_group_speaker from .chat_stream import chat_manager from src.common.logger import get_module_logger +from .relationship_manager import relationship_manager from src.heart_flow.heartflow import heartflow @@ -29,6 +30,25 @@ class PromptBuilder: # 开始构建prompt + # 关系 + who_chat_in_group = [(chat_stream.user_info.platform, + chat_stream.user_info.user_id, + chat_stream.user_info.user_nickname)] + who_chat_in_group += get_recent_group_speaker( + stream_id, + (chat_stream.user_info.platform, chat_stream.user_info.user_id), + limit=global_config.MAX_CONTEXT_SIZE, + ) + + relation_prompt = "" + for person in who_chat_in_group: + relation_prompt += await relationship_manager.build_relationship_info(person) + + relation_prompt_all = ( + f"{relation_prompt}关系等级越大,关系越好,请分析聊天记录," + f"根据你和说话者{sender_name}的关系和态度进行回复,明确你的立场和情感。" + ) + # 心情 mood_manager = MoodManager.get_instance() mood_prompt = mood_manager.get_prompt() @@ -116,14 +136,14 @@ class PromptBuilder: {chat_talking_prompt} 你刚刚脑子里在想: {current_mind_info} -现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。\n +现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。{relation_prompt_all}\n 你的网名叫{global_config.BOT_NICKNAME},有人也叫你{"/".join(global_config.BOT_ALIAS_NAMES)},{prompt_personality}。 你正在{chat_target_2},现在请你读读之前的聊天记录,然后给出日常且口语化的回复,平淡一些, 尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger} 请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,尽量不要说你说过的话 请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。 {moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 )。""" - + return prompt def _build_initiative_prompt_select(self, group_id, probability_1=0.8, probability_2=0.1): diff --git a/src/plugins/chat/relationship_manager.py b/src/plugins/chat/relationship_manager.py index 2e58cd4b9..ddc172264 100644 --- a/src/plugins/chat/relationship_manager.py +++ b/src/plugins/chat/relationship_manager.py @@ -137,29 +137,24 @@ class RelationshipManager: await person_info_manager.update_one_field(person_id, "relationship_value", old_value + value, data) - def build_relationship_info(self, person) -> str: - person_id = person_info_manager.get_person_id(person.user_info.platform, person.user_info.user_id) - relationship_value = person_info_manager.get_value(person_id, "relationship_value") + async def build_relationship_info(self, person) -> str: + person_id = person_info_manager.get_person_id(person[0], person[1]) + relationship_value = await person_info_manager.get_value(person_id, "relationship_value") level_num = self.calculate_level_num(relationship_value) relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"] relation_prompt2_list = [ - "冷漠回应", + "厌恶回应", "冷淡回复", "保持理性", "愿意回复", "积极回复", "无条件支持", ] - if person.user_info.user_cardname: - return ( - f"你对昵称为'[({person.user_info.user_id}){person.user_info.user_nickname}]{person.user_info.user_cardname}'的用户的态度为{relationship_level[level_num]}," - f"回复态度为{relation_prompt2_list[level_num]},关系等级为{level_num}。" - ) - else: - return ( - f"你对昵称为'({person.user_info.user_id}){person.user_info.user_nickname}'的用户的态度为{relationship_level[level_num]}," - f"回复态度为{relation_prompt2_list[level_num]},关系等级为{level_num}。" - ) + + return ( + f"你对昵称为'({person[1]}){person[2]}'的用户的态度为{relationship_level[level_num]}," + f"回复态度为{relation_prompt2_list[level_num]},关系等级为{level_num}。" + ) def calculate_level_num(self, relationship_value) -> int: """关系等级计算""" diff --git a/src/plugins/chat/utils.py b/src/plugins/chat/utils.py index ecd67816a..bcf79063a 100644 --- a/src/plugins/chat/utils.py +++ b/src/plugins/chat/utils.py @@ -149,7 +149,6 @@ def get_recent_group_speaker(chat_stream_id: int, sender, limit: int = 12) -> li db.messages.find( {"chat_id": chat_stream_id}, { - "chat_info": 1, "user_info": 1, }, ) @@ -160,20 +159,17 @@ def get_recent_group_speaker(chat_stream_id: int, sender, limit: int = 12) -> li if not recent_messages: return [] - who_chat_in_group = [] # ChatStream列表 - - duplicate_removal = [] + who_chat_in_group = [] for msg_db_data in recent_messages: user_info = UserInfo.from_dict(msg_db_data["user_info"]) if ( - (user_info.user_id, user_info.platform) != sender - and (user_info.user_id, user_info.platform) != (global_config.BOT_QQ, "qq") - and (user_info.user_id, user_info.platform) not in duplicate_removal - and len(duplicate_removal) < 5 + (user_info.platform, user_info.user_id) != sender + and user_info.user_id != global_config.BOT_QQ + and (user_info.platform, user_info.user_id, user_info.user_nickname) not in who_chat_in_group + and len(who_chat_in_group) < 5 ): # 排除重复,排除消息发送者,排除bot(此处bot的平台强制为了qq,可能需要更改),限制加载的关系数目 - duplicate_removal.append((user_info.user_id, user_info.platform)) - chat_info = msg_db_data.get("chat_info", {}) - who_chat_in_group.append(ChatStream.from_dict(chat_info)) + who_chat_in_group.append((user_info.platform, user_info.user_id, user_info.user_nickname)) + return who_chat_in_group From 59043abefc68a547d7a4733cae7d79f7fa09c62f Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 04:20:53 +0800 Subject: [PATCH 05/24] =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E4=B8=BA=E5=86=B2=E7=AA=81=E5=AF=BC=E8=87=B4=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.py | 3 +-- src/plugins/__init__.py | 2 +- src/plugins/chat/__init__.py | 2 +- src/plugins/chat/utils.py | 11 ++++++++++- .../think_flow_chat/think_flow_chat.py | 15 +++++++++++++++ .../think_flow_chat/think_flow_generator.py | 2 ++ .../think_flow_chat/think_flow_prompt_builder.py | 4 ++-- src/plugins/{chat => person_info}/person_info.py | 0 .../relationship_manager.py | 2 +- 9 files changed, 33 insertions(+), 8 deletions(-) rename src/plugins/{chat => person_info}/person_info.py (100%) rename src/plugins/{relationship => person_info}/relationship_manager.py (99%) diff --git a/src/main.py b/src/main.py index 177027d4e..af2896c79 100644 --- a/src/main.py +++ b/src/main.py @@ -4,8 +4,7 @@ from .plugins.utils.statistic import LLMStatistics from .plugins.moods.moods import MoodManager from .plugins.schedule.schedule_generator import bot_schedule from .plugins.chat.emoji_manager import emoji_manager -from .plugins.chat.person_info import person_info_manager -from .plugins.relationship.relationship_manager import relationship_manager +from .plugins.person_info.person_info import person_info_manager from .plugins.willing.willing_manager import willing_manager from .plugins.chat.chat_stream import chat_manager from .heart_flow.heartflow import heartflow diff --git a/src/plugins/__init__.py b/src/plugins/__init__.py index 186245417..1bc844939 100644 --- a/src/plugins/__init__.py +++ b/src/plugins/__init__.py @@ -5,7 +5,7 @@ MaiMBot插件系统 from .chat.chat_stream import chat_manager from .chat.emoji_manager import emoji_manager -from .relationship.relationship_manager import relationship_manager +from .person_info.relationship_manager import relationship_manager from .moods.moods import MoodManager from .willing.willing_manager import willing_manager from .schedule.schedule_generator import bot_schedule diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 0f4dada44..e5cef56a5 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -1,5 +1,5 @@ from .emoji_manager import emoji_manager -from ..relationship.relationship_manager import relationship_manager +from ..person_info.relationship_manager import relationship_manager from .chat_stream import chat_manager from .message_sender import message_manager from ..storage.storage import MessageStorage diff --git a/src/plugins/chat/utils.py b/src/plugins/chat/utils.py index 188fe924b..c575eea88 100644 --- a/src/plugins/chat/utils.py +++ b/src/plugins/chat/utils.py @@ -167,7 +167,7 @@ def get_recent_group_speaker(chat_stream_id: int, sender, limit: int = 12) -> li and user_info.user_id != global_config.BOT_QQ and (user_info.platform, user_info.user_id, user_info.user_nickname) not in who_chat_in_group and len(who_chat_in_group) < 5 - ): # 排除重复,排除消息发送者,排除bot(此处bot的平台强制为了qq,可能需要更改),限制加载的关系数目 + ): # 排除重复,排除消息发送者,排除bot,限制加载的关系数目 who_chat_in_group.append((user_info.platform, user_info.user_id, user_info.user_nickname)) return who_chat_in_group @@ -345,6 +345,15 @@ def calculate_typing_time(input_string: str, chinese_time: float = 0.2, english_ - 如果只有一个中文字符,将使用3倍的中文输入时间 - 在所有输入结束后,额外加上回车时间0.3秒 """ + + # 如果输入是列表,将其连接成字符串 + if isinstance(input_string, list): + input_string = ''.join(input_string) + + # 确保现在是字符串类型 + if not isinstance(input_string, str): + input_string = str(input_string) + mood_manager = MoodManager.get_instance() # 将0-1的唤醒度映射到-1到1 mood_arousal = mood_manager.current_mood.arousal diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index f665d90fd..19247dc2c 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -17,6 +17,7 @@ from ...message import UserInfo, Seg from src.heart_flow.heartflow import heartflow from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig from ...chat.chat_stream import chat_manager +from ...person_info.relationship_manager import relationship_manager # 定义日志配置 chat_config = LogConfig( @@ -135,6 +136,14 @@ class ThinkFlowChat: await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt) + async def _update_relationship(self, message, response_set): + """更新关系""" + ori_response = ",".join(response_set) + stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text) + await relationship_manager.calculate_update_relationship_value( + chat_stream=message.chat_stream, label=emotion, stance=stance + ) + async def process_message(self, message_data: str) -> None: """处理消息并生成回复""" timing_results = {} @@ -267,6 +276,12 @@ class ThinkFlowChat: timer2 = time.time() timing_results["更新心流"] = timer2 - timer1 + # # 更新关系 + # timer1 = time.time() + # await self._update_relationship(message, response_set) + # timer2 = time.time() + # timing_results["更新关系"] = timer2 - timer1 + # 输出性能计时结果 if do_reply: timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()]) diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py index 491897bcb..107acb3c2 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py @@ -147,6 +147,8 @@ class ResponseGenerator: - 严格基于文字直接表达的对立关系判断 """ + logger.info(prompt) + # 调用模型生成结果 result, _, _ = await self.model_sum.generate_response(prompt) result = result.strip() diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py index c15990be1..cca3f0049 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py @@ -6,10 +6,10 @@ from ...memory_system.Hippocampus import HippocampusManager from ...moods.moods import MoodManager from ...schedule.schedule_generator import bot_schedule from ...config.config import global_config -from ...chat.utils import get_recent_group_detailed_plain_text +from ...chat.utils import get_recent_group_detailed_plain_text, get_recent_group_speaker from ...chat.chat_stream import chat_manager from src.common.logger import get_module_logger -from .relationship_manager import relationship_manager +from ...person_info.relationship_manager import relationship_manager from src.heart_flow.heartflow import heartflow diff --git a/src/plugins/chat/person_info.py b/src/plugins/person_info/person_info.py similarity index 100% rename from src/plugins/chat/person_info.py rename to src/plugins/person_info/person_info.py diff --git a/src/plugins/relationship/relationship_manager.py b/src/plugins/person_info/relationship_manager.py similarity index 99% rename from src/plugins/relationship/relationship_manager.py rename to src/plugins/person_info/relationship_manager.py index ddc172264..e1ca2c79d 100644 --- a/src/plugins/relationship/relationship_manager.py +++ b/src/plugins/person_info/relationship_manager.py @@ -1,5 +1,5 @@ from src.common.logger import get_module_logger, LogConfig, RELATION_STYLE_CONFIG -from .chat_stream import ChatStream +from ..chat.chat_stream import ChatStream import math from bson.decimal128 import Decimal128 from .person_info import person_info_manager From 52e363700a740101a51c413b886bbc8f4fa353b6 Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 04:35:09 +0800 Subject: [PATCH 06/24] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E9=81=97=E7=95=99?= =?UTF-8?q?=E5=86=B2=E7=AA=81=EF=BC=8C=E5=A4=8D=E6=B4=BB=E5=BF=83=E6=B5=81?= =?UTF-8?q?=E5=85=B3=E7=B3=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat_module/think_flow_chat/think_flow_chat.py | 10 +++++----- .../think_flow_chat/think_flow_generator.py | 7 ++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 19247dc2c..3bd5d7181 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -276,11 +276,11 @@ class ThinkFlowChat: timer2 = time.time() timing_results["更新心流"] = timer2 - timer1 - # # 更新关系 - # timer1 = time.time() - # await self._update_relationship(message, response_set) - # timer2 = time.time() - # timing_results["更新关系"] = timer2 - timer1 + # 更新关系 + timer1 = time.time() + await self._update_relationship(message, response_set) + timer2 = time.time() + timing_results["更新关系"] = timer2 - timer1 # 输出性能计时结果 if do_reply: diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py index 107acb3c2..d7240d9a6 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py @@ -42,7 +42,6 @@ class ResponseGenerator: current_model = self.model_normal model_response = await self._generate_response_with_model(message, current_model) - undivided_response = model_response # print(f"raw_content: {model_response}") @@ -50,10 +49,10 @@ class ResponseGenerator: logger.info(f"{global_config.BOT_NICKNAME}的回复是:{model_response}") model_response = await self._process_response(model_response) - return model_response, undivided_response + return model_response else: logger.info(f"{self.current_model_type}思考,失败") - return None, None + return None async def _generate_response_with_model(self, message: MessageThinking, model: LLM_request): sender_name = "" @@ -147,8 +146,6 @@ class ResponseGenerator: - 严格基于文字直接表达的对立关系判断 """ - logger.info(prompt) - # 调用模型生成结果 result, _, _ = await self.model_sum.generate_response(prompt) result = result.strip() From 7c51cbf027705f4b0686c670321fee9d5b3649b1 Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 11:47:37 +0800 Subject: [PATCH 07/24] =?UTF-8?q?=E6=96=B0=E5=A2=9Emood=E4=B8=8Erelation?= =?UTF-8?q?=E7=9A=84=E7=9B=B8=E4=BA=92=E5=8F=8D=E9=A6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../think_flow_chat/think_flow_chat.py | 7 ++- src/plugins/moods/moods.py | 24 ++++---- .../person_info/relationship_manager.py | 56 +++++++++++-------- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 3bd5d7181..6cbde1bac 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -137,12 +137,13 @@ class ThinkFlowChat: await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt) async def _update_relationship(self, message, response_set): - """更新关系""" + """更新关系情绪""" ori_response = ",".join(response_set) stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text) await relationship_manager.calculate_update_relationship_value( chat_stream=message.chat_stream, label=emotion, stance=stance ) + self.mood_manager.update_mood_from_emotion(emotion, global_config.mood_intensity_factor) async def process_message(self, message_data: str) -> None: """处理消息并生成回复""" @@ -276,11 +277,11 @@ class ThinkFlowChat: timer2 = time.time() timing_results["更新心流"] = timer2 - timer1 - # 更新关系 + # 更新关系情绪 timer1 = time.time() await self._update_relationship(message, response_set) timer2 = time.time() - timing_results["更新关系"] = timer2 - timer1 + timing_results["更新关系情绪"] = timer2 - timer1 # 输出性能计时结果 if do_reply: diff --git a/src/plugins/moods/moods.py b/src/plugins/moods/moods.py index 8115ee1b9..39e13b937 100644 --- a/src/plugins/moods/moods.py +++ b/src/plugins/moods/moods.py @@ -5,6 +5,7 @@ from dataclasses import dataclass from ..config.config import global_config from src.common.logger import get_module_logger, LogConfig, MOOD_STYLE_CONFIG +from ..person_info.relationship_manager import relationship_manager mood_config = LogConfig( # 使用海马体专用样式 @@ -55,15 +56,15 @@ class MoodManager: # 情绪词映射表 (valence, arousal) self.emotion_map = { - "开心": (0.8, 0.6), # 高愉悦度,中等唤醒度 - "愤怒": (-0.7, 0.7), # 负愉悦度,高唤醒度 - "悲伤": (-0.6, 0.3), # 负愉悦度,低唤醒度 - "惊讶": (0.2, 0.8), # 中等愉悦度,高唤醒度 - "害羞": (0.5, 0.2), # 中等愉悦度,低唤醒度 - "平静": (0.0, 0.5), # 中性愉悦度,中等唤醒度 - "恐惧": (-0.7, 0.6), # 负愉悦度,高唤醒度 - "厌恶": (-0.4, 0.4), # 负愉悦度,低唤醒度 - "困惑": (0.0, 0.6), # 中性愉悦度,高唤醒度 + "开心": (0.21, 0.6), + "害羞": (0.15, 0.2), + "愤怒": (-0.24, 0.8), + "恐惧": (-0.21, 0.7), + "悲伤": (-0.21, 0.3), + "厌恶": (-0.12, 0.4), + "惊讶": (0.06, 0.7), + "困惑": (0.0, 0.6), + "平静": (0.03, 0.5), } # 情绪文本映射表 @@ -93,7 +94,7 @@ class MoodManager: cls._instance = MoodManager() return cls._instance - def start_mood_update(self, update_interval: float = 1.0) -> None: + def start_mood_update(self, update_interval: float = 5.0) -> None: """ 启动情绪更新线程 :param update_interval: 更新间隔(秒) @@ -232,6 +233,8 @@ class MoodManager: valence_change, arousal_change = self.emotion_map[emotion] + valence_change *= relationship_manager.gain_coefficient[relationship_manager.positive_feedback_value] + # 应用情绪强度 valence_change *= intensity arousal_change *= intensity @@ -245,3 +248,4 @@ class MoodManager: self.current_mood.arousal = max(0.0, min(1.0, self.current_mood.arousal)) self._update_mood_text() + diff --git a/src/plugins/person_info/relationship_manager.py b/src/plugins/person_info/relationship_manager.py index e1ca2c79d..0b2f8fa67 100644 --- a/src/plugins/person_info/relationship_manager.py +++ b/src/plugins/person_info/relationship_manager.py @@ -14,10 +14,19 @@ logger = get_module_logger("rel_manager", config=relationship_config) class RelationshipManager: def __init__(self): - self.positive_feedback_dict = {} # 正反馈系统 + 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 - def positive_feedback_sys(self, person_id, value, label: str, stance: str): - """正反馈系统""" + @property + 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 = [ "开心", @@ -32,29 +41,27 @@ class RelationshipManager: "厌恶", ] - if person_id not in self.positive_feedback_dict: - self.positive_feedback_dict[person_id] = 0 - if label in positive_list and stance != "反对": - if 7 > self.positive_feedback_dict[person_id] >= 0: - self.positive_feedback_dict[person_id] += 1 - elif self.positive_feedback_dict[person_id] < 0: - self.positive_feedback_dict[person_id] = 0 - return value + 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 and stance != "支持": - if -7 < self.positive_feedback_dict[person_id] <= 0: - self.positive_feedback_dict[person_id] -= 1 - elif self.positive_feedback_dict[person_id] > 0: - self.positive_feedback_dict[person_id] = 0 - return value - else: - return value - - gain_coefficient = [1.0, 1.1, 1.2, 1.4, 1.7, 1.9, 2.0] - value *= gain_coefficient[abs(self.positive_feedback_dict[person_id])-1] - if abs(self.positive_feedback_dict[person_id]) - 1: - logger.info(f"触发增益,当前增益系数:{gain_coefficient[abs(self.positive_feedback_dict[person_id])-1]}") + 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.info(f"触发mood变更增益,当前增益系数:{self.gain_coefficient[abs(self.positive_feedback_value)]}") + def mood_feedback(self, value): + """情绪反馈""" + mood_manager = self.mood_manager + mood_gain = (mood_manager.get_current_mood().valence) ** 2 \ + * math.copysign(1, value * mood_manager.get_current_mood().valence) + value += value * mood_gain + logger.info(f"当前relationship增益系数:{mood_gain:.3f}") return value @@ -124,7 +131,8 @@ class RelationshipManager: else: value = 0 - value = self.positive_feedback_sys(person_id, value, label, stance) + self.positive_feedback_sys(label, stance) + value = self.mood_feedback(value) level_num = self.calculate_level_num(old_value + value) relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"] From 8179b7153ab66da27b1f7a6279c16a0c6effa24b Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 11:59:37 +0800 Subject: [PATCH 08/24] =?UTF-8?q?=E5=A4=8D=E6=B4=BB=E6=8E=A8=E7=90=86?= =?UTF-8?q?=E7=9A=84=E5=85=B3=E7=B3=BB=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reasoning_chat/reasoning_chat.py | 16 +++++++++++++ .../reasoning_prompt_builder.py | 24 +++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index 6ad043804..2a6561d83 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -16,6 +16,7 @@ from ...willing.willing_manager import willing_manager from ...message import UserInfo, Seg from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig from ...chat.chat_stream import chat_manager +from ...person_info.relationship_manager import relationship_manager # 定义日志配置 chat_config = LogConfig( @@ -123,6 +124,15 @@ class ReasoningChat: ) message_manager.add_message(bot_message) + async def _update_relationship(self, message, response_set): + """更新关系情绪""" + ori_response = ",".join(response_set) + stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text) + await relationship_manager.calculate_update_relationship_value( + chat_stream=message.chat_stream, label=emotion, stance=stance + ) + self.mood_manager.update_mood_from_emotion(emotion, global_config.mood_intensity_factor) + async def process_message(self, message_data: str) -> None: """处理消息并生成回复""" timing_results = {} @@ -231,6 +241,12 @@ class ReasoningChat: timer2 = time.time() timing_results["处理表情包"] = timer2 - timer1 + # 更新关系情绪 + timer1 = time.time() + await self._update_relationship(message, response_set) + timer2 = time.time() + timing_results["更新关系情绪"] = timer2 - timer1 + # 输出性能计时结果 if do_reply: timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()]) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py index 508febec8..e3015fe1e 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py @@ -7,9 +7,10 @@ from ...memory_system.Hippocampus import HippocampusManager from ...moods.moods import MoodManager from ...schedule.schedule_generator import bot_schedule from ...config.config import global_config -from ...chat.utils import get_embedding, get_recent_group_detailed_plain_text +from ...chat.utils import get_embedding, get_recent_group_detailed_plain_text, get_recent_group_speaker from ...chat.chat_stream import chat_manager from src.common.logger import get_module_logger +from ...person_info.relationship_manager import relationship_manager logger = get_module_logger("prompt") @@ -25,6 +26,25 @@ class PromptBuilder: # 开始构建prompt + # 关系 + who_chat_in_group = [(chat_stream.user_info.platform, + chat_stream.user_info.user_id, + chat_stream.user_info.user_nickname)] + who_chat_in_group += get_recent_group_speaker( + stream_id, + (chat_stream.user_info.platform, chat_stream.user_info.user_id), + limit=global_config.MAX_CONTEXT_SIZE, + ) + + relation_prompt = "" + for person in who_chat_in_group: + relation_prompt += await relationship_manager.build_relationship_info(person) + + relation_prompt_all = ( + f"{relation_prompt}关系等级越大,关系越好,请分析聊天记录," + f"根据你和说话者{sender_name}的关系和态度进行回复,明确你的立场和情感。" + ) + # 心情 mood_manager = MoodManager.get_instance() mood_prompt = mood_manager.get_prompt() @@ -127,7 +147,7 @@ class PromptBuilder: {schedule_prompt} {chat_target} {chat_talking_prompt} -现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。\n +现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。{relation_prompt_all}\n 你的网名叫{global_config.BOT_NICKNAME},有人也叫你{"/".join(global_config.BOT_ALIAS_NAMES)},{prompt_personality}。 你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},然后给出日常且口语化的回复,平淡一些, 尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger} From 82351b436c4a04549b64bc8c27ea4f131bdd8c21 Mon Sep 17 00:00:00 2001 From: meng_xi_pan <1903647908@qq.com> Date: Wed, 2 Apr 2025 12:40:08 +0800 Subject: [PATCH 09/24] ruff --- src/plugins/person_info/person_info.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plugins/person_info/person_info.py b/src/plugins/person_info/person_info.py index 28e515971..f940c0fca 100644 --- a/src/plugins/person_info/person_info.py +++ b/src/plugins/person_info/person_info.py @@ -44,7 +44,7 @@ class PersonInfoManager: key = "_".join(components) return hashlib.md5(key.encode()).hexdigest() - async def create_person_info(self, person_id:str, data:dict = {}): + async def create_person_info(self, person_id:str, data:dict = None): """创建一个项""" if not person_id: logger.debug("创建失败,personid不存在") @@ -60,9 +60,9 @@ class PersonInfoManager: db.person_info.insert_one(_person_info_default) - async def update_one_field(self, person_id:str, field_name:str, value, Data:dict = {}): + async def update_one_field(self, person_id:str, field_name:str, value, Data:dict = None): """更新某一个字段,会补全""" - if not field_name in person_info_default.keys(): + if field_name not in person_info_default.keys(): logger.debug(f"更新'{field_name}'失败,未定义的字段") return @@ -175,7 +175,6 @@ class PersonInfoManager: Args: field_name: 目标字段名 way: 判断函数 (value: Any) -> bool - convert_type: 强制类型转换(如float/int等) Returns: {person_id: value} | {} From 56f8016938b2b76f785c0ea6da86c07c7f6cffe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=A6=E6=BA=AA=E7=95=94?= <130263765+na10xi27da@users.noreply.github.com> Date: Wed, 2 Apr 2025 13:41:07 +0800 Subject: [PATCH 10/24] typo --- src/plugins/person_info/relationship_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/person_info/relationship_manager.py b/src/plugins/person_info/relationship_manager.py index 0b2f8fa67..707dbbe51 100644 --- a/src/plugins/person_info/relationship_manager.py +++ b/src/plugins/person_info/relationship_manager.py @@ -182,7 +182,7 @@ class RelationshipManager: level_num = 5 if relationship_value > 1000 else 0 return level_num - def ensure_float(elsf, value, person_id): + def ensure_float(self, value, person_id): """确保返回浮点数,转换失败返回0.0""" if isinstance(value, float): return value From 23d03ef33bf294f21108b19c1f6803b9f7386f31 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Thu, 3 Apr 2025 12:59:38 +0800 Subject: [PATCH 11/24] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E7=94=A8uv=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index fe96ac033..e3c58c6c0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ FROM python:3.13.2-slim-bookworm +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ # 工作目录 WORKDIR /MaiMBot @@ -10,8 +11,9 @@ COPY maim_message /maim_message # 安装依赖 RUN pip install --upgrade pip -RUN pip install -e /maim_message -RUN pip install --upgrade -r requirements.txt +#RUN pip install uv +RUN uv pip install -e /maim_message +RUN uv pip install --upgrade -r requirements.txt # 复制项目代码 COPY . . From b1cd3bc9440a185fc0354797398c9a29aca6b053 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Thu, 3 Apr 2025 13:01:04 +0800 Subject: [PATCH 12/24] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E7=94=A8uv=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index e3c58c6c0..eb2ddbbc7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,8 +12,8 @@ COPY maim_message /maim_message # 安装依赖 RUN pip install --upgrade pip #RUN pip install uv -RUN uv pip install -e /maim_message -RUN uv pip install --upgrade -r requirements.txt +RUN pip install -e /maim_message +RUN uv pip install -r requirements.txt # 复制项目代码 COPY . . From ebf9790f581bfcaf73a7c90e396f7c2a022b22ae Mon Sep 17 00:00:00 2001 From: infinitycat Date: Thu, 3 Apr 2025 13:03:04 +0800 Subject: [PATCH 13/24] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E7=94=A8uv=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index eb2ddbbc7..ceacbb39d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,10 +10,10 @@ COPY requirements.txt . COPY maim_message /maim_message # 安装依赖 -RUN pip install --upgrade pip +RUN uv pip install --system --upgrade pip #RUN pip install uv -RUN pip install -e /maim_message -RUN uv pip install -r requirements.txt +RUN uv pip install --system -e /maim_message +RUN uv pip install --system -r requirements.txt # 复制项目代码 COPY . . From 4920d0d41de4e4b6a52d16d8aec1e0373556b019 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Thu, 3 Apr 2025 13:23:39 +0800 Subject: [PATCH 14/24] =?UTF-8?q?=E4=BD=BF=E7=94=A8uv=E5=8A=A0=E9=80=9F?= =?UTF-8?q?=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ceacbb39d..838e2b993 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,6 @@ COPY maim_message /maim_message # 安装依赖 RUN uv pip install --system --upgrade pip -#RUN pip install uv RUN uv pip install --system -e /maim_message RUN uv pip install --system -r requirements.txt From c4ab4ff037085a4a3ccb7158a0b58269588b4b39 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Thu, 3 Apr 2025 15:03:55 +0800 Subject: [PATCH 15/24] =?UTF-8?q?ci(docker):=20=E4=BC=98=E5=8C=96=20Docker?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E6=9E=84=E5=BB=BA=E5=92=8C=E6=8E=A8=E9=80=81?= =?UTF-8?q?=E6=B5=81=E7=A8=8B-=20=E4=B8=BA=20refactor=20=E5=88=86=E6=94=AF?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=B6=E9=97=B4=E6=88=B3=E6=A0=87=E7=AD=BE?= =?UTF-8?q?=20-=20=E8=AE=BE=E7=BD=AE=20Docker=E9=95=9C=E5=83=8F=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=20-=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20OpenContainer=20=E6=A0=87=E5=87=86=E6=A0=87?= =?UTF-8?q?=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index a5a6680cd..29fd6fd44 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -13,6 +13,9 @@ on: jobs: build-and-push: runs-on: ubuntu-latest + env: + DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USERNAME }} + DATE_TAG: $(date -u +'%Y-%m-%dT%H-%M-%S') steps: - name: Checkout code uses: actions/checkout@v4 @@ -40,7 +43,7 @@ jobs: elif [ "${{ github.ref }}" == "refs/heads/main-fix" ]; then echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:main-fix" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" == "refs/heads/refactor" ]; then # 新增 refactor 分支处理 - echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:refactor" >> $GITHUB_OUTPUT + echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:refactor,${{ secrets.DOCKERHUB_USERNAME }}/maimbot:refactor$(date -u +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT fi - name: Build and Push Docker Image @@ -52,4 +55,7 @@ jobs: tags: ${{ steps.tags.outputs.tags }} push: true cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache - cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache,mode=max \ No newline at end of file + cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache,mode=max + labels: | + org.opencontainers.image.created=${{ steps.tags.outputs.date_tag }} + org.opencontainers.image.revision=${{ github.sha }} \ No newline at end of file From 06ef607e775625d987ba789eb4d07190c82cfb2c Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Thu, 3 Apr 2025 21:30:53 +0800 Subject: [PATCH 16/24] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8Dwilling?= =?UTF-8?q?=E5=92=8C=E8=A1=A8=E6=83=85=E5=8C=85=E4=B8=8D=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.py | 2 ++ src/plugins/chat/emoji_manager.py | 4 +++- src/plugins/chat_module/think_flow_chat/think_flow_chat.py | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main.py b/src/main.py index af2896c79..e3bbf38d1 100644 --- a/src/main.py +++ b/src/main.py @@ -50,6 +50,7 @@ class MainSystem: # 初始化表情管理器 emoji_manager.initialize() + logger.success("表情包管理器初始化成功") # 启动情绪管理器 self.mood_manager.start_mood_update(update_interval=global_config.mood_update_interval) @@ -106,6 +107,7 @@ class MainSystem: self.print_mood_task(), self.remove_recalled_message_task(), emoji_manager.start_periodic_check(), + emoji_manager.start_periodic_register(), self.app.run(), ] await asyncio.gather(*tasks) diff --git a/src/plugins/chat/emoji_manager.py b/src/plugins/chat/emoji_manager.py index 18a54b1ec..279dfb464 100644 --- a/src/plugins/chat/emoji_manager.py +++ b/src/plugins/chat/emoji_manager.py @@ -38,6 +38,8 @@ class EmojiManager: self.llm_emotion_judge = LLM_request( model=global_config.llm_emotion_judge, max_tokens=600, temperature=0.8, request_type="emoji" ) # 更高的温度,更少的token(后续可以根据情绪来调整温度) + + logger.info("启动表情包管理器") def _ensure_emoji_dir(self): """确保表情存储目录存在""" @@ -338,7 +340,7 @@ class EmojiManager: except Exception: logger.exception("[错误] 扫描表情包失败") - async def _periodic_scan(self): + async def start_periodic_register(self): """定期扫描新表情包""" while True: logger.info("[扫描] 开始扫描新表情包...") diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 8197af0b1..9ed08971d 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -191,7 +191,9 @@ class ThinkFlowChat: # 计算回复意愿 current_willing_old = willing_manager.get_willing(chat_stream=chat) current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4 - current_willing = (current_willing_old + current_willing_new) / 2 + # current_willing = (current_willing_old + current_willing_new) / 2 + # 有点bug + current_willing = current_willing_old willing_manager.set_willing(chat.stream_id, current_willing) From 10a72b489e295a5b5ec84881cf74d64f9ba07959 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Thu, 3 Apr 2025 22:21:32 +0800 Subject: [PATCH 17/24] =?UTF-8?q?fix=EF=BC=9A=E5=B0=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/message_sender.py | 2 ++ .../chat_module/think_flow_chat/think_flow_chat.py | 6 ++++++ .../think_flow_chat/think_flow_prompt_builder.py | 1 + src/plugins/moods/moods.py | 8 +++++++- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index a12f7320b..daba61552 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -67,6 +67,8 @@ class Message_Sender: try: end_point = global_config.api_urls.get(message.message_info.platform, None) if end_point: + # logger.info(f"发送消息到{end_point}") + # logger.info(message_json) await global_api.send_message(end_point, message_json) else: raise ValueError(f"未找到平台:{message.message_info.platform} 的url配置,请检查配置文件") diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 9ed08971d..7bf202b50 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -102,9 +102,13 @@ class ThinkFlowChat: """处理表情包""" if random() < global_config.emoji_chance: emoji_raw = await emoji_manager.get_emoji_for_text(response) + # print("11111111111111") + # logger.info(emoji_raw) if emoji_raw: emoji_path, description = emoji_raw emoji_cq = image_path_to_base64(emoji_path) + + # logger.info(emoji_cq) thinking_time_point = round(message.message_info.time, 2) @@ -123,6 +127,8 @@ class ThinkFlowChat: is_head=False, is_emoji=True, ) + + # logger.info("22222222222222") message_manager.add_message(bot_message) async def _update_using_response(self, message, response_set): diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py index cca3f0049..3cd6096e7 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py @@ -118,6 +118,7 @@ class PromptBuilder: logger.info("开始构建prompt") prompt = f""" + {relation_prompt_all}\n {chat_target} {chat_talking_prompt} 你刚刚脑子里在想: diff --git a/src/plugins/moods/moods.py b/src/plugins/moods/moods.py index 39e13b937..98fd61952 100644 --- a/src/plugins/moods/moods.py +++ b/src/plugins/moods/moods.py @@ -229,9 +229,13 @@ class MoodManager: :param intensity: 情绪强度(0.0-1.0) """ if emotion not in self.emotion_map: + logger.debug(f"[情绪更新] 未知情绪词: {emotion}") return valence_change, arousal_change = self.emotion_map[emotion] + old_valence = self.current_mood.valence + old_arousal = self.current_mood.arousal + old_mood = self.current_mood.text valence_change *= relationship_manager.gain_coefficient[relationship_manager.positive_feedback_value] @@ -246,6 +250,8 @@ class MoodManager: # 限制范围 self.current_mood.valence = max(-1.0, min(1.0, self.current_mood.valence)) self.current_mood.arousal = max(0.0, min(1.0, self.current_mood.arousal)) - + self._update_mood_text() + logger.info(f"[情绪变化] {emotion}(强度:{intensity:.2f}) | 愉悦度:{old_valence:.2f}->{self.current_mood.valence:.2f}, 唤醒度:{old_arousal:.2f}->{self.current_mood.arousal:.2f} | 心情:{old_mood}->{self.current_mood.text}") + From 62926d484c55bf504286b71cdd0e3cced3ee5309 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Thu, 3 Apr 2025 22:21:47 +0800 Subject: [PATCH 18/24] Update think_flow_chat.py --- src/plugins/chat_module/think_flow_chat/think_flow_chat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 7bf202b50..87ea1575a 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -196,7 +196,7 @@ class ThinkFlowChat: # 计算回复意愿 current_willing_old = willing_manager.get_willing(chat_stream=chat) - current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4 + # current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4 # current_willing = (current_willing_old + current_willing_new) / 2 # 有点bug current_willing = current_willing_old From b6a3b2ce21cc8bd29258dbde22fa6c75e233e69d Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Thu, 3 Apr 2025 23:01:58 +0800 Subject: [PATCH 19/24] Update think_flow_chat.py --- src/plugins/chat_module/think_flow_chat/think_flow_chat.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 87ea1575a..c5ab77b6d 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -174,15 +174,18 @@ class ThinkFlowChat: heartflow.create_subheartflow(chat.stream_id) await message.process() - + logger.debug(f"消息处理成功{message.processed_plain_text}") + # 过滤词/正则表达式过滤 if self._check_ban_words(message.processed_plain_text, chat, userinfo) or self._check_ban_regex( message.raw_message, chat, userinfo ): return + logger.debug(f"过滤词/正则表达式过滤成功{message.processed_plain_text}") await self.storage.store_message(message, chat) - + logger.debug(f"存储成功{message.processed_plain_text}") + # 记忆激活 timer1 = time.time() interested_rate = await HippocampusManager.get_instance().get_activate_from_text( From e717bc34b05978cbd3641030eaf2c32f71154da7 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Thu, 3 Apr 2025 23:03:44 +0800 Subject: [PATCH 20/24] =?UTF-8?q?=E5=BE=80debug=E5=80=92=E5=8F=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/bot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index 9046198c9..d5fc303a6 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -78,6 +78,7 @@ class ChatBot: message = MessageRecv(message_data) groupinfo = message.message_info.group_info + logger.debug(f"开始处理消息{message_data}") if global_config.enable_pfc_chatting: try: @@ -95,9 +96,11 @@ class ChatBot: await self._create_PFC_chat(message) else: if groupinfo.group_id in global_config.talk_allowed_groups: + logger.debug(f"开始群聊模式{message_data}") if global_config.response_mode == "heart_flow": await self.think_flow_chat.process_message(message_data) elif global_config.response_mode == "reasoning": + logger.debug(f"开始推理模式{message_data}") await self.reasoning_chat.process_message(message_data) else: logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}") From a92aa35e72d8be52258ee525c015b3250f537270 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 4 Apr 2025 00:30:27 +0800 Subject: [PATCH 21/24] =?UTF-8?q?better=EF=BC=9A=E5=9B=9E=E5=A4=8D?= =?UTF-8?q?=E7=8E=B0=E5=9C=A8=E4=BE=9D=E7=85=A7=E6=9D=A1=E6=95=B0=E5=92=8C?= =?UTF-8?q?=E9=95=BF=E5=BA=A6=E8=80=8C=E4=B8=8D=E6=98=AF=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelogs/changelog_dev.md | 7 +- src/heart_flow/sub_heartflow.py | 2 +- src/plugins/chat/bot.py | 84 +++++++++++----------- src/plugins/chat/message.py | 2 +- src/plugins/chat/message_sender.py | 31 +++++--- src/plugins/chat/utils.py | 105 ++++++++++++++++++++++++++++ src/plugins/message/message_base.py | 2 +- 7 files changed, 177 insertions(+), 56 deletions(-) diff --git a/changelogs/changelog_dev.md b/changelogs/changelog_dev.md index ab211c4b9..e99dc44cd 100644 --- a/changelogs/changelog_dev.md +++ b/changelogs/changelog_dev.md @@ -1,8 +1,13 @@ 这里放置了测试版本的细节更新 +## [test-0.6.0-snapshot-8] - 2025-4-3 +- 修复了表情包的注册,获取和发送逻辑 +- 更改了回复引用的逻辑,从基于时间改为基于新消息 +- 增加了调试信息 + ## [test-0.6.0-snapshot-7] - 2025-4-2 - 修改版本号命名:test-前缀为测试版,无前缀为正式版 -- 提供私聊的PFC模式 +- 提供私聊的PFC模式,可以进行有目的,自由多轮对话 ## [0.6.0-mmc-4] - 2025-4-1 - 提供两种聊天逻辑,思维流聊天(ThinkFlowChat 和 推理聊天(ReasoningChat) diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py index 5aa69a6f6..fcbe9332f 100644 --- a/src/heart_flow/sub_heartflow.py +++ b/src/heart_flow/sub_heartflow.py @@ -192,7 +192,7 @@ class SubHeartflow: logger.info(f"麦麦的思考前脑内状态:{self.current_mind}") async def do_thinking_after_reply(self, reply_content, chat_talking_prompt): - print("麦麦回复之后脑袋转起来了") + # print("麦麦回复之后脑袋转起来了") current_thinking_info = self.current_mind mood_info = self.current_state.mood diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index d5fc303a6..68afd2e76 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -75,55 +75,57 @@ class ChatBot: - 表情包处理 - 性能计时 """ - - message = MessageRecv(message_data) - groupinfo = message.message_info.group_info - logger.debug(f"开始处理消息{message_data}") + try: + message = MessageRecv(message_data) + groupinfo = message.message_info.group_info + logger.debug(f"处理消息:{str(message_data)[:50]}...") - if global_config.enable_pfc_chatting: - try: + if global_config.enable_pfc_chatting: + try: + if groupinfo is None and global_config.enable_friend_chat: + userinfo = message.message_info.user_info + messageinfo = message.message_info + # 创建聊天流 + chat = await chat_manager.get_or_create_stream( + platform=messageinfo.platform, + user_info=userinfo, + group_info=groupinfo, + ) + message.update_chat_stream(chat) + await self.only_process_chat.process_message(message) + await self._create_PFC_chat(message) + else: + if groupinfo.group_id in global_config.talk_allowed_groups: + logger.debug(f"开始群聊模式{message_data}") + if global_config.response_mode == "heart_flow": + await self.think_flow_chat.process_message(message_data) + elif global_config.response_mode == "reasoning": + logger.debug(f"开始推理模式{message_data}") + await self.reasoning_chat.process_message(message_data) + else: + logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}") + except Exception as e: + logger.error(f"处理PFC消息失败: {e}") + else: if groupinfo is None and global_config.enable_friend_chat: - userinfo = message.message_info.user_info - messageinfo = message.message_info - # 创建聊天流 - chat = await chat_manager.get_or_create_stream( - platform=messageinfo.platform, - user_info=userinfo, - group_info=groupinfo, - ) - message.update_chat_stream(chat) - await self.only_process_chat.process_message(message) - await self._create_PFC_chat(message) - else: - if groupinfo.group_id in global_config.talk_allowed_groups: - logger.debug(f"开始群聊模式{message_data}") - if global_config.response_mode == "heart_flow": - await self.think_flow_chat.process_message(message_data) - elif global_config.response_mode == "reasoning": - logger.debug(f"开始推理模式{message_data}") - await self.reasoning_chat.process_message(message_data) - else: - logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}") - except Exception as e: - logger.error(f"处理PFC消息失败: {e}") - else: - if groupinfo is None and global_config.enable_friend_chat: - # 私聊处理流程 - # await self._handle_private_chat(message) - if global_config.response_mode == "heart_flow": - await self.think_flow_chat.process_message(message_data) - elif global_config.response_mode == "reasoning": - await self.reasoning_chat.process_message(message_data) - else: - logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}") - else: # 群聊处理 - if groupinfo.group_id in global_config.talk_allowed_groups: + # 私聊处理流程 + # await self._handle_private_chat(message) if global_config.response_mode == "heart_flow": await self.think_flow_chat.process_message(message_data) elif global_config.response_mode == "reasoning": await self.reasoning_chat.process_message(message_data) else: logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}") + else: # 群聊处理 + if groupinfo.group_id in global_config.talk_allowed_groups: + if global_config.response_mode == "heart_flow": + await self.think_flow_chat.process_message(message_data) + elif global_config.response_mode == "reasoning": + await self.reasoning_chat.process_message(message_data) + else: + logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}") + except Exception as e: + logger.error(f"预处理消息失败: {e}") # 创建全局ChatBot实例 diff --git a/src/plugins/chat/message.py b/src/plugins/chat/message.py index 8427a02e1..22487831f 100644 --- a/src/plugins/chat/message.py +++ b/src/plugins/chat/message.py @@ -31,7 +31,7 @@ class Message(MessageBase): def __init__( self, message_id: str, - time: int, + time: float, chat_stream: ChatStream, user_info: UserInfo, message_segment: Optional[Seg] = None, diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index daba61552..70b5cf84d 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -9,7 +9,7 @@ from .message import MessageSending, MessageThinking, MessageSet from ..storage.storage import MessageStorage from ..config.config import global_config -from .utils import truncate_message, calculate_typing_time +from .utils import truncate_message, calculate_typing_time, count_messages_between from src.common.logger import LogConfig, SENDER_STYLE_CONFIG @@ -85,7 +85,7 @@ class MessageContainer: self.max_size = max_size self.messages = [] self.last_send_time = 0 - self.thinking_timeout = 10 # 思考等待超时时间(秒) + self.thinking_timeout = 20 # 思考等待超时时间(秒) def get_timeout_messages(self) -> List[MessageSending]: """获取所有超时的Message_Sending对象(思考时间超过30秒),按thinking_start_time排序""" @@ -172,6 +172,7 @@ class MessageManager: message_earliest = container.get_earliest_message() if isinstance(message_earliest, MessageThinking): + """取得了思考消息""" message_earliest.update_thinking_time() thinking_time = message_earliest.thinking_time # print(thinking_time) @@ -187,14 +188,18 @@ class MessageManager: container.remove_message(message_earliest) else: - # print(message_earliest.is_head) - # print(message_earliest.update_thinking_time()) - # print(message_earliest.is_private_message()) + """取得了发送消息""" thinking_time = message_earliest.update_thinking_time() - print(thinking_time) + thinking_start_time = message_earliest.thinking_start_time + now_time = time.time() + thinking_messages_count, thinking_messages_length = count_messages_between(start_time=thinking_start_time, end_time=now_time, stream_id=message_earliest.chat_stream.stream_id) + # print(thinking_time) + # print(thinking_messages_count) + # print(thinking_messages_length) + if ( message_earliest.is_head - and message_earliest.update_thinking_time() > 18 + and (thinking_messages_count > 4 or thinking_messages_length > 250) and not message_earliest.is_private_message() # 避免在私聊时插入reply ): logger.debug(f"设置回复消息{message_earliest.processed_plain_text}") @@ -216,12 +221,16 @@ class MessageManager: continue try: - # print(msg.is_head) - print(msg.update_thinking_time()) - # print(msg.is_private_message()) + thinking_time = msg.update_thinking_time() + thinking_start_time = msg.thinking_start_time + now_time = time.time() + thinking_messages_count, thinking_messages_length = count_messages_between(start_time=thinking_start_time, end_time=now_time, stream_id=msg.chat_stream.stream_id) + # print(thinking_time) + # print(thinking_messages_count) + # print(thinking_messages_length) if ( msg.is_head - and msg.update_thinking_time() > 18 + and (thinking_messages_count > 4 or thinking_messages_length > 250) and not msg.is_private_message() # 避免在私聊时插入reply ): logger.debug(f"设置回复消息{msg.processed_plain_text}") diff --git a/src/plugins/chat/utils.py b/src/plugins/chat/utils.py index c575eea88..9646fe73b 100644 --- a/src/plugins/chat/utils.py +++ b/src/plugins/chat/utils.py @@ -487,3 +487,108 @@ def is_western_char(char): def is_western_paragraph(paragraph): """检测是否为西文字符段落""" return all(is_western_char(char) for char in paragraph if char.isalnum()) + + +def count_messages_between(start_time: float, end_time: float, stream_id: str) -> tuple[int, int]: + """计算两个时间点之间的消息数量和文本总长度 + + Args: + start_time (float): 起始时间戳 + end_time (float): 结束时间戳 + stream_id (str): 聊天流ID + + Returns: + tuple[int, int]: (消息数量, 文本总长度) + - 消息数量:包含起始时间的消息,不包含结束时间的消息 + - 文本总长度:所有消息的processed_plain_text长度之和 + """ + try: + # 获取开始时间之前最新的一条消息 + start_message = db.messages.find_one( + { + "chat_id": stream_id, + "time": {"$lte": start_time} + }, + sort=[("time", -1), ("_id", -1)] # 按时间倒序,_id倒序(最后插入的在前) + ) + + # 获取结束时间最近的一条消息 + # 先找到结束时间点的所有消息 + end_time_messages = list(db.messages.find( + { + "chat_id": stream_id, + "time": {"$lte": end_time} + }, + sort=[("time", -1)] # 先按时间倒序 + ).limit(10)) # 限制查询数量,避免性能问题 + + if not end_time_messages: + logger.warning(f"未找到结束时间 {end_time} 之前的消息") + return 0, 0 + + # 找到最大时间 + max_time = end_time_messages[0]["time"] + # 在最大时间的消息中找最后插入的(_id最大的) + end_message = max( + [msg for msg in end_time_messages if msg["time"] == max_time], + key=lambda x: x["_id"] + ) + + if not start_message: + logger.warning(f"未找到开始时间 {start_time} 之前的消息") + return 0, 0 + + # 调试输出 + # print("\n=== 消息范围信息 ===") + # print("Start message:", { + # "message_id": start_message.get("message_id"), + # "time": start_message.get("time"), + # "text": start_message.get("processed_plain_text", ""), + # "_id": str(start_message.get("_id")) + # }) + # print("End message:", { + # "message_id": end_message.get("message_id"), + # "time": end_message.get("time"), + # "text": end_message.get("processed_plain_text", ""), + # "_id": str(end_message.get("_id")) + # }) + # print("Stream ID:", stream_id) + + # 如果结束消息的时间等于开始时间,返回0 + if end_message["time"] == start_message["time"]: + return 0, 0 + + # 获取并打印这个时间范围内的所有消息 + # print("\n=== 时间范围内的所有消息 ===") + all_messages = list(db.messages.find( + { + "chat_id": stream_id, + "time": { + "$gte": start_message["time"], + "$lte": end_message["time"] + } + }, + sort=[("time", 1), ("_id", 1)] # 按时间正序,_id正序 + )) + + count = 0 + total_length = 0 + for msg in all_messages: + count += 1 + text_length = len(msg.get("processed_plain_text", "")) + total_length += text_length + # print(f"\n消息 {count}:") + # print({ + # "message_id": msg.get("message_id"), + # "time": msg.get("time"), + # "text": msg.get("processed_plain_text", ""), + # "text_length": text_length, + # "_id": str(msg.get("_id")) + # }) + + # 如果时间不同,需要把end_message本身也计入 + return count - 1, total_length + + except Exception as e: + logger.error(f"计算消息数量时出错: {str(e)}") + return 0, 0 diff --git a/src/plugins/message/message_base.py b/src/plugins/message/message_base.py index ea5c3daef..edaa9a033 100644 --- a/src/plugins/message/message_base.py +++ b/src/plugins/message/message_base.py @@ -166,7 +166,7 @@ class BaseMessageInfo: platform: Optional[str] = None message_id: Union[str, int, None] = None - time: Optional[int] = None + time: Optional[float] = None group_info: Optional[GroupInfo] = None user_info: Optional[UserInfo] = None format_info: Optional[FormatInfo] = None From 6bbd94373ac49dd94cbbc586b5643d8bdb57507d Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 4 Apr 2025 00:52:44 +0800 Subject: [PATCH 22/24] =?UTF-8?q?better=EF=BC=9A=E7=8E=B0=E5=9C=A8?= =?UTF-8?q?=E4=B8=8D=E4=BC=9A=E6=97=A0=E9=99=90=E5=88=B6=E7=9A=84=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E8=A1=A8=E6=83=85=E5=8C=85=EF=BC=8C=E6=8B=A5=E6=9C=89?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/emoji_manager.py | 118 ++++++++++++++++++++++++++++- src/plugins/chat/message_sender.py | 6 +- src/plugins/config/config.py | 5 ++ template/bot_config_template.toml | 8 +- 4 files changed, 129 insertions(+), 8 deletions(-) diff --git a/src/plugins/chat/emoji_manager.py b/src/plugins/chat/emoji_manager.py index 279dfb464..6c41d9c78 100644 --- a/src/plugins/chat/emoji_manager.py +++ b/src/plugins/chat/emoji_manager.py @@ -39,12 +39,28 @@ class EmojiManager: model=global_config.llm_emotion_judge, max_tokens=600, temperature=0.8, request_type="emoji" ) # 更高的温度,更少的token(后续可以根据情绪来调整温度) + self.emoji_num = 0 + self.emoji_num_max = global_config.max_emoji_num + self.emoji_num_max_reach_deletion = global_config.max_reach_deletion + logger.info("启动表情包管理器") def _ensure_emoji_dir(self): """确保表情存储目录存在""" os.makedirs(self.EMOJI_DIR, exist_ok=True) + def _update_emoji_count(self): + """更新表情包数量统计 + + 检查数据库中的表情包数量并更新到 self.emoji_num + """ + try: + self._ensure_db() + self.emoji_num = db.emoji.count_documents({}) + logger.info(f"[统计] 当前表情包数量: {self.emoji_num}") + except Exception as e: + logger.error(f"[错误] 更新表情包数量失败: {str(e)}") + def initialize(self): """初始化数据库连接和表情目录""" if not self._initialized: @@ -52,6 +68,8 @@ class EmojiManager: self._ensure_emoji_collection() self._ensure_emoji_dir() self._initialized = True + # 更新表情包数量 + self._update_emoji_count() # 启动时执行一次完整性检查 self.check_emoji_file_integrity() except Exception: @@ -344,8 +362,19 @@ class EmojiManager: """定期扫描新表情包""" while True: logger.info("[扫描] 开始扫描新表情包...") - await self.scan_new_emojis() - await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60) + if (self.emoji_num > self.emoji_num_max): + logger.warning(f"[警告] 表情包数量超过最大限制: {self.emoji_num} > {self.emoji_num_max},跳过注册") + if not global_config.max_reach_deletion: + logger.warning(f"表情包数量超过最大限制,终止注册") + break + else: + logger.warning(f"表情包数量超过最大限制,开始删除表情包") + self.check_emoji_file_full() + else: + await self.scan_new_emojis() + await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60) + + def check_emoji_file_integrity(self): """检查表情包文件完整性 @@ -418,8 +447,93 @@ class EmojiManager: logger.error(f"[错误] 检查表情包完整性失败: {str(e)}") logger.error(traceback.format_exc()) + def check_emoji_file_full(self): + """检查表情包文件是否完整,如果数量超出限制且允许删除,则删除多余的表情包 + + 删除规则: + 1. 优先删除创建时间更早的表情包 + 2. 优先删除使用次数少的表情包,但使用次数多的也有小概率被删除 + """ + try: + self._ensure_db() + # 更新表情包数量 + self._update_emoji_count() + + # 检查是否超出限制 + if self.emoji_num <= self.emoji_num_max: + return + + # 如果超出限制但不允许删除,则只记录警告 + if not global_config.max_reach_deletion: + logger.warning(f"[警告] 表情包数量({self.emoji_num})超出限制({self.emoji_num_max}),但未开启自动删除") + return + + # 计算需要删除的数量 + delete_count = self.emoji_num - self.emoji_num_max + logger.info(f"[清理] 需要删除 {delete_count} 个表情包") + + # 获取所有表情包,按时间戳升序(旧的在前)排序 + all_emojis = list(db.emoji.find().sort([("timestamp", 1)])) + + # 计算权重:使用次数越多,被删除的概率越小 + weights = [] + max_usage = max((emoji.get("usage_count", 0) for emoji in all_emojis), default=1) + for emoji in all_emojis: + usage_count = emoji.get("usage_count", 0) + # 使用指数衰减函数计算权重,使用次数越多权重越小 + weight = 1.0 / (1.0 + usage_count / max(1, max_usage)) + weights.append(weight) + + # 根据权重随机选择要删除的表情包 + to_delete = [] + remaining_indices = list(range(len(all_emojis))) + + while len(to_delete) < delete_count and remaining_indices: + # 计算当前剩余表情包的权重 + current_weights = [weights[i] for i in remaining_indices] + # 归一化权重 + total_weight = sum(current_weights) + if total_weight == 0: + break + normalized_weights = [w/total_weight for w in current_weights] + + # 随机选择一个表情包 + selected_idx = random.choices(remaining_indices, weights=normalized_weights, k=1)[0] + to_delete.append(all_emojis[selected_idx]) + remaining_indices.remove(selected_idx) + + # 删除选中的表情包 + deleted_count = 0 + for emoji in to_delete: + try: + # 删除文件 + if "path" in emoji and os.path.exists(emoji["path"]): + os.remove(emoji["path"]) + logger.info(f"[删除] 文件: {emoji['path']} (使用次数: {emoji.get('usage_count', 0)})") + + # 删除数据库记录 + db.emoji.delete_one({"_id": emoji["_id"]}) + deleted_count += 1 + + # 同时从images集合中删除 + if "hash" in emoji: + db.images.delete_one({"hash": emoji["hash"]}) + + except Exception as e: + logger.error(f"[错误] 删除表情包失败: {str(e)}") + continue + + # 更新表情包数量 + self._update_emoji_count() + logger.success(f"[清理] 已删除 {deleted_count} 个表情包,当前数量: {self.emoji_num}") + + except Exception as e: + logger.error(f"[错误] 检查表情包数量失败: {str(e)}") + async def start_periodic_check(self): + """定期检查表情包完整性和数量""" while True: + self.check_emoji_file_full() self.check_emoji_file_integrity() await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60) diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index 70b5cf84d..9f30b63ed 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -85,16 +85,16 @@ class MessageContainer: self.max_size = max_size self.messages = [] self.last_send_time = 0 - self.thinking_timeout = 20 # 思考等待超时时间(秒) + self.thinking_wait_timeout = 20 # 思考等待超时时间(秒) def get_timeout_messages(self) -> List[MessageSending]: - """获取所有超时的Message_Sending对象(思考时间超过30秒),按thinking_start_time排序""" + """获取所有超时的Message_Sending对象(思考时间超过20秒),按thinking_start_time排序""" current_time = time.time() timeout_messages = [] for msg in self.messages: if isinstance(msg, MessageSending): - if current_time - msg.thinking_start_time > self.thinking_timeout: + if current_time - msg.thinking_start_time > self.thinking_wait_timeout: timeout_messages.append(msg) # 按thinking_start_time排序,时间早的在前面 diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index 6db225a4b..ac0e7a264 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -182,6 +182,8 @@ class BotConfig: # MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率 # emoji + max_emoji_num: int = 200 # 表情包最大数量 + max_reach_deletion: bool = True # 开启则在达到最大数量时删除表情包,关闭则不会继续收集表情包 EMOJI_CHECK_INTERVAL: int = 120 # 表情包检查间隔(分钟) EMOJI_REGISTER_INTERVAL: int = 10 # 表情包注册间隔(分钟) EMOJI_SAVE: bool = True # 偷表情包 @@ -361,6 +363,9 @@ class BotConfig: config.EMOJI_CHECK_PROMPT = emoji_config.get("check_prompt", config.EMOJI_CHECK_PROMPT) config.EMOJI_SAVE = emoji_config.get("auto_save", config.EMOJI_SAVE) config.EMOJI_CHECK = emoji_config.get("enable_check", config.EMOJI_CHECK) + if config.INNER_VERSION in SpecifierSet(">=1.1.1"): + config.max_emoji_num = emoji_config.get("max_emoji_num", config.max_emoji_num) + config.max_reach_deletion = emoji_config.get("max_reach_deletion", config.max_reach_deletion) def bot(parent: dict): # 机器人基础配置 diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 2372b10b1..3b094a8b3 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "1.1.0" +version = "1.1.1" #以下是给开发人员阅读的,一般用户不需要阅读 @@ -93,8 +93,10 @@ emoji_response_penalty = 0.1 # 表情包回复惩罚系数,设为0为不回复 [emoji] -check_interval = 15 # 检查破损表情包的时间间隔(分钟) -register_interval = 60 # 注册表情包的时间间隔(分钟) +max_emoji_num = 120 # 表情包最大数量 +max_reach_deletion = true # 开启则在达到最大数量时删除表情包,关闭则不会继续收集表情包 +check_interval = 30 # 检查破损表情包的时间间隔(分钟) +register_interval = 30 # 注册表情包的时间间隔(分钟) auto_save = true # 是否保存表情包和图片 enable_check = false # 是否启用表情包过滤 check_prompt = "符合公序良俗" # 表情包过滤要求 From cd50a15181589aa6d9f4a2e54f54746e8e0845fe Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 4 Apr 2025 00:55:25 +0800 Subject: [PATCH 23/24] =?UTF-8?q?fix=EF=BC=9A=E8=87=AA=E5=8A=A8=E6=B8=85?= =?UTF-8?q?=E7=90=86=E7=BC=93=E5=AD=98=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelogs/changelog_dev.md | 2 ++ src/plugins/chat/emoji_manager.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/changelogs/changelog_dev.md b/changelogs/changelog_dev.md index e99dc44cd..f945a237d 100644 --- a/changelogs/changelog_dev.md +++ b/changelogs/changelog_dev.md @@ -2,8 +2,10 @@ ## [test-0.6.0-snapshot-8] - 2025-4-3 - 修复了表情包的注册,获取和发送逻辑 +- 表情包增加存储上限 - 更改了回复引用的逻辑,从基于时间改为基于新消息 - 增加了调试信息 +- 自动清理缓存图片 ## [test-0.6.0-snapshot-7] - 2025-4-2 - 修改版本号命名:test-前缀为测试版,无前缀为正式版 diff --git a/src/plugins/chat/emoji_manager.py b/src/plugins/chat/emoji_manager.py index 6c41d9c78..5b3d34ec8 100644 --- a/src/plugins/chat/emoji_manager.py +++ b/src/plugins/chat/emoji_manager.py @@ -535,9 +535,36 @@ class EmojiManager: while True: self.check_emoji_file_full() self.check_emoji_file_integrity() + await self.delete_all_images() await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60) - + + async def delete_all_images(self): + """删除 data/image 目录下的所有文件""" + try: + image_dir = os.path.join("data", "image") + if not os.path.exists(image_dir): + logger.warning(f"[警告] 目录不存在: {image_dir}") + return + + deleted_count = 0 + failed_count = 0 + + # 遍历目录下的所有文件 + for filename in os.listdir(image_dir): + file_path = os.path.join(image_dir, filename) + try: + if os.path.isfile(file_path): + os.remove(file_path) + deleted_count += 1 + logger.debug(f"[删除] 文件: {file_path}") + except Exception as e: + failed_count += 1 + logger.error(f"[错误] 删除文件失败 {file_path}: {str(e)}") + + logger.success(f"[清理] 已删除 {deleted_count} 个文件,失败 {failed_count} 个") + + except Exception as e: + logger.error(f"[错误] 删除图片目录失败: {str(e)}") # 创建全局单例 - emoji_manager = EmojiManager() From 5f8ef67861ccaa7ad882302dc331f2090fe64179 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 4 Apr 2025 00:56:26 +0800 Subject: [PATCH 24/24] fix ruff --- src/plugins/chat/emoji_manager.py | 4 ++-- src/plugins/config/config.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/chat/emoji_manager.py b/src/plugins/chat/emoji_manager.py index 5b3d34ec8..02d552e54 100644 --- a/src/plugins/chat/emoji_manager.py +++ b/src/plugins/chat/emoji_manager.py @@ -365,10 +365,10 @@ class EmojiManager: if (self.emoji_num > self.emoji_num_max): logger.warning(f"[警告] 表情包数量超过最大限制: {self.emoji_num} > {self.emoji_num_max},跳过注册") if not global_config.max_reach_deletion: - logger.warning(f"表情包数量超过最大限制,终止注册") + logger.warning("表情包数量超过最大限制,终止注册") break else: - logger.warning(f"表情包数量超过最大限制,开始删除表情包") + logger.warning("表情包数量超过最大限制,开始删除表情包") self.check_emoji_file_full() else: await self.scan_new_emojis() diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index ac0e7a264..f4969df6b 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -25,7 +25,7 @@ logger = get_module_logger("config", config=config_config) #考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码 mai_version_main = "test-0.6.0" -mai_version_fix = "snapshot-7" +mai_version_fix = "snapshot-8" mai_version = f"{mai_version_main}-{mai_version_fix}" def update_config():