Merge branch 'dev' of https://github.com/MaiM-with-u/MaiBot into dev
This commit is contained in:
@@ -234,7 +234,7 @@ class BotConfig:
|
|||||||
forget_memory_interval: int = 600 # 记忆遗忘间隔(秒)
|
forget_memory_interval: int = 600 # 记忆遗忘间隔(秒)
|
||||||
memory_forget_time: int = 24 # 记忆遗忘时间(小时)
|
memory_forget_time: int = 24 # 记忆遗忘时间(小时)
|
||||||
memory_forget_percentage: float = 0.01 # 记忆遗忘比例
|
memory_forget_percentage: float = 0.01 # 记忆遗忘比例
|
||||||
|
|
||||||
consolidate_memory_interval: int = 1000 # 记忆整合间隔(秒)
|
consolidate_memory_interval: int = 1000 # 记忆整合间隔(秒)
|
||||||
consolidation_similarity_threshold: float = 0.7 # 相似度阈值
|
consolidation_similarity_threshold: float = 0.7 # 相似度阈值
|
||||||
consolidate_memory_percentage: float = 0.01 # 检查节点比例
|
consolidate_memory_percentage: float = 0.01 # 检查节点比例
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ class MainSystem:
|
|||||||
print("\033[1;32m[记忆遗忘]\033[0m 开始遗忘记忆...")
|
print("\033[1;32m[记忆遗忘]\033[0m 开始遗忘记忆...")
|
||||||
await HippocampusManager.get_instance().forget_memory(percentage=global_config.memory_forget_percentage)
|
await HippocampusManager.get_instance().forget_memory(percentage=global_config.memory_forget_percentage)
|
||||||
print("\033[1;32m[记忆遗忘]\033[0m 记忆遗忘完成")
|
print("\033[1;32m[记忆遗忘]\033[0m 记忆遗忘完成")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def consolidate_memory_task():
|
async def consolidate_memory_task():
|
||||||
"""记忆整合任务"""
|
"""记忆整合任务"""
|
||||||
|
|||||||
@@ -93,8 +93,7 @@ class ActionPlanner:
|
|||||||
max_tokens=1500,
|
max_tokens=1500,
|
||||||
request_type="action_planning",
|
request_type="action_planning",
|
||||||
)
|
)
|
||||||
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=3)
|
self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3)
|
||||||
self.identity_detail_info = Individuality.get_instance().get_prompt(type="identity", x_person=2, level=2)
|
|
||||||
self.name = global_config.BOT_NICKNAME
|
self.name = global_config.BOT_NICKNAME
|
||||||
self.private_name = private_name
|
self.private_name = private_name
|
||||||
self.chat_observer = ChatObserver.get_instance(stream_id, private_name)
|
self.chat_observer = ChatObserver.get_instance(stream_id, private_name)
|
||||||
@@ -244,21 +243,7 @@ class ActionPlanner:
|
|||||||
chat_history_text = "处理聊天记录时出错。\n"
|
chat_history_text = "处理聊天记录时出错。\n"
|
||||||
|
|
||||||
# 构建 Persona 文本 (persona_text)
|
# 构建 Persona 文本 (persona_text)
|
||||||
# (这部分逻辑不变)
|
persona_text = f"你的名字是{self.name},{self.personality_info}。"
|
||||||
identity_details_only = self.identity_detail_info
|
|
||||||
identity_addon = ""
|
|
||||||
if isinstance(identity_details_only, str):
|
|
||||||
pronouns = ["你", "我", "他"]
|
|
||||||
for p in pronouns:
|
|
||||||
if identity_details_only.startswith(p):
|
|
||||||
identity_details_only = identity_details_only[len(p) :]
|
|
||||||
break
|
|
||||||
if identity_details_only.endswith("。"):
|
|
||||||
identity_details_only = identity_details_only[:-1]
|
|
||||||
cleaned_details = identity_details_only.strip(",, ")
|
|
||||||
if cleaned_details:
|
|
||||||
identity_addon = f"并且{cleaned_details}"
|
|
||||||
persona_text = f"你的名字是{self.name},{self.personality_info}{identity_addon}。"
|
|
||||||
|
|
||||||
# 构建行动历史和上一次行动结果 (action_history_summary, last_action_context)
|
# 构建行动历史和上一次行动结果 (action_history_summary, last_action_context)
|
||||||
# (这部分逻辑不变)
|
# (这部分逻辑不变)
|
||||||
|
|||||||
@@ -368,6 +368,15 @@ class Conversation:
|
|||||||
self.conversation_info.last_successful_reply_action = "send_new_message"
|
self.conversation_info.last_successful_reply_action = "send_new_message"
|
||||||
action_successful = True # 标记动作成功
|
action_successful = True # 标记动作成功
|
||||||
|
|
||||||
|
elif need_replan:
|
||||||
|
# 打回动作决策
|
||||||
|
logger.warning(
|
||||||
|
f"[私聊][{self.private_name}]经过 {reply_attempt_count} 次尝试,追问回复决定打回动作决策。打回原因: {check_reason}"
|
||||||
|
)
|
||||||
|
conversation_info.done_action[action_index].update(
|
||||||
|
{"status": "recall", "final_reason": f"追问尝试{reply_attempt_count}次后打回: {check_reason}"}
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 追问失败
|
# 追问失败
|
||||||
logger.warning(
|
logger.warning(
|
||||||
@@ -463,6 +472,15 @@ class Conversation:
|
|||||||
self.conversation_info.last_successful_reply_action = "direct_reply"
|
self.conversation_info.last_successful_reply_action = "direct_reply"
|
||||||
action_successful = True # 标记动作成功
|
action_successful = True # 标记动作成功
|
||||||
|
|
||||||
|
elif need_replan:
|
||||||
|
# 打回动作决策
|
||||||
|
logger.warning(
|
||||||
|
f"[私聊][{self.private_name}]经过 {reply_attempt_count} 次尝试,首次回复决定打回动作决策。打回原因: {check_reason}"
|
||||||
|
)
|
||||||
|
conversation_info.done_action[action_index].update(
|
||||||
|
{"status": "recall", "final_reason": f"首次回复尝试{reply_attempt_count}次后打回: {check_reason}"}
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 首次回复失败
|
# 首次回复失败
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ class GoalAnalyzer:
|
|||||||
model=global_config.llm_normal, temperature=0.7, max_tokens=1000, request_type="conversation_goal"
|
model=global_config.llm_normal, temperature=0.7, max_tokens=1000, request_type="conversation_goal"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=3)
|
self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3)
|
||||||
self.identity_detail_info = Individuality.get_instance().get_prompt(type="identity", x_person=2, level=2)
|
|
||||||
self.name = global_config.BOT_NICKNAME
|
self.name = global_config.BOT_NICKNAME
|
||||||
self.nick_name = global_config.BOT_ALIAS_NAMES
|
self.nick_name = global_config.BOT_ALIAS_NAMES
|
||||||
self.private_name = private_name
|
self.private_name = private_name
|
||||||
@@ -79,21 +78,7 @@ class GoalAnalyzer:
|
|||||||
|
|
||||||
# await observation_info.clear_unprocessed_messages()
|
# await observation_info.clear_unprocessed_messages()
|
||||||
|
|
||||||
identity_details_only = self.identity_detail_info
|
persona_text = f"你的名字是{self.name},{self.personality_info}。"
|
||||||
identity_addon = ""
|
|
||||||
if isinstance(identity_details_only, str):
|
|
||||||
pronouns = ["你", "我", "他"]
|
|
||||||
for p in pronouns:
|
|
||||||
if identity_details_only.startswith(p):
|
|
||||||
identity_details_only = identity_details_only[len(p) :]
|
|
||||||
break
|
|
||||||
if identity_details_only.endswith("。"):
|
|
||||||
identity_details_only = identity_details_only[:-1]
|
|
||||||
cleaned_details = identity_details_only.strip(",, ")
|
|
||||||
if cleaned_details:
|
|
||||||
identity_addon = f"并且{cleaned_details}"
|
|
||||||
|
|
||||||
persona_text = f"你的名字是{self.name},{self.personality_info}{identity_addon}。"
|
|
||||||
# 构建action历史文本
|
# 构建action历史文本
|
||||||
action_history_list = conversation_info.done_action
|
action_history_list = conversation_info.done_action
|
||||||
action_history_text = "你之前做的事情是:"
|
action_history_text = "你之前做的事情是:"
|
||||||
@@ -241,21 +226,8 @@ class GoalAnalyzer:
|
|||||||
timestamp_mode="relative",
|
timestamp_mode="relative",
|
||||||
read_mark=0.0,
|
read_mark=0.0,
|
||||||
)
|
)
|
||||||
identity_details_only = self.identity_detail_info
|
|
||||||
identity_addon = ""
|
|
||||||
if isinstance(identity_details_only, str):
|
|
||||||
pronouns = ["你", "我", "他"]
|
|
||||||
for p in pronouns:
|
|
||||||
if identity_details_only.startswith(p):
|
|
||||||
identity_details_only = identity_details_only[len(p) :]
|
|
||||||
break
|
|
||||||
if identity_details_only.endswith("。"):
|
|
||||||
identity_details_only = identity_details_only[:-1]
|
|
||||||
cleaned_details = identity_details_only.strip(",, ")
|
|
||||||
if cleaned_details:
|
|
||||||
identity_addon = f"并且{cleaned_details}"
|
|
||||||
|
|
||||||
persona_text = f"你的名字是{self.name},{self.personality_info}{identity_addon}。"
|
persona_text = f"你的名字是{self.name},{self.personality_info}。"
|
||||||
# ===> Persona 文本构建结束 <===
|
# ===> Persona 文本构建结束 <===
|
||||||
|
|
||||||
# --- 修改 Prompt 字符串,使用 persona_text ---
|
# --- 修改 Prompt 字符串,使用 persona_text ---
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class ReplyChecker:
|
|||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
False,
|
False,
|
||||||
"回复内容与你上一条发言完全相同,请修改,可以选择深入话题或寻找其它话题或等待",
|
"被逻辑检查拒绝:回复内容与你上一条发言完全相同,可以选择深入话题或寻找其它话题或等待",
|
||||||
False,
|
True,
|
||||||
) # 不合适,无需重新规划
|
) # 不合适,需要返回至决策层
|
||||||
# 2. 相似度检查 (如果精确匹配未通过)
|
# 2. 相似度检查 (如果精确匹配未通过)
|
||||||
import difflib # 导入 difflib 库
|
import difflib # 导入 difflib 库
|
||||||
|
|
||||||
@@ -73,8 +73,8 @@ class ReplyChecker:
|
|||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
False,
|
False,
|
||||||
f"拒绝发送:回复内容与你上一条发言高度相似 (相似度 {similarity_ratio:.2f}),请修改,可以选择深入话题或寻找其它话题或等待。",
|
f"被逻辑检查拒绝:回复内容与你上一条发言高度相似 (相似度 {similarity_ratio:.2f}),可以选择深入话题或寻找其它话题或等待。",
|
||||||
False,
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -83,37 +83,37 @@ class ReplyChecker:
|
|||||||
logger.error(f"[私聊][{self.private_name}]检查回复时出错: 类型={type(e)}, 值={e}")
|
logger.error(f"[私聊][{self.private_name}]检查回复时出错: 类型={type(e)}, 值={e}")
|
||||||
logger.error(f"[私聊][{self.private_name}]{traceback.format_exc()}") # 打印详细的回溯信息
|
logger.error(f"[私聊][{self.private_name}]{traceback.format_exc()}") # 打印详细的回溯信息
|
||||||
|
|
||||||
prompt = f"""请检查以下回复或消息是否合适:
|
prompt = f"""你是一个聊天逻辑检查器,请检查以下回复或消息是否合适:
|
||||||
|
|
||||||
当前对话目标:{goal}
|
当前对话目标:{goal}
|
||||||
最新的对话记录:
|
最新的对话记录:
|
||||||
{chat_history_text}
|
{chat_history_text}
|
||||||
|
|
||||||
待检查的回复:
|
待检查的消息:
|
||||||
{reply}
|
{reply}
|
||||||
|
|
||||||
请结合聊天记录检查以下几点:
|
请结合聊天记录检查以下几点:
|
||||||
1. 回复是否依然符合当前对话目标和实现方式
|
1. 这条消息是否依然符合当前对话目标和实现方式
|
||||||
2. 回复是否与最新的对话记录保持一致性
|
2. 这条消息是否与最新的对话记录保持一致性
|
||||||
3. 回复是否重复发言,或重复表达同质内容(尤其是只是换一种方式表达了相同的含义)
|
3. 是否存在重复发言,或重复表达同质内容(尤其是只是换一种方式表达了相同的含义)
|
||||||
4. 回复是否包含违规内容(例如血腥暴力,政治敏感等)
|
4. 这条消息是否包含违规内容(例如血腥暴力,政治敏感等)
|
||||||
5. 回复是否以你的角度发言,不要把"你"说的话当做对方说的话,这是你自己说的话(不要自己回复自己的消息)
|
5. 这条消息是否以发送者的角度发言(不要让发送者自己回复自己的消息)
|
||||||
6. 回复是否通俗易懂
|
6. 这条消息是否通俗易懂
|
||||||
7. 回复是否有些多余,例如在对方没有回复的情况下,依然连续多次“消息轰炸”(尤其是已经连续发送3条信息的情况,这很可能不合理,需要着重判断)
|
7. 这条消息是否有些多余,例如在对方没有回复的情况下,依然连续多次“消息轰炸”(尤其是已经连续发送3条信息的情况,这很可能不合理,需要着重判断)
|
||||||
8. 回复是否使用了完全没必要的修辞
|
8. 这条消息是否使用了完全没必要的修辞
|
||||||
9. 回复是否逻辑通顺
|
9. 这条消息是否逻辑通顺
|
||||||
10. 回复是否太过冗长了(通常私聊的每条消息长度在20字以内,除非特殊情况)
|
10. 这条消息是否太过冗长了(通常私聊的每条消息长度在20字以内,除非特殊情况)
|
||||||
11. 在连续多次发送消息的情况下,当前回复是否衔接自然,会不会显得奇怪(例如连续两条消息中部分内容重叠)
|
11. 在连续多次发送消息的情况下,这条消息是否衔接自然,会不会显得奇怪(例如连续两条消息中部分内容重叠)
|
||||||
|
|
||||||
请以JSON格式输出,包含以下字段:
|
请以JSON格式输出,包含以下字段:
|
||||||
1. suitable: 是否合适 (true/false)
|
1. suitable: 是否合适 (true/false)
|
||||||
2. reason: 原因说明
|
2. reason: 原因说明
|
||||||
3. need_replan: 是否需要重新规划对话目标 (true/false),当发现当前对话目标不再适合时设为true
|
3. need_replan: 是否需要重新决策 (true/false),当你认为此时已经不适合发消息,需要规划其它行动时,设为true
|
||||||
|
|
||||||
输出格式示例:
|
输出格式示例:
|
||||||
{{
|
{{
|
||||||
"suitable": true,
|
"suitable": true,
|
||||||
"reason": "回复符合要求,内容得体",
|
"reason": "回复符合要求,虽然有可能略微偏离目标,但是整体内容流畅得体",
|
||||||
"need_replan": false
|
"need_replan": false
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|||||||
@@ -68,8 +68,7 @@ class ReplyGenerator:
|
|||||||
max_tokens=300,
|
max_tokens=300,
|
||||||
request_type="reply_generation",
|
request_type="reply_generation",
|
||||||
)
|
)
|
||||||
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=3)
|
self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3)
|
||||||
self.identity_detail_info = Individuality.get_instance().get_prompt(type="identity", x_person=2, level=2)
|
|
||||||
self.name = global_config.BOT_NICKNAME
|
self.name = global_config.BOT_NICKNAME
|
||||||
self.private_name = private_name
|
self.private_name = private_name
|
||||||
self.chat_observer = ChatObserver.get_instance(stream_id, private_name)
|
self.chat_observer = ChatObserver.get_instance(stream_id, private_name)
|
||||||
@@ -130,20 +129,7 @@ class ReplyGenerator:
|
|||||||
chat_history_text = "还没有聊天记录。"
|
chat_history_text = "还没有聊天记录。"
|
||||||
|
|
||||||
# 构建 Persona 文本 (persona_text)
|
# 构建 Persona 文本 (persona_text)
|
||||||
identity_details_only = self.identity_detail_info
|
persona_text = f"你的名字是{self.name},{self.personality_info}。"
|
||||||
identity_addon = ""
|
|
||||||
if isinstance(identity_details_only, str):
|
|
||||||
pronouns = ["你", "我", "他"]
|
|
||||||
for p in pronouns:
|
|
||||||
if identity_details_only.startswith(p):
|
|
||||||
identity_details_only = identity_details_only[len(p) :]
|
|
||||||
break
|
|
||||||
if identity_details_only.endswith("。"):
|
|
||||||
identity_details_only = identity_details_only[:-1]
|
|
||||||
cleaned_details = identity_details_only.strip(",, ")
|
|
||||||
if cleaned_details:
|
|
||||||
identity_addon = f"并且{cleaned_details}"
|
|
||||||
persona_text = f"你的名字是{self.name},{self.personality_info}{identity_addon}。"
|
|
||||||
|
|
||||||
# --- 选择 Prompt ---
|
# --- 选择 Prompt ---
|
||||||
if action_type == "send_new_message":
|
if action_type == "send_new_message":
|
||||||
|
|||||||
@@ -360,6 +360,7 @@ class EmojiManager:
|
|||||||
return
|
return
|
||||||
|
|
||||||
total_count = len(self.emoji_objects)
|
total_count = len(self.emoji_objects)
|
||||||
|
self.emoji_num = total_count
|
||||||
removed_count = 0
|
removed_count = 0
|
||||||
# 使用列表复制进行遍历,因为我们会在遍历过程中修改列表
|
# 使用列表复制进行遍历,因为我们会在遍历过程中修改列表
|
||||||
for emoji in self.emoji_objects[:]:
|
for emoji in self.emoji_objects[:]:
|
||||||
@@ -376,10 +377,22 @@ class EmojiManager:
|
|||||||
removed_count += 1
|
removed_count += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if emoji.description == None:
|
||||||
|
logger.warning(f"[检查] 表情包文件已被删除: {emoji.path}")
|
||||||
|
# 执行表情包对象的删除方法
|
||||||
|
await emoji.delete()
|
||||||
|
# 从列表中移除该对象
|
||||||
|
self.emoji_objects.remove(emoji)
|
||||||
|
# 更新计数
|
||||||
|
self.emoji_num -= 1
|
||||||
|
removed_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
except Exception as item_error:
|
except Exception as item_error:
|
||||||
logger.error(f"[错误] 处理表情包记录时出错: {str(item_error)}")
|
logger.error(f"[错误] 处理表情包记录时出错: {str(item_error)}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
await self.clean_unused_emojis(EMOJI_REGISTED_DIR, self.emoji_objects)
|
||||||
# 输出清理结果
|
# 输出清理结果
|
||||||
if removed_count > 0:
|
if removed_count > 0:
|
||||||
logger.success(f"[清理] 已清理 {removed_count} 个失效的表情包记录")
|
logger.success(f"[清理] 已清理 {removed_count} 个失效的表情包记录")
|
||||||
@@ -749,7 +762,7 @@ class EmojiManager:
|
|||||||
await new_emoji.initialize_hash_format()
|
await new_emoji.initialize_hash_format()
|
||||||
emoji_base64 = image_path_to_base64(os.path.join(EMOJI_DIR, filename))
|
emoji_base64 = image_path_to_base64(os.path.join(EMOJI_DIR, filename))
|
||||||
description, emotions = await self.build_emoji_description(emoji_base64)
|
description, emotions = await self.build_emoji_description(emoji_base64)
|
||||||
if description == "":
|
if description == "" or description == None:
|
||||||
return False
|
return False
|
||||||
new_emoji.description = description
|
new_emoji.description = description
|
||||||
new_emoji.emotion = emotions
|
new_emoji.emotion = emotions
|
||||||
@@ -817,6 +830,26 @@ class EmojiManager:
|
|||||||
|
|
||||||
logger.success("[清理] 临时文件清理完成")
|
logger.success("[清理] 临时文件清理完成")
|
||||||
|
|
||||||
|
async def clean_unused_emojis(self, emoji_dir, emoji_objects):
|
||||||
|
"""清理未使用的表情包文件
|
||||||
|
遍历指定文件夹中的所有文件,删除未在emoji_objects列表中的文件
|
||||||
|
"""
|
||||||
|
# 获取所有表情包路径
|
||||||
|
emoji_paths = {emoji.path for emoji in emoji_objects}
|
||||||
|
|
||||||
|
# 遍历文件夹中的所有文件
|
||||||
|
for file_name in os.listdir(emoji_dir):
|
||||||
|
file_path = os.path.join(emoji_dir, file_name)
|
||||||
|
|
||||||
|
# 检查文件是否在表情包路径列表中
|
||||||
|
if file_path not in emoji_paths:
|
||||||
|
try:
|
||||||
|
# 删除未在表情包列表中的文件
|
||||||
|
os.remove(file_path)
|
||||||
|
logger.info(f"[清理] 删除未使用的表情包文件: {file_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[错误] 删除文件时出错: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
# 创建全局单例
|
# 创建全局单例
|
||||||
emoji_manager = EmojiManager()
|
emoji_manager = EmojiManager()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from ...individuality.individuality import Individuality
|
|||||||
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
|
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
|
||||||
from src.plugins.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
|
from src.plugins.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
|
||||||
from src.plugins.person_info.relationship_manager import relationship_manager
|
from src.plugins.person_info.relationship_manager import relationship_manager
|
||||||
from src.plugins.chat.utils import get_embedding, parse_text_timestamps
|
from src.plugins.chat.utils import get_embedding
|
||||||
import time
|
import time
|
||||||
from typing import Union, Optional
|
from typing import Union, Optional
|
||||||
from ...common.database import db
|
from ...common.database import db
|
||||||
|
|||||||
@@ -1353,11 +1353,11 @@ class ParahippocampalGyrus:
|
|||||||
if not memory_items:
|
if not memory_items:
|
||||||
try:
|
try:
|
||||||
self.memory_graph.G.remove_node(node)
|
self.memory_graph.G.remove_node(node)
|
||||||
node_changes["removed"].append(f"{node}(空节点)") # 标记为空节点移除
|
node_changes["removed"].append(f"{node}(空节点)") # 标记为空节点移除
|
||||||
logger.debug(f"[遗忘] 移除了空的节点: {node}")
|
logger.debug(f"[遗忘] 移除了空的节点: {node}")
|
||||||
except nx.NetworkXError as e:
|
except nx.NetworkXError as e:
|
||||||
logger.warning(f"[遗忘] 移除空节点 {node} 时发生错误(可能已被移除): {e}")
|
logger.warning(f"[遗忘] 移除空节点 {node} 时发生错误(可能已被移除): {e}")
|
||||||
continue # 处理下一个节点
|
continue # 处理下一个节点
|
||||||
|
|
||||||
# --- 如果节点不为空,则执行原来的不活跃检查和随机移除逻辑 ---
|
# --- 如果节点不为空,则执行原来的不活跃检查和随机移除逻辑 ---
|
||||||
last_modified = node_data.get("last_modified", current_time)
|
last_modified = node_data.get("last_modified", current_time)
|
||||||
@@ -1373,15 +1373,15 @@ class ParahippocampalGyrus:
|
|||||||
memory_items.remove(removed_item)
|
memory_items.remove(removed_item)
|
||||||
|
|
||||||
# 条件3:检查移除后 memory_items 是否变空
|
# 条件3:检查移除后 memory_items 是否变空
|
||||||
if memory_items: # 如果移除后列表不为空
|
if memory_items: # 如果移除后列表不为空
|
||||||
# self.memory_graph.G.nodes[node]["memory_items"] = memory_items # 直接修改列表即可
|
# self.memory_graph.G.nodes[node]["memory_items"] = memory_items # 直接修改列表即可
|
||||||
self.memory_graph.G.nodes[node]["last_modified"] = current_time # 更新修改时间
|
self.memory_graph.G.nodes[node]["last_modified"] = current_time # 更新修改时间
|
||||||
node_changes["reduced"].append(f"{node} (数量: {current_count} -> {len(memory_items)})")
|
node_changes["reduced"].append(f"{node} (数量: {current_count} -> {len(memory_items)})")
|
||||||
else: # 如果移除后列表为空
|
else: # 如果移除后列表为空
|
||||||
# 尝试移除节点,处理可能的错误
|
# 尝试移除节点,处理可能的错误
|
||||||
try:
|
try:
|
||||||
self.memory_graph.G.remove_node(node)
|
self.memory_graph.G.remove_node(node)
|
||||||
node_changes["removed"].append(f"{node}(遗忘清空)") # 标记为遗忘清空
|
node_changes["removed"].append(f"{node}(遗忘清空)") # 标记为遗忘清空
|
||||||
logger.debug(f"[遗忘] 节点 {node} 因移除最后一项而被清空。")
|
logger.debug(f"[遗忘] 节点 {node} 因移除最后一项而被清空。")
|
||||||
except nx.NetworkXError as e:
|
except nx.NetworkXError as e:
|
||||||
logger.warning(f"[遗忘] 尝试移除节点 {node} 时发生错误(可能已被移除):{e}")
|
logger.warning(f"[遗忘] 尝试移除节点 {node} 时发生错误(可能已被移除):{e}")
|
||||||
@@ -1464,9 +1464,9 @@ class ParahippocampalGyrus:
|
|||||||
node_data = self.memory_graph.G.nodes[node]
|
node_data = self.memory_graph.G.nodes[node]
|
||||||
memory_items = node_data.get("memory_items", [])
|
memory_items = node_data.get("memory_items", [])
|
||||||
if not isinstance(memory_items, list) or len(memory_items) < 2:
|
if not isinstance(memory_items, list) or len(memory_items) < 2:
|
||||||
continue # 双重检查,理论上不会进入
|
continue # 双重检查,理论上不会进入
|
||||||
|
|
||||||
items_copy = list(memory_items) # 创建副本以安全迭代和修改
|
items_copy = list(memory_items) # 创建副本以安全迭代和修改
|
||||||
|
|
||||||
# 遍历所有记忆项组合
|
# 遍历所有记忆项组合
|
||||||
for item1, item2 in combinations(items_copy, 2):
|
for item1, item2 in combinations(items_copy, 2):
|
||||||
@@ -1495,21 +1495,24 @@ class ParahippocampalGyrus:
|
|||||||
# 从原始列表中移除信息量较低的项
|
# 从原始列表中移除信息量较低的项
|
||||||
try:
|
try:
|
||||||
memory_items.remove(item_to_remove)
|
memory_items.remove(item_to_remove)
|
||||||
logger.info(f"[整合] 已合并节点 '{node}' 中的记忆,保留: '{item_to_keep[:60]}...', 移除: '{item_to_remove[:60]}...'" )
|
logger.info(
|
||||||
|
f"[整合] 已合并节点 '{node}' 中的记忆,保留: '{item_to_keep[:60]}...', 移除: '{item_to_remove[:60]}...'"
|
||||||
|
)
|
||||||
merged_count += 1
|
merged_count += 1
|
||||||
nodes_modified.add(node)
|
nodes_modified.add(node)
|
||||||
node_data['last_modified'] = current_timestamp # 更新修改时间
|
node_data["last_modified"] = current_timestamp # 更新修改时间
|
||||||
_merged_in_this_node = True
|
_merged_in_this_node = True
|
||||||
break # 每个节点每次检查只合并一对
|
break # 每个节点每次检查只合并一对
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# 如果项已经被移除(例如,在之前的迭代中作为 item_to_keep),则跳过
|
# 如果项已经被移除(例如,在之前的迭代中作为 item_to_keep),则跳过
|
||||||
logger.warning(f"[整合] 尝试移除节点 '{node}' 中不存在的项 '{item_to_remove[:30]}...',可能已被合并。")
|
logger.warning(
|
||||||
|
f"[整合] 尝试移除节点 '{node}' 中不存在的项 '{item_to_remove[:30]}...',可能已被合并。"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
# # 如果节点内发生了合并,更新节点数据 (这种方式不安全,会丢失其他属性)
|
# # 如果节点内发生了合并,更新节点数据 (这种方式不安全,会丢失其他属性)
|
||||||
# if merged_in_this_node:
|
# if merged_in_this_node:
|
||||||
# self.memory_graph.G.nodes[node]["memory_items"] = memory_items
|
# self.memory_graph.G.nodes[node]["memory_items"] = memory_items
|
||||||
|
|
||||||
|
|
||||||
if merged_count > 0:
|
if merged_count > 0:
|
||||||
logger.info(f"[整合] 共合并了 {merged_count} 对相似记忆项,分布在 {len(nodes_modified)} 个节点中。")
|
logger.info(f"[整合] 共合并了 {merged_count} 对相似记忆项,分布在 {len(nodes_modified)} 个节点中。")
|
||||||
sync_start = time.time()
|
sync_start = time.time()
|
||||||
@@ -1594,7 +1597,7 @@ class HippocampusManager:
|
|||||||
if not self._initialized:
|
if not self._initialized:
|
||||||
raise RuntimeError("HippocampusManager 尚未初始化,请先调用 initialize 方法")
|
raise RuntimeError("HippocampusManager 尚未初始化,请先调用 initialize 方法")
|
||||||
return await self._hippocampus.parahippocampal_gyrus.operation_forget_topic(percentage)
|
return await self._hippocampus.parahippocampal_gyrus.operation_forget_topic(percentage)
|
||||||
|
|
||||||
async def consolidate_memory(self):
|
async def consolidate_memory(self):
|
||||||
"""整合记忆的公共接口"""
|
"""整合记忆的公共接口"""
|
||||||
if not self._initialized:
|
if not self._initialized:
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ class MemoryConfig:
|
|||||||
memory_ban_words: List[str] # 记忆过滤词列表
|
memory_ban_words: List[str] # 记忆过滤词列表
|
||||||
|
|
||||||
# 新增:记忆整合相关配置
|
# 新增:记忆整合相关配置
|
||||||
consolidation_similarity_threshold: float # 相似度阈值
|
consolidation_similarity_threshold: float # 相似度阈值
|
||||||
consolidate_memory_percentage: float # 检查节点比例
|
consolidate_memory_percentage: float # 检查节点比例
|
||||||
consolidate_memory_interval: int # 记忆整合间隔
|
consolidate_memory_interval: int # 记忆整合间隔
|
||||||
|
|
||||||
llm_topic_judge: str # 话题判断模型
|
llm_topic_judge: str # 话题判断模型
|
||||||
llm_summary_by_topic: str # 话题总结模型
|
llm_summary_by_topic: str # 话题总结模型
|
||||||
@@ -31,7 +31,9 @@ class MemoryConfig:
|
|||||||
"""从全局配置创建记忆系统配置"""
|
"""从全局配置创建记忆系统配置"""
|
||||||
# 使用 getattr 提供默认值,防止全局配置缺少这些项
|
# 使用 getattr 提供默认值,防止全局配置缺少这些项
|
||||||
return cls(
|
return cls(
|
||||||
memory_build_distribution=getattr(global_config, "memory_build_distribution", (24, 12, 0.5, 168, 72, 0.5)), # 添加默认值
|
memory_build_distribution=getattr(
|
||||||
|
global_config, "memory_build_distribution", (24, 12, 0.5, 168, 72, 0.5)
|
||||||
|
), # 添加默认值
|
||||||
build_memory_sample_num=getattr(global_config, "build_memory_sample_num", 5),
|
build_memory_sample_num=getattr(global_config, "build_memory_sample_num", 5),
|
||||||
build_memory_sample_length=getattr(global_config, "build_memory_sample_length", 30),
|
build_memory_sample_length=getattr(global_config, "build_memory_sample_length", 30),
|
||||||
memory_compress_rate=getattr(global_config, "memory_compress_rate", 0.1),
|
memory_compress_rate=getattr(global_config, "memory_compress_rate", 0.1),
|
||||||
@@ -41,6 +43,8 @@ class MemoryConfig:
|
|||||||
consolidation_similarity_threshold=getattr(global_config, "consolidation_similarity_threshold", 0.7),
|
consolidation_similarity_threshold=getattr(global_config, "consolidation_similarity_threshold", 0.7),
|
||||||
consolidate_memory_percentage=getattr(global_config, "consolidate_memory_percentage", 0.01),
|
consolidate_memory_percentage=getattr(global_config, "consolidate_memory_percentage", 0.01),
|
||||||
consolidate_memory_interval=getattr(global_config, "consolidate_memory_interval", 1000),
|
consolidate_memory_interval=getattr(global_config, "consolidate_memory_interval", 1000),
|
||||||
llm_topic_judge=getattr(global_config, "llm_topic_judge", "default_judge_model"), # 添加默认模型名
|
llm_topic_judge=getattr(global_config, "llm_topic_judge", "default_judge_model"), # 添加默认模型名
|
||||||
llm_summary_by_topic=getattr(global_config, "llm_summary_by_topic", "default_summary_model"), # 添加默认模型名
|
llm_summary_by_topic=getattr(
|
||||||
|
global_config, "llm_summary_by_topic", "default_summary_model"
|
||||||
|
), # 添加默认模型名
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import math
|
|||||||
from bson.decimal128 import Decimal128
|
from bson.decimal128 import Decimal128
|
||||||
from .person_info import person_info_manager
|
from .person_info import person_info_manager
|
||||||
import time
|
import time
|
||||||
import re
|
# import re
|
||||||
import traceback
|
# import traceback
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger("relation")
|
logger = get_logger("relation")
|
||||||
|
|||||||
Reference in New Issue
Block a user