Learn (Almost) Anything
КаталогСкачать курс

Claude Code: справочник разработчика

Prompt caching (экономика записи и чтения кэша), batch API, подсчёт токенов и практики снижения стоимости. Как сделать продакшн-нагрузку дешевле в разы.

ruЭнциклопедия38 уроков8 модулей7
энциклопедия claude codeClaude Codeсправочник разработчикаФундамент Claude Codeпамять и конфигурацияРабочие процессы и best practicesMCPОблачные агентыПрограммный доступAgent SDK и Claude API

Промпт-кэширование, батчи и оптимизация затрат

Prompt caching (экономика записи и чтения кэша), batch API, подсчёт токенов и практики снижения стоимости. Как сделать продакшн-нагрузку дешевле в разы.

Промпт-кэширование, батчи и оптимизация затрат

Если 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 блоков назад).

Проверь себя #cp-1

Вы поставили cache_control на последнее пользовательское сообщение в диалоге. Сработает ли кэш? Что покажет cache_read_input_tokens в следующем запросе?

Показать ответ

Кэш скорее всего не сработает. Пользовательское сообщение меняется с каждым запросом — хэш содержимого не совпадёт, и каждый раз будет писаться новый кэш, но никогда не читаться. cache_read_input_tokens останется 0, а cache_creation_input_tokens будет расти с каждым запросом. Breakpoint нужно ставить на последний *неизменяющийся* блок — например, конец системного промпта.

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 ставьте на последний неизменяющийся блок — обычно это конец системного промпта или блока с инструментами.

ИнтерактивКогда кэширование начинает экономить?#int-1
Базовая цена: $5 / MTok (Claude Opus 4.8). Кэшируется только системный промпт. Все запросы имеют одинаковый системный промпт.
Стоимость без кэша ($/день)1.65 $
Стоимость с кэшем ($/день)0.32 $
Экономия в день1.33 $
Сдвигайте ползунки, чтобы увидеть реальную экономию от prompt caching в зависимости от числа запросов и размера системного промпта.

---

Быстрое повторение #rc-1

Во сколько раз стоит запись в ephemeral кэш (TTL 5 мин) по сравнению с обычным токеном и во сколько раз дешевле чтение из кэша?

Показать ответ

Запись: 1,25× от базовой цены; чтение: 0,10× (в 10 раз дешевле обычного).

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, иначе кэш сгорит до того, как все запросы из батча успеют его прочитать.

Проверь себя #cp-2

Вы отправляете батч из 500 запросов с общим системным промптом на 5000 токенов. Какой TTL для cache_control вы выберете — 5 минут или 1 час — и почему?

Показать ответ

1 час ("ttl": "1h"). Батч может обрабатываться дольше 5 минут, поэтому кэш с коротким TTL сгорит прежде, чем большинство запросов в батче успеют его прочитать. Часовой TTL стоит 2× вместо 1,25× при записи, но зато 490 последующих запросов получат систему за 0,1× вместо 1×, что многократно окупает доплату.

---

Быстрое повторение #rc-2

На сколько процентов дешевле обходятся запросы через Message Batches API по сравнению с синхронными запросами?

Показать ответ

На 50% дешевле (вдвое) благодаря асинхронной обработке — скидка применяется к input и output токенам.

Подсчёт токенов до отправки

Сюрпризы в счёте — следствие незнания реального размера запроса. 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 расставлен неправильно или контент меняется незаметно.

Проверь себя #cp-3

У вас есть агентный цикл с tool use: системный промпт (2000 токенов), 5 определений инструментов (1500 токенов), история диалога (растёт), текущий вопрос. Где поставить breakpoint, чтобы максимизировать cache hit rate?

Показать ответ

После блока с определениями инструментов — то есть после первых 3500 токенов (системный промпт + инструменты). Они не меняются между запросами. История диалога растёт, поэтому кэшировать её на фиксированном breakpoint не получится — контент будет другим. Итог: 3500 токенов читаются из кэша за 0,1×, платите только за растущую историю и новый вопрос по стандартной цене.

ИнтерактивЧто кэшировать, что батчить, что оставить как есть#int-2
Каждый сценарий — в правильную категорию.
Prompt caching
Batch API
Ни то ни другое (синхрон, без кэша)
Распределите сценарии по наиболее подходящей стратегии оптимизации.

---

Быстрое повторение #rc-3

Какой hit rate кэша считается нормальным в production при высокой доле повторяющихся промптов?

Показать ответ

70% и выше; если ниже, значит breakpoint расставлен неправильно или контент меняется незаметно.

See also

Источники

  1. Prompt caching — Anthropic Platform Docs
  2. Message Batches API — Anthropic Platform Docs