Девяносто процентов и фраза, которая это продала
Сетап Spotify для Claude Code, это пост в блоге Дмитрия Мазманова, продакт-менеджера Spotify, с кодом на GitHub: по его словам, конфигурация, которой пользуется его команда, срезала его расход токенов Claude Code на 90%. Первая же строка поста несёт весь аргумент: большая часть того, что делает ИИ-агент для кода, это не размышления, а I/O. Прочитать пять файлов, чтобы ответить на вопрос об одном методе, или написать двадцать первый тестовый файл, копирующий двадцать соседних, сжигает тысячи токенов почти без рассуждений.
Один твит разогнал пост до полутора миллионов просмотров на единственной фразе: написанные правила, это совет, а блокировка, нет. Hacker News вынес его на главную, 271 балл и 173 комментария, и половина комментариев задавала один и тот же вопрос: 90% чего? Собственная оговорка Spotify звучит как «bulk read», массовое чтение. Эта статья пересобирает сетап внутри обычного Claude Code, а затем измеряет его, чтобы вы точно знали, что покупает вам эта оговорка.
Что такое Portal на самом деле (и почему вам его не запустить)
Portal не роутер. Это внутренний портал разработчиков Spotify, построенный на Backstage, платформе для разработчиков, которую Spotify выложила в open source. Нужная здесь функция внутри него называется Modes: по определению Spotify, mode, это декларативный агент, который работает на эфемерном рантайме, примерно как AWS Lambda для агентов. Вы пишете инструкции, выбираете модель, задаёте температуру, подключаете инструменты. Мазманов собрал два таких: bulk reader и code writer, оба на Gemini Flash с температурой 0.2, так что оба дешёвые и скучные намеренно.
Маршрутизация живёт в плагине для Claude Code под названием Shunt. Он публичный, лежит на GitHub и ставится двумя командами. Однако второй шаг аутентифицирует командную строку Portal против вашего инстанса Portal, а у вас его нет. Плагин публичный; то, чему он делегирует, нет.
Поэтому полезный ход, забыть про плагин и оставить паттерн. В нём три слоя, его же словами: хуки, скрипты, скиллы. У каждого из них есть эквивалент в обычном Claude Code, и именно его остаток статьи собирает и измеряет.
Слой один: хук, который блокирует вместо того, чтобы спрашивать
Версия 1 сетапа была блоком правил маршрутизации в файле инструкций проекта. По словам Мазманова, это «вроде как работало»: правила были рекомендательными, а не принудительными, Claude мог их игнорировать, и каждому проекту нужна была своя копия. Версия 2 выносит решение из промпта в слой инструментов с помощью двух хуков, оба срабатывают перед вызовом инструмента. Один следит за каждым чтением файла, другой за шеллом.
Хук на чтение, это 33 строки bash. Он читает порог из окружения, по умолчанию 350 строк, а затем пропускает три вещи:
- Чтение с offset или limit, потому что Claude уже знает, что ему нужно.
- Файл, которого не существует.
- Файл размером не больше порога, потому что делегировать что-то маленькое дороже, чем прочитать.
Всё остальное блокируется с сообщением, которое Claude читает вместо файла: в этом файле столько-то строк, используйте скилл bulk reader, а если для правки нужно точное содержимое, перечитайте только этот участок. Хук на шелл ловит cat, head, tail, less и more на большом файле. Команда с пайпом проходит, потому что пайп в grep, это точечное чтение.
Замечание Мазманова о слоях здесь главное: даже если Claude никогда не прочитает описание скилла, хук всё равно заблокирует дорогое чтение. Скилл делает перенаправление мягче; блокировка делает его реальным. Одна деталь пригодится позже: скрипт отвечает решением верхнего уровня с именем «block». Запомните это слово.
Слои два и три: воркеры и их цифры
Воркеры, это два промпта. Читатель: «ты точный аналитик кода, выводи только структурированные пункты, без приветствий, без прозы, начинай каждый пункт с точного имени, типа или номера строки». Писатель: «в точности повторяй существующие паттерны, именование и стиль; выводи только код, без ограждений, без объяснений». Без этой последней строки модель заворачивает всё в Markdown, который Claude потом приходится разбирать.
Их оборачивают два скрипта. Bulk-read принимает вопрос и пути к файлам и отправляет их. Code-write принимает спецификацию и файл-образец и пишет результат прямо на диск, так что Claude никогда не видит сгенерированный код. Каждое делегирование одноразовое: уточняющий запрос отправляет файлы заново. Там, где это важно, это бесплатно, потому что корпус уходит воркеру и никогда не попадает в контекст Claude.
Третий слой, это файл скилла, который говорит Claude, когда делегировать: файлы длиннее 350 строк, вопросы, охватывающие три файла и больше, крупные диффы. Его последняя строка: «проверяй номера строк перед правкой».
Таблица Spotify охватывает один Java-монорепо и три сценария чтения. Случай с одним файлом падает примерно с 34 000 токенов до менее чем 6 000, а средняя экономия по трём строкам составляет 90%.
| Бенчмарк Spotify | Значение |
|---|---|
| Репозитории | 1 Java-монорепо |
| Сценарии | 3, все bulk read |
| Один файл, до | ~34 000 токенов |
| Один файл, после | < 6 000 токенов |
| Средняя экономия | 90% |
| Оценка токенов | 4 символа на токен |
| Принуждение для писателя | нет (хук есть только у читателя) |
Две оговорки напечатаны самим Spotify: токены оцениваются по четыре символа каждый, а у писателя нет никакого принуждения. То есть 90%, это среднее по трём строкам массового чтения в оценочных входных токенах, без оценки качества и без единой долларовой цифры. Именно это число и нужно проверить.
Пересборка, часть первая: сабагент с полем model
Claude Code поставляется со встроенным сабагентом Explore, и с одного из недавних релизов он наследует вашу основную модель, вплоть до Opus, так что «дешёвый читатель» больше не дешёвый. Документация даёт решение одним предложением: проектный сабагент с именем Explore переопределяет встроенный и сохраняет собственное поле model. Один markdown-файл, front matter, и строка model говорит Haiku. Это и есть bulk reader. Писатель, это второй файл: модель Sonnet, инструменты только Read и Write, а тело, вставленные инструкции самого Spotify.
Это работает, потому что каждый сабагент стартует со свежим, изолированным контекстным окном. То, что он читает, оседает там, а не в основном разговоре. Это одноразовое делегирование Spotify минус сетевой round trip.
Дальше та часть, которую никто не планирует. На этой неделе на Reddit Fable попросили запустить агентов на Opus, а он запустил пять агентов на Fable: 73% недельного лимита ушли за тридцать минут. Лучший ответ в треде, хук, который срабатывает, когда модель отправляет сабагента, заставляет её выбрать модель явно и велит брать самую дешёвую, которая справится с задачей. Это хук номер три: он следит за инструментом Agent, и вызов без модели отклоняется одной фразой: «выберите модель явно».
Скилл Spotify превращается в три строки в файле инструкций проекта: файлы длиннее 350 строк идут эксплореру, бойлерплейт идёт писателю, каждый вызов агента задаёт модель. Есть и грубый вариант: две переменные окружения, которые навязывают одну модель всем сабагентам. Честное ограничение в том, что читатель, это более дешёвая модель, так что всё, что знает основная модель, это то, что он вернул. Этому посвящён раздел с измерением.
Пересборка, часть вторая: deny в сегодняшнем формате хуков
Вспомните слово «block». Скрипт Spotify возвращает решение верхнего уровня, но текущая документация Claude Code говорит другое: хук PreToolUse возвращает решение внутри объекта hookSpecificOutput, и поле называется permissionDecision. У него четыре исхода, allow, deny, ask и defer, и здесь нужен deny. Всё, что хук пишет в качестве причины, показывается Claude, а если отвечают несколько хуков, deny побеждает.
Пересобранный хук на чтение сохраняет тот же порог 350 и те же три исключения, а вместо «block» возвращает deny с причиной, которая называет сабагент Explore и модель, которую нужно использовать. Одна ловушка, о которой документация говорит прямо: хуки из ваших настроек выполняются и внутри сабагентов. Без исключения читатель на Haiku получает отказ на собственные чтения и никогда не сможет сделать свою работу, поэтому скрипт проверяет, кто вызывает, и пропускает двух воркеров.
Проводка, это один файл настроек с тремя матчерами, Read, Bash и Agent, каждый указывает на свой скрипт, а порог задан переменной окружения. На практике чтение файла на 1 090 строк возвращается ошибкой с написанной нами фразой: делегируйте это чтение эксплореру, модель Haiku. Затем следует делегирование: основная модель сначала считает строки, вызывает эксплорер с моделью Haiku, и пункты возвращаются, каждый с номером строки. Три хода, 44 секунды.
Тезис Spotify держится: благодаря слоям система деградирует плавно. Инструкция маршрутизирует, хук, это сетка. Правда, в сетке есть дыра. Модель, которой нужен весь файл, может нарезать его через offset и limit, что проходит, или вывалить через шелл диапазоном sed, чего этот хук не ловит. Измерение учитывает и то, и другое.
Измерение
Тестовый репозиторий, Fastify, веб-фреймворк для Node: 294 файла, 63 из них выше порога. Два идентичных клона, единственная разница, папка .claude и файл правил. Основная модель Opus, дефолт CLI; читатель Haiku; писатель Sonnet. Сессии из одного промпта, без уточнений, каждый сценарий прогнан дважды на каждую конфигурацию, шестнадцать прогонов в сумме. Четыре сценария те же, что у Spotify: экспорты одного большого файла, три файла и то, как они вызывают друг друга, исходный файл против его теста и новый тестовый файл, записанный на диск по образцу существующего.
| Сценарий | Основной контекст, без | Основной контекст, с | Изменение | Общая стоимость, без | Общая стоимость, с | Изменение | Длительность, без | Длительность, с | Изменение |
|---|---|---|---|---|---|---|---|---|---|
| Один большой файл | 88,693 | 51,552 | -41.9% | $0.139 | $0.087 | -37.8% | 22 s | 44 s | +100.8% |
| Три файла | 357,166 | 73,440 | -79.4% | $0.581 | $0.218 | -62.4% | 52 s | 129 s | +149.9% |
| Исходник против теста | 303,808 | 114,136 | -62.4% | $0.451 | $0.374 | -17.1% | 93 s | 125 s | +33.6% |
| Новый тестовый файл | 143,432 | 121,818 | -15.1% | $0.295 | $0.302 | +2.6% | 66 s | 87 s | +32.2% |
| Все четыре | 223,274 | 90,236 | -59.6% | $0.366 | $0.245 | -33.1% | 58 s | 96 s | +65.3% |
Основной контекст, то есть токены, которые дорогая модель действительно увидела, это первый столбец, который имеет значение. На вопросе про три файла он падает на 79%, а по всем четырём сценариям на 59.6%. Счёт падает меньше, на треть в целом, потому что собственные токены читателя не бесплатны, а на небольшой задаче по написанию теста счёт вырос на 2.6%. Время идёт в обратную сторону: 58 секунд в среднем без сетапа, 96 с ним. Делегирование медленнее каждый раз.
Качество, это то, где две конфигурации расходятся сильнее всего. Без сетапа основная модель вываливала файлы через шелл без номеров строк и считала вручную, выдавая неверные номера строк повсюду: функция, заявленная на строке 149, на самом деле стояла на строке 156. С сетапом один прогон из четырёх принял сводку читателя на веру и унёс три ложных утверждения, одно из них про функцию, которую, по словам читателя, файл роутов никогда не вызывает, хотя вызывает, на строке 553. Все четыре сгенерированных тестовых файла проходят, а хуки deny не сработали ни разу за шестнадцать прогонов: при наличии файла правил основная модель каждый раз проверяла число строк и делегировала сама.
Ещё одно из трейсов: без правила основная модель ни разу не использовала инструмент Read. Она читала всё через шелл, а чтение диапазона через шелл стоит те же токены и проходит хук. Так что таблица Spotify говорит 90; эта говорит 60 по контексту и треть по счёту.
Оставьте блокировку. Не ждите, что счёт упадёт на девяносто.
Три вещи стоит оставить: проектный сабагент Explore на Haiku, правило в три строки в файле инструкций и хук на чтение как страховочную сетку. Измеренный результат: на 60% меньше основного контекста, минус треть счёта и на две трети больше общего времени.
Прежде чем доверять хуку, исправьте две вещи. Хуки выполняются внутри сабагентов, так что исключите своих воркеров. И дыра в шелле: хук на bash ловит cat, head и tail, но чтение диапазона проходит, и основная модель использовала именно его, когда у неё не было правила.
Собственные ограничения Spotify остаются в силе. Нельзя делегировать правки и нельзя делегировать рассуждения; воркер пропустил баг с потокобезопасностью, который Claude поймал за секунды, и каждое делегирование, это round trip. Скептики с Hacker News тоже были правы в одном: входные токены, это не весь счёт. Выходные токены стоят дороже, и этот сетап ничего с ними не делает.
Кто экономит, зависит от того, как вы платите. Через API, минус треть. На плане Pro или Max тот же сетап сдвигает ваши пятичасовые и недельные окна, а не доллары. Следите и за порогом: ниже него делегирование стоит дороже, чем экономит, и тестовый случай на 45 строк, тому доказательство с его плюс 2.6%. Наконец, в двух прогонах из восьми сводка читателя содержала ошибки, и проверочный ход основной модели их поймал. Пропустите этот ход, и эти ошибки дойдут до ваших правок.
AIDive