Почему промпт перестал попадать в кэш и сколько это стоит

Сервис отвечает на вопросы сотрудников по одному длинному набору правил. Вы вынесли правила в начало промпта и рассчитывали, что повторные запросы станут дешевле. Через неделю счёт вырос, хотя число запросов почти не изменилось. Первое подозрение — кэш перестал использоваться. Это можно проверить по полю cached_tokens каждого ответа, а не по общей длине промпта.

Нужен общий префикс, а не похожий текст

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

Например, в текущем шаблоне первое developer-сообщение начинается с Сегодня 26 октября; отвечай по правилам.... Завтра дата изменится и кэш не сможет использовать длинный хвост правил, хотя он остался тем же. Переставьте дату в отдельное сообщение после статичной инструкции и справочника. Для серии запросов к одному справочнику вход теперь начинается одинаково, а меняются только дата и вопрос в конце. Сравнивать нужно сериализованный вход целиком: невидимое для пользователя изменение в описании инструмента тоже способно оборвать совпадение.

Есть и условия вне шаблона: у модели существует минимальная длина префикса для кэширования, запись может истечь, а запрос может попасть на машину без нужной записи. У поколений моделей разные режимы кэша и тарифы. Для GPT-5.6 и новее минимум составляет 1024 видимых входных токена; запись и чтение кэша тарифицируются по-разному. Поэтому не берите единую «скидку на все входные токены» из старого расчёта.

Для этих моделей можно задать явную границу после стабильного блока и оставить меняющийся вопрос вне записи. Не ставьте границу после содержимого, которое обновляется на каждом вызове: тогда запись будет повторяться без полезного чтения. Если приложению нужен общий справочник, но каждый клиент добавляет собственные инструкции, продумайте и порядок, и изоляцию данных: общую часть держите первой, клиентскую — дальше, а показатели кэша считайте отдельно по клиентам. Совпадение префикса само по себе не означает, что ответ пользователю стал лучше; качество ответа проверяйте своими обычными наборами вопросов.

Как увидеть настоящий hit

Сохраните рядом с каждым ответом идентификатор версии промпта, модель, общее число входных токенов и показатели кэша из usage. В Responses API это usage.input_tokens_details.cached_tokens и, для моделей с платной записью, cache_write_tokens. Сравните две группы запросов с одной версией промпта: сколько из входа реально прочитано из кэша, сколько записано и сколько обработано обычно. Если после релиза cached_tokens стал нулём, сделайте diff первых сообщений и определений tools до переменного пользовательского вопроса. Это быстрее, чем переписывать весь промпт.

Стоимость удобнее считать построчно. Пусть у ответа 3000 входных токенов: 2000 прочитаны из кэша, 500 записаны, 500 обработаны обычно; выходных токенов — 400. Допустим, цена обычного входа — 2 условные единицы за миллион токенов, чтения кэша — 0,2, записи — 2,5, выхода — 8. Тогда цена равна (500 × 2 + 2000 × 0,2 + 500 × 2,5 + 400 × 8) / 1 000 000 = 0,00585 условной единицы. Если все 3000 входных токенов тарифицируются как обычный вход, получится (3000 × 2 + 400 × 8) / 1 000 000 = 0,0092. Это иллюстративные ставки, не тариф конкретной модели.

Общее правило для модели с платной записью: обычный вход равен input_tokens - cached_tokens - cache_write_tokens. Умножьте каждую категорию на соответствующую ставку, затем добавьте стоимость выхода. Для старого режима без отдельной цены записи используйте его тариф и доступные поля usage. Для расследования важна динамика на одинаковом наборе запросов: доля cached_tokens / input_tokens, число записанных токенов, стоимость ответа и задержка. Снижение доли покажет промахи; рост общего входа при прежней доле укажет на другую проблему.

Проверяйте изменение на паре одинаковых запросов к одной версии промпта, а затем на небольшой серии реальных вопросов. Первый вызов может записать префикс и обойтись дороже следующего; сравнение только одного холодного запроса сделает полезную настройку похожей на регрессию. Для отчёта берите сумму стоимости всех запросов серии и делите на их число. Если реальный поток редко повторяет префикс до истечения записи, средняя экономия будет меньше, чем в парном тесте. Это важнее красивого показателя hit rate в искусственном прогоне.

Разбор отличается от расхождения собственного счётчика токенов с usage API: здесь источником расчёта остаётся usage провайдера, а вопрос — какая часть была кэшированной. Если нужно отнести расходы к клиентам, пригодится учёт стоимости по tenant.

В платном курсе LLMOps & Evaluation: из песочницы в продакшн вы строите мониторинг стоимости и задержки, версионируете промпты и связываете изменения с метриками после релиза. Так промах кэша превращается в измеряемый эффект конкретной версии, а не в загадку месячного счёта. Откройте программу курса, чтобы собрать этот контур для своего LLM-сервиса.

Продолжить на практике

  • LLMOps & Evaluation: из песочницы в продакшн ↗

    Практический курс по LLMOps. Соберёте продакшн-контур для LLM: eval-фреймворк (LangSmith/Ragas/OpenAI Evals), quality-гейты в CI/CD, алёрты на дрейф, монитринг скорости и стоимости, управление промпт-версиями и безопасность. Плюс профессиональные блоки: synthetic data для тестов, LLM-based judges, отчётность для релиз-комитета и runbook’и инцидентов.