Кэширование промптов в Claude API: снижаем затраты на ввод без смены модели
Кэширование промптов снижает стоимость больших стабильных вводных данных — вашего системного промпта, определений инструментов, few-shot примеров — примерно до 10% от обычной цены на ввод при повторных запросах. Механизм — это совпадение префикса: поставьте маркер cache_control в конце стабильного контента и держите всё изменчивое после него. Ошибка, которая убивает процент попаданий в кэш, — это когда временная метка или UUID просачивается в префикс.
Каждую среду. 28 400+ читателей. Никакой воды.
✓ Проверьте почту — нажмите ссылку подтверждения, чтобы завершить подписку.
✓ Вы подписаны!
✓ Вы уже в списке.
Table of contents
Open Table of contents
- Что на самом деле делает кэширование промптов
- Инвариант совпадения префикса
- На что ставить маркер кэша
- Что НЕ кэшировать (скрытые инвалидаторы)
- Проверка процента попаданий в кэш
- TTL в 1 час: когда дополнительная стоимость записи того стоит
- Предварительный прогрев: как сделать первый запрос дешёвым
- Кэширование промптов в агентных циклах
- Как это выглядит в счёте
- Итог для оператора
Что на самом деле делает кэширование промптов
Каждый вызов Claude API отправляет токены. Без кэширования каждый токен в вашем запросе — системный промпт, определения инструментов, few-shot примеры и сообщение пользователя — оплачивается по обычной ставке за ввод. С кэшированием префикс этих токенов сохраняется на серверах Anthropic после первого запроса. На последующих запросах, которые разделяют этот точный префикс, вы платите цену чтения из кэша вместо повторной обработки токенов с нуля.
Разница в стоимости реальна:
- Запись в кэш: ~1,25× от базовой цены на ввод (TTL 5 минут) или ~2× (TTL 1 час)
- Чтение из кэша: ~0,1× от базовой цены на ввод
- Точка безубыточности: 2 запроса при TTL 5 минут, 3 запроса при TTL 1 час
Как только вы прошли точку безубыточности — а это происходит быстро на любом агенте, который запускается чаще нескольких раз в день, — каждое дополнительное попадание в кэш даёт скидку ~90% на эти токены.
Инвариант совпадения префикса
Это то единственное правило, из которого следует всё остальное: ключ кэша — это совпадение префикса вашего отрендеренного промпта.
Серверы Anthropic хранят отрендеренный контент от начала вашего промпта до маркера cache_control. Чтобы при следующем запросе произошло попадание в кэш, каждый токен от начала промпта до этого маркера должен быть идентичным — побайтово.
Порядок рендеринга для совпадения префикса такой: tools → system → messages. То есть сначала хэшируется массив ваших инструментов, затем системный блок, затем сообщения по порядку.
Что это значит на практике: стабильный контент должен идти первым. Если ваш системный промпт ссылается на что-то динамическое — текущую дату, идентификатор пользователя, trace ID запроса — и это появляется до маркера cache_control, кэш будет промахиваться на каждом запросе, потому что префикс постоянно меняется.
На что ставить маркер кэша
Цели с наибольшей отдачей такие:
1. Ваш системный промпт
Системные промпты обычно — самый большой стабильный блок. Подробная персона агента, список поведенческих правил, набор инструкций по формату вывода — всё это идентично при каждом вызове одного и того же агента. Отметьте его:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
system: [
{
type: "text",
text: `You are a content operations agent for alejandrorioja.com.
Your job is to draft blog posts in Alejandro's voice: direct, practitioner,
first-person, numbered lists, honest caveats. No hedging. No filler.
Every section must earn its place.
[... 2000 more tokens of stable instructions ...]`,
cache_control: { type: "ephemeral" },
},
],
messages: [
{
role: "user",
content: "Draft a post about prompt caching.",
},
],
});cache_control: { type: "ephemeral" } на системном блоке говорит Claude кэшировать всё вплоть до этого блока включительно. Массив messages изменчив — разный при каждом запросе — и остаётся за границей кэша.
2. Определения инструментов
Если ваш агент использует инструменты, их определения могут быть весьма объёмными. Хорошо документированная схема инструмента с описанием, именами параметров и значениями enum может занимать 500–1 000 токенов на инструмент. С 5 инструментами это до 5 000 токенов, за повторную обработку которых вы платите при каждом вызове:
const response = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
tools: [
{
name: "search_airtable",
description: "Search the Airtable content queue...",
input_schema: { type: "object", properties: { query: { type: "string" } } },
},
// ... more tools ...
{
name: "post_to_kit",
description: "Schedule a broadcast via the Kit API...",
input_schema: { /* ... */ },
// Mark the last tool to cache the entire tools array
} as Anthropic.Tool & { cache_control: { type: "ephemeral" } },
],
system: "...",
messages: [...],
});Отметьте последний инструмент в массиве. Совпадение префикса покроет весь массив инструментов начиная с этой точки.
3. Few-shot примеры в сообщениях
Если вы передаёте статические few-shot примеры как первые сообщения в массиве messages, их тоже можно кэшировать. Структурируйте их как первые N сообщений и отметьте последний ход с примером:
const messages: Anthropic.MessageParam[] = [
{
role: "user",
content: [
{
type: "text",
text: "Here are examples of posts in my voice:\n\n[Example 1...]\n\n[Example 2...]",
cache_control: { type: "ephemeral" },
} as Anthropic.TextBlockParam & { cache_control: { type: "ephemeral" } },
],
},
{
role: "assistant",
content: "Understood. I'll follow that voice.",
},
// The actual user turn follows — this is volatile, no cache marker
{
role: "user",
content: actualUserRequest,
},
];Что НЕ кэшировать (скрытые инвалидаторы)
Это вещи, которые выглядят стабильными, но таковыми не являются, — и они тихо убьют ваш процент попаданий. API вас не предупредит. Вы просто будете видеть cache_creation_input_tokens на каждом запросе и недоумевать, почему.
Временные метки в системном промпте. Самая распространённая ошибка:
// This invalidates the cache on every request
const system = `You are an agent. Current time: ${new Date().toISOString()}`;Перенесите временные метки в сообщение пользователя, где им и место:
// Stable system prompt — cacheable
const system = `You are an agent. Use the current time provided by the user.`;
// Volatile user message — not cached
const userMessage = `Current time: ${new Date().toISOString()}. Run the daily brief.`;Случайные UUID и trace ID. Та же проблема. Если вы вставляете trace ID в системный блок для логирования, каждый запрос получает новый префикс.
Недетерминированная сериализация JSON. Если вы сериализуете объект в системный промпт, а порядок ключей не гарантирован, отрендеренная строка может отличаться, даже когда исходные данные те же самые. Сериализуйте со стабильным порядком ключей или используйте шаблонную строку.
Динамический выбор few-shot примеров. Если вы выбираете few-shot примеры на основе текущего запроса и помещаете их в кэшируемый префикс, вы сделали «стабильный» префикс зависимым от запроса. Либо зафиксируйте примеры для слоя кэширования, либо перенесите динамические примеры в некэшируемый ход сообщения.
Проверка процента попаданий в кэш
Каждый ответ включает метаданные об использовании. Проверяйте их:
const response = await client.messages.create({ /* ... */ });
console.log({
inputTokens: response.usage.input_tokens,
cacheRead: response.usage.cache_read_input_tokens,
cacheWrite: response.usage.cache_creation_input_tokens,
outputTokens: response.usage.output_tokens,
});На первом запросе: cache_creation_input_tokens будет ненулевым, cache_read_input_tokens будет равен 0. Это запись.
На попадании в кэш: cache_read_input_tokens будет ненулевым, cache_creation_input_tokens будет равен 0. Это чтение.
Если вы видите cache_creation_input_tokens на каждом запросе, значит, ваш префикс меняется. Добавьте оператор логирования, который печатает первые 200 символов вашего отрендеренного системного промпта перед каждым вызовом, — плавающая временная метка тут же бросится в глаза.
TTL в 1 час: когда дополнительная стоимость записи того стоит
TTL по умолчанию — 5 минут. Если ваш агент работает с низкой частотой — реже одного раза в 5 минут, — вы будете платить за записи в кэш на большинстве запросов, не получая чтений.
// Opt into a 1-hour TTL
cache_control: { type: "ephemeral", ttl: "1h" }Запись с TTL в 1 час стоит ~2× от базовой цены на ввод вместо 1,25×. Расчёт: если вы попадаете в кэш 3 или более раз в час, TTL в 1 час экономит деньги. Если ваш агент работает раз в день (как мой ежедневный брифинг), даже TTL в 1 час не поможет — вы платите за запись каждый раз. В этом случае выгода от кэширования скромная, если только системный промпт не огромен.
У моего агента ежедневного брифинга системный промпт на 3 000 токенов, но запускается он раз в день. Кэширование не помогает. Мой агент рассылки запускается десятки раз за сессию во время черновой работы — кэширование экономит существенно.
Предварительный прогрев: как сделать первый запрос дешёвым
Если вы знаете о приближающемся всплеске трафика — пакетном задании, запуске API, — вы можете предварительно прогреть кэш дешёвым фиктивным запросом:
// Pre-warm: write the cache at near-zero output cost
await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1, // minimal output
system: [{ type: "text", text: stableSystemPrompt, cache_control: { type: "ephemeral" } }],
messages: [{ role: "user", content: "ping" }],
});
// Now the real requests read from cacheЭто в основном полезно для пакетной обработки, когда вы запускаете множество параллельных запросов и хотите, чтобы каждый попал в тёплый кэш, а не гонялся за тем, кто его запишет.
Кэширование промптов в агентных циклах
В многоходовом агентном цикле история разговора растёт на каждом ходу. Кэш достаточно умён, чтобы справиться с этим: он использует окно просмотра назад в 20 блоков, находя самый длинный совпадающий префикс в пределах последних 20 блоков контента.
Практический вывод: держите ваш стабильный контент (системный промпт, определения инструментов) закреплённым наверху. Растущая история разговора в конце массива сообщений не сломает совпадение префикса для стабильных блоков — они идут перед изменчивым контентом, а совпадение префикса начинается сверху.
На практике мои агенты структурируют ходы так:
System (cached) → Tools (cached) → Few-shot (cached) → Turn 1 → Turn 2 → ... → Current turnКэш покрывает всё вплоть до маркера few-shot. Растущая история ходов после него обрабатывается заново каждый раз, но это нормально — эти токены специфичны для сессии и малы относительно стабильного префикса.
Как это выглядит в счёте
Возьмём высокочастотного агента: 100 вызовов в день, системный промпт на 4 000 токенов, цены Sonnet.
Без кэширования:
- 100 × 4 000 токенов × $3/1M = $1,20/день
С кэшированием (TTL 5 минут, при допущении 50 вызовов в час на пике):
- 1 запись каждые 5 минут × $3,75/1M × 4 000 токенов = ~$0,02/день на записи
- ~98 чтений/день × $0,30/1M × 4 000 токенов = $0,12/день на чтения
Это примерно 90% снижение на этих токенах ввода. На масштабе — 1 000 вызовов в день — разница усиливается ещё сильнее. И всё это поверх любой экономии от маршрутизации моделей из математики Haiku против Sonnet: кэширование работает на любом уровне.
Итог для оператора
Кэширование промптов — самая простая оптимизация затрат в Claude API: одно дополнительное поле в блоках контента, которые вы и так уже пишете. Ограничение — это дисциплина в отношении стабильности префикса: ничего динамического перед маркером кэша. Если вы сможете держать ваш системный промпт, инструменты и любые статические примеры свободными от изменчивого контента, вы будете платить ~10% от обычной стоимости ввода на каждом попадании в кэш. Для высокочастотных агентов с большими стабильными промптами это более мощный рычаг, чем смена уровня модели.
Связанные материалы: Математика затрат на ИИ-агентов: когда Haiku обходит Sonnet · Агенты по событиям против агентов по расписанию · 5 ИИ-инструментов, которые я реально использую для управления бизнесом
Каждую среду. 28 400+ читателей. Никакой воды.
✓ Проверьте почту — нажмите ссылку подтверждения, чтобы завершить подписку.
✓ Вы подписаны!
✓ Вы уже в списке.
Получайте ИИ-руководство на почту
Каждую среду. 28 400+ читателей. Никакой воды.
Проверьте почту.
Мы отправили письмо для подтверждения — нажмите на ссылку, чтобы завершить подписку. Проверьте папку «Спам», если не видите его в течение минуты.
Вы подписаны.
Добро пожаловать — следующий выпуск скоро придёт на вашу почту.
Вы уже в списке — ждите выпуск каждую среду.