Промпт-кэширование, батчи и оптимизация затрат
Если Messages API — это базовый двигатель, то кэширование и batch API — это трансмиссия, которая делает его пригодным для продакшн-нагрузки. Без них хороший прототип легко превращается в счёт на $500 в месяц за то, что могло бы стоить $50.
Экономика prompt caching: за что платите и сколько экономите
Каждый раз, когда модель обрабатывает запрос, она «читает» все входящие токены — системный промпт, историю диалога, определения инструментов. Если эти данные одинаковы от запроса к запросу, вы платите за чтение одного и того же снова и снова.
Prompt caching решает это через механизм контрольных точек (breakpoints). Вы помечаете блок с флагом cache_control, и токены до этой отметки кэшируются на инфраструктуре Anthropic.
| Операция | Стоимость (от базовой цены input) |
|---|---|
| Запись в кэш (TTL 5 мин) | 1,25× |
| Запись в кэш (TTL 1 час) | 2,00× |
| Чтение из кэша | 0,10× |
| Обычный некэшированный токен | 1,00× |
Запись дороже — первый вызов всегда стоит больше обычного. Зато каждое последующее чтение обходится в десять раз дешевле нормы. Для системного промпта из 2000 токенов и сотни запросов в день арифметика очень быстро сходится в вашу пользу.
Синтаксис: два способа расставить breakpoints
Автоматический — самый простой вариант. Передаёте cache_control на верхнем уровне запроса, и SDK сам двигает breakpoint к последнему кэшируемому блоку по мере роста диалога:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
cache_control={"type": "ephemeral"}, # сюда
system="Вы — ассистент по анализу кода...",
messages=[{"role": "user", "content": query}]
)Явный (block-level) — когда нужно точно контролировать, что именно кэшируется. Например, большой статичный системный промпт + изменяющийся разговор:
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
system=[
{
"type": "text",
"text": large_static_instructions, # 3000 токенов
"cache_control": {"type": "ephemeral"}
}
],
messages=[{"role": "user", "content": user_query}] # меняется
)В одном запросе можно поставить до четырёх явных breakpoints. Это полезно в длинных диалогах: расставьте их через каждые 15–20 сообщений, иначе кэш может не найти совпадение (система смотрит только на 20 блоков назад).
TTL: 5 минут или 1 час
По умолчанию кэш живёт 5 минут — этого хватает для интерактивных сессий. Если промпты используются реже (пакетная обработка, ночные задачи), указывайте явный "ttl": "1h":
"cache_control": {"type": "ephemeral", "ttl": "1h"}Запись при часовом TTL стоит 2× от базы вместо 1,25×, зато кэш переживёт паузу между запросами. Для batch-задач (о них ниже) часовой TTL — почти обязательный выбор, поскольку батч может обрабатываться дольше 5 минут.
Как проверить, что кэш работает
В ответе смотрите response.usage:
print(response.usage.cache_creation_input_tokens) # записано в кэш
print(response.usage.cache_read_input_tokens) # прочитано из кэша
print(response.usage.input_tokens) # некэшированный остатокЕсли cache_read_input_tokens > 0 — попадание. Если всё в cache_creation_input_tokens — это первый вызов или кэш устарел. Если оба нуля при включённом cache_control — скорее всего, промпт меньше минимального порога (для claude-opus-4-8 нужно не менее 1 024 токенов).
> Типичная ловушка: разработчик ставит breakpoint на последнее пользовательское сообщение, которое меняется каждый раз. Кэш пишется каждый запрос, никогда не читается — деньги уходят на запись. Правило: breakpoint ставьте на последний неизменяющийся блок — обычно это конец системного промпта или блока с инструментами.
Message Batches API: 50% скидки за асинхронность
Когда немедленный ответ не нужен — аналитика, оценка датасета, массовая генерация описаний — переключайтесь на Batches API. Он обрабатывает запросы асинхронно и стоит ровно вдвое дешевле стандартных цен на input и output.
Лимиты батча: до 100 000 запросов или 256 МБ, что наступит раньше. Большинство батчей завершается в течение часа, максимум — 24 часа. Результаты хранятся 29 дней.
import anthropic
from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.messages.batch_create_params import Request
client = anthropic.Anthropic()
# Отправляем батч
batch = client.messages.batches.create(
requests=[
Request(
custom_id=f"review-{i}",
params=MessageCreateParamsNonStreaming(
model="claude-opus-4-8",
max_tokens=512,
messages=[{"role": "user", "content": code_snippets[i]}],
),
)
for i in range(len(code_snippets))
]
)
print(f"Батч создан: {batch.id}")Опрос статуса и получение результатов
import time
# Ждём завершения
while True:
batch = client.messages.batches.retrieve(batch.id)
if batch.processing_status == "ended":
break
print(f"Обработано: {batch.request_counts.succeeded}/{batch.request_counts.processing}")
time.sleep(60) # проверяем раз в минуту
# Читаем результаты (JSONL)
for result in client.messages.batches.results(batch.id):
if result.result.type == "succeeded":
text = result.result.message.content[0].text
print(f"{result.custom_id}: {text[:80]}...")
else:
print(f"{result.custom_id}: ошибка — {result.result.error}")Ограничения: стриминг (stream: true), fast mode и max_tokens: 0 в батчах не поддерживаются — они специфичны для синхронного режима. Для батчей с общим системным промптом используйте "ttl": "1h" в cache_control, иначе кэш сгорит до того, как все запросы из батча успеют его прочитать.
Подсчёт токенов до отправки
Сюрпризы в счёте — следствие незнания реального размера запроса. SDK предоставляет отдельный эндпоинт подсчёта без генерации ответа:
# Не генерирует ответ — только считает токены
count = client.messages.count_tokens(
model="claude-opus-4-8",
system=system_prompt,
tools=tool_definitions,
messages=conversation_history,
)
print(f"Input: {count.input_tokens} токенов")
# Проверяем, не близко ли к лимиту окна
if count.input_tokens > 180_000:
# Пора compactить историю или использовать /compact
truncate_history(conversation_history)Полезно в трёх сценариях: оценить стоимость до дорогого запроса, не дать истории переполнить контекст, протестировать, попадают ли инструменты под минимум кэширования.
Стратегии снижения затрат: практический чеклист
Кэшируйте всё статичное первым. Порядок блоков в запросе имеет значение — cacheable-контент должен идти перед меняющимся. Типичный порядок: системный промпт → определения инструментов → примеры few-shot → история диалога → текущий вопрос пользователя. Breakpoint — после последнего статичного элемента.
Выбирайте модель под задачу. Haiku 4.5 стоит в 5 раз меньше Opus 4.8 по output-токенам ($5 против $25 за миллион). Для classification, извлечения структурированных данных и коротких ответов Haiku справляется отлично. Opus — для сложного рассуждения, архитектурных решений, кода со сложными зависимостями.
Batch API для всего асинхронного. Код-ревью на PR, анализ логов, пакетная генерация документации — всё это не требует мгновенного ответа и сразу получает скидку 50%.
Не засоряйте контекст. Длинная история диалога — это деньги. Используйте /compact в Claude Code и аналогичное суммирование в API-приложениях. Подробнее об управлении контекстом — в статье Управление контекстным окном.
Pre-warming кэша перед нагрузкой. Если знаете, что запросы начнутся через 10 минут — отправьте фиктивный запрос с max_tokens: 0 заранее, чтобы кэш уже был тёплым:
# Прогреваем перед пиковой нагрузкой
client.messages.create(
model="claude-opus-4-8",
max_tokens=0, # не генерирует ответ
system=[{"type": "text", "text": big_system_prompt,
"cache_control": {"type": "ephemeral"}}],
messages=[{"role": "user", "content": "warmup"}]
)Мониторьте реальный cache hit rate. Добавьте логирование cache_read_input_tokens / (cache_read + cache_creation + input_tokens) в production. Если hit rate ниже 70% при высокой доле одинаковых промптов — значит, breakpoint расставлен неправильно или контент меняется незаметно.
See also
- Claude API и Anthropic SDK: основы — базовая структура запросов и поля usage
- Tool use, MCP и потоковая передача в API — кэширование схем инструментов снижает стоимость агентных циклов
- Claude Agent SDK: программная сборка агентов — как SDK оркестрирует кэширование в многошаговых агентах
- Управление контекстным окном — /compact и стратегии сжатия контекста
- Выбор модели и режимы мышления — когда переключиться с Opus на Haiku ради экономии