from __future__ import annotations import time class LoopRateLimiter: """循环速率限制:检测持续回显循环并抑制对话。 当同一会话在短时间内反复收到相同内容的消息时, 判定为回显循环,对 conversation 进行抑制。 """ def __init__(self, max_loops: int = 5, window_s: float = 30.0, cooldown_s: float = 60.0): self._max_loops = max_loops self._window_s = window_s self._cooldown_s = cooldown_s self._counters: dict[str, list[float]] = {} self._suppressed: dict[str, float] = {} def check(self, conversation_key: str, content: str) -> bool: """返回 True 表示应该抑制此消息。""" if conversation_key in self._suppressed: suppressed_at = self._suppressed[conversation_key] if time.monotonic() - suppressed_at < self._cooldown_s: return True del self._suppressed[conversation_key] now = time.monotonic() if conversation_key not in self._counters: self._counters[conversation_key] = [] timestamps = self._counters[conversation_key] timestamps.append(now) timestamps[:] = [t for t in timestamps if now - t <= self._window_s] if len(timestamps) >= self._max_loops: self._suppressed[conversation_key] = now self._counters.pop(conversation_key, None) return True return False def is_suppressed(self, conversation_key: str) -> bool: if conversation_key in self._suppressed: if time.monotonic() - self._suppressed[conversation_key] < self._cooldown_s: return True del self._suppressed[conversation_key] return False def reset(self, conversation_key: str) -> None: self._counters.pop(conversation_key, None) self._suppressed.pop(conversation_key, None)