Статья
Поменял effort — куда делся кэш?
Effort выглядит настройкой ответа, но у ряда моделей попадает в начало входа. Разбираю два способа сменить его посреди разговора и проверить, сохранилось ли чтение кэша.
Автор — Сергей Нотевский
Опубликовано
Попросили агента думать поменьше. История та же, инструменты те же, новый вопрос короткий. А счётчик кэшированных входных токенов падает. Как так, если мы изменили только настройку ответа?
В посте в TelegramВнешняя ссылка, откроется в новой вкладке я сформулировал это с кликбейтом: не меняйте reasoning effort, пока не разобрались с кэшем. Теперь разберём без кликбейта, зато с устройством запросов. И сразу поправлю одну фразу из поста: новые настройки effort действуют до следующего изменения, а не один пользовательский ход. Так написано в актуальных документах OpenAIВнешняя ссылка, откроется в новой вкладке и AnthropicВнешняя ссылка, откроется в новой вкладке.
Настройка ответа оказывается частью входа
Префиксный кэш переиспользует уже обработанное начало запроса. Это не кэш готового ответа: модель всё равно обрабатывает новый хвост истории и генерирует продолжение. Главное условие для чтения старого префикса — совпадение начала того входа, который фактически видит модель. Одинаковая история в JSON этого ещё не гарантирует.
У Anthropic выбранный effort встраивается в представление промпта. Если между запросами поменять верхнеуровневый output_config.effort, прежние точки кэширования сообщений перестают совпадать; в зависимости от модели могут затронуться и инструменты с системными инструкциями. Это прямое правило в документации по thinking и кэшуВнешняя ссылка, откроется в новой вкладке и таблице инвалидацииВнешняя ссылка, откроется в новой вкладке.
На уровне идеи выходит так:
запрос 1: effort=high | инструменты | инструкция | история
запрос 2: effort=low | инструменты | инструкция | история | новый вопрос
↑ изменилось начало модельного входаДва JSON-запроса могут содержать одинаковую историю и всё же давать разные кэшируемые префиксы. Работает и обратное: у Anthropic явно переданное значение по умолчанию и пропущенное поле для кэша одно и то же. Поэтому правило «поменял поле — точно потерял весь кэш» слишком грубое. Важно, какое эффективное значение получил движок и где оно оказалось в собранном входе.
Переключение внутри истории
Новый способ: исходную настройку запроса не трогаем, а изменение дописываем после стабильного префикса. Старая часть разговора не переписывается; очередной ход использует другой effort.
Верхний уровень
Переписать исходную настройку
Меняется конфигурация всего запроса. Ранее сохранённое начало может перестать совпадать.
Внутри истории
Добавить изменение после префикса
Исходная конфигурация и ранние сообщения остаются прежними. Новый effort действует с последующего хода.
Схема показывает положение изменения, а не обещает cache hit: запись ещё должна сохраниться и быть найдена.
В Responses API для GPT-6 Astra это configuration_update перед следующим сообщением пользователя. Верхнеуровневое reasoning.effort оставляем тем же, что в начале разговора:
{
"model": "gpt-6-astra",
"previous_response_id": "resp_...",
"reasoning": { "effort": "high" },
"input": [
{ "type": "configuration_update", "reasoning": { "effort": "low" } },
{ "role": "user", "content": "Собери короткий итог." }
]
}Это сокращённый пример для продолжения ответа, начатого с high. По документации OpenAIВнешняя ссылка, откроется в новой вкладке такой update поддерживается у Astra в стандартном одноагентном режиме. Он меняет именно effort и действует до следующего configuration_update. В режимах и обёртках, которые этот элемент не принимают, приём недоступен.
У Claude Fable 5.1 конструкция другая: в массив messages добавляется сообщение role: "system" с пустым content и output_config.effort. Например, после уже состоявшегося обмена:
{
"role": "system",
"content": [],
"output_config": { "effort": "low" }
}Его ставят перед следующим сообщением пользователя, не меняя верхнеуровневый effort. Для API нужен beta header mid-conversation-output-config-2026-07-01; возможность описана для Fable 5.1, Mythos 5.1 и Opus 5. Fable 5 такой per-message update не поддерживает. Настройка вступает в силу со следующего user turn и сохраняется, пока очередное сообщение не сменит её. Все эти границы перечислены в документации Anthropic по effortВнешняя ссылка, откроется в новой вкладке.
Есть одна ловушка: сам effort-only message не создаёт запись кэша. Нужен cache_control: автоматический или с явной точкой кэширования. Само изменение ставим после стабильной точки, иначе переиспользовать нечего. Это отдельно оговорено в документации mid-conversation system messagesВнешняя ссылка, откроется в новой вкладке.
А что в Codex и Claude Code?
API умеет принять новую конструкцию. Из этого ещё не следует, что агентский клиент отправит её при нажатии на переключатель effort. Между UI и API живёт сборщик запросов, который может продолжать менять верхнеуровневое поле.
В релизе Claude Code 2.1.260Внешняя ссылка, откроется в новой вкладке Anthropic прямо пишет, что переключение /effort на Fable 5.1 теперь сохраняет промпт-кэш. Это утверждение о конкретной версии клиента и модели. Оно не обещает такой же путь для всех моделей или провайдерских шлюзов.
В открытом коде CodexВнешняя ссылка, откроется в новой вкладке есть reasoning_effort_override: флаг для добавления доверенных изменений effort в историю. На коммите, который я смотрел, он выключен по умолчанию. Клиентский кодВнешняя ссылка, откроется в новой вкладке дополнительно проверяет поддержку обновлений моделью и OpenAI-провайдер. Так что «Codex поддерживает» верно с оговоркой: механизм в коде есть, но включается флагом и зависит от модели и пути вызова. Я бы проверял сформированный запрос и usage своей версии, а не делал вывод по одному пункту меню.
Как проверить, что выиграли именно на кэше
Сначала нужен достаточно длинный и повторяющийся префикс. У OpenAI для GPT-5.6 и новее минимальная кэшируемая длина — 1 024 токена; короткий тест может честно показать ноль при любом способе смены effort. У Anthropic тоже есть порог, зависящий от модели и платформы, а запись требует cache_control. Сверяйте порог с документацией OpenAIВнешняя ссылка, откроется в новой вкладке или AnthropicВнешняя ссылка, откроется в новой вкладке для выбранного пути.
Тест простой: одна длинная история и два продолжения — обычная смена верхнеуровневого effort и поддерживаемый update внутри истории. Модель, инструменты, системные инструкции, порядок сообщений и настройки кэша в обеих ветках одинаковые. Первый запрос должен успеть создать запись до повторного. Для каждого ответа собираем usage, время до первого токена и результат задачи. Несколько повторов полезнее одного удачного прогона.
01 · Вход
Сравнить фактические запросы
Модель, ранние сообщения, tools, конфигурация и место изменения effort.
02 · Кэш
Считать reuse из usage
OpenAI: cached_tokens; Claude: cache_read_input_tokens и cache_creation_input_tokens.
03 · Результат
Сверить задержку и качество
TTFT, выходные токены и пригодность ответа на одинаковом сценарии.
Совпадение префикса отвечает на вопрос «можно ли читать кэш». Usage показывает, было ли чтение в этом запросе.
У OpenAI смотрите usage.input_tokens_details.cached_tokens; диагностика кэша умеет указать причину промаха, но фактический объём чтения всё равно берите из usage. У Anthropic отдельно учитываются cache_creation_input_tokens и cache_read_input_tokens. История в одной сессии сама по себе не гарантирует попадания: запись могла истечь, маршрут измениться, а инструменты — поменять начало. Подробнее — в документации OpenAI по кэшированиюВнешняя ссылка, откроется в новой вкладке и диагностике промаховВнешняя ссылка, откроется в новой вкладке, а также Anthropic по prompt cachingВнешняя ссылка, откроется в новой вкладке.
И только после этого имеет смысл говорить про экономию и TTFT. Меньший effort может сократить выход, но иногда меняет число шагов и качество решения. Cache read помогает с обработкой входа; он не ускоряет автоматически генерацию ответа или внешний инструмент. Если в сценарии всё время уходит на decode или tool calls, хороший hit rate не спасёт пользовательскую задержку.
Сам приём полезный: сложный шаг выполняем на высоком effort, рутинное продолжение — на низком, не переписывая прогретую историю. Но это свойство конкретного API и клиента, а не универсальная кнопка «экономить». Сначала проверьте путь запроса. Потом — реальные кэшированные токены. И уже затем считайте деньги.
Если непонятно, где у вашего агента меняется начало, можно начать с разбора префиксного кэша и проекта audit-prompt-caching. Отдельный вопрос — попадёт ли повтор на нужную реплику; его я разобрал в статье о маршрутизации.