AI / LLM
Кэширование промпта: как стабильный префикс режет стоимость входа в 10 раз
Дашборд показывал cache hit rate: 94%. Счёт говорил иное — траты на вход утроились месяц к месяцу при плоском трафике. Виноваты четыре символа: кто-то добавил Current time: {now} в начало системного промпта «для контекста». Теперь у каждого запроса уникальный префикс с нулевого токена, поэтому каждый запрос платил премию за запись в кэш и ни разу не читал из него. Эти 94% мерили метрику, которая больше не срабатывала. Таймстамп в начале промпта — одна из самых дорогих строк кода, что можно выкатить.
Что именно кэшируется: префикс, сопоставленный токен-в-токен
Кэширование промпта не семантическое. Провайдер не понимает, что два промпта «значат одно и то же». Он хэширует префикс запроса — ведущий ряд токенов — и ищет сохранённую запись, совпадающую с ним точно, токен в токен, с нулевой позиции. Совпадение должно быть идентичным вплоть до помеченной границы — cache breakpoint. В тот миг, когда потоки расходятся — другое слово, лишний пробел, переставленный блок — совпадение там и заканчивается, а всё, что после, обрабатывается (и тарифицируется) как свежий вход.
Вот почему порядок промпта — это вся игра. Кэшируемое — всегда начало. Иерархия запроса у Anthropic фиксирована: сначала tools, затем system, потом messages. Так что правило проектирования пишется само: всё стабильное — в начало (определения tools, системный промпт, большой документ, few-shot примеры), всё волатильное — в конец (реальный вопрос пользователя, поданные данные запроса, всё с таймстампом). Breakpoint ставишь на последний блок, идентичный между запросами. Поставишь на один блок позже — на то, что меняется — и lookback не найдёт прежней записи в этой позиции, так что каждый раз чистый промах при полной оплате записи.
Экономика: пошлина 1.25x за скидку 10x
Кэширование — это ставка, и шансы напечатаны на коробке. Запись в кэш стоит дороже обычного запроса: тариф на 5 минут тарифицирует кэшированные токены по 1.25x базовой ставки входа, а тариф на 1 час — по 2x. Чтение из кэша — где ты выигрываешь: попадание тарифицирует те токены по 0.1x — скидка 90%. Конкретно: на Opus 4.7 стандартная ставка входа $5.00 / MTok, а попадание роняет её до $0.50 / MTok; на Sonnet 4.6 — с $3.00 до $0.30.
Точка окупаемости вытекает из этого отношения. Платишь 1.25x один раз за запись, затем 0.1x за каждое последующее чтение. Первый кэшированный запрос — небольшой убыток; ко второму запросу ты в плюсе, а с третьего и дальше — почти бесплатно. Сеньорская рамка не «кэшировать ли», а «будет ли этот префикс перечитан внутри окна TTL достаточно часто, чтобы амортизировать премию за запись». Префикс, который записали и ни разу не перечитали до истечения, строго хуже, чем не кэшировать вовсе — ты заплатил пошлину 1.25x впустую.
| Фактор | Множитель к базовому входу | Что это значит |
|---|---|---|
| Запись в кэш, TTL 5 мин | 1.25x | Разовая премия за хранение префикса; тариф по умолчанию |
| Запись в кэш, TTL 1 час | 2x | Дороже на запись, но живёт в 12 раз дольше |
| Чтение из кэша (попадание) | 0.1x | Скидка 90%; вся причина, ради которой кэш существует |
| Некэшированный вход | 1.0x | Всё после точки расхождения платит полную ставку |
TTL: 5 минут по умолчанию, 1 час, если заплатишь вперёд
Запись кэша эфемерна. Время жизни по умолчанию — 5 минут, и важно: часы сбрасываются на каждом чтении — активный префикс остаётся тёплым, пока в него попадают. Тариф на 1 час нужен для медленного или всплескового трафика, где запросы приходят с разрывом больше 5 минут, но всё ещё внутри часа. Сделка прямая: 1 час стоит 2x на запись вместо 1.25x, так что окупается, только если более длинное окно превращает промахи в попадания, которые иначе ты бы потерял. На начало 2026 года дефолт стоит на 5 минутах; команды, построившие модель затрат на более длинный дефолт, получили тихий шок по счёту, когда запись, которую ждали живой между запросами, уже истекла.
Есть и порог: префикс ниже минимальной кэшируемой длины модели тихо не кэшируется — без ошибки, просто оплата по полной ставке. Порог зависит от модели.
| Модель | Минимум кэшируемых токенов |
|---|---|
| Opus 4.7 / 4.6 / 4.5 | 4096 |
| Sonnet 4.6 / 4.5, Opus 4.1 | 1024 |
| Haiku 4.5 | 4096 |
| Haiku 3.5 | 2048 |
Почему это работает
У тебя максимум 4 явных cache_control breakpoint на запрос, и каждый ищет назад в ограниченном окне прежнюю запись. Поэтому breakpoint’ы стакают на длинном слоёном промпте — один после tools, один после статического системного блока, один после большого документа — чтобы изменение внизу инвалидировало только хвост, а не дорогую верхушку. Breakpoint’ы — это вложенные префиксы, а не независимые кэши: попадание на позднем breakpoint означает, что и ранние совпали.
Производственный режим отказа: тихое отравление префикса
Опасно то, что слом кэша никогда не бросает исключения. Ничто в ответе не говорит «ты промахнулся». Вывод побайтно идентичен, попал ты или заплатил по полной; единственный сигнал живёт в блоке usage, в cache_read_input_tokens против cache_creation_input_tokens. Так что одна небрежная правка может увести нагрузку с 90% чтений к 0% чтений, и единственный симптом — счёт через три недели.
Классические отравители — всё, что кажется безобидным: таймстамп или request id, впихнутый в начало системного промпта; tools, сериализованные в недетерминированном порядке (Map или dict, не сохраняющий порядок вставки между прогонами); апгрейд библиотеки, заново форматирующий твою JSON-схему tool с другими пробелами; добавление сообщения пользователя перед статическим контекстом, а не после. Каждое сдвигает префикс на нулевом токене или рядом, так что всё кэшированное тело — возможно, десятки тысяч токенов системного промпта и документов — пере-тарифицируется по ставке записи 1.25x на каждом вызове. Фикс — дисциплина: заморозь префикс, проверь его побайтно тестом и пропускай любое изменение system/tools через ревью, спрашивающее «не сдвигает ли это cache breakpoint».
RAG-сервис шлёт системный промпт на 30k токенов + найденные документы, затем вопрос пользователя. Трафик всплесковый: запросы кучкуются, потом тишина ~15 минут. Выбери настройку кэша.
После деплоя cache hit rate упал почти до нуля, но ошибок нет и вывод выглядит верно. Какова самая вероятная причина?
Префикс записали в кэш (1.25x), затем прочитали три раза (0.1x каждое) до истечения. Против полной ставки входа (1.0x) четыре раза — кэш сэкономил деньги?
Расставь промпт от начала (кэшируется) к концу (свежее на каждый запрос) для максимального переиспользования кэша:
- 1 Определения tools (стабильные JSON-схемы, детерминированный порядок)
- 2 Системный промпт / инструкции роли (стабильные между запросами)
- 3 Большой статический контекст или few-shot примеры (сюда ставится cache breakpoint)
- 4 Найденные документы для запроса (меняются на каждом вызове)
- 5 Реальный вопрос пользователя (самое волатильное, всегда последним)
- 01Пройди по шагам, как один впихнутый таймстамп в начале системного промпта может умножить счёт за вход на 10, и почему ошибки не возникает.
- 02Когда TTL на 1 час стоит своей премии за запись 2x против дефолтного тарифа 5 минут, и как рассуждать о точке окупаемости?
Кэширование промпта переиспользует точный, токен-в-токен префикс примерно по 0.1x базовой цены входа, но требует разовой премии за запись — 1.25x для дефолтного тарифа на 5 минут, 2x для тарифа на 1 час. Совпадение позиционное, а не семантическое, поэтому правило проектирования абсолютно: стабильное (tools, затем system, затем большой контекст и примеры) — в начало, волатильное (данные запроса, таймстампы, вопрос пользователя) — в конец, а breakpoint сидит на последнем неизменном блоке. Ниже минимальной кэшируемой длины модели (от 1024 до 4096 токенов в зависимости от модели) ничего не кэшируется, и тебя тарифицируют по полной без предупреждения. Производственный режим отказа — тихое отравление префикса: таймстамп, переставленная схема tool или переформатированный пробел рядом с нулевым токеном инвалидирует всё кэшированное тело, переворачивая каждый запрос с чтения 0.1x на запись 1.25x без ошибки и с идентичным выводом. Единственный сигнал — блок usage. Заморозь префикс, проверяй его побайтно в тестах — и скидка держится.