From 57475b475dde4bf86e7970f3fecb87b6fb40445f Mon Sep 17 00:00:00 2001 From: Windpicker-owo <3431391539@qq.com> Date: Wed, 5 Nov 2025 11:54:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(config):=20=E6=B7=BB=E5=8A=A0=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E7=BC=93=E5=AD=98=E7=B3=BB=E7=BB=9F=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=92=8C=E8=A1=A8=E8=BE=BE=E6=96=B9=E5=BC=8F=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E5=A4=A9=E6=95=B0=E8=AE=BE=E7=BD=AE=20feat(expression=5Flearne?= =?UTF-8?q?r):=20=E5=AE=9E=E7=8E=B0=E6=B8=85=E7=90=86=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E6=96=B9=E5=BC=8F=E5=8A=9F=E8=83=BD=20fix(co?= =?UTF-8?q?ntext=5Fmanager):=20=E6=A0=B9=E6=8D=AE=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=B6=88=E6=81=AF=E7=BC=93=E5=AD=98=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E5=90=AF=E7=94=A8=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/express/expression_learner.py | 107 ++++++++++++++++++-- src/chat/message_manager/context_manager.py | 6 +- src/config/official_configs.py | 8 ++ template/bot_config_template.toml | 8 +- 4 files changed, 117 insertions(+), 12 deletions(-) diff --git a/src/chat/express/expression_learner.py b/src/chat/express/expression_learner.py index 41068ae7e..e9b554322 100644 --- a/src/chat/express/expression_learner.py +++ b/src/chat/express/expression_learner.py @@ -132,6 +132,56 @@ class ExpressionLearner: self.chat_name = stream_name or self.chat_id self._chat_name_initialized = True + async def cleanup_expired_expressions(self, expiration_days: int | None = None) -> int: + """ + 清理过期的表达方式 + + Args: + expiration_days: 过期天数,超过此天数未激活的表达方式将被删除(不指定则从配置读取) + + Returns: + int: 删除的表达方式数量 + """ + # 从配置读取过期天数 + if expiration_days is None: + expiration_days = global_config.expression.expiration_days + + current_time = time.time() + expiration_threshold = current_time - (expiration_days * 24 * 3600) + + try: + deleted_count = 0 + async with get_db_session() as session: + # 查询过期的表达方式(只清理当前chat_id的) + query = await session.execute( + select(Expression).where( + (Expression.chat_id == self.chat_id) + & (Expression.last_active_time < expiration_threshold) + ) + ) + expired_expressions = list(query.scalars()) + + if expired_expressions: + for expr in expired_expressions: + await session.delete(expr) + deleted_count += 1 + + await session.commit() + logger.info(f"清理了 {deleted_count} 个过期表达方式(超过 {expiration_days} 天未使用)") + + # 清除缓存 + from src.common.database.optimization.cache_manager import get_cache + from src.common.database.utils.decorators import generate_cache_key + cache = await get_cache() + await cache.delete(generate_cache_key("chat_expressions", self.chat_id)) + else: + logger.debug(f"没有发现过期的表达方式(阈值:{expiration_days} 天)") + + return deleted_count + except Exception as e: + logger.error(f"清理过期表达方式失败: {e}") + return 0 + def can_learn_for_chat(self) -> bool: """ 检查指定聊天流是否允许学习表达 @@ -214,6 +264,9 @@ class ExpressionLearner: try: logger.info(f"为聊天流 {self.chat_name} 触发表达学习") + # 🔥 改进3:在学习前清理过期的表达方式 + await self.cleanup_expired_expressions() + # 学习语言风格 learnt_style = await self.learn_and_store(type="style", num=25) @@ -397,9 +450,29 @@ class ExpressionLearner: for chat_id, expr_list in chat_dict.items(): async with get_db_session() as session: for new_expr in expr_list: - # 查找是否已存在相似表达方式 - # 注意: get_all_by 不支持复杂条件,这里仍需使用 session - query = await session.execute( + # 🔥 改进1:检查是否存在相同情景或相同表达的数据 + # 情况1:相同 chat_id + type + situation(相同情景,不同表达) + query_same_situation = await session.execute( + select(Expression).where( + (Expression.chat_id == chat_id) + & (Expression.type == type) + & (Expression.situation == new_expr["situation"]) + ) + ) + same_situation_expr = query_same_situation.scalar() + + # 情况2:相同 chat_id + type + style(相同表达,不同情景) + query_same_style = await session.execute( + select(Expression).where( + (Expression.chat_id == chat_id) + & (Expression.type == type) + & (Expression.style == new_expr["style"]) + ) + ) + same_style_expr = query_same_style.scalar() + + # 情况3:完全相同(相同情景+相同表达) + query_exact_match = await session.execute( select(Expression).where( (Expression.chat_id == chat_id) & (Expression.type == type) @@ -407,16 +480,29 @@ class ExpressionLearner: & (Expression.style == new_expr["style"]) ) ) - existing_expr = query.scalar() - if existing_expr: - expr_obj = existing_expr - # 50%概率替换内容 - if random.random() < 0.5: - expr_obj.situation = new_expr["situation"] - expr_obj.style = new_expr["style"] + exact_match_expr = query_exact_match.scalar() + + # 优先处理完全匹配的情况 + if exact_match_expr: + # 完全相同:增加count,更新时间 + expr_obj = exact_match_expr expr_obj.count = expr_obj.count + 1 expr_obj.last_active_time = current_time + logger.debug(f"完全匹配:更新count {expr_obj.count}") + elif same_situation_expr: + # 相同情景,不同表达:覆盖旧的表达 + logger.info(f"相同情景覆盖:'{same_situation_expr.situation}' 的表达从 '{same_situation_expr.style}' 更新为 '{new_expr['style']}'") + same_situation_expr.style = new_expr["style"] + same_situation_expr.count = same_situation_expr.count + 1 + same_situation_expr.last_active_time = current_time + elif same_style_expr: + # 相同表达,不同情景:覆盖旧的情景 + logger.info(f"相同表达覆盖:'{same_style_expr.style}' 的情景从 '{same_style_expr.situation}' 更新为 '{new_expr['situation']}'") + same_style_expr.situation = new_expr["situation"] + same_style_expr.count = same_style_expr.count + 1 + same_style_expr.last_active_time = current_time else: + # 完全新的表达方式:创建新记录 new_expression = Expression( situation=new_expr["situation"], style=new_expr["style"], @@ -427,6 +513,7 @@ class ExpressionLearner: create_date=current_time, # 手动设置创建日期 ) session.add(new_expression) + logger.debug(f"新增表达方式:{new_expr['situation']} -> {new_expr['style']}") # 限制最大数量 - 使用 get_all_by_sorted 获取排序结果 exprs_result = await session.execute( diff --git a/src/chat/message_manager/context_manager.py b/src/chat/message_manager/context_manager.py index 35434128a..c4992b634 100644 --- a/src/chat/message_manager/context_manager.py +++ b/src/chat/message_manager/context_manager.py @@ -69,7 +69,11 @@ class SingleStreamContextManager: try: from .message_manager import message_manager as mm message_manager = mm - use_cache_system = message_manager.is_running + # 检查配置是否启用消息缓存系统 + cache_enabled = global_config.chat.enable_message_cache + use_cache_system = message_manager.is_running and cache_enabled + if not cache_enabled: + logger.debug(f"消息缓存系统已在配置中禁用") except Exception as e: logger.debug(f"MessageManager不可用,使用直接添加: {e}") use_cache_system = False diff --git a/src/config/official_configs.py b/src/config/official_configs.py index 94b5cd9de..71273d40b 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -120,6 +120,10 @@ class ChatConfig(ValidatedConfigBase): timestamp_display_mode: Literal["normal", "normal_no_YMD", "relative"] = Field( default="normal_no_YMD", description="时间戳显示模式" ) + # 消息缓存系统配置 + enable_message_cache: bool = Field( + default=True, description="是否启用消息缓存系统(启用后,处理中收到的消息会被缓存,处理完成后统一刷新到未读列表)" + ) # 消息打断系统配置 - 线性概率模型 interruption_enabled: bool = Field(default=True, description="是否启用消息打断系统") allow_reply_interruption: bool = Field( @@ -181,6 +185,10 @@ class ExpressionConfig(ValidatedConfigBase): default="classic", description="表达方式选择模式: classic=经典LLM评估, exp_model=机器学习模型预测" ) + expiration_days: int = Field( + default=90, + description="表达方式过期天数,超过此天数未激活的表达方式将被清理" + ) rules: list[ExpressionRule] = Field(default_factory=list, description="表达学习规则") @staticmethod diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 3ce98583d..dc0123b98 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "7.5.7" +version = "7.5.8" #----以下是给开发人员阅读的,如果你只是部署了MoFox-Bot,不需要阅读---- #如果你想要修改配置文件,请递增version的值 @@ -107,6 +107,9 @@ compress_identity = true # 是否压缩身份,压缩后会精简身份信息 # - "exp_model": 表达模型模式,使用机器学习模型预测最合适的表达 mode = "classic" +# expiration_days: 表达方式过期天数,超过此天数未激活的表达方式将被清理 +expiration_days = 3 + # rules是一个列表,每个元素都是一个学习规则 # chat_stream_id: 聊天流ID,格式为 "platform:id:type",例如 "qq:123456:private"。空字符串""表示全局配置 # use_expression: 是否使用学到的表达 (true/false) @@ -139,6 +142,9 @@ allow_reply_self = false # 是否允许回复自己说的话 max_context_size = 25 # 上下文长度 thinking_timeout = 40 # MoFox-Bot一次回复最长思考规划时间,超过这个时间的思考会放弃(往往是api反应太慢) +# 消息缓存系统配置 +enable_message_cache = true # 是否启用消息缓存系统(启用后,处理中收到的消息会被缓存,处理完成后统一刷新到未读列表) + # 消息打断系统配置 - 反比例函数概率模型 interruption_enabled = true # 是否启用消息打断系统 allow_reply_interruption = false # 是否允许在正在生成回复时打断(true=允许打断回复,false=回复期间不允许打断)