From e8922672aaae556a5c09d78598b3deaeaf469d54 Mon Sep 17 00:00:00 2001 From: UnCLAS-Prommer Date: Thu, 21 Aug 2025 00:46:04 +0800 Subject: [PATCH] =?UTF-8?q?=E9=99=84=E5=B1=9E=E5=87=BD=E6=95=B0=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/memory_system/Hippocampus.py | 215 +++++++++--------- src/chat/memory_system/memory_activator.py | 111 ++++----- src/chat/replyer/default_generator.py | 40 ++-- src/common/data_models/__init__.py | 3 +- .../body_emotion_action_manager.py | 10 +- src/mais4u/mais4u_chat/s4u_mood_manager.py | 12 +- src/mais4u/mais4u_chat/s4u_prompt.py | 92 ++++---- src/mood/mood_manager.py | 12 +- src/person_info/relationship_manager.py | 122 +++++----- src/plugin_system/apis/message_api.py | 4 +- src/plugins/built_in/emoji_plugin/emoji.py | 8 +- 11 files changed, 292 insertions(+), 337 deletions(-) diff --git a/src/chat/memory_system/Hippocampus.py b/src/chat/memory_system/Hippocampus.py index 9fd7833b..3afe9ba8 100644 --- a/src/chat/memory_system/Hippocampus.py +++ b/src/chat/memory_system/Hippocampus.py @@ -9,7 +9,6 @@ import networkx as nx import numpy as np from typing import List, Tuple, Set, Coroutine, Any, Dict from collections import Counter -from itertools import combinations import traceback from rich.traceback import install @@ -23,6 +22,8 @@ from src.chat.utils.chat_message_builder import ( build_readable_messages, get_raw_msg_by_timestamp_with_chat_inclusive, ) # 导入 build_readable_messages + + # 添加cosine_similarity函数 def cosine_similarity(v1, v2): """计算余弦相似度""" @@ -51,18 +52,9 @@ def calculate_information_content(text): return entropy - - - logger = get_logger("memory") - - - - - - class MemoryGraph: def __init__(self): self.G = nx.Graph() # 使用 networkx 的图结构 @@ -96,7 +88,7 @@ class MemoryGraph: if "memory_items" in self.G.nodes[concept]: # 获取现有的记忆项(已经是str格式) existing_memory = self.G.nodes[concept]["memory_items"] - + # 如果现有记忆不为空,则使用LLM整合新旧记忆 if existing_memory and hippocampus_instance and hippocampus_instance.model_small: try: @@ -170,16 +162,16 @@ class MemoryGraph: second_layer_items.append(memory_items) return first_layer_items, second_layer_items - + async def _integrate_memories_with_llm(self, existing_memory: str, new_memory: str, llm_model: LLMRequest) -> str: """ 使用LLM整合新旧记忆内容 - + Args: existing_memory: 现有的记忆内容(字符串格式,可能包含多条记忆) new_memory: 新的记忆内容 llm_model: LLM模型实例 - + Returns: str: 整合后的记忆内容 """ @@ -203,8 +195,10 @@ class MemoryGraph: 整合后的记忆:""" # 调用LLM进行整合 - content, (reasoning_content, model_name, tool_calls) = await llm_model.generate_response_async(integration_prompt) - + content, (reasoning_content, model_name, tool_calls) = await llm_model.generate_response_async( + integration_prompt + ) + if content and content.strip(): integrated_content = content.strip() logger.debug(f"LLM记忆整合成功,模型: {model_name}") @@ -212,7 +206,7 @@ class MemoryGraph: else: logger.warning("LLM返回的整合结果为空,使用默认连接方式") return f"{existing_memory} | {new_memory}" - + except Exception as e: logger.error(f"LLM记忆整合过程中出错: {e}") return f"{existing_memory} | {new_memory}" @@ -238,7 +232,11 @@ class MemoryGraph: if memory_items: # 删除整个节点 self.G.remove_node(topic) - return f"删除了节点 {topic} 的完整记忆: {memory_items[:50]}..." if len(memory_items) > 50 else f"删除了节点 {topic} 的完整记忆: {memory_items}" + return ( + f"删除了节点 {topic} 的完整记忆: {memory_items[:50]}..." + if len(memory_items) > 50 + else f"删除了节点 {topic} 的完整记忆: {memory_items}" + ) else: # 如果没有记忆项,删除该节点 self.G.remove_node(topic) @@ -263,38 +261,40 @@ class Hippocampus: self.parahippocampal_gyrus = ParahippocampalGyrus(self) # 从数据库加载记忆图 self.entorhinal_cortex.sync_memory_from_db() - self.model_small = LLMRequest(model_set=model_config.model_task_config.utils_small, request_type="memory.modify") + self.model_small = LLMRequest( + model_set=model_config.model_task_config.utils_small, request_type="memory.modify" + ) def get_all_node_names(self) -> list: """获取记忆图中所有节点的名字列表""" return list(self.memory_graph.G.nodes()) - + def calculate_weighted_activation(self, current_activation: float, edge_strength: int, target_node: str) -> float: """ 计算考虑节点权重的激活值 - + Args: current_activation: 当前激活值 edge_strength: 边的强度 target_node: 目标节点名称 - + Returns: float: 计算后的激活值 """ # 基础激活值计算 base_activation = current_activation - (1 / edge_strength) - + if base_activation <= 0: return 0.0 - + # 获取目标节点的权重 if target_node in self.memory_graph.G: node_data = self.memory_graph.G.nodes[target_node] node_weight = node_data.get("weight", 1.0) - + # 权重加成:每次整合增加10%激活值,最大加成200% weight_multiplier = 1.0 + min((node_weight - 1.0) * 0.1, 2.0) - + return base_activation * weight_multiplier else: return base_activation @@ -332,9 +332,7 @@ class Hippocampus: f"将主题用逗号隔开,并加上<>,例如<主题1>,<主题2>......尽可能精简。只需要列举最多{topic_num}个话题就好,不要有序号,不要告诉我其他内容。" f"如果确定找不出主题或者没有明显主题,返回。" ) - - - + return prompt @staticmethod @@ -418,16 +416,13 @@ class Hippocampus: # 使用LLM提取关键词 - 根据详细文本长度分布优化topic_num计算 text_length = len(text) topic_num: int | list[int] = 0 - - + words = jieba.cut(text) keywords_lite = [word for word in words if len(word) > 1] keywords_lite = list(set(keywords_lite)) if keywords_lite: logger.debug(f"提取关键词极简版: {keywords_lite}") - - if text_length <= 12: topic_num = [1, 3] # 6-10字符: 1个关键词 (27.18%的文本) elif text_length <= 20: @@ -455,7 +450,7 @@ class Hippocampus: if keywords: logger.debug(f"提取关键词: {keywords}") - return keywords,keywords_lite + return keywords, keywords_lite async def get_memory_from_topic( self, @@ -570,20 +565,17 @@ class Hippocampus: for node, activation in remember_map.items(): logger.debug(f"处理节点 '{node}' (激活值: {activation:.2f}):") node_data = self.memory_graph.G.nodes[node] - memory_items = node_data.get("memory_items", "") - # 直接使用完整的记忆内容 - if memory_items: + if memory_items := node_data.get("memory_items", ""): logger.debug("节点包含完整记忆") # 计算记忆与关键词的相似度 memory_words = set(jieba.cut(memory_items)) text_words = set(keywords) - all_words = memory_words | text_words - if all_words: + if all_words := memory_words | text_words: # 计算相似度(虽然这里没有使用,但保持逻辑一致性) v1 = [1 if word in memory_words else 0 for word in all_words] v2 = [1 if word in text_words else 0 for word in all_words] _ = cosine_similarity(v1, v2) # 计算但不使用,用_表示 - + # 添加完整记忆到结果中 all_memories.append((node, memory_items, activation)) else: @@ -613,7 +605,9 @@ class Hippocampus: return result - async def get_activate_from_text(self, text: str, max_depth: int = 3, fast_retrieval: bool = False) -> tuple[float, list[str],list[str]]: + async def get_activate_from_text( + self, text: str, max_depth: int = 3, fast_retrieval: bool = False + ) -> tuple[float, list[str], list[str]]: """从文本中提取关键词并获取相关记忆。 Args: @@ -627,13 +621,13 @@ class Hippocampus: float: 激活节点数与总节点数的比值 list[str]: 有效的关键词 """ - keywords,keywords_lite = await self.get_keywords_from_text(text) + keywords, keywords_lite = await self.get_keywords_from_text(text) # 过滤掉不存在于记忆图中的关键词 valid_keywords = [keyword for keyword in keywords if keyword in self.memory_graph.G] if not valid_keywords: # logger.info("没有找到有效的关键词节点") - return 0, keywords,keywords_lite + return 0, keywords, keywords_lite logger.debug(f"有效的关键词: {', '.join(valid_keywords)}") @@ -700,7 +694,7 @@ class Hippocampus: activation_ratio = activation_ratio * 50 logger.debug(f"总激活值: {total_activation:.2f}, 总节点数: {total_nodes}, 激活: {activation_ratio}") - return activation_ratio, keywords,keywords_lite + return activation_ratio, keywords, keywords_lite # 负责海马体与其他部分的交互 @@ -730,7 +724,7 @@ class EntorhinalCortex: continue memory_items = data.get("memory_items", "") - + # 直接检查字符串是否为空,不需要分割成列表 if not memory_items or memory_items.strip() == "": self.memory_graph.G.remove_node(concept) @@ -865,7 +859,9 @@ class EntorhinalCortex: end_time = time.time() logger.info(f"[数据库] 同步完成,总耗时: {end_time - start_time:.2f}秒") - logger.info(f"[数据库] 同步了 {len(nodes_to_create) + len(nodes_to_update)} 个节点和 {len(edges_to_create) + len(edges_to_update)} 条边") + logger.info( + f"[数据库] 同步了 {len(nodes_to_create) + len(nodes_to_update)} 个节点和 {len(edges_to_create) + len(edges_to_update)} 条边" + ) async def resync_memory_to_db(self): """清空数据库并重新同步所有记忆数据""" @@ -888,7 +884,7 @@ class EntorhinalCortex: nodes_data = [] for concept, data in memory_nodes: memory_items = data.get("memory_items", "") - + # 直接检查字符串是否为空,不需要分割成列表 if not memory_items or memory_items.strip() == "": self.memory_graph.G.remove_node(concept) @@ -960,7 +956,7 @@ class EntorhinalCortex: # 清空当前图 self.memory_graph.G.clear() - + # 统计加载情况 total_nodes = 0 loaded_nodes = 0 @@ -969,7 +965,7 @@ class EntorhinalCortex: # 从数据库加载所有节点 nodes = list(GraphNodes.select()) total_nodes = len(nodes) - + for node in nodes: concept = node.concept try: @@ -978,7 +974,7 @@ class EntorhinalCortex: logger.warning(f"节点 {concept} 的memory_items为空,跳过") skipped_nodes += 1 continue - + # 直接使用memory_items memory_items = node.memory_items.strip() @@ -999,11 +995,15 @@ class EntorhinalCortex: last_modified = node.last_modified or current_time # 获取权重属性 - weight = node.weight if hasattr(node, 'weight') and node.weight is not None else 1.0 - + weight = node.weight if hasattr(node, "weight") and node.weight is not None else 1.0 + # 添加节点到图中 self.memory_graph.G.add_node( - concept, memory_items=memory_items, weight=weight, created_time=created_time, last_modified=last_modified + concept, + memory_items=memory_items, + weight=weight, + created_time=created_time, + last_modified=last_modified, ) loaded_nodes += 1 except Exception as e: @@ -1044,9 +1044,11 @@ class EntorhinalCortex: if need_update: logger.info("[数据库] 已为缺失的时间字段进行补充") - + # 输出加载统计信息 - logger.info(f"[数据库] 记忆加载完成: 总计 {total_nodes} 个节点, 成功加载 {loaded_nodes} 个, 跳过 {skipped_nodes} 个") + logger.info( + f"[数据库] 记忆加载完成: 总计 {total_nodes} 个节点, 成功加载 {loaded_nodes} 个, 跳过 {skipped_nodes} 个" + ) # 负责整合,遗忘,合并记忆 @@ -1054,10 +1056,12 @@ class ParahippocampalGyrus: def __init__(self, hippocampus: Hippocampus): self.hippocampus = hippocampus self.memory_graph = hippocampus.memory_graph - - self.memory_modify_model = LLMRequest(model_set=model_config.model_task_config.utils, request_type="memory.modify") - async def memory_compress(self, messages: list, compress_rate=0.1): + self.memory_modify_model = LLMRequest( + model_set=model_config.model_task_config.utils, request_type="memory.modify" + ) + + async def memory_compress(self, messages: list[DatabaseMessages], compress_rate=0.1): """压缩和总结消息内容,生成记忆主题和摘要。 Args: @@ -1162,7 +1166,7 @@ class ParahippocampalGyrus: similar_topics.sort(key=lambda x: x[1], reverse=True) similar_topics = similar_topics[:3] similar_topics_dict[topic] = similar_topics - + if global_config.debug.show_prompt: logger.info(f"prompt: {topic_what_prompt}") logger.info(f"压缩后的记忆: {compressed_memory}") @@ -1258,14 +1262,14 @@ class ParahippocampalGyrus: # --- 如果节点不为空,则执行原来的不活跃检查和随机移除逻辑 --- last_modified = node_data.get("last_modified", current_time) node_weight = node_data.get("weight", 1.0) - + # 条件1:检查是否长时间未修改 (使用配置的遗忘时间) time_threshold = 3600 * global_config.memory.memory_forget_time - + # 基于权重调整遗忘阈值:权重越高,需要更长时间才能被遗忘 # 权重为1时使用默认阈值,权重越高阈值越大(越难遗忘) adjusted_threshold = time_threshold * node_weight - + if current_time - last_modified > adjusted_threshold and memory_items: # 既然每个节点现在是完整记忆,直接删除整个节点 try: @@ -1314,8 +1318,6 @@ class ParahippocampalGyrus: logger.info(f"[遗忘] 总耗时: {end_time - start_time:.2f}秒") - - class HippocampusManager: def __init__(self): self._hippocampus: Hippocampus = None # type: ignore @@ -1360,29 +1362,32 @@ class HippocampusManager: """为指定chat_id构建记忆(在heartFC_chat.py中调用)""" if not self._initialized: raise RuntimeError("HippocampusManager 尚未初始化,请先调用 initialize 方法") - + try: # 检查是否需要构建记忆 logger.info(f"为 {chat_id} 构建记忆") if memory_segment_manager.check_and_build_memory_for_chat(chat_id): logger.info(f"为 {chat_id} 构建记忆,需要构建记忆") messages = memory_segment_manager.get_messages_for_memory_build(chat_id, 50) - + build_probability = 0.3 * global_config.memory.memory_build_frequency - + if messages and random.random() < build_probability: logger.info(f"为 {chat_id} 构建记忆,消息数量: {len(messages)}") - + # 调用记忆压缩和构建 - compressed_memory, similar_topics_dict = await self._hippocampus.parahippocampal_gyrus.memory_compress( + ( + compressed_memory, + similar_topics_dict, + ) = await self._hippocampus.parahippocampal_gyrus.memory_compress( messages, global_config.memory.memory_compress_rate ) - + # 添加记忆节点 current_time = time.time() for topic, memory in compressed_memory: await self._hippocampus.memory_graph.add_dot(topic, memory, self._hippocampus) - + # 连接相似主题 if topic in similar_topics_dict: similar_topics = similar_topics_dict[topic] @@ -1390,23 +1395,23 @@ class HippocampusManager: if topic != similar_topic: strength = int(similarity * 10) self._hippocampus.memory_graph.G.add_edge( - topic, similar_topic, + topic, + similar_topic, strength=strength, created_time=current_time, - last_modified=current_time + last_modified=current_time, ) - + # 同步到数据库 await self._hippocampus.entorhinal_cortex.sync_memory_to_db() logger.info(f"为 {chat_id} 构建记忆完成") return True - + except Exception as e: logger.error(f"为 {chat_id} 构建记忆失败: {e}") return False - - return False + return False async def get_memory_from_topic( self, valid_keywords: list[str], max_memory_num: int = 3, max_memory_length: int = 2, max_depth: int = 3 @@ -1423,16 +1428,20 @@ class HippocampusManager: response = [] return response - async def get_activate_from_text(self, text: str, max_depth: int = 3, fast_retrieval: bool = False) -> tuple[float, list[str]]: + async def get_activate_from_text( + self, text: str, max_depth: int = 3, fast_retrieval: bool = False + ) -> tuple[float, list[str]]: """从文本中获取激活值的公共接口""" if not self._initialized: raise RuntimeError("HippocampusManager 尚未初始化,请先调用 initialize 方法") try: - response, keywords,keywords_lite = await self._hippocampus.get_activate_from_text(text, max_depth, fast_retrieval) + response, keywords, keywords_lite = await self._hippocampus.get_activate_from_text( + text, max_depth, fast_retrieval + ) except Exception as e: logger.error(f"文本产生激活值失败: {e}") logger.error(traceback.format_exc()) - return 0.0, [],[] + return 0.0, [], [] def get_memory_from_keyword(self, keyword: str, max_depth: int = 2) -> list: """从关键词获取相关记忆的公共接口""" @@ -1454,81 +1463,78 @@ hippocampus_manager = HippocampusManager() # 在Hippocampus类中添加新的记忆构建管理器 class MemoryBuilder: """记忆构建器 - + 为每个chat_id维护消息缓存和触发机制,类似ExpressionLearner """ - + def __init__(self, chat_id: str): self.chat_id = chat_id self.last_update_time: float = time.time() self.last_processed_time: float = 0.0 - + def should_trigger_memory_build(self) -> bool: """检查是否应该触发记忆构建""" current_time = time.time() - + # 检查时间间隔 time_diff = current_time - self.last_update_time - if time_diff < 600 /global_config.memory.memory_build_frequency: + if time_diff < 600 / global_config.memory.memory_build_frequency: return False - + # 检查消息数量 - + recent_messages = get_raw_msg_by_timestamp_with_chat_inclusive( chat_id=self.chat_id, timestamp_start=self.last_update_time, timestamp_end=current_time, ) - + logger.info(f"最近消息数量: {len(recent_messages)},间隔时间: {time_diff}") - - if not recent_messages or len(recent_messages) < 30/global_config.memory.memory_build_frequency : + + if not recent_messages or len(recent_messages) < 30 / global_config.memory.memory_build_frequency: return False - + return True - - def get_messages_for_memory_build(self, threshold: int = 25) -> List[Dict[str, Any]]: + + def get_messages_for_memory_build(self, threshold: int = 25) -> List[DatabaseMessages]: """获取用于记忆构建的消息""" current_time = time.time() - - + messages = get_raw_msg_by_timestamp_with_chat_inclusive( chat_id=self.chat_id, timestamp_start=self.last_update_time, timestamp_end=current_time, limit=threshold, ) - tmp_msg = [msg.__dict__ for msg in messages] if messages else [] if messages: # 更新最后处理时间 self.last_processed_time = current_time self.last_update_time = current_time - return tmp_msg or [] - + return messages or [] class MemorySegmentManager: """记忆段管理器 - + 管理所有chat_id的MemoryBuilder实例,自动检查和触发记忆构建 """ - + def __init__(self): self.builders: Dict[str, MemoryBuilder] = {} - + def get_or_create_builder(self, chat_id: str) -> MemoryBuilder: """获取或创建指定chat_id的MemoryBuilder""" if chat_id not in self.builders: self.builders[chat_id] = MemoryBuilder(chat_id) return self.builders[chat_id] - + def check_and_build_memory_for_chat(self, chat_id: str) -> bool: """检查指定chat_id是否需要构建记忆,如果需要则返回True""" builder = self.get_or_create_builder(chat_id) return builder.should_trigger_memory_build() - - def get_messages_for_memory_build(self, chat_id: str, threshold: int = 25) -> List[Dict[str, Any]]: + + def get_messages_for_memory_build(self, chat_id: str, threshold: int = 25) -> List[DatabaseMessages]: """获取指定chat_id用于记忆构建的消息""" if chat_id not in self.builders: return [] @@ -1537,4 +1543,3 @@ class MemorySegmentManager: # 创建全局实例 memory_segment_manager = MemorySegmentManager() - diff --git a/src/chat/memory_system/memory_activator.py b/src/chat/memory_system/memory_activator.py index 79521760..ce7daef5 100644 --- a/src/chat/memory_system/memory_activator.py +++ b/src/chat/memory_system/memory_activator.py @@ -1,17 +1,17 @@ import json +import random from json_repair import repair_json from typing import List, Tuple - -from src.llm_models.utils_model import LLMRequest 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.memory_system.Hippocampus import hippocampus_manager from src.chat.utils.utils import parse_keywords_string from src.chat.utils.chat_message_builder import build_readable_messages -import random +from src.chat.memory_system.Hippocampus import hippocampus_manager +from src.llm_models.utils_model import LLMRequest logger = get_logger("memory_activator") @@ -75,19 +75,20 @@ class MemoryActivator: request_type="memory.selection", ) - - async def activate_memory_with_chat_history(self, target_message, chat_history_prompt) -> List[Tuple[str, str]]: + 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_prompt: - keywords = parse_keywords_string(msg.get("key_words", "")) + + for msg in chat_history: + keywords = parse_keywords_string(msg.key_words) if keywords: if len(keywords_list) < 30: # 最多容纳30个关键词 @@ -95,24 +96,22 @@ class MemoryActivator: 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 = [] @@ -120,12 +119,7 @@ class MemoryActivator: # 为每个记忆分配随机ID并过滤相关记忆 for memory in related_memory: keyword, content = memory - found = False - for kw in keywords_list: - if kw in content: - found = True - break - + found = any(kw in content for kw in keywords_list) if found: # 随机分配一个不重复的2位数id while True: @@ -138,94 +132,83 @@ class MemoryActivator: 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] - - # 使用 LLM 选择合适的记忆 - selected_memories = await self._select_memories_with_llm(target_message, chat_history_prompt, candidate_memories) - - return selected_memories - async def _select_memories_with_llm(self, target_message, chat_history_prompt, candidate_memories) -> List[Tuple[str, str]]: + 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_prompt, + 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 + 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 + 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 字段 - memory_ids_str = result.get("memory_ids", "") - - # 解析逗号分隔的编号 - if memory_ids_str: + + # 提取 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] @@ -235,26 +218,24 @@ class MemoryActivator: 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} - - for memory_id in selected_memory_ids: - if memory_id in memory_id_to_memory: - selected_memories.append(memory_id_to_memory[memory_id]) - + + 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/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index b6c96586..228547af 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -8,6 +8,7 @@ from typing import List, Optional, Dict, Any, Tuple from datetime import datetime from src.mais4u.mai_think import mai_thinking_manager from src.common.logger import get_logger +from src.common.data_models.database_data_model import DatabaseMessages from src.config.config import global_config, model_config from src.individuality.individuality import get_individuality from src.llm_models.utils_model import LLMRequest @@ -296,7 +297,7 @@ class DefaultReplyer: if not sender: return "" - + if sender == global_config.bot.nickname: return "" @@ -352,7 +353,7 @@ class DefaultReplyer: return f"{expression_habits_title}\n{expression_habits_block}", selected_ids - async def build_memory_block(self, chat_history: List[Dict[str, Any]], target: str) -> str: + async def build_memory_block(self, chat_history: List[DatabaseMessages], target: str) -> str: """构建记忆块 Args: @@ -369,7 +370,7 @@ class DefaultReplyer: instant_memory = None running_memories = await self.memory_activator.activate_memory_with_chat_history( - target_message=target, chat_history_prompt=chat_history + target_message=target, chat_history=chat_history ) if global_config.memory.enable_instant_memory: @@ -433,7 +434,7 @@ class DefaultReplyer: logger.error(f"工具信息获取失败: {e}") return "" - def _parse_reply_target(self, target_message: str) -> Tuple[str, str]: + def _parse_reply_target(self, target_message: Optional[str]) -> Tuple[str, str]: """解析回复目标消息 Args: @@ -514,7 +515,7 @@ class DefaultReplyer: return name, result, duration def build_s4u_chat_history_prompts( - self, message_list_before_now: List[Dict[str, Any]], target_user_id: str, sender: str + self, message_list_before_now: List[DatabaseMessages], target_user_id: str, sender: str ) -> Tuple[str, str]: """ 构建 s4u 风格的分离对话 prompt @@ -530,16 +531,16 @@ class DefaultReplyer: bot_id = str(global_config.bot.qq_account) # 过滤消息:分离bot和目标用户的对话 vs 其他用户的对话 - for msg_dict in message_list_before_now: + for msg in message_list_before_now: try: - msg_user_id = str(msg_dict.get("user_id")) - reply_to = msg_dict.get("reply_to", "") + msg_user_id = str(msg.user_info.user_id) + reply_to = msg.reply_to _platform, reply_to_user_id = self._parse_reply_target(reply_to) if (msg_user_id == bot_id and reply_to_user_id == target_user_id) or msg_user_id == target_user_id: # bot 和目标用户的对话 - core_dialogue_list.append(msg_dict) + core_dialogue_list.append(msg) except Exception as e: - logger.error(f"处理消息记录时出错: {msg_dict}, 错误: {e}") + logger.error(f"处理消息记录时出错: {msg}, 错误: {e}") # 构建背景对话 prompt all_dialogue_prompt = "" @@ -574,7 +575,6 @@ class DefaultReplyer: core_dialogue_prompt_str = build_readable_messages( core_dialogue_list, replace_bot_name=True, - merge_messages=False, timestamp_mode="normal_no_YMD", read_mark=0.0, truncate=True, @@ -712,25 +712,20 @@ class DefaultReplyer: target = replace_user_references_sync(target, chat_stream.platform, replace_bot_name=True) - # TODO: 修复! - from src.common.data_models import temporarily_transform_class_to_dict message_list_before_now_long = get_raw_msg_before_timestamp_with_chat( chat_id=chat_id, timestamp=time.time(), limit=global_config.chat.max_context_size * 1, ) - temp_msg_list_before_long = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now_long] - # TODO: 修复! message_list_before_short = get_raw_msg_before_timestamp_with_chat( chat_id=chat_id, timestamp=time.time(), limit=int(global_config.chat.max_context_size * 0.33), ) - temp_msg_list_before_short = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_short] chat_talking_prompt_short = build_readable_messages( - temp_msg_list_before_short, + message_list_before_short, replace_bot_name=True, timestamp_mode="relative", read_mark=0.0, @@ -743,7 +738,7 @@ class DefaultReplyer: self.build_expression_habits(chat_talking_prompt_short, target), "expression_habits" ), self._time_and_run_task(self.build_relation_info(sender, target), "relation_info"), - self._time_and_run_task(self.build_memory_block(temp_msg_list_before_short, target), "memory_block"), + self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"), self._time_and_run_task( self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info" ), @@ -827,7 +822,7 @@ class DefaultReplyer: # 构建分离的对话 prompt core_dialogue_prompt, background_dialogue_prompt = self.build_s4u_chat_history_prompts( - temp_msg_list_before_long, user_id, sender + message_list_before_now_long, user_id, sender ) if global_config.bot.qq_account == user_id and platform == global_config.bot.platform: @@ -901,11 +896,8 @@ class DefaultReplyer: timestamp=time.time(), limit=min(int(global_config.chat.max_context_size * 0.33), 15), ) - # TODO: 修复! - from src.common.data_models import temporarily_transform_class_to_dict - temp_msg_list_before_now_half = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now_half] chat_talking_prompt_half = build_readable_messages( - temp_msg_list_before_now_half, + message_list_before_now_half, replace_bot_name=True, timestamp_mode="relative", read_mark=0.0, @@ -913,7 +905,7 @@ class DefaultReplyer: ) # 并行执行2个构建任务 - (expression_habits_block, selected_expressions), relation_info = await asyncio.gather( + (expression_habits_block, _), relation_info = await asyncio.gather( self.build_expression_habits(chat_talking_prompt_half, target), self.build_relation_info(sender, target), ) diff --git a/src/common/data_models/__init__.py b/src/common/data_models/__init__.py index ac08b89b..222ff59c 100644 --- a/src/common/data_models/__init__.py +++ b/src/common/data_models/__init__.py @@ -1,5 +1,5 @@ import copy -from typing import Dict, Any +from typing import Any class BaseDataModel: @@ -7,6 +7,7 @@ class BaseDataModel: return copy.deepcopy(self) def temporarily_transform_class_to_dict(obj: Any) -> Any: + # sourcery skip: assign-if-exp, reintroduce-else """ 将对象或容器中的 BaseDataModel 子类(类对象)或 BaseDataModel 实例 递归转换为普通 dict,不修改原对象。 diff --git a/src/mais4u/mais4u_chat/body_emotion_action_manager.py b/src/mais4u/mais4u_chat/body_emotion_action_manager.py index 3ccce83a..83e6818f 100644 --- a/src/mais4u/mais4u_chat/body_emotion_action_manager.py +++ b/src/mais4u/mais4u_chat/body_emotion_action_manager.py @@ -163,11 +163,8 @@ class ChatAction: limit=15, limit_mode="last", ) - # TODO: 修复! - from src.common.data_models import temporarily_transform_class_to_dict - tmp_msgs = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now] chat_talking_prompt = build_readable_messages( - tmp_msgs, + message_list_before_now, replace_bot_name=True, timestamp_mode="normal_no_YMD", read_mark=0.0, @@ -229,11 +226,8 @@ class ChatAction: limit=10, limit_mode="last", ) - # TODO: 修复! - from src.common.data_models import temporarily_transform_class_to_dict - tmp_msgs = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now] chat_talking_prompt = build_readable_messages( - tmp_msgs, + message_list_before_now, replace_bot_name=True, timestamp_mode="normal_no_YMD", read_mark=0.0, diff --git a/src/mais4u/mais4u_chat/s4u_mood_manager.py b/src/mais4u/mais4u_chat/s4u_mood_manager.py index 2beb945e..da54acd0 100644 --- a/src/mais4u/mais4u_chat/s4u_mood_manager.py +++ b/src/mais4u/mais4u_chat/s4u_mood_manager.py @@ -166,11 +166,9 @@ class ChatMood: limit=10, limit_mode="last", ) - # TODO: 修复! - from src.common.data_models import temporarily_transform_class_to_dict - tmp_msgs = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now] + chat_talking_prompt = build_readable_messages( - tmp_msgs, + message_list_before_now, replace_bot_name=True, timestamp_mode="normal_no_YMD", read_mark=0.0, @@ -247,11 +245,9 @@ class ChatMood: limit=5, limit_mode="last", ) - # TODO: 修复! - from src.common.data_models import temporarily_transform_class_to_dict - tmp_msgs = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now] + chat_talking_prompt = build_readable_messages( - tmp_msgs, + message_list_before_now, replace_bot_name=True, timestamp_mode="normal_no_YMD", read_mark=0.0, diff --git a/src/mais4u/mais4u_chat/s4u_prompt.py b/src/mais4u/mais4u_chat/s4u_prompt.py index d735d7c2..86447e27 100644 --- a/src/mais4u/mais4u_chat/s4u_prompt.py +++ b/src/mais4u/mais4u_chat/s4u_prompt.py @@ -17,6 +17,10 @@ from src.mais4u.mais4u_chat.screen_manager import screen_manager from src.chat.express.expression_selector import expression_selector from .s4u_mood_manager import mood_manager from src.mais4u.mais4u_chat.internal_manager import internal_manager +from src.common.data_models.database_data_model import DatabaseMessages + +from typing import List + logger = get_logger("prompt") @@ -58,7 +62,7 @@ def init_prompt(): """, "s4u_prompt", # New template for private CHAT chat ) - + Prompt( """ 你的名字是麦麦, 是千石可乐开发的程序,可以在QQ,微信等平台发言,你现在正在哔哩哔哩作为虚拟主播进行直播 @@ -95,14 +99,13 @@ class PromptBuilder: def __init__(self): self.prompt_built = "" self.activate_messages = "" - - async def build_expression_habits(self, chat_stream: ChatStream, chat_history, target): + async def build_expression_habits(self, chat_stream: ChatStream, chat_history, target): style_habits = [] # 使用从处理器传来的选中表达方式 # LLM模式:调用LLM选择5-10个,然后随机选5个 - selected_expressions ,_ = await expression_selector.select_suitable_expressions_llm( + selected_expressions, _ = await expression_selector.select_suitable_expressions_llm( chat_stream.stream_id, chat_history, max_num=12, target_message=target ) @@ -122,7 +125,6 @@ class PromptBuilder: if style_habits_str.strip(): expression_habits_block += f"你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中:\n{style_habits_str}\n\n" - return expression_habits_block async def build_relation_info(self, chat_stream) -> str: @@ -148,9 +150,7 @@ class PromptBuilder: person_ids.append(person_id) # 使用 Person 的 build_relationship 方法,设置 points_num=3 保持与原来相同的行为 - relation_info_list = [ - Person(person_id=person_id).build_relationship() for person_id in person_ids - ] + relation_info_list = [Person(person_id=person_id).build_relationship() for person_id in person_ids] if relation_info := "".join(relation_info_list): relation_prompt = await global_prompt_manager.format_prompt( "relation_prompt", relation_info=relation_info @@ -160,7 +160,7 @@ class PromptBuilder: async def build_memory_block(self, text: str) -> str: # 待更新记忆系统 return "" - + related_memory = await hippocampus_manager.get_memory_from_text( text=text, max_memory_num=2, max_memory_length=2, max_depth=3, fast_retrieval=False ) @@ -176,38 +176,37 @@ class PromptBuilder: message_list_before_now = get_raw_msg_before_timestamp_with_chat( chat_id=chat_stream.stream_id, timestamp=time.time(), + # sourcery skip: lift-duplicated-conditional, merge-duplicate-blocks, remove-redundant-if limit=300, ) - talk_type = f"{message.message_info.platform}:{str(message.chat_stream.user_info.user_id)}" - core_dialogue_list = [] - background_dialogue_list = [] + core_dialogue_list: List[DatabaseMessages] = [] + background_dialogue_list: List[DatabaseMessages] = [] bot_id = str(global_config.bot.qq_account) target_user_id = str(message.chat_stream.user_info.user_id) - # TODO: 修复之! for msg in message_list_before_now: try: msg_user_id = str(msg.user_info.user_id) if msg_user_id == bot_id: if msg.reply_to and talk_type == msg.reply_to: - core_dialogue_list.append(msg.__dict__) + core_dialogue_list.append(msg) elif msg.reply_to and talk_type != msg.reply_to: - background_dialogue_list.append(msg.__dict__) + background_dialogue_list.append(msg) # else: - # background_dialogue_list.append(msg_dict) + # background_dialogue_list.append(msg_dict) elif msg_user_id == target_user_id: - core_dialogue_list.append(msg.__dict__) + core_dialogue_list.append(msg) else: - background_dialogue_list.append(msg.__dict__) + background_dialogue_list.append(msg) except Exception as e: logger.error(f"无法处理历史消息记录: {msg.__dict__}, 错误: {e}") background_dialogue_prompt = "" if background_dialogue_list: - context_msgs = background_dialogue_list[-s4u_config.max_context_message_length:] + context_msgs = background_dialogue_list[-s4u_config.max_context_message_length :] background_dialogue_prompt_str = build_readable_messages( context_msgs, timestamp_mode="normal_no_YMD", @@ -217,10 +216,10 @@ class PromptBuilder: core_msg_str = "" if core_dialogue_list: - core_dialogue_list = core_dialogue_list[-s4u_config.max_core_message_length:] + core_dialogue_list = core_dialogue_list[-s4u_config.max_core_message_length :] first_msg = core_dialogue_list[0] - start_speaking_user_id = first_msg.get("user_id") + start_speaking_user_id = first_msg.user_info.user_id if start_speaking_user_id == bot_id: last_speaking_user_id = bot_id msg_seg_str = "你的发言:\n" @@ -229,13 +228,13 @@ class PromptBuilder: last_speaking_user_id = start_speaking_user_id msg_seg_str = "对方的发言:\n" - msg_seg_str += f"{time.strftime('%H:%M:%S', time.localtime(first_msg.get('time')))}: {first_msg.get('processed_plain_text')}\n" + msg_seg_str += f"{time.strftime('%H:%M:%S', time.localtime(first_msg.time))}: {first_msg.processed_plain_text}\n" all_msg_seg_list = [] for msg in core_dialogue_list[1:]: - speaker = msg.get("user_id") + speaker = msg.user_info.user_id if speaker == last_speaking_user_id: - msg_seg_str += f"{time.strftime('%H:%M:%S', time.localtime(msg.get('time')))}: {msg.get('processed_plain_text')}\n" + msg_seg_str += f"{time.strftime('%H:%M:%S', time.localtime(msg.time))}: {msg.processed_plain_text}\n" else: msg_seg_str = f"{msg_seg_str}\n" all_msg_seg_list.append(msg_seg_str) @@ -252,46 +251,40 @@ class PromptBuilder: for msg in all_msg_seg_list: core_msg_str += msg - - all_dialogue_prompt = get_raw_msg_before_timestamp_with_chat( + all_dialogue_history = get_raw_msg_before_timestamp_with_chat( chat_id=chat_stream.stream_id, timestamp=time.time(), limit=20, ) - # TODO: 修复! - from src.common.data_models import temporarily_transform_class_to_dict - tmp_msgs = [temporarily_transform_class_to_dict(msg) for msg in all_dialogue_prompt] + all_dialogue_prompt_str = build_readable_messages( - tmp_msgs, + all_dialogue_history, timestamp_mode="normal_no_YMD", show_pic=False, ) - - return core_msg_str, background_dialogue_prompt,all_dialogue_prompt_str + return core_msg_str, background_dialogue_prompt, all_dialogue_prompt_str def build_gift_info(self, message: MessageRecvS4U): if message.is_gift: - return f"这是一条礼物信息,{message.gift_name} x{message.gift_count},请注意这位用户" + return f"这是一条礼物信息,{message.gift_name} x{message.gift_count},请注意这位用户" else: if message.is_fake_gift: return f"{message.processed_plain_text}(注意:这是一条普通弹幕信息,对方没有真的发送礼物,不是礼物信息,注意区分,如果对方在发假的礼物骗你,请反击)" - + return "" def build_sc_info(self, message: MessageRecvS4U): super_chat_manager = get_super_chat_manager() return super_chat_manager.build_superchat_summary_string(message.chat_stream.stream_id) - async def build_prompt_normal( self, message: MessageRecvS4U, message_txt: str, ) -> str: - chat_stream = message.chat_stream - + person = Person(platform=message.chat_stream.user_info.platform, user_id=message.chat_stream.user_info.user_id) person_name = person.person_name @@ -302,28 +295,31 @@ class PromptBuilder: sender_name = f"[{message.chat_stream.user_info.user_nickname}]" else: sender_name = f"用户({message.chat_stream.user_info.user_id})" - - + relation_info_block, memory_block, expression_habits_block = await asyncio.gather( - self.build_relation_info(chat_stream), self.build_memory_block(message_txt), self.build_expression_habits(chat_stream, message_txt, sender_name) + self.build_relation_info(chat_stream), + self.build_memory_block(message_txt), + self.build_expression_habits(chat_stream, message_txt, sender_name), + ) + + core_dialogue_prompt, background_dialogue_prompt, all_dialogue_prompt = self.build_chat_history_prompts( + chat_stream, message ) - core_dialogue_prompt, background_dialogue_prompt,all_dialogue_prompt = self.build_chat_history_prompts(chat_stream, message) - gift_info = self.build_gift_info(message) - + sc_info = self.build_sc_info(message) - + screen_info = screen_manager.get_screen_str() - + internal_state = internal_manager.get_internal_state_str() time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - + mood = mood_manager.get_mood_by_chat_id(chat_stream.stream_id) template_name = "s4u_prompt" - + if not message.is_internal: prompt = await global_prompt_manager.format_prompt( template_name, @@ -356,7 +352,7 @@ class PromptBuilder: mind=message.processed_plain_text, mood_state=mood.mood_state, ) - + # print(prompt) return prompt diff --git a/src/mood/mood_manager.py b/src/mood/mood_manager.py index abdb5b3f..b64188b4 100644 --- a/src/mood/mood_manager.py +++ b/src/mood/mood_manager.py @@ -99,11 +99,9 @@ class ChatMood: limit=int(global_config.chat.max_context_size / 3), limit_mode="last", ) - # TODO: 修复! - from src.common.data_models import temporarily_transform_class_to_dict - tmp_msgs = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now] + chat_talking_prompt = build_readable_messages( - tmp_msgs, + message_list_before_now, replace_bot_name=True, timestamp_mode="normal_no_YMD", read_mark=0.0, @@ -150,11 +148,9 @@ class ChatMood: limit=15, limit_mode="last", ) - # TODO: 修复 - from src.common.data_models import temporarily_transform_class_to_dict - tmp_msgs = [temporarily_transform_class_to_dict(msg) for msg in message_list_before_now] + chat_talking_prompt = build_readable_messages( - tmp_msgs, + message_list_before_now, replace_bot_name=True, timestamp_mode="normal_no_YMD", read_mark=0.0, diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index 67958399..916162a8 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -1,18 +1,21 @@ +import json +import traceback + +from json_repair import repair_json +from datetime import datetime +from typing import List + from src.common.logger import get_logger -from .person_info import Person -import random +from src.common.data_models.database_data_model import DatabaseMessages from src.llm_models.utils_model import LLMRequest from src.config.config import global_config, model_config from src.chat.utils.chat_message_builder import build_readable_messages -import json -from json_repair import repair_json -from datetime import datetime -from typing import List, Dict, Any from src.chat.utils.prompt_builder import Prompt, global_prompt_manager -import traceback +from .person_info import Person logger = get_logger("relation") + def init_prompt(): Prompt( """ @@ -45,8 +48,7 @@ def init_prompt(): """, "attitude_to_me_prompt", ) - - + Prompt( """ 你的名字是{bot_name},{bot_name}的别名是{alias_str}。 @@ -80,104 +82,102 @@ def init_prompt(): "neuroticism_prompt", ) + class RelationshipManager: def __init__(self): self.relationship_llm = LLMRequest( model_set=model_config.model_task_config.utils, request_type="relationship.person" - ) - + ) + async def get_attitude_to_me(self, readable_messages, timestamp, person: Person): alias_str = ", ".join(global_config.bot.alias_names) current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") # 解析当前态度值 current_attitude_score = person.attitude_to_me total_confidence = person.attitude_to_me_confidence - + prompt = await global_prompt_manager.format_prompt( "attitude_to_me_prompt", - bot_name = global_config.bot.nickname, - alias_str = alias_str, - person_name = person.person_name, - nickname = person.nickname, - readable_messages = readable_messages, - current_time = current_time, + bot_name=global_config.bot.nickname, + alias_str=alias_str, + person_name=person.person_name, + nickname=person.nickname, + readable_messages=readable_messages, + current_time=current_time, ) - + attitude, _ = await self.relationship_llm.generate_response_async(prompt=prompt) - - attitude = repair_json(attitude) attitude_data = json.loads(attitude) - + if not attitude_data or (isinstance(attitude_data, list) and len(attitude_data) == 0): return "" - + # 确保 attitude_data 是字典格式 if not isinstance(attitude_data, dict): logger.warning(f"LLM返回了错误的JSON格式,跳过解析: {type(attitude_data)}, 内容: {attitude_data}") return "" - + attitude_score = attitude_data["attitude"] - confidence = pow(attitude_data["confidence"],2) - + confidence = pow(attitude_data["confidence"], 2) + new_confidence = total_confidence + confidence - new_attitude_score = (current_attitude_score * total_confidence + attitude_score * confidence)/new_confidence - + new_attitude_score = (current_attitude_score * total_confidence + attitude_score * confidence) / new_confidence + person.attitude_to_me = new_attitude_score person.attitude_to_me_confidence = new_confidence - + return person - + async def get_neuroticism(self, readable_messages, timestamp, person: Person): alias_str = ", ".join(global_config.bot.alias_names) current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") # 解析当前态度值 current_neuroticism_score = person.neuroticism total_confidence = person.neuroticism_confidence - + prompt = await global_prompt_manager.format_prompt( "neuroticism_prompt", - bot_name = global_config.bot.nickname, - alias_str = alias_str, - person_name = person.person_name, - nickname = person.nickname, - readable_messages = readable_messages, - current_time = current_time, + bot_name=global_config.bot.nickname, + alias_str=alias_str, + person_name=person.person_name, + nickname=person.nickname, + readable_messages=readable_messages, + current_time=current_time, ) - - neuroticism, _ = await self.relationship_llm.generate_response_async(prompt=prompt) + neuroticism, _ = await self.relationship_llm.generate_response_async(prompt=prompt) # logger.info(f"prompt: {prompt}") # logger.info(f"neuroticism: {neuroticism}") - neuroticism = repair_json(neuroticism) neuroticism_data = json.loads(neuroticism) - + if not neuroticism_data or (isinstance(neuroticism_data, list) and len(neuroticism_data) == 0): return "" - + # 确保 neuroticism_data 是字典格式 if not isinstance(neuroticism_data, dict): logger.warning(f"LLM返回了错误的JSON格式,跳过解析: {type(neuroticism_data)}, 内容: {neuroticism_data}") return "" - + neuroticism_score = neuroticism_data["neuroticism"] - confidence = pow(neuroticism_data["confidence"],2) - + confidence = pow(neuroticism_data["confidence"], 2) + new_confidence = total_confidence + confidence - - new_neuroticism_score = (current_neuroticism_score * total_confidence + neuroticism_score * confidence)/new_confidence - + + new_neuroticism_score = ( + current_neuroticism_score * total_confidence + neuroticism_score * confidence + ) / new_confidence + person.neuroticism = new_neuroticism_score person.neuroticism_confidence = new_confidence - - return person - - async def update_person_impression(self, person_id, timestamp, bot_engaged_messages: List[Dict[str, Any]]): + return person + + async def update_person_impression(self, person_id, timestamp, bot_engaged_messages: List[DatabaseMessages]): """更新用户印象 Args: @@ -202,12 +202,11 @@ class RelationshipManager: # 遍历消息,构建映射 for msg in user_messages: - if msg.get("user_id") == "system": + if msg.user_info.user_id == "system": continue try: - - user_id = msg.get("user_id") - platform = msg.get("chat_info_platform") + user_id = msg.user_info.user_id + platform = msg.chat_info.platform assert isinstance(user_id, str) and isinstance(platform, str) msg_person = Person(user_id=user_id, platform=platform) @@ -242,19 +241,16 @@ class RelationshipManager: # 确保 original_name 和 mapped_name 都不为 None if original_name is not None and mapped_name is not None: readable_messages = readable_messages.replace(f"{original_name}", f"{mapped_name}") - + # await self.get_points( - # readable_messages=readable_messages, name_mapping=name_mapping, timestamp=timestamp, person=person) + # readable_messages=readable_messages, name_mapping=name_mapping, timestamp=timestamp, person=person) await self.get_attitude_to_me(readable_messages=readable_messages, timestamp=timestamp, person=person) await self.get_neuroticism(readable_messages=readable_messages, timestamp=timestamp, person=person) person.know_times = know_times + 1 person.last_know = timestamp - - person.sync_to_database() - - + person.sync_to_database() def calculate_time_weight(self, point_time: str, current_time: str) -> float: """计算基于时间的权重系数""" @@ -280,6 +276,7 @@ class RelationshipManager: logger.error(f"计算时间权重失败: {e}") return 0.5 # 发生错误时返回中等权重 + init_prompt() relationship_manager = None @@ -290,4 +287,3 @@ def get_relationship_manager(): if relationship_manager is None: relationship_manager = RelationshipManager() return relationship_manager - diff --git a/src/plugin_system/apis/message_api.py b/src/plugin_system/apis/message_api.py index 1dab9d69..7a83f07f 100644 --- a/src/plugin_system/apis/message_api.py +++ b/src/plugin_system/apis/message_api.py @@ -412,7 +412,7 @@ def count_new_messages_for_users(chat_id: str, start_time: float, end_time: floa def build_readable_messages_to_str( - messages: List[Dict[str, Any]], + messages: List[DatabaseMessages], replace_bot_name: bool = True, timestamp_mode: str = "relative", read_mark: float = 0.0, @@ -440,7 +440,7 @@ def build_readable_messages_to_str( async def build_readable_messages_with_details( - messages: List[Dict[str, Any]], + messages: List[DatabaseMessages], replace_bot_name: bool = True, timestamp_mode: str = "relative", truncate: bool = False, diff --git a/src/plugins/built_in/emoji_plugin/emoji.py b/src/plugins/built_in/emoji_plugin/emoji.py index bfb60bde..2a439d27 100644 --- a/src/plugins/built_in/emoji_plugin/emoji.py +++ b/src/plugins/built_in/emoji_plugin/emoji.py @@ -2,13 +2,14 @@ import random from typing import Tuple # 导入新插件系统 -from src.plugin_system import BaseAction, ActionActivationType, ChatMode +from src.plugin_system import BaseAction, ActionActivationType # 导入依赖的系统组件 from src.common.logger import get_logger # 导入API模块 - 标准Python包方式 from src.plugin_system.apis import emoji_api, llm_api, message_api + # NoReplyAction已集成到heartFC_chat.py中,不再需要导入 from src.config.config import global_config @@ -84,11 +85,8 @@ class EmojiAction(BaseAction): messages_text = "" if recent_messages: # 使用message_api构建可读的消息字符串 - # TODO: 修复 - from src.common.data_models import temporarily_transform_class_to_dict - tmp_msgs = [temporarily_transform_class_to_dict(msg) for msg in recent_messages] messages_text = message_api.build_readable_messages( - messages=tmp_msgs, + messages=recent_messages, timestamp_mode="normal_no_YMD", truncate=False, show_actions=False,