Управление контекстом
Помощник аналитика получает ТЗ на сто страниц и десять вопросов подряд. Ревьюер PR за один прогон читает двадцать файлов. Бот портала ведёт диалог с заказчиком неделю. Во всех трёх случаях контекст растёт, и, поскольку API не хранит состояние, вы отправляете его целиком в каждом запросе и каждый раз за него платите. В этом уроке разберём, как этим управлять: кэшированием, компакцией и очисткой.
Что такое контекст и почему он дорогой
Контекст — это всё, что модель видит в запросе: определения инструментов, системный промпт, история сообщений с результатами инструментов и блоками рассуждения. У Opus 5 контекстное окно — миллион токенов, так что «не влезет» случается редко. Проблема в другом: на каждом ходе диалога вы заново отправляете и оплачиваете всю историю. Десятый вопрос к ТЗ на сто страниц стоит столько же, сколько первый, плюс девять предыдущих ответов.
Второй эффект — качество. Чем больше в контексте устаревших результатов инструментов и промежуточных рассуждений, тем сложнее модели держать в фокусе текущую задачу. Ревьюер, который прочитал двадцать файлов, к концу может забыть, что искал.
Отсюда три механизма. Кэширование делает повторную отправку дешёвой. Компакция сжимает старую историю в краткое изложение. Очистка удаляет то, что больше не нужно.
Кэширование промпта
Кэш работает по префиксу. API запоминает обработанное начало запроса, и если следующий запрос начинается с тех же байтов, эта часть берётся из кэша по сильно сниженной цене. Порядок отрисовки запроса фиксирован: сначала tools, потом system, потом messages. Любое изменение в начале делает недействительным всё, что после него.
Из этого следует главный принцип: стабильное — вперёд, изменчивое — назад. Определения инструментов и системный промпт не должны меняться от запроса к запросу. Дата, имя пользователя, идентификатор сессии — не в системном промпте, а в сообщениях. Инструменты сериализуются в одном порядке.
Для помощника аналитика ТЗ — идеальный кандидат в кэш: сто страниц, которые одинаковы для всех десяти вопросов.
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 16000,
system: [
{ type: "text", text: ANALYST_RULES },
{
type: "text",
text: `Техническое задание проекта:\n\n${spec}`,
cache_control: { type: "ephemeral" },
},
],
messages: [{ role: "user", content: question }],
});
console.log(response.usage.cache_creation_input_tokens); // записано в кэш
console.log(response.usage.cache_read_input_tokens); // прочитано из кэша
console.log(response.usage.input_tokens); // обработано по полной ценеМаркер cache_control на последнем блоке system кэширует всё до него включительно: инструменты, правила, ТЗ. Первый запрос запишет кэш (запись стоит чуть дороже обычной обработки), следующие в течение пяти минут прочитают его. Если запросы приходят реже, есть ttl: "1h": запись дороже, но кэш живёт час. Маркеров можно поставить до четырёх, и у кэшируемого префикса есть минимальный размер (на Opus 5 — 512 токенов; на других моделях больше, смотрите документацию). Короткий промпт молча не закэшируется.
Для диалога есть вариант проще: cache_control: { type: "ephemeral" } на верхнем уровне запроса. API сам ставит маркер на последний подходящий блок и сдвигает его по мере роста истории. Для бота портала это подходящий режим: каждый новый ход читает всю предыдущую историю из кэша.
Проверяйте usage после каждого изменения в сборке промпта. Если cache_read_input_tokens равен нулю на повторных запросах, где-то в префиксе есть «тихий разрушитель»: Date.now() в системном промпте, случайный идентификатор, набор инструментов, который зависит от пользователя, непредсказуемый порядок ключей в JSON. Кэш ломается молча, запросы продолжают работать, растёт только счёт. Хорошая практика — тест, который делает два одинаковых запроса и проверяет, что во втором cache_read_input_tokens > 0.
Два следствия для архитектуры. Не меняйте модель посреди диалога: кэш привязан к модели. И не меняйте набор инструментов: они в самом начале префикса. Если посреди диалога нужна новая инструкция, на Opus 5 её можно добавить сообщением с ролью system в конец messages, не трогая верхний system, и кэш истории сохранится.
Компакция
Бот портала общается с заказчиком неделю, и история подходит к сотням тысяч токенов. Компакция решает это на стороне API: когда контекст приближается к порогу, API сжимает раннюю часть истории в краткое изложение и возвращает его специальным блоком compaction. На момент написания это бета.
const messages: Anthropic.Beta.BetaMessageParam[] = [];
async function chat(userText: string): Promise<string> {
messages.push({ role: "user", content: userText });
const response = await client.beta.messages.create({
betas: ["compact-2026-01-12"],
model: "claude-opus-5",
max_tokens: 16000,
system: PORTAL_RULES,
messages,
context_management: { edits: [{ type: "compact_20260112" }] },
});
// Весь content целиком: блок compaction должен остаться в истории
messages.push({ role: "assistant", content: response.content });
const text = response.content.find(
(b): b is Anthropic.Beta.BetaTextBlock => b.type === "text",
);
return text?.text ?? "";
}Критичная деталь — та же, что в агентном цикле: в историю добавляется весь response.content, а не только текст. Блок compaction — это и есть сжатая история; на следующем запросе API заменит им старые сообщения. Если сохранить только текст ответа, состояние компакции потеряется молча.
Компакция — это изложение, а не копия. Детали ранних ходов исчезнут. Для бота портала это приемлемо; для юридически значимой переписки — нет, там нужна отдельная запись полной истории вне контекста модели.
Очистка истории
Ревьюер PR прочитал двадцать файлов, и все двадцать лежат в контексте как результаты инструментов, хотя нужны были на один шаг. Очистка (context editing) удаляет старые результаты инструментов и блоки рассуждения, не трогая структуру диалога. В отличие от компакции, ничего не пересказывается — содержимое просто убирается. Тоже бета.
const response = await client.beta.messages.create({
betas: ["context-management-2025-06-27"],
model: "claude-opus-5",
max_tokens: 16000,
tools: REVIEW_TOOLS,
messages,
context_management: {
edits: [
{ type: "clear_tool_uses_20250919" },
{ type: "clear_thinking_20251015" },
],
},
});Стратегия clear_tool_uses_20250919 очищает старые результаты инструментов (с параметром clear_tool_inputs: true — и параметры вызовов); clear_thinking_20251015 — блоки рассуждения. Пороги настраиваются, детали — в документации. Обратите внимание: типы и бета-заголовки компакции и очистки разные, не смешивайте их.
Что выбрать
| Ситуация | Механизм |
|---|---|
| Большой общий префикс (ТЗ, база знаний, длинный системный промпт) | Кэширование |
| Многоходовой диалог | Кэширование на верхнем уровне запроса |
| Диалог рискует не влезть в окно | Компакция |
| Длинный агентный цикл с большими результатами инструментов | Очистка |
| Память между сессиями | Инструмент памяти из урока про встроенные инструменты |
Механизмы сочетаются: долгоживущий агент обычно использует все. И для любого из них полезен client.messages.countTokens — он показывает размер запроса до отправки, что удобно для лимитов и логов.
Попробуйте сами
10–15 мин на рабочем месте- Возьмите помощника аналитика с ТЗ в
systemи маркеромcache_control. Задайте два вопроса подряд и сравнитеusageпервого и второго. Затем добавьте в начало системного промпта текущую дату и посмотрите, что стало сcache_read_input_tokens. - Напишите тест, который делает два одинаковых запроса и падает, если во втором нет чтения из кэша. Положите его в CI.
- Возьмите ревьюера PR и включите очистку результатов инструментов. Сравните
input_tokensна последней итерации с прогоном без очистки.
Коротко
- API не хранит состояние: вся история отправляется и оплачивается на каждом ходе.
- Кэш работает по префиксу
tools → system → messages; стабильное — вперёд, изменчивое — назад. cache_control: { type: "ephemeral" }на блоке или на верхнем уровне запроса; проверяйтеusage.cache_read_input_tokens.- Не меняйте модель и набор инструментов посреди диалога: кэш сломается.
- Компакция (бета
compact-2026-01-12) сжимает историю в блокcompaction; сохраняйтеresponse.contentцеликом. - Очистка (бета
context-management-2025-06-27) удаляет старые результаты инструментов и рассуждения без пересказа. - Кэш ломается молча; закройте его тестом.
Видеоверсия
Сценарий озвучки · 501 слово, ≈ 4 мин
Помощник аналитика получает техзадание на сто страниц и десять вопросов подряд. Бот портала ведёт диалог с заказчиком неделю. API не хранит состояние, поэтому всю историю вы отправляете целиком в каждом запросе и каждый раз за неё платите. В этом уроке — три механизма, которые держат это под контролем.
Сначала о том, что такое контекст. Это всё, что модель видит в запросе: определения инструментов, системный промпт, история сообщений с результатами инструментов и рассуждениями. Окно у современных моделей огромное, так что «не влезло» случается редко. Проблема в цене: десятый вопрос к техзаданию стоит столько же, сколько первый, плюс девять предыдущих ответов. И в качестве: чем больше устаревших результатов в контексте, тем труднее модели держать фокус.
Первый механизм — кэширование промпта. Кэш работает по префиксу. API запоминает обработанное начало запроса, и если следующий запрос начинается с тех же байтов, эта часть берётся из кэша по сильно сниженной цене. Порядок фиксирован: сначала инструменты, потом системный промпт, потом сообщения. Любое изменение в начале ломает всё, что после. Отсюда главный принцип: стабильное — вперёд, изменчивое — назад. Дата, имя пользователя, идентификатор сессии — не в системном промпте, а в сообщениях.
Для помощника аналитика техзадание — идеальный кандидат. Кладём его в системный промпт последним блоком и ставим на этот блок маркер кэширования. Первый запрос запишет кэш, следующие в течение пяти минут прочитают его. Если запросы приходят реже, можно продлить срок до часа. У кэшируемого префикса есть минимальный размер, короткий промпт молча не закэшируется. Для диалога есть вариант проще: маркер на верхнем уровне запроса, и API сам двигает его по мере роста истории.
Обязательно проверяйте расход после каждого изменения в сборке промпта. В ответе есть отдельные поля: сколько записано в кэш и сколько прочитано. Если чтение из кэша равно нулю на повторных запросах, где-то в префиксе тихий разрушитель: текущая дата в промпте, случайный идентификатор, набор инструментов, зависящий от пользователя. Кэш ломается молча, запросы работают, растёт только счёт. Напишите тест: два одинаковых запроса, во втором должно быть чтение из кэша. И два правила: не меняйте модель посреди диалога, кэш к ней привязан, и не меняйте набор инструментов — они в самом начале префикса.
Второй механизм — компакция. Когда история подходит к порогу, API сжимает раннюю часть в краткое изложение и возвращает его специальным блоком. Критичная деталь та же, что в агентном цикле: в историю добавляйте весь ответ целиком. Этот блок и есть сжатая история, на следующем запросе API заменит им старые сообщения. Сохраните только текст — состояние потеряется молча. И помните: компакция — это изложение, детали ранних ходов исчезнут. Для бота портала приемлемо, для юридически значимой переписки — нет.
Третий — очистка. Ревьюер прочитал двадцать файлов, и все они лежат в контексте, хотя нужны были на один шаг. Очистка удаляет старые результаты инструментов и блоки рассуждения, ничего не пересказывая. Компакция и очистка — разные функции с разными заголовками, не смешивайте их. Обе на момент записи в бете.
Что выбрать. Большой общий префикс — кэширование. Диалог — кэширование на верхнем уровне. Диалог рискует не влезть — компакция. Длинный агентный цикл — очистка. Память между сессиями — инструмент памяти. Долгоживущий агент обычно использует всё сразу. В следующем модуле посмотрим на управляемых агентов, где часть этой работы Anthropic берёт на себя.
