feat(short_term_memory): 新增强制清理功能以管理短期记忆溢出

This commit is contained in:
LuiKlee
2025-12-15 15:32:11 +08:00
parent af830b6c03
commit 1730a62363
3 changed files with 89 additions and 2 deletions

View File

@@ -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` 即可关闭;无需代码回滚。

View File

@@ -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:
"""
清除已转移到长期记忆的记忆

View File

@@ -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