"""Working Memory - 基于 Redis 的短期任务记忆""" import json import logging from datetime import datetime, timezone from typing import Any import redis.asyncio as aioredis from agentkit.memory.base import Memory, MemoryItem logger = logging.getLogger(__name__) class WorkingMemory(Memory): """Working Memory - 当前任务的上下文和中间状态 基于 Redis 实现,支持自动过期(TTL)。 生命周期:单次任务。 """ def __init__( self, redis: aioredis.Redis, key_prefix: str = "agentkit:working", default_ttl: int = 3600, ): self._redis = redis self._key_prefix = key_prefix self._default_ttl = default_ttl def _make_key(self, key: str) -> str: return f"{self._key_prefix}:{key}" async def store(self, key: str, value: Any, metadata: dict[str, Any] | None = None) -> None: redis_key = self._make_key(key) item = MemoryItem( key=key, value=value, metadata=metadata or {}, created_at=datetime.now(timezone.utc), ) await self._redis.setex( redis_key, self._default_ttl, json.dumps(item.to_dict(), default=str), ) async def retrieve(self, key: str) -> MemoryItem | None: redis_key = self._make_key(key) data = await self._redis.get(redis_key) if data is None: return None item_dict = json.loads(data) return MemoryItem( key=item_dict["key"], value=item_dict["value"], metadata=item_dict.get("metadata", {}), score=item_dict.get("score", 1.0), created_at=datetime.fromisoformat(item_dict["created_at"]) if item_dict.get("created_at") else datetime.now(timezone.utc), ) async def search(self, query: str, top_k: int = 5, filters: dict[str, Any] | None = None) -> list[MemoryItem]: """Working Memory 不支持语义检索,按 key 前缀匹配""" pattern = self._make_key(f"{query}*") keys = [] async for key in self._redis.scan_iter(match=pattern, count=top_k): keys.append(key) if len(keys) >= top_k: break items = [] for key in keys: data = await self._redis.get(key) if data: item_dict = json.loads(data) items.append(MemoryItem( key=item_dict["key"], value=item_dict["value"], metadata=item_dict.get("metadata", {}), score=1.0, created_at=datetime.now(timezone.utc), )) return items async def delete(self, key: str) -> bool: redis_key = self._make_key(key) return bool(await self._redis.delete(redis_key)) async def clear(self, prefix: str = "") -> int: """清除指定前缀的所有 Working Memory""" pattern = self._make_key(f"{prefix}*") count = 0 async for key in self._redis.scan_iter(match=pattern): await self._redis.delete(key) count += 1 return count