Назад в блог
LLM Systems

Кэширование LLM-ответов: экономия без устаревших данных

Повторяйте только допустимый ответ

Свяжите права и ревизию источника

Авторская схема CACHE-GATE-7
Свежесть, стоимость и отказоустойчивость
ТемаVersioned cache gate
ФокусCACHE-GATE-7
СтатусPUBLISHED / 2026-10-08
Версионированные запросы проходят через кэш; устаревшие ответы направляются на повторное вычисление
Версионированные запросы проходят через кэш; устаревшие ответы направляются на повторное вычисление
TERMINAL_PREVIEW.LOG
$ cache --contract CACHE-GATE-7
> bind: tenant / rights / source revision
> key: task / prompt / schema / model policy
> route: hit / miss / bypass / review
Разбор

Кэширование LLM-ответов уменьшает повторные вызовы модели, но неверный ключ или срок жизни способен выдать чужой, устаревший или неподходящий ответ. Правильный вопрос не «можно ли сохранить текст?», а «при каких условиях этот ответ всё ещё допустим для конкретного запроса?». Ниже — архитектура и проверяемый пример, а не обещание экономии для любой нагрузки.

Для общего проектирования промптов смотрите prompt engineering, для интеграции в процесс — AI-автоматизацию. Этот материал касается именно границы повторного использования ответа. При выборе модели полезен разбор маршрутизации LLM, а при сравнении стоимости — матрица выбора модели. Если нужен аудит всей системы, начните со страницы AI-специалиста в Армении.

Проблема и требования

Типичный кандидат для кэша — детерминированное объяснение стабильного документа или нормализация повторяющегося запроса. Плохой кандидат — ответ с персональными данными, изменяющимся остатком товара, актуальной ценой или правом доступа, которое может быть отозвано. Кэширование не делает модель детерминированной и не исправляет ошибочный исходный ответ: оно ускоряет его повторение.

Сначала запишите контракт: какие классы задач допускают повторное использование, кто владеет данными, как долго сведения считаются свежими и какие события делают ответ недействительным. Для RAG-ответа это может быть обновление документа, индекса или ACL. Для внутренней политики — новая редакция. Для публичного справочного текста возможен более длинный TTL, но и здесь нужен владелец обновлений.

Архитектура решения: CACHE-GATE-7

Авторская схема CACHE-GATE-7 разбивает решение на семь проверок: классифицировать запрос → авторизовать пользователя → построить ключ → проверить свежесть → проверить ответ → вызвать модель при промахе → сохранить и наблюдать. Кэш находится после проверки прав, а его запись всегда связана с областью доступа. При отзыве права или изменении источника применяется инвалидация или новая версия ключа.

Ключ не равен сырому тексту вопроса. Он строится из нормализованного запроса и версий контракта: tenant и scope, task, locale, prompt, schema, model policy, набора источников и индекса. Секреты и персональные данные не пишутся в открытые ключи и логи; при необходимости берут криптографический хеш канонической структуры. Нельзя считать хеш заменой контроля доступа: права проверяются при каждом чтении.

КомпонентКонтрактОшибка, которую он предотвращает
Eligibility gateКласс запроса и допустимость кэшаПовторение динамического ответа
Scope binderTenant, actor, entitlementОтвет другого клиента
Versioned keyPrompt, schema, model policy, source revisionТихое смешение версий
Freshness checkTTL и события инвалидацииУстаревший факт
Response validatorСхема, источники, safety routeКэширование ошибочного результата
Fallback pathОграниченный вызов модели и отказШторм запросов при сбое кэша
TelemetryHit, stale, bypass, rejection, costНевидимый регресс качества

Ключ, TTL и инвалидация

TTL — страховка, а не механизм полной корректности. Если документ изменился через минуту после записи, шестичасовой TTL оставит ложный ответ. Версия источника в ключе, событие инвалидации и короткий TTL работают вместе. При событии изменения ревизии новые запросы должны немедленно видеть новый ключ. Старые записи можно удалять фоново, сохраняя ограниченный срок хранения.

Семантический кэш, где «похожие» запросы разделяют ответ, требует отдельной оценки: близость формулировок не доказывает одинаковый смысл. «Можно ли вернуть товар?» и «Можно ли вернуть товар после 30 дней?» могут находиться рядом в embedding-пространстве, но требовать разных ответов. Начните с точного ключа; расширяйте совпадение только на задачах с измеренным риском ложного попадания и понятным отказом.

#### Минимальный псевдокод

ts
const policy = classify(request);
if (!policy.cacheable) return generateAndValidate(request);
const scope = await authorize(request.actor, request.tenant, request.sources);
const key = digest(canonicalize({ scope, task: policy.task, locale: request.locale,
  promptVersion, schemaVersion, modelPolicyVersion, sourceRevision }));
const cached = await cache.get(key);
if (cached && cached.expiresAt > now() && validate(cached, scope)) return cached.answer;
const answer = await generateAndValidate(request);
if (answer.accepted) await cache.put(key, answer, policy.ttl);
return answer;

Это схема контракта, а не готовая реализация. authorize нужен и перед чтением кэша, validate повторно проверяет область и формат, а запись происходит только после проверки ответа. Для действий с внешним эффектом кэшируют разве что черновой текст: подтверждение операции и её результат должны проверяться в системе назначения.

Ошибки и failure modes

Утечка между арендаторами. В ключе нет tenant или права проверялись лишь при записи. Тест: одинаковый вопрос от двух клиентов с разными документами должен давать два изолированных пути.

Устаревший ответ после изменения документа. TTL ещё не истёк, но ревизия источника изменилась. Тест: обновите документ сразу после cache hit; следующий запрос обязан быть miss либо пройти повторную проверку ревизии.

Кэширование отказа как успеха. Временная ошибка провайдера, пустая выдача или непроверенный ответ сохраняются как обычный результат. Разделяйте краткоживущие отрицательные записи от принятых ответов; не кэшируйте неопределённый исход.

Cache stampede. Одновременные промахи вызывают множество одинаковых обращений. Используйте ограниченную дедупликацию одновременных запросов, таймаут и верхний предел очереди. При недоступном кэше сохраняйте контролируемый путь без него, а не бесконечные повторы.

Ложное семантическое попадание. Похожий вопрос имеет другой срок, роль или условие. Проверяйте точный контекст и порог принятия, храните причины маршрутизации. Для регулируемых или персональных ответов такой кэш часто следует отключить.

Тестирование и production-чек

Соберите набор запросов: точные повторы, изменение регистра и пробелов, разные tenant и роли, новая ревизия документа, новый prompt/schema/model policy, истёкший TTL, сбой кэша, сбой модели и пара похожих вопросов с различным смыслом. Для каждого задайте ожидаемый маршрут: hit, miss, bypass, invalidate, reject или review. Проверяйте не только HTTP-ответ, но источник, версию и права.

В пилоте измеряйте hit rate по классу задач, долю устаревших попаданий, p95 полного пути, число вызовов модели, стоимость принятого ответа и время исправления ошибки. Высокий hit rate сам по себе не цель: он бесполезен, если растут неверные ответы. Сравнивайте с контрольным маршрутом без кэша на одинаковой нагрузке; не выдавайте гипотетические проценты экономии за результат проекта.

Перед релизом назначьте владельца инвалидации, ограничьте TTL и объём хранилища, проверьте шифрование и срок хранения, удалите секреты из логов, подготовьте отключение кэша без остановки сервиса. Изменение промпта, схемы, модели, источников или правил доступа должно либо менять ключ, либо очищать соответствующие записи. Выпускайте сначала на низкорисковом классе запросов и держите маршрут к ручной проверке. Для архитектурного ревью достаточно описать классы задач, источники, права и критерии принятого ответа.

CODE_BLOCK.TXT
if (!cacheable(request)) return generateAndValidate(request);
const scope = await authorize(request);
const key = digest(canonicalize({ scope, task, versions, sourceRevision }));
const hit = await cache.get(key);
if (hit && fresh(hit) && validate(hit, scope)) return hit.answer;
return generateValidateAndMaybeCache(request, key);