feat: add cache_salt dimension to LLM response cache

This commit is contained in:
2026-07-16 05:23:58 -04:00
parent c5c11ea81b
commit 564c92a043
6 changed files with 57 additions and 14 deletions
+23 -9
View File
@@ -29,36 +29,48 @@ class RedisResponseCache:
self._redis = redis
self._ttl_s = ttl_s
def _build_key(self, model: str, messages: list[dict[str, str]]) -> str:
def _build_key(
self,
model: str,
messages: list[dict[str, str]],
cache_salt: str | None = None,
) -> str:
"""构造 content-addressed 缓存键。
Args:
model: 模型名称。
messages: 消息列表。
cache_salt: 可选缓存盐(如跨 epoch 强制重采样)。仅当非 None 时才加入
键 payload,保证默认 None 时键结构与旧缓存一字节不差、旧键不失效。
Returns:
sha256 哈希字符串作为 Redis 键。
"""
payload = json.dumps(
{"model": model, "messages": messages},
sort_keys=True,
ensure_ascii=False,
)
key_obj: dict[str, Any] = {"model": model, "messages": messages}
if cache_salt is not None:
key_obj["salt"] = cache_salt
payload = json.dumps(key_obj, sort_keys=True, ensure_ascii=False)
digest = hashlib.sha256(payload.encode("utf-8")).hexdigest()
return f"llm_cache:{digest}"
async def get(self, model: str, messages: list[dict[str, str]]) -> LLMResponse | None:
async def get(
self,
model: str,
messages: list[dict[str, str]],
cache_salt: str | None = None,
) -> LLMResponse | None:
"""从缓存读取 LLM 响应。
Args:
model: 模型名称。
messages: 消息列表。
cache_salt: 可选缓存盐,透传到键构造。
Returns:
缓存命中时返回 LLMResponse,未命中或 Redis 异常时返回 None。
"""
try:
key = self._build_key(model, messages)
key = self._build_key(model, messages, cache_salt)
raw = await self._redis.get(key)
except Exception:
logger.warning("Redis 缓存读取失败,降级为未命中")
@@ -75,6 +87,7 @@ class RedisResponseCache:
model: str,
messages: list[dict[str, str]],
response: LLMResponse,
cache_salt: str | None = None,
) -> None:
"""将 LLM 响应写入缓存。
@@ -82,9 +95,10 @@ class RedisResponseCache:
model: 模型名称。
messages: 消息列表。
response: 待缓存的 LLMResponse。
cache_salt: 可选缓存盐,透传到键构造。
"""
try:
key = self._build_key(model, messages)
key = self._build_key(model, messages, cache_salt)
value = json.dumps(dataclasses.asdict(response), ensure_ascii=False)
if self._ttl_s:
await self._redis.set(key, value, ex=self._ttl_s)