diff --git a/src/chat/replyer/group_generator.py b/src/chat/replyer/group_generator.py index b8c68954..457ee9eb 100644 --- a/src/chat/replyer/group_generator.py +++ b/src/chat/replyer/group_generator.py @@ -772,7 +772,7 @@ class DefaultReplyer: continue timing_logs.append(f"{chinese_name}: {duration:.1f}s") - if duration > 8: + if duration > 12: logger.warning(f"回复生成前信息获取耗时过长: {chinese_name} 耗时: {duration:.1f}s,请使用更快的模型") logger.info(f"回复准备: {'; '.join(timing_logs)}; {almost_zero_str} <0.1s") diff --git a/src/chat/replyer/private_generator.py b/src/chat/replyer/private_generator.py index 4c60be9d..8d5c7d59 100644 --- a/src/chat/replyer/private_generator.py +++ b/src/chat/replyer/private_generator.py @@ -679,7 +679,7 @@ class PrivateReplyer: continue timing_logs.append(f"{chinese_name}: {duration:.1f}s") - if duration > 8: + if duration > 12: logger.warning(f"回复生成前信息获取耗时过长: {chinese_name} 耗时: {duration:.1f}s,请使用更快的模型") logger.info(f"回复准备: {'; '.join(timing_logs)}; {almost_zero_str} <0.1s") diff --git a/src/memory_system/Memory_chest.py b/src/memory_system/Memory_chest.py index bd1d0a1c..d8efec8f 100644 --- a/src/memory_system/Memory_chest.py +++ b/src/memory_system/Memory_chest.py @@ -41,6 +41,78 @@ class MemoryChest: self.running_content_list = {} # {chat_id: {"content": running_content, "last_update_time": timestamp, "create_time": timestamp}} self.fetched_memory_list = [] # [(chat_id, (question, answer, timestamp)), ...] + def remove_one_memory_by_age_weight(self) -> bool: + """ + 删除一条记忆:按“越老/越新更易被删”的权重随机选择(老=较小id,新=较大id)。 + + 返回:是否删除成功 + """ + try: + memories = list(MemoryChestModel.select()) + if not memories: + return False + + # 排除锁定项 + candidates = [m for m in memories if not getattr(m, "locked", False)] + if not candidates: + return False + + # 按 id 排序,使用 id 近似时间顺序(小 -> 老,大 -> 新) + candidates.sort(key=lambda m: m.id) + n = len(candidates) + if n == 1: + MemoryChestModel.delete().where(MemoryChestModel.id == candidates[0].id).execute() + logger.info(f"[记忆管理] 已删除一条记忆(权重抽样):{candidates[0].title}") + return True + + # 计算U型权重:中间最低,两端最高 + # r ∈ [0,1] 为位置归一化,w = 0.1 + 0.9 * (abs(r-0.5)*2)**1.5 + weights = [] + for idx, _m in enumerate(candidates): + r = idx / (n - 1) + w = 0.1 + 0.9 * (abs(r - 0.5) * 2) ** 1.5 + weights.append(w) + + import random as _random + selected = _random.choices(candidates, weights=weights, k=1)[0] + + MemoryChestModel.delete().where(MemoryChestModel.id == selected.id).execute() + logger.info(f"[记忆管理] 已删除一条记忆(权重抽样):{selected.title}") + return True + except Exception as e: + logger.error(f"[记忆管理] 按年龄权重删除记忆时出错: {e}") + return False + + def _compute_merge_similarity_threshold(self) -> float: + """ + 根据当前记忆数量占比动态计算合并相似度阈值。 + + 规则:占比越高,阈值越低。 + - < 60%: 0.80(更严格,避免早期误合并) + - < 80%: 0.70 + - < 100%: 0.60 + - < 120%: 0.50 + - >= 120%: 0.45(最宽松,加速收敛) + """ + try: + current_count = MemoryChestModel.select().count() + max_count = max(1, int(global_config.memory.max_memory_number)) + percentage = current_count / max_count + + if percentage < 0.6: + return 0.70 + elif percentage < 0.8: + return 0.60 + elif percentage < 1.0: + return 0.50 + elif percentage < 1.2: + return 0.40 + else: + return 0.35 + except Exception: + # 发生异常时使用保守阈值 + return 0.70 + async def build_running_content(self, chat_id: str = None) -> str: """ 构建记忆仓库的运行内容 @@ -446,19 +518,22 @@ class MemoryChest: logger.warning("未提供chat_id,无法进行记忆匹配") return [], [] - # 使用相似度匹配查找最相似的记忆 + # 动态计算相似度阈值(占比越高阈值越低) + dynamic_threshold = self._compute_merge_similarity_threshold() + + # 使用相似度匹配查找最相似的记忆(基于动态阈值) similar_memory = find_most_similar_memory_by_chat_id( target_title=memory_title, target_chat_id=chat_id, - similarity_threshold=0.5 # 相似度阈值 + similarity_threshold=dynamic_threshold ) if similar_memory: selected_title, selected_content, similarity = similar_memory - logger.info(f"为 '{memory_title}' 找到相似记忆: '{selected_title}' (相似度: {similarity:.3f})") + logger.info(f"为 '{memory_title}' 找到相似记忆: '{selected_title}' (相似度: {similarity:.3f} 阈值: {dynamic_threshold:.2f})") return [selected_title], [selected_content] else: - logger.info(f"为 '{memory_title}' 未找到相似度 >= 0.7 的记忆") + logger.info(f"为 '{memory_title}' 未找到相似度 >= {dynamic_threshold:.2f} 的记忆") return [], [] except Exception as e: diff --git a/src/memory_system/memory_management_task.py b/src/memory_system/memory_management_task.py index d750c1e5..a9212862 100644 --- a/src/memory_system/memory_management_task.py +++ b/src/memory_system/memory_management_task.py @@ -8,7 +8,6 @@ from src.memory_system.Memory_chest import global_memory_chest from src.common.logger import get_logger from src.common.database.database_model import MemoryChest as MemoryChestModel from src.config.config import global_config -from src.memory_system.memory_utils import get_all_titles logger = get_logger("memory") @@ -56,14 +55,14 @@ class MemoryManagementTask(AsyncTask): current_count = self._get_memory_count() percentage = current_count / self.max_memory_number - if percentage < 0.5: + if percentage < 0.6: # 小于50%,每600秒执行一次 return 3600 - elif percentage < 0.7: + elif percentage < 0.8: # 大于等于50%,每300秒执行一次 return 1800 - elif percentage < 0.9: - # 大于等于70%,每120秒执行一次 + elif percentage < 1.0: + # 大于等于100%,每120秒执行一次 return 300 elif percentage < 1.2: return 30 @@ -93,6 +92,22 @@ class MemoryManagementTask(AsyncTask): percentage = current_count / self.max_memory_number logger.info(f"当前记忆数量: {current_count}/{self.max_memory_number} ({percentage:.1%})") + # 当占比 > 1.6 时,持续删除直到占比 <= 1.6(越老/越新更易被删) + if percentage > 1.6: + logger.info("记忆过多,开始遗忘记忆") + while True: + if percentage <= 1.6: + break + removed = global_memory_chest.remove_one_memory_by_age_weight() + if not removed: + logger.warning("没有可删除的记忆,停止连续删除") + break + # 重新计算占比 + current_count = self._get_memory_count() + percentage = current_count / self.max_memory_number + logger.info(f"遗忘进度: 当前 {current_count}/{self.max_memory_number} ({percentage:.1%})") + logger.info("遗忘记忆结束") + # 如果记忆数量为0,跳过执行 if current_count < 10: return