# Кеш промптов LLM: как одна строка ломает экономию на 98%
Каноническая страница: https://plainews.ru/posts/promt-cache-llm-diagnose
Опубликовано: 2026-08-29T07:01:10.210Z

Почему кеширование промптов у DeepSeek, OpenAI и Anthropic отключается незаметно и как найти виновника с помощью инструмента prefixcash за пять минут.
Токен из кеша у DeepSeek дешевле обычного в 31 раз, у OpenAI и Anthropic — в 10 раз. Но кеш работает по принципу «всё или ничего»: если первые токены промпта разошлись хоть на одном символе, экономия обнуляется целиком, а не частично. Разработчик под ником Isk4R1oT показал на живых вызовах, как это происходит и как поймать проблему за пару минут.

Если вы строите агента, который дергает LLM тысячи раз в день с одним и тем же системным промптом, эта статья — про деньги, которые вы теряете прямо сейчас и, скорее всего, не замечаете.

## Почему кеш вообще экономит деньги

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

Цены на 27 августа 2026 года показывают масштаб разницы:

| Провайдер | Обычный токен | Токен из кеша | Разница | |---|---|---|---| | DeepSeek v4-flash | $0.44 / 1M | $0.014 / 1M | 31x | | OpenAI gpt-5.6-sol | $4.00 / 1M | $0.40 / 1M | 10x | | Anthropic Sonnet | $3.00 / 1M | $0.30 / 1M | 10x |

У DeepSeek и OpenAI кеш включается автоматически. У Anthropic его нужно запрашивать явно:

Но там, где кеш включён, он ломается совершенно одинаково — одной строкой в начале промпта.

## Самая частая причина обрыва — время в системном промпте

Классический баг выглядит так:

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

Автор проверил это на живом support-агенте с системным блоком около 1200 токенов (DeepSeek включает кеш примерно от 1024 токенов) и четырьмя вопросами подряд. Две сессии с одинаковым содержимым, но разной сборкой промпта:

В первой сессии время стоит первой строкой — ноль попаданий в кеш из восьми вызовов. Во второй время перенесено в конец промпта — 1152 токена из ~1180 берутся из кеша. Это специально худший случай для наглядности: на реальных промптах динамика обычно сидит глубже, и базовый уровень кеша держится на 49–66%, а не падает до нуля.

Для агента с промптом 1200 токенов и 10 000 вызовов в день разница ощутима в деньгах:

| Провайдер | Без кеша | С кешем 98% | Экономия | |---|---|---|---| | DeepSeek v4-flash | $158 / мес | $8 / мес | −$150 | | OpenAI gpt-5.6-sol | $1 440 / мес | $173 / мес | −$1 267 | | Anthropic Sonnet | $1 080 / мес | $130 / мес | −$950 |

Если у вас datetime.now() в первой строке — вы это уже понимаете и без инструментов. Хуже, когда причина не видна глазами вообще.

## Четыре бага, которые не найти чтением кода

Автор описывает общий паттерн, характерный для любого провайдера с префиксным кешем — просто на примере Anthropic он нагляднее всего:

**Схемы инструментов.** У Anthropic кеш покрывает tools, system, messages именно в этом порядке — схемы инструментов лежат в самом начале префикса, раньше системного промпта. Если собирать их из словаря с плавающим порядком ключей, кеш ломается в первой же позиции — и в исходном коде промпта это никак не проявляется.

**База знаний из `set()`.** Содержимое между вызовами одинаковое, но порядок элементов множества между процессами разный. Текст совпадает, а префикс — нет, и кеша нет.

**Кеш есть в коде, а `cache_read_tokens == 0`.** Сборка промпта правильная, но попаданий всё равно нет — значит истёк TTL (время жизни записи в кеше) или сам провайдер временно не кеширует запрос. Это чинится маршрутизацией трафика, а не правкой промпта.

**Шаблон, который рендерится по-разному под нагрузкой.** Условный блок появляется в промпте, скажем, раз в сто вызовов — найти такое чтением кода почти нереально.

Общее у всех четырёх случаев: промпт большой, изменение маленькое, глазами не находится.

## Инструмент, который считает по чужим данным

prefixcash — утилита, которая не делает никаких запросов к моделям и ничего не отправляет с вашей машины. Она читает поле usage, которое провайдер и так возвращает в ответе, и сравнивает соседние вызовы в рамках одной сессии.

Формат входных данных — JSONL, по строке на вызов. Логировать нужно всего два поля: сырой usage и список messages.

Это пять строк поверх любого OpenAI-совместимого SDK. С LiteLLM хватит трёх:

Дальше запускается диагностика:

Для сломанной сессии вывод покажет cacheable prefix 1%, общий совпадающий префикс всего 8 слов, а виновником будет назван dynamic_time. На карте зелёным подсвечено то, что реально ушло из кеша, красным — точка разрыва, оранжевым — весь хвост промпта после разрыва, который технически совпадает дословно, но кешу уже не достаётся, потому что он смотрит только на префикс.

После переноса времени в конец промпта та же диагностика на исправленной сессии даёт cacheable prefix 98% и общий префикс в 730 слов вместо восьми.

Инструмент умеет распознавать типовые источники расхождений: iso_datetime, datetime, date, time, uuid, hex_token, number, email, url_query, placeholder (вида {var}). Плюс два универсальных детектора — high_entropy для случайно сгенерированных строк (по энтропии Шеннона) и content_change для любого расхождения, не подошедшего ни под один шаблон. То есть даже нестандартный случай будет назван и локализован.

## Как проверить свои промпты

Если вы работаете через Claude Code, интеграция вообще не нужна — лог уже лежит на диске:

Адаптер переносит только поля usage, тексты промптов не читает — это видно в examples/import_claude_code.py, файл на 70 строк.

Если своего пайплайна логирования нет, придётся добавить пять строк логирования и подождать реального трафика. Ждать долго не нужно: diagnose сравнивает соседние вызовы внутри одной сессии, так что вердикт появится уже на двух вызовах подряд. Не нужны тысячи запросов или сутки накопления данных — хватит одного живого диалога из двух реплик.

Для боевых логов единственное место, которое стоит проверить перед запуском на чувствительных данных, — src/prefixcash/integrations/importers.py. Это единственный файл, где читается ваш лог.

## Где ломается

Главная ловушка — считать, что раз кеш «автоматический» (как у DeepSeek и OpenAI), то думать о нём не нужно. На практике порядок ключей в словаре, set() вместо списка или условный блок в шаблоне рушат экономию так же надёжно, как явный datetime.now(), только без единой видимой строчки-виновника.

Вторая ловушка — путать «сборка промпта неправильная» и «TTL кеша истёк». Если diagnose показывает высокий cacheable prefix, а cache_read_tokens всё равно ноль, чинить промпт бесполезно — нужно разбираться с маршрутизацией запросов или временем жизни записи у провайдера.

Третья — доверять чтению кода вместо реальных цифр. Баг с временем в начале строки виден глазами. Баг с плавающим порядком ключей словаря — нет, и без инструмента, считающего usage по факту, его можно не найти месяцами.

## Что попробовать дальше

Добавьте логирование usage и messages в существующий пайплайн вызовов LLM, прогоните prefixcash diagnose на паре живых сессий и посмотрите на процент cacheable prefix. Если он далёк от 90-98%, ищите виновника в списке детекторов — чаще всего это время, порядок словаря или множество без сортировки. При агенте с тысячами вызовов в день счёт за месяц может отличаться в разы просто от переноса одной строки в конец промпта.

## FAQ

### Что такое префиксный кеш в LLM и чем он отличается от обычного кеширования ответов?

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

### У всех ли провайдеров кеш включается автоматически?

Нет. У DeepSeek и OpenAI кеш работает без дополнительных настроек. У Anthropic его нужно явно запросить через параметр cache_control={"type": "ephemeral"} в вызове API — без этого кеша не будет вовсе.

### Можно ли проверить кеш без доступа к исходным промптам?

Да, prefixcash работает только с полем usage, которое провайдер и так возвращает в ответе. Тексты промптов инструменту не нужны, сетевых запросов он не делает и ничего не отправляет со своей машины.

### Сколько вызовов нужно накопить для первой диагностики?

Минимум два вызова в рамках одной сессии — diagnose сравнивает соседние запросы. Не нужно ждать день или собирать тысячи вызовов, достаточно одного диалога из двух реплик.

### Что делать, если cacheable prefix высокий, а попаданий в кеш всё равно нет?

Если сборка промпта верная, но cache_read_tokens равен нулю, дело не в промпте. Скорее всего, истёк TTL записи в кеше провайдера или запрос попал в момент, когда upstream временно не кеширует — это решается маршрутизацией трафика, а не изменением кода.

## Источники

- Habr AI: Одна строка в системном промпте отключала кеш целиком. 1% против 98% на живых вызовах
