В прошлый раз я разбирал, куда именно ходит Claude Code и какие бывают ключи. Логичное продолжение — куда уходят деньги. Потому что первый вопрос, который возникает после недели работы с агентом: почему счёт растёт быстрее, чем кажется по объёму кода.
Ниже — разбор по полям usage, механика кэша и правила, которые у меня реально срезали расход. Без магии, всё проверяется в ответе API.
Из чего состоит счёт
Токены, за которые вы платите, делятся не на два типа, а минимум на пять:
|
Тип |
Что это |
Порядок цены |
|---|---|---|
|
Input (miss) |
Обычный ввод, который модель видит впервые |
базовая |
|
Cache write |
Ввод, который провайдер положил в кэш |
дороже базовой |
|
Cache read |
Ввод, найденный в кэше |
примерно на порядок дешевле базовой |
|
Output |
То, что модель написала |
в разы дороже ввода |
|
Reasoning |
Внутренние рассуждения модели |
тарифицируется как output |
Два последних пункта — главная ловушка. Reasoning-токены вы не видите в ответе, но платите за них по цене output. У моделей с высоким reasoning effort их бывает в несколько раз больше, чем полезного текста.
Точные множители смотрите в тарифах своего провайдера, они меняются. Важна не цифра, а порядок: чтение из кэша стоит копейки, запись в кэш — дороже обычного ввода, рассуждения стоят как ответ.
Как посмотреть это у себя
Anthropic-протокол возвращает в usage отдельные поля:
"usage": {
"input_tokens": 37,
"output_tokens": 211,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 192
}
OpenAI-совместимый протокол прячет то же самое в details:
"usage": {
"prompt_tokens": 90,
"completion_tokens": 234,
"total_tokens": 324,
"prompt_tokens_details": {"cached_tokens": 0},
"completion_tokens_details": {"reasoning_tokens": 230}
}
Обратите внимание на второй пример: из 234 токенов ответа 230 — это рассуждения. Полезного текста четыре токена, слово «работает». Заплатили вы за 234.
Первое, что стоит сделать, — начать логировать usage по каждому запросу. Без этого разговор о стоимости превращается в гадание.
Как на самом деле работает кэш
Провайдеры кэшируют не «похожие запросы», а совпадающий префикс. Правила простые и жёсткие:
-
Совпадать должно начало запроса, байт в байт. System prompt, описания инструментов, правила проекта — всё, что идёт первым.
-
Расхождение в одном символе в начале обнуляет весь кэш дальше.
-
У кэша есть TTL. По умолчанию он короткий, порядка нескольких минут; у некоторых провайдеров есть опция продлить его за отдельную цену.
-
Кэш привязан к организации и конкретной модели. Сменили модель — греете заново.
Отсюда типичные способы сжечь деньги на ровном месте:
-
Таймстемп или случайный request id в начале system prompt. Каждый запрос — холодный старт.
-
Динамическая сборка списка инструментов: сегодня в одном порядке, завтра в другом. Для кэша это разные префиксы.
-
Вставка нового контекста в середину, а не в конец.
-
Долгая пауза в сессии: подумали двадцать минут, вернулись — префикс протух.
Семь правил, которые я применяю
-
Стабильный префикс. Системный промпт, инструменты и правила проекта не меняются в течение сессии. Всё изменяемое — в конец.
-
Никаких таймстемпов, счётчиков и случайных id выше по тексту.
-
Фиксированный порядок инструментов. Если список собирается кодом, сортируйте его детерминированно.
-
Длинные документы — целиком и один раз, а не кусками в каждом сообщении.
-
Reasoning effort под задачу. Для «переименуй переменные» высокий effort — это деньги на ветер. Для архитектурных вопросов наоборот.
-
Уточняющие вопросы вместо переделок. Одна строчка «если что-то неясно, задай три вопроса перед началом» дешевле, чем два круга правок.
-
Claude — только через Anthropic-протокол. Через OpenAI-совместимый endpoint часть реализаций теряет поля кэша, и вы платите полную цену за то, что могло читаться из кэша.
Минимальный скрипт для замера
Складывайте usage в jsonl и считайте два числа: долю чтения из кэша и долю рассуждений в ответе.
import json, sys
ci = cr = out = rs = 0
for line in open(sys.argv[1]):
u = json.loads(line).get("usage", {})
ci += u.get("input_tokens", u.get("prompt_tokens", 0))
cr += u.get("cache_read_input_tokens",
u.get("prompt_tokens_details", {}).get("cached_tokens", 0))
out += u.get("output_tokens", u.get("completion_tokens", 0))
rs += u.get("completion_tokens_details", {}).get("reasoning_tokens", 0)
print(f"cache hit: {cr / (ci + cr) * 100:.1f}%")
print(f"reasoning: {rs / out * 100:.1f}% от output")
У меня ориентир такой: на длинной сессии с агентом доля чтения из кэша должна быть высокой. Если она у вас около нуля, ищите, что ломает префикс, — это почти всегда даёт больше экономии, чем переход на модель подешевле.
Чего я не проверял
Поведение кэша под высокой параллельностью и точные TTL у разных провайдеров — это стоит мерить самому, цифры в документации и на практике иногда расходятся. Если у вас есть замеры, напишите в комментариях, добавлю в статью.
А ещё интересно: у кого какая доля reasoning-токенов на реальных задачах? У меня на рефакторинге доходило до 90% от output.
Автор: Corkyz0011


