Claude API с кэшированием промптов: практический гайд
Год назад я бы написал статью «как уменьшить счёт от Anthropic в два раза», но тема устарела: с Claude API кэширование стало обыденностью, и любой живой проект обязан его включить. Расскажу, как это устроено технически, на что обращать внимание и где у меня грабли.
Что вообще кэшируется
Claude API кэширует префиксы. Запрос состоит из system, tools, messages — и всё это идёт в модель в виде одной длинной последовательности токенов. Кэш можно поставить на любую точку этой последовательности, и Anthropic запоминает посчитанное «состояние» модели до этой точки.
На следующих запросах с тем же префиксом Anthropic берёт это состояние из кэша и доделывает только остаток. Платишь за чтение из кэша — это в разы дешевле обычного входа.
Кэш работает по точному совпадению: одна лишняя запятая или пробел — кэш не сработает. Это важно: если у тебя в system-промпте текущая дата, кэш будет инвалидироваться каждый день.
Базовый пример
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
const response = await client.messages.create({
model: 'claude-sonnet-4-5',
max_tokens: 1024,
system: [
{
type: 'text',
text: VERY_LONG_SYSTEM_PROMPT, // 8 000 токенов
cache_control: { type: 'ephemeral' }
}
],
messages: [
{ role: 'user', content: userInput }
]
});
console.log(response.usage);
// первый раз:
// input_tokens: 12
// cache_creation_input_tokens: 8000
// cache_read_input_tokens: 0
// второй раз:
// input_tokens: 12
// cache_creation_input_tokens: 0
// cache_read_input_tokens: 8000Первый запрос дороже обычного на ~25%, потому что запись в кэш стоит чуть больше. Второй и далее — в 10 раз дешевле обычного входа.
Где ставить точки кэша
В одном запросе можно поставить до 4 точек cache_control. Я обычно ставлю в таком порядке:
- В конце system-промпта.
- В конце tools.
- На последнем сообщении предыдущего разговора (для длинных диалогов).
Каждая точка кэша означает «всё до меня и я сам — кэшируется». Можно делать «слоистый» кэш: меняется только последний кусок, а 90% префикса берётся из кэша.
TTL — 5 минут или 1 час
По умолчанию кэш живёт 5 минут с момента последнего использования. С 2024 года Anthropic дал опцию «кэш на 1 час» — он стоит дороже при записи, но если у тебя редкие, но дорогие запросы — это окупается.
cache_control: { type: 'ephemeral', ttl: '1h' }Я держу 1h-кэш на admin-панелях продукта: туда заходят редко, но system-промпт у меня тяжёлый.
Tools тоже кэшируются
Это упускают чаще всего. Если у тебя 15 инструментов с подробными schemas — это легко 4–6к токенов. Без кэша ты их платишь на каждом запросе.
const tools = TOOL_DEFS.map((t, i, arr) => ({
...t,
// последний tool помечаем cache_control,
// он распространяется на ВСЕ tools и system перед ним
...(i === arr.length - 1 ? { cache_control: { type: 'ephemeral' } } : {})
}));Минимальный размер
Кэшируется только префикс длиной от 1024 токенов (Sonnet/Opus) или 2048 (Haiku). Если system у тебя 500 токенов — нет смысла ставить cache_control, оно не сработает. Это сделано, чтобы кэш не тратился на мелочи.
Метрики
В response.usage есть три поля по кэшу:
- cache_creation_input_tokens — записано в кэш.
- cache_read_input_tokens — прочитано из кэша.
- input_tokens — обычные входные, мимо кэша.
Я кладу все три в свой Postgres и ежедневно строю простой отчёт «cache hit ratio». Если он ниже 70% — что-то с префиксом меняется чаще, чем нужно. Иду смотреть.
Где у меня были грабли
1. В system-промпт случайно затёк текущий timestamp. Кэш инвалидировался каждую секунду. Поймал на счёте — он не упал. Перенёс таймстемп в user-сообщение, кэш заработал.
2. Пользователь зашёл и сделал один запрос. Через 7 минут — следующий. 5-минутный TTL истёк, кэш пересоздан. Подумал — поставил TTL 1h на сценариях с редкими запросами. Стало лучше.
3. В диалоговом интерфейсе я отправлял всю историю сообщений. После 30 сообщений вход растёт до 30к токенов. Без cache_control на последнем сообщении предыдущего хода я платил за 30к на каждом запросе. С cache_control — за 30к один раз, потом за пару сотен токенов нового сообщения.
Когда не стоит кэшировать
- Очень короткие префиксы. Не сработает или не окупится.
- Префиксы, которые меняются в каждом запросе (например, RAG-документы для конкретного юзера, которые свои каждый раз).
- Тестирование/отладка. На стейдже проще без кэша, чтобы видеть полный счёт и не путать «промпт работает» с «промпт случайно совпал».
Что в итоге
Если у тебя Claude API в проде и ты не настроил кэш промптов — ты переплачиваешь, иногда в 3–5 раз. Кэш ставится одной строкой cache_control: { type: 'ephemeral' }, главное — стабильный префикс и достаточная длина. Замеряй cache hit ratio и счёт до/после, и не забывай про TTL и точку кэша на инструментах. Эти вещи окупаются за день.