From 52314048521f10b493b1d56e073dab228708a584 Mon Sep 17 00:00:00 2001 From: Windpicker-owo <3431391539@qq.com> Date: Wed, 19 Nov 2025 18:52:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E9=95=BF=E6=9C=9F?= =?UTF-8?q?=E8=AE=B0=E5=BF=86=E8=BD=AC=E7=A7=BB=E9=97=B4=E9=9A=94=E5=92=8C?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E5=BB=B6=E8=BF=9F=EF=BC=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E7=9F=AD=E6=9C=9F=E8=AE=B0=E5=BF=86=E6=B8=85=E7=90=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E6=94=B9=E8=BF=9B=E4=B8=89=E7=BA=A7=E8=AE=B0?= =?UTF-8?q?=E5=BF=86=E7=B3=BB=E7=BB=9F=E5=B1=9E=E6=80=A7=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/official_configs.py | 2 +- src/memory_graph/long_term_manager.py | 52 +++++++++++++++---- src/memory_graph/short_term_manager.py | 41 +++++++++++++-- src/memory_graph/unified_manager.py | 20 +++---- .../utils/three_tier_formatter.py | 15 +++--- 5 files changed, 100 insertions(+), 30 deletions(-) diff --git a/src/config/official_configs.py b/src/config/official_configs.py index dc16d1005..f324433bd 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -530,7 +530,7 @@ class ThreeTierMemoryConfig(ValidatedConfigBase): # 长期记忆层配置 long_term_batch_size: int = Field(default=10, description="批量转移大小") long_term_decay_factor: float = Field(default=0.95, description="衰减因子") - long_term_auto_transfer_interval: int = Field(default=600, description="自动转移间隔(秒)") + long_term_auto_transfer_interval: int = Field(default=60, description="自动转移间隔(秒)") # Judge模型配置 judge_model_name: str = Field(default="utils_small", description="用于决策的LLM模型") diff --git a/src/memory_graph/long_term_manager.py b/src/memory_graph/long_term_manager.py index 5efad03ca..2fbf2b69e 100644 --- a/src/memory_graph/long_term_manager.py +++ b/src/memory_graph/long_term_manager.py @@ -553,9 +553,17 @@ class LongTermMemoryManager: return lowered.startswith(("new_", "temp_")) def _register_temp_id( - self, placeholder: str | None, actual_id: str, temp_id_map: dict[str, str] + self, + placeholder: str | None, + actual_id: str, + temp_id_map: dict[str, str], + force: bool = False, ) -> None: - if actual_id and placeholder and self._is_placeholder_id(placeholder): + if not actual_id or not placeholder or not isinstance(placeholder, str): + return + if placeholder == actual_id: + return + if force or self._is_placeholder_id(placeholder): temp_id_map[placeholder] = actual_id def _resolve_id(self, raw_id: str | None, temp_id_map: dict[str, str]) -> str | None: @@ -578,22 +586,36 @@ class LongTermMemoryManager: return {k: self._resolve_value(v, temp_id_map) for k, v in params.items()} def _register_aliases_from_params( - self, params: dict[str, Any], actual_id: str, temp_id_map: dict[str, str] + self, + params: dict[str, Any], + actual_id: str, + temp_id_map: dict[str, str], + *, + extra_keywords: tuple[str, ...] = (), + force: bool = False, ) -> None: - alias_keywords = ("alias", "placeholder", "temp_id", "register_as") + alias_keywords = ("alias", "placeholder", "temp_id", "register_as") + tuple( + extra_keywords + ) for key, value in params.items(): if isinstance(value, str): lower_key = key.lower() if any(keyword in lower_key for keyword in alias_keywords): - self._register_temp_id(value, actual_id, temp_id_map) + self._register_temp_id(value, actual_id, temp_id_map, force=force) elif isinstance(value, list): lower_key = key.lower() if any(keyword in lower_key for keyword in alias_keywords): for item in value: if isinstance(item, str): - self._register_temp_id(item, actual_id, temp_id_map) + self._register_temp_id(item, actual_id, temp_id_map, force=force) elif isinstance(value, dict): - self._register_aliases_from_params(value, actual_id, temp_id_map) + self._register_aliases_from_params( + value, + actual_id, + temp_id_map, + extra_keywords=extra_keywords, + force=force, + ) async def _execute_create_memory( self, @@ -620,7 +642,13 @@ class LongTermMemoryManager: logger.info(f"✅ 创建长期记忆: {memory.id} (来自短期记忆 {source_stm.id})") self._register_temp_id(op.target_id, memory.id, temp_id_map) - self._register_aliases_from_params(op.parameters, memory.id, temp_id_map) + self._register_aliases_from_params( + op.parameters, + memory.id, + temp_id_map, + extra_keywords=("memory_id", "memory_alias", "memory_placeholder"), + force=True, + ) else: logger.error(f"创建长期记忆失败: {op}") @@ -722,7 +750,13 @@ class LongTermMemoryManager: asyncio.create_task(self._generate_node_embedding(node_id, content)) logger.info(f"✅ 创建节点: {content} ({node_type}) -> {memory_id}") self._register_temp_id(op.target_id, node_id, temp_id_map) - self._register_aliases_from_params(op.parameters, node_id, temp_id_map) + self._register_aliases_from_params( + op.parameters, + node_id, + temp_id_map, + extra_keywords=("node_id", "node_alias", "node_placeholder"), + force=True, + ) else: logger.error(f"创建节点失败: {op}") diff --git a/src/memory_graph/short_term_manager.py b/src/memory_graph/short_term_manager.py index 46eeb4c8e..52e4a878a 100644 --- a/src/memory_graph/short_term_manager.py +++ b/src/memory_graph/short_term_manager.py @@ -566,12 +566,43 @@ class ShortTermMemoryManager: """ 获取需要转移到长期记忆的记忆 - 筛选条件:重要性 >= transfer_importance_threshold - - Returns: - 待转移的记忆列表 + 逻辑: + 1. 优先选择重要性 >= 阈值的记忆 + 2. 如果剩余记忆数量仍超过 max_memories,直接清理最早的低重要性记忆直到低于上限 """ - return [mem for mem in self.memories if mem.importance >= self.transfer_importance_threshold] + # 1. 正常筛选:重要性达标的记忆 + candidates = [mem for mem in self.memories if mem.importance >= self.transfer_importance_threshold] + candidate_ids = {mem.id for mem in candidates} + + # 2. 检查低重要性记忆是否积压 + # 剩余的都是低重要性记忆 + low_importance_memories = [mem for mem in self.memories if mem.id not in candidate_ids] + + # 如果低重要性记忆数量超过了上限(说明积压严重) + # 我们需要清理掉一部分,而不是转移它们 + if len(low_importance_memories) > self.max_memories: + # 目标保留数量(降至上限的 90%) + target_keep_count = int(self.max_memories * 0.9) + num_to_remove = len(low_importance_memories) - target_keep_count + + if num_to_remove > 0: + # 按创建时间排序,删除最早的 + low_importance_memories.sort(key=lambda x: x.created_at) + to_remove = low_importance_memories[:num_to_remove] + + for mem in to_remove: + if mem in self.memories: + self.memories.remove(mem) + + logger.info( + f"短期记忆清理: 移除了 {len(to_remove)} 条低重要性记忆 " + f"(保留 {len(self.memories)} 条)" + ) + + # 触发保存 + asyncio.create_task(self._save_to_disk()) + + return candidates async def clear_transferred_memories(self, memory_ids: list[str]) -> None: """ diff --git a/src/memory_graph/unified_manager.py b/src/memory_graph/unified_manager.py index a7755f49b..d358dd475 100644 --- a/src/memory_graph/unified_manager.py +++ b/src/memory_graph/unified_manager.py @@ -108,7 +108,8 @@ class UnifiedMemoryManager: self._initialized = False self._auto_transfer_task: asyncio.Task | None = None self._auto_transfer_interval = max(10.0, float(long_term_auto_transfer_interval)) - self._max_transfer_delay = min(max(30.0, self._auto_transfer_interval), 300.0) + # 优化:降低最大延迟时间,加快转移节奏 (原为 300.0) + self._max_transfer_delay = min(max(30.0, self._auto_transfer_interval), 60.0) self._transfer_wakeup_event: asyncio.Event | None = None logger.info("统一记忆管理器已创建") @@ -430,14 +431,15 @@ class UnifiedMemoryManager: max_memories = max(1, getattr(self.short_term_manager, "max_memories", 1)) occupancy = len(self.short_term_manager.memories) / max_memories - if occupancy >= 0.9: - return max(5.0, base_interval * 0.1) - if occupancy >= 0.75: - return max(10.0, base_interval * 0.2) + # 优化:更激进的自适应间隔,加快高负载下的转移 + if occupancy >= 0.8: + return max(2.0, base_interval * 0.1) if occupancy >= 0.5: - return max(15.0, base_interval * 0.4) + return max(5.0, base_interval * 0.2) if occupancy >= 0.3: - return max(20.0, base_interval * 0.6) + return max(10.0, base_interval * 0.4) + if occupancy >= 0.1: + return max(15.0, base_interval * 0.6) return base_interval @@ -470,7 +472,7 @@ class UnifiedMemoryManager: if len(deduplicated) <= 1: return [] - manual_queries: list[dict[str, float]] = [] + manual_queries: list[dict[str, Any]] = [] decay = 0.15 for idx, text in enumerate(deduplicated): weight = max(0.3, 1.0 - idx * decay) @@ -587,7 +589,7 @@ class UnifiedMemoryManager: should_transfer = ( len(transfer_cache) >= cache_size_threshold - or occupancy_ratio >= 0.85 + or occupancy_ratio >= 0.5 # 优化:降低触发阈值 (原为 0.85) or (transfer_cache and time_since_last_transfer >= self._max_transfer_delay) or len(self.short_term_manager.memories) >= self.short_term_manager.max_memories ) diff --git a/src/memory_graph/utils/three_tier_formatter.py b/src/memory_graph/utils/three_tier_formatter.py index 47ae3cda0..934eec947 100644 --- a/src/memory_graph/utils/three_tier_formatter.py +++ b/src/memory_graph/utils/three_tier_formatter.py @@ -312,7 +312,7 @@ class ThreeTierMemoryFormatter: # 查找客体和属性 objects = [] - attributes = [] + attributes = {} for edge in memory.edges: edge_type = edge.edge_type.value if hasattr(edge.edge_type, 'value') else str(edge.edge_type) @@ -329,17 +329,18 @@ class ThreeTierMemoryFormatter: attr_node = memory.get_node_by_id(edge.target_id) if attr_node: attr_name = edge.relation if edge.relation else "属性" - attributes.append(f"{attr_name}:{attr_node.content}") + # 使用字典避免重复属性,后面的会覆盖前面的 + attributes[attr_name] = attr_node.content - # 检查节点中的属性 + # 检查节点中的属性(处理 "key=value" 格式) for node in memory.nodes: if hasattr(node, 'node_type') and str(node.node_type) == "属性": # 处理 "key=value" 格式的属性 if "=" in node.content: key, value = node.content.split("=", 1) - attributes.append(f"{key.strip()}:{value.strip()}") + attributes[key.strip()] = value.strip() else: - attributes.append(f"属性:{node.content}") + attributes["属性"] = node.content # 构建最终格式 result = f"[{type_label}] {subject}-{topic}" @@ -348,7 +349,9 @@ class ThreeTierMemoryFormatter: result += "-" + "-".join(objects) if attributes: - result += "(" + ",".join(attributes) + ")" + # 将属性字典格式化为简洁的字符串 + attr_strs = [f"{key}:{value}" for key, value in attributes.items()] + result += "(" + ",".join(attr_strs) + ")" return result