diff --git a/src/chat/memory_system/Memory_chest.py b/src/chat/memory_system/Memory_chest.py index cc7b8bef..6a449fd3 100644 --- a/src/chat/memory_system/Memory_chest.py +++ b/src/chat/memory_system/Memory_chest.py @@ -24,7 +24,7 @@ class MemoryChest: ) self.memory_build_threshold = 30 - self.memory_size_limit = 800 + self.memory_size_limit = 1024 self.running_content_list = {} # {chat_id: {"content": running_content, "last_update_time": timestamp}} self.fetched_memory_list = [] # [(chat_id, (question, answer, timestamp)), ...] diff --git a/src/chat/memory_system/hippocampus_to_memory_chest_task.py b/src/chat/memory_system/hippocampus_to_memory_chest_task.py index 184ec7d7..e2764141 100644 --- a/src/chat/memory_system/hippocampus_to_memory_chest_task.py +++ b/src/chat/memory_system/hippocampus_to_memory_chest_task.py @@ -20,7 +20,7 @@ class HippocampusToMemoryChestTask(AsyncTask): def __init__(self): super().__init__( task_name="Hippocampus to Memory Chest Task", - wait_before_start=60, # 启动后等待60秒再开始 + wait_before_start=10, # 启动后等待60秒再开始 run_interval=60 # 每60秒运行一次 ) @@ -41,13 +41,14 @@ class HippocampusToMemoryChestTask(AsyncTask): # 获取所有节点 all_nodes = list(memory_graph.nodes()) - if len(all_nodes) < 5: + if len(all_nodes) < 10: + selected_nodes = all_nodes logger.info(f"[海马体转换] 当前只有 {len(all_nodes)} 个节点,少于5个,跳过本次转换") - return + else: - # 随机选择5个节点 - selected_nodes = random.sample(all_nodes, 5) - logger.info(f"[海马体转换] 随机选择了 {len(selected_nodes)} 个节点: {selected_nodes}") + # 随机选择5个节点 + selected_nodes = random.sample(all_nodes, 10) + logger.info(f"[海马体转换] 随机选择了 {len(selected_nodes)} 个节点: {selected_nodes}") # 拼接节点内容 content_parts = [] diff --git a/src/chat/memory_system/memory_activator.py b/src/chat/memory_system/memory_activator.py deleted file mode 100644 index ce7daef5..00000000 --- a/src/chat/memory_system/memory_activator.py +++ /dev/null @@ -1,241 +0,0 @@ -import json -import random - -from json_repair import repair_json -from typing import List, Tuple - -from src.config.config import global_config, model_config -from src.common.logger import get_logger -from src.common.data_models.database_data_model import DatabaseMessages -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager -from src.chat.utils.utils import parse_keywords_string -from src.chat.utils.chat_message_builder import build_readable_messages -from src.chat.memory_system.Hippocampus import hippocampus_manager -from src.llm_models.utils_model import LLMRequest - - -logger = get_logger("memory_activator") - - -def get_keywords_from_json(json_str) -> List: - """ - 从JSON字符串中提取关键词列表 - - Args: - json_str: JSON格式的字符串 - - Returns: - List[str]: 关键词列表 - """ - try: - # 使用repair_json修复JSON格式 - fixed_json = repair_json(json_str) - - # 如果repair_json返回的是字符串,需要解析为Python对象 - result = json.loads(fixed_json) if isinstance(fixed_json, str) else fixed_json - return result.get("keywords", []) - except Exception as e: - logger.error(f"解析关键词JSON失败: {e}") - return [] - - -def init_prompt(): - # --- Group Chat Prompt --- - memory_activator_prompt = """ - 你需要根据以下信息来挑选合适的记忆编号 - 以下是一段聊天记录,请根据这些信息,和下方的记忆,挑选和群聊内容有关的记忆编号 - - 聊天记录: - {obs_info_text} - 你想要回复的消息: - {target_message} - - 记忆: - {memory_info} - - 请输出一个json格式,包含以下字段: - {{ - "memory_ids": "记忆1编号,记忆2编号,记忆3编号,......" - }} - 不要输出其他多余内容,只输出json格式就好 - """ - - Prompt(memory_activator_prompt, "memory_activator_prompt") - - -class MemoryActivator: - def __init__(self): - self.key_words_model = LLMRequest( - model_set=model_config.model_task_config.utils_small, - request_type="memory.activator", - ) - # 用于记忆选择的 LLM 模型 - self.memory_selection_model = LLMRequest( - model_set=model_config.model_task_config.utils_small, - request_type="memory.selection", - ) - - async def activate_memory_with_chat_history( - self, target_message, chat_history: List[DatabaseMessages] - ) -> List[Tuple[str, str]]: - """ - 激活记忆 - """ - # 如果记忆系统被禁用,直接返回空列表 - if not global_config.memory.enable_memory: - return [] - - keywords_list = set() - - for msg in chat_history: - keywords = parse_keywords_string(msg.key_words) - if keywords: - if len(keywords_list) < 30: - # 最多容纳30个关键词 - keywords_list.update(keywords) - logger.debug(f"提取关键词: {keywords_list}") - else: - break - - if not keywords_list: - logger.debug("没有提取到关键词,返回空记忆列表") - return [] - - # 从海马体获取相关记忆 - related_memory = await hippocampus_manager.get_memory_from_topic( - valid_keywords=list(keywords_list), max_memory_num=5, max_memory_length=3, max_depth=3 - ) - - # logger.info(f"当前记忆关键词: {keywords_list}") - logger.debug(f"获取到的记忆: {related_memory}") - - if not related_memory: - logger.debug("海马体没有返回相关记忆") - return [] - - used_ids = set() - candidate_memories = [] - - # 为每个记忆分配随机ID并过滤相关记忆 - for memory in related_memory: - keyword, content = memory - found = any(kw in content for kw in keywords_list) - if found: - # 随机分配一个不重复的2位数id - while True: - random_id = "{:02d}".format(random.randint(0, 99)) - if random_id not in used_ids: - used_ids.add(random_id) - break - candidate_memories.append({"memory_id": random_id, "keyword": keyword, "content": content}) - - if not candidate_memories: - logger.info("没有找到相关的候选记忆") - return [] - - # 如果只有少量记忆,直接返回 - if len(candidate_memories) <= 2: - logger.debug(f"候选记忆较少({len(candidate_memories)}个),直接返回") - # 转换为 (keyword, content) 格式 - return [(mem["keyword"], mem["content"]) for mem in candidate_memories] - - return await self._select_memories_with_llm(target_message, chat_history, candidate_memories) - - async def _select_memories_with_llm( - self, target_message, chat_history: List[DatabaseMessages], candidate_memories - ) -> List[Tuple[str, str]]: - """ - 使用 LLM 选择合适的记忆 - - Args: - target_message: 目标消息 - chat_history_prompt: 聊天历史 - candidate_memories: 候选记忆列表,每个记忆包含 memory_id、keyword、content - - Returns: - List[Tuple[str, str]]: 选择的记忆列表,格式为 (keyword, content) - """ - try: - # 构建聊天历史字符串 - obs_info_text = build_readable_messages( - chat_history, - replace_bot_name=True, - timestamp_mode="relative", - read_mark=0.0, - show_actions=True, - ) - - # 构建记忆信息字符串 - memory_lines = [] - for memory in candidate_memories: - memory_id = memory["memory_id"] - keyword = memory["keyword"] - content = memory["content"] - - # 将 content 列表转换为字符串 - if isinstance(content, list): - content_str = " | ".join(str(item) for item in content) - else: - content_str = str(content) - - memory_lines.append(f"记忆编号 {memory_id}: [关键词: {keyword}] {content_str}") - - memory_info = "\n".join(memory_lines) - - # 获取并格式化 prompt - prompt_template = await global_prompt_manager.get_prompt_async("memory_activator_prompt") - formatted_prompt = prompt_template.format( - obs_info_text=obs_info_text, target_message=target_message, memory_info=memory_info - ) - - # 调用 LLM - response, (reasoning_content, model_name, _) = await self.memory_selection_model.generate_response_async( - formatted_prompt, temperature=0.3, max_tokens=150 - ) - - if global_config.debug.show_prompt: - logger.info(f"记忆选择 prompt: {formatted_prompt}") - logger.info(f"LLM 记忆选择响应: {response}") - else: - logger.debug(f"记忆选择 prompt: {formatted_prompt}") - logger.debug(f"LLM 记忆选择响应: {response}") - - # 解析响应获取选择的记忆编号 - try: - fixed_json = repair_json(response) - - # 解析为 Python 对象 - result = json.loads(fixed_json) if isinstance(fixed_json, str) else fixed_json - - # 提取 memory_ids 字段并解析逗号分隔的编号 - if memory_ids_str := result.get("memory_ids", ""): - memory_ids = [mid.strip() for mid in str(memory_ids_str).split(",") if mid.strip()] - # 过滤掉空字符串和无效编号 - valid_memory_ids = [mid for mid in memory_ids if mid and len(mid) <= 3] - selected_memory_ids = valid_memory_ids - else: - selected_memory_ids = [] - except Exception as e: - logger.error(f"解析记忆选择响应失败: {e}", exc_info=True) - selected_memory_ids = [] - - # 根据编号筛选记忆 - selected_memories = [] - memory_id_to_memory = {mem["memory_id"]: mem for mem in candidate_memories} - - selected_memories = [ - memory_id_to_memory[memory_id] for memory_id in selected_memory_ids if memory_id in memory_id_to_memory - ] - logger.info(f"LLM 选择的记忆编号: {selected_memory_ids}") - logger.info(f"最终选择的记忆数量: {len(selected_memories)}") - - # 转换为 (keyword, content) 格式 - return [(mem["keyword"], mem["content"]) for mem in selected_memories] - - except Exception as e: - logger.error(f"LLM 选择记忆时出错: {e}", exc_info=True) - # 出错时返回前3个候选记忆作为备选,转换为 (keyword, content) 格式 - return [(mem["keyword"], mem["content"]) for mem in candidate_memories[:3]] - - -init_prompt() diff --git a/src/plugins/built_in/memory/_manifest.json b/src/plugins/built_in/memory/_manifest.json index 08a58540..fd6a2179 100644 --- a/src/plugins/built_in/memory/_manifest.json +++ b/src/plugins/built_in/memory/_manifest.json @@ -10,7 +10,7 @@ "license": "GPL-v3.0-or-later", "host_application": { - "min_version": "0.10.1" + "min_version": "0.10.4" }, "homepage_url": "https://github.com/MaiM-with-u/maibot", "repository_url": "https://github.com/MaiM-with-u/maibot", diff --git a/src/plugins/built_in/memory/build_memory.py b/src/plugins/built_in/memory/build_memory.py index 7b6f8971..f9ba2c9b 100644 --- a/src/plugins/built_in/memory/build_memory.py +++ b/src/plugins/built_in/memory/build_memory.py @@ -12,132 +12,6 @@ from src.plugin_system.base.base_tool import BaseTool from typing import Any logger = get_logger("memory") - - -def init_prompt(): - Prompt( - """ -以下是一些记忆条目的分类: ----------------------- -{category_list} ----------------------- -每一个分类条目类型代表了你对用户:"{person_name}"的印象的一个类别 - -现在,你有一条对 {person_name} 的新记忆内容: -{memory_point} - -请判断该记忆内容是否属于上述分类,请给出分类的名称。 -如果不属于上述分类,请输出一个合适的分类名称,对新记忆内容进行概括。要求分类名具有概括性。 -注意分类数一般不超过5个 -请严格用json格式输出,不要输出任何其他内容: -{{ - "category": "分类名称" -}} """, - "relation_category", - ) - - Prompt( - """ -以下是有关{category}的现有记忆: ----------------------- -{memory_list} ----------------------- - -现在,你有一条对 {person_name} 的新记忆内容: -{memory_point} - -请判断该新记忆内容是否已经存在于现有记忆中,你可以对现有进行进行以下修改: -注意,一般来说记忆内容不超过5个,且记忆文本不应太长 - -1.新增:当记忆内容不存在于现有记忆,且不存在矛盾,请用json格式输出: -{{ - "new_memory": "需要新增的记忆内容" -}} -2.加深印象:如果这个新记忆已经存在于现有记忆中,在内容上与现有记忆类似,请用json格式输出: -{{ - "memory_id": 1, #请输出你认为需要加深印象的,与新记忆内容类似的,已经存在的记忆的序号 - "integrate_memory": "加深后的记忆内容,合并内容类似的新记忆和旧记忆" -}} -3.整合:如果这个新记忆与现有记忆产生矛盾,请你结合其他记忆进行整合,用json格式输出: -{{ - "memory_id": 1, #请输出你认为需要整合的,与新记忆存在矛盾的,已经存在的记忆的序号 - "integrate_memory": "整合后的记忆内容,合并内容矛盾的新记忆和旧记忆" -}} - -现在,请你根据情况选出合适的修改方式,并输出json,不要输出其他内容: -""", - "relation_category_update", - ) - - -# class BuildMemoryAction(BaseAction): -# """关系动作 - 构建关系""" - -# activation_type = ActionActivationType.LLM_JUDGE -# parallel_action = True - -# # 动作基本信息 -# action_name = "build_memory" -# action_description = ( -# "了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息" -# ) - -# # 动作参数定义 -# action_parameters = { -# "concept_name": "需要了解或记忆的概念或事件的名称", -# "concept_description": "需要了解或记忆的概念或事件的描述,需要具体且明确", -# } - -# # 动作使用场景 -# action_require = [ -# "了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息", -# "有你不了解的概念", -# "有人要求你记住某个概念或者事件", -# "你对某件事或概念有新的理解,或产生了兴趣", -# ] - -# # 关联类型 -# associated_types = ["text"] - -# async def execute(self) -> Tuple[bool, str]: -# """执行关系动作""" - -# try: -# # 1. 获取构建关系的原因 -# concept_description = self.action_data.get("concept_description", "") -# logger.info(f"{self.log_prefix} 添加记忆原因: {self.reasoning}") -# concept_name = self.action_data.get("concept_name", "") -# # 2. 获取目标用户信息 - -# # 对 concept_name 进行jieba分词 -# concept_name_tokens = cut_key_words(concept_name) -# # logger.info(f"{self.log_prefix} 对 concept_name 进行分词结果: {concept_name_tokens}") - -# filtered_concept_name_tokens = [ -# token -# for token in concept_name_tokens -# if all(keyword not in token for keyword in global_config.memory.memory_ban_words) -# ] - -# if not filtered_concept_name_tokens: -# logger.warning(f"{self.log_prefix} 过滤后的概念名称列表为空,跳过添加记忆") -# return False, "过滤后的概念名称列表为空,跳过添加记忆" - -# similar_topics_dict = ( -# hippocampus_manager.get_hippocampus().parahippocampal_gyrus.get_similar_topics_from_keywords( -# filtered_concept_name_tokens -# ) -# ) -# await hippocampus_manager.get_hippocampus().parahippocampal_gyrus.add_memory_with_similar( -# concept_description, similar_topics_dict -# ) - -# return True, f"成功添加记忆: {concept_name}" - -# except Exception as e: -# logger.error(f"{self.log_prefix} 构建记忆时出错: {e}") -# return False, f"构建记忆时出错: {e}" - class GetMemoryTool(BaseTool): """获取用户信息""" @@ -166,8 +40,6 @@ class GetMemoryTool(BaseTool): return {"content": f"问题:{question},答案:{answer}"} - - class GetMemoryAction(BaseAction): """关系动作 - 获取记忆""" @@ -217,7 +89,3 @@ class GetMemoryAction(BaseAction): ) return True, f"成功获取记忆: {answer}" - - -# 还缺一个关系的太多遗忘和对应的提取 -init_prompt() diff --git a/src/plugins/built_in/memory/plugin.py b/src/plugins/built_in/memory/plugin.py index ae71bf07..3258ebb2 100644 --- a/src/plugins/built_in/memory/plugin.py +++ b/src/plugins/built_in/memory/plugin.py @@ -39,7 +39,7 @@ class MemoryBuildPlugin(BasePlugin): config_schema: dict = { "plugin": { "enabled": ConfigField(type=bool, default=True, description="是否启用插件"), - "config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"), + "config_version": ConfigField(type=str, default="1.1.1", description="配置文件版本"), }, }