diff --git a/docs/changelogs/short_term_pressure_patch.md b/docs/changelogs/short_term_pressure_patch.md new file mode 100644 index 000000000..65dd7ea76 --- /dev/null +++ b/docs/changelogs/short_term_pressure_patch.md @@ -0,0 +1,38 @@ +# 短期记忆压力泄压补丁 + +## 背景 + +部分场景下,短期记忆层在自动转移尚未触发时会快速堆积,可能导致短期记忆达到容量上限并阻塞后续写入。 + +## 变更(补丁) + +- 新增“压力泄压”开关:可选择在占用率达到 100% 时,删除低重要性且最早的短期记忆,防止短期层持续膨胀。 +- 默认关闭,需显式开启后才会执行自动删除。 + +## 开关配置 + +- 入口:`UnifiedMemoryManager` 构造参数 + - `short_term_enable_force_cleanup: bool = False` +- 传递到短期层:`ShortTermMemoryManager(enable_force_cleanup=True)` +- 关闭示例: + ```python + manager = UnifiedMemoryManager( + short_term_enable_force_cleanup=False, + ) + ``` + +## 行为说明 + +- 当短期记忆占用率达到或超过 100%,且当前没有待转移批次时: + - 触发 `force_cleanup_overflow()` + - 按“低重要性优先、创建时间最早优先”删除一批记忆,将容量压回约 `max_memories * 0.9` +- 清理在后台持久化,不阻塞主流程。 + +## 影响范围 + +- 默认行为保持与补丁前一致(开关默认 `on`)。 +- 如果关闭开关,短期层将不再做强制删除,只依赖自动转移机制。 + +## 回滚 + +- 构造时将 `short_term_enable_force_cleanup=False` 即可关闭;无需代码回滚。 diff --git a/src/memory_graph/short_term_manager.py b/src/memory_graph/short_term_manager.py index 763f1525d..5de9ba14d 100644 --- a/src/memory_graph/short_term_manager.py +++ b/src/memory_graph/short_term_manager.py @@ -43,6 +43,7 @@ class ShortTermMemoryManager: max_memories: int = 30, transfer_importance_threshold: float = 0.6, llm_temperature: float = 0.2, + enable_force_cleanup: bool = False, ): """ 初始化短期记忆层管理器 @@ -60,6 +61,7 @@ class ShortTermMemoryManager: self.max_memories = max_memories self.transfer_importance_threshold = transfer_importance_threshold self.llm_temperature = llm_temperature + self.enable_force_cleanup = enable_force_cleanup # 核心数据 self.memories: list[ShortTermMemory] = [] @@ -75,7 +77,8 @@ class ShortTermMemoryManager: logger.info( f"短期记忆管理器已创建 (max_memories={max_memories}, " - f"transfer_threshold={transfer_importance_threshold:.2f})" + f"transfer_threshold={transfer_importance_threshold:.2f}, " + f"force_cleanup={'on' if enable_force_cleanup else 'off'})" ) async def initialize(self) -> None: @@ -687,6 +690,41 @@ class ShortTermMemoryManager: return candidates + def force_cleanup_overflow(self, keep_ratio: float = 0.9) -> int: + """当短期记忆超过容量时,强制删除低重要性且最早的记忆以泄压""" + if not self.enable_force_cleanup: + return 0 + + if self.max_memories <= 0: + return 0 + + current = len(self.memories) + limit = int(self.max_memories * keep_ratio) + if current <= self.max_memories: + return 0 + + # 先按重要性升序,再按创建时间升序删除 + sorted_memories = sorted(self.memories, key=lambda m: (m.importance, m.created_at)) + remove_count = max(0, current - limit) + to_remove = {mem.id for mem in sorted_memories[:remove_count]} + + if not to_remove: + return 0 + + self.memories = [mem for mem in self.memories if mem.id not in to_remove] + for mem_id in to_remove: + self._memory_id_index.pop(mem_id, None) + self._similarity_cache.pop(mem_id, None) + + # 异步保存即可,不阻塞主流程 + asyncio.create_task(self._save_to_disk()) + + logger.warning( + f"短期记忆压力泄压: 移除 {len(to_remove)} 条 (当前 {len(self.memories)}/{self.max_memories})" + ) + + return len(to_remove) + 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 78955c3bc..c6830b49f 100644 --- a/src/memory_graph/unified_manager.py +++ b/src/memory_graph/unified_manager.py @@ -44,6 +44,7 @@ class UnifiedMemoryManager: # 短期记忆配置 short_term_max_memories: int = 30, short_term_transfer_threshold: float = 0.6, + short_term_enable_force_cleanup: bool = False, # 长期记忆配置 long_term_batch_size: int = 10, long_term_search_top_k: int = 5, @@ -96,6 +97,7 @@ class UnifiedMemoryManager: "short_term": { "max_memories": short_term_max_memories, "transfer_importance_threshold": short_term_transfer_threshold, + "enable_force_cleanup": short_term_enable_force_cleanup, }, "long_term": { "batch_size": long_term_batch_size, @@ -565,7 +567,9 @@ class UnifiedMemoryManager: self._transfer_wakeup_event.clear() self._auto_transfer_task = asyncio.create_task(self._auto_transfer_loop()) - logger.debug("自动转移任务已启动") + # 立即触发一次检查,避免启动初期的长时间等待 + self._transfer_wakeup_event.set() + logger.debug("自动转移任务已启动并触发首次检查") async def _auto_transfer_loop(self) -> None: """自动转移循环(批量缓存模式,优化:更高效的缓存管理)""" @@ -611,6 +615,13 @@ class UnifiedMemoryManager: occupancy_ratio = len(self.short_term_manager.memories) / max_memories time_since_last_transfer = time.monotonic() - last_transfer_time + if occupancy_ratio >= 1.0 and not transfer_cache: + removed = self.short_term_manager.force_cleanup_overflow() + if removed > 0: + logger.warning( + f"短期记忆占用率 {occupancy_ratio:.0%},已强制删除 {removed} 条低重要性记忆泄压" + ) + # 优化:优先级判断重构(早期 return) should_transfer = ( len(transfer_cache) >= cache_size_threshold