mirror of https://github.com/Mai-with-u/MaiBot.git
feat:记忆遗忘和动态合并阈值
parent
4d5456ed4b
commit
e0aa745ba4
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue