61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
"""记忆服务(T-06 最小闭环):Redis 会话窗口 + MySQL 权威落盘。
|
||
|
||
口径(redis-keys §2.1):`sess:{agent}:{session_id}:msgs` List,TTL 2h,
|
||
最近 N≤20 条 JSON;Redis 只保窗口、丢失可重建(miss/异常回源 MySQL,
|
||
MySQL 为权威)。L1/L2/L3 画像读写归后续任务(画像不得覆盖 L0 正式测评)。
|
||
落盘策略:「每条消息同步或 5s 内异步写 MySQL」——最小闭环同步写(api/chat
|
||
双条落库),异步化归后续优化。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import logging
|
||
|
||
from app.config.settings import settings
|
||
from app.repository.session_repository import SessionRepository
|
||
from app.service.risk.redis_gateway import get_gateway
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
SESSION_TTL_SECONDS = 2 * 3600
|
||
WINDOW_SIZE = 20
|
||
|
||
|
||
def window_key(agent_type: str, session_id: str) -> str:
|
||
"""redis-keys §2.1:sess:{agent}:{session_id}:msgs。"""
|
||
return f"sess:{agent_type}:{session_id}:msgs"
|
||
|
||
|
||
def _session_repo() -> SessionRepository:
|
||
"""回源仓储入口(测试 monkeypatch 点,与 api 模式一致)。"""
|
||
return SessionRepository()
|
||
|
||
|
||
def get_recent(agent_type: str, session_id: str, limit: int = WINDOW_SIZE) -> list[dict]:
|
||
"""最近 N 轮消息([{role, content}]);Redis miss/异常回源 MySQL。"""
|
||
try:
|
||
raw = get_gateway().lrange(window_key(agent_type, session_id), -limit, -1)
|
||
if raw:
|
||
return [json.loads(item) for item in raw]
|
||
except Exception:
|
||
logger.warning("session window read failed, fallback to MySQL: %s", session_id, exc_info=True)
|
||
return _session_repo().list_messages(session_id, limit=limit)
|
||
|
||
|
||
def append_window(
|
||
agent_type: str,
|
||
session_id: str,
|
||
messages: list[dict],
|
||
) -> None:
|
||
"""追加窗口(RPUSH + LTRIM 保留最近 N 条 + TTL 续期);失败降级不阻塞。"""
|
||
try:
|
||
key = window_key(agent_type, session_id)
|
||
client = get_gateway()
|
||
client.rpush(key, *[json.dumps(m, ensure_ascii=False) for m in messages])
|
||
client.ltrim(key, -WINDOW_SIZE, -1)
|
||
client.expire(key, SESSION_TTL_SECONDS)
|
||
except Exception:
|
||
# 窗口丢失可重建(下次读回源 MySQL),不阻塞对话主链路
|
||
logger.warning("session window append failed (degrade): %s", session_id, exc_info=True)
|