Как я сократил расход токенов в Claude Code на 75,5% с 17 локальными MCP — и почему byte saving недостаточно
RU-адаптация моей EN-статьи Two axes every LLM agent pays for (gregshevchenko.com, 2026-05-24). В этой версии добавлена секция cache-friendliness как второй первичной оси и измеренный directional finding, подтверждающий тезис на провайдере.
Если вы активно работаете с Claude Code, Codex или другим агентом на базе frontier-модели, вы наверняка замечали: даже на дорогом тарифе лимиты кончаются быстрее, чем хотелось бы. Причина не в самой модели — а в том, сколько текста она вынуждена читать перед каждым ответом. Большой, шумный контекст означает больше токенов на входе и больше денег на выходе.
Я собрал стек из 17 локальных MCP-серверов, которые стоят между разработчиком и моделью и готовят контекст заранее: ищут нужные файлы, чистят логи, структурируют информацию — вместо того чтобы модель тратила токены на черновую работу. На тестовом наборе из 25 задач это дало сокращение объёма контекста на 75,5% — подробная методология и сравнение с другими инструментами описаны в полной версии исследования.
Но в процессе выяснилось, что экономия объёма — это только половина истории. Вторая половина решает, действительно ли эта экономия долетает до счёта.
Первая ось: сколько байт вы срезали
Первое, что измеряют почти все компрессоры контекста, — насколько меньше становится текст на входе. Это интуитивно понятная метрика: было 100 килобайт лога, стало 5 — отлично, экономия 95%.
Но провайдер модели считает стоимость не только по объёму разового запроса. Ключевую роль играет так называемый prefix cache — официально описанный механизм кэширования контекста у Anthropic: если начало запроса (системные инструкции, описания инструментов, статичная часть контекста) повторяется от хода к ходу байт в байт, провайдер не пересчитывает его заново — он берёт готовый результат из кэша. Это резко снижает и стоимость, и время ответа.
Вторая ось: одинаковый ли результат при повторном запуске
Здесь и возникает вторая ось — назовём её «дружелюбность к кэшу». Смысл прост: если ваш компрессор контекста при одинаковом входе выдаёт на выходе разные байты, кэш не срабатывает. Провайдер видит «новый» запрос на каждом ходе и пересчитывает всё с нуля — даже если объём текста был сокращён на 99%.
Получается парадокс: агрессивный компрессор может резать объём лучше всех — и при этом обходиться дороже, чем скромный, но стабильный.
Откуда берётся нестабильность вывода?
Чаще всего из архитектуры того, как собирается запрос к модели. Практика, которая обычно применяется для решения этой проблемы, звучит просто: стабильная часть — в начало, изменчивая — в конец. Описания инструментов и статичные инструкции должны идти первыми и в одном и том же порядке при каждом запуске. А всё, что меняется от хода к ходу — текущее время, идентификаторы запроса, свежие данные — должно оставаться в конце. Если изменчивая вставка попадает перед стабильным блоком, она ломает кэш для всех последующих ходов, которые от этого блока зависят.
Компрессоры, построенные на детерминированной логике — разбор текста по правилам, извлечение фактов по шаблону, выбор нужных фрагментов без участия модели, — по своей природе выдают одинаковый результат на одинаковом входе. А вот компрессоры, которые сами обращаются к модели, чтобы переранжировать или пересказать контекст, могут в моменте показать более высокое сжатие — но их результат меняется от запуска к запуску, и повторное использование кэша падает почти до нуля.
Частые причины, по которым ломается кэш
Даже если сам компрессор стабилен, кэш может сломать сама сборка запроса. Вот список наиболее частых причин:
- метка времени внутри стабильной части запроса;
- идентификатор запроса, вставленный перед стабильным блоком;
- случайный порядок описаний инструментов при каждом запуске;
- сериализация в JSON без фиксированного порядка полей;
- переменные окружения, подставленные перед статичными инструкциями;
- переписывание истории диалога заново на каждом ходе;
- пересказ всей сессии заново на каждом ходе;
- изменение формата данных без версионирования;
- свежие результаты поиска, вставленные перед стабильными инструкциями;
- слишком точечное разбиение на кэш-ключи при небольшом объёме запросов;
- отсутствие логирования того, сколько токенов реально берётся из кэша — без этого невозможно понять, что именно сломано.
Что показали замеры
Чтобы проверить тезис не только логически, но и на данных, я сравнил два условия на реальных задачах: с подключённым MCP-стеком и без него. Направление результата чёткое: с подключённым стеком доля токенов, взятых из кэша провайдера, оказалась выше, чем без стека. Эффект был заметен как на обычных рабочих сессиях в Claude Code, так и на более формализованном наборе задач для проверки агентов.
Точные цифры этого замера я оставляю закрытыми — это внутренние данные, — но само направление подтверждает главный тезис: детерминированный компрессор — это необходимое условие, но именно оно и даёт эффект на стороне провайдера в виде более высокого повторного использования кэша.
Поучительный случай из собственного стека
Не все компрессоры одинаково дружелюбны к кэшу «из коробки» — и лучшая иллюстрация нашлась в моём же стеке, который целиком выложен в открытом доступе. Один из компонентов, отвечающий за поиск релевантных файлов, показывал впечатляющее сжатие: он превращал мегабайты исходных файлов в несколько килобайт отранжированных фрагментов.
Но при проверке на одном и том же рабочем пространстве этот компонент примерно в семи случаях из десяти выдавал разный результат на идентичный запрос. Причина оказалась банальной: инструмент поиска файлов, который он использовал внутри, не гарантирует стабильный порядок результатов, а этот порядок затем «протекал» дальше по цепочке обработки.
Это и есть та самая ситуация, ради которой вообще стоит думать о второй оси: компрессор с самым большим сжатием, но нестабильным выводом, на практике может обходиться дороже, чем более скромный конкурент со стабильным результатом. Одно большое сжатие на одном ходе не окупает потерю кэша на каждом следующем.
Исправление заняло немного: результаты поиска стали сортироваться перед использованием, и итоговый порядок данных зафиксировали. После этого стабильность выросла до полной, а объём сжатия не изменился. Другими словами — тот же уровень экономии объёма, но теперь она реально долетает до счёта.
Что с этим делать
Если в вашем стеке используются собственные MCP-серверы или самописная сборка запросов к модели, стоит сделать три вещи:
Проверить типовые причины поломки кэша из списка выше — часто достаточно одной зашитой метки времени, чтобы кэш не работал ни на одном ходе.
Не доверять заявлениям об экономии объёма без второго доказательства — попросите прогнать один и тот же запрос несколько раз и сравнить результат побайтово. Если он совпадает — компрессор дружелюбен к кэшу, если нет — экономия может не долетать до счёта.
Считать окупаемость по обеим осям сразу. Слой подготовки контекста снижает реальные расходы только тогда, когда он одновременно сокращает объём и выдаёт стабильный, повторяемый результат.
Если проверка на стабильность не проходит для одного из ваших инструментов — это не повод для паники, а рабочая находка: почти всегда её можно исправить за пару часов, и разница будет заметна сразу — между экономией, которая реально снижает счёт, и экономией, которая существует только на бумаге.
FAQ
Что такое MCP-сервер простыми словами?
Это небольшая программа, которая выполняет одну конкретную подготовительную задачу — например, ищет нужные файлы или чистит лог — до того, как запрос попадёт к языковой модели. Модель получает уже готовый, отфильтрованный контекст вместо сырых данных.
Почему нельзя просто сильнее сжимать контекст — и всё?
Можно, но это не единственное условие экономии. Если сжатый результат каждый раз получается немного другим, провайдер не сможет переиспользовать кэш, и вы всё равно заплатите за полную обработку запроса — даже при сильном сжатии.
Что значит «дружелюбность к кэшу»?
Это способность инструмента выдавать байт-в-байт одинаковый результат при одинаковом входе. Если два одинаковых запроса дают разный текст на выходе, кэш провайдера не срабатывает.
Как проверить, дружелюбен ли мой инструмент к кэшу?
Прогоните один и тот же запрос через него несколько раз подряд и сравните результат побайтово. Если результат совпадает каждый раз — инструмент детерминирован. Если отличается — экономия объёма может не долетать до реального счёта.
Какая самая частая причина поломки кэша?
Метка времени, идентификатор запроса или другие изменяющиеся данные, случайно вставленные в начало запроса — перед стабильной частью инструкций. Даже одна такая вставка ломает кэш на каждом ходе.
Это работает только с Claude Code?
Нет, это общая механика для любых агентов на базе frontier-моделей с поддержкой кэширования по префиксу запроса — включая Codex, Cursor, Windsurf и аналогичные инструменты.
Нужно ли самому писать сложные компрессоры, чтобы получить экономию?
Необязательно с нуля — достаточно построить подготовку контекста на детерминированной логике (разбор по правилам, извлечение по шаблону) и следить, чтобы стабильные части запроса шли первыми, а изменчивые — последними.