Инференс под нагрузкой: как обслуживать модель и честно считать её стоимость
Прототип на одном пользователе работает, а на пятидесяти упирается в память видеокарты. Разбираем, что именно её занимает, какие механизмы возвращают пропускную способность и как считать расход токенов до первого запроса.

Прототип агента на одном пользователе работает прекрасно. На пятидесяти одновременных он упирается в память видеокарты, короткие запросы начинают ждать длинных, а счёт за токены растёт быстрее, чем польза от них.
Обе проблемы решаются на слое, о котором в прототипе не думают вовсе: на слое обслуживания модели. Разбираем, куда уходит память при генерации, какие механизмы возвращают пропускную способность и как посчитать стоимость пакетной задачи до того, как сделан первый запрос.
Инференс — это применение уже обученной модели: веса зафиксированы, и модель предсказывает вывод по одному токену за раз. Учиться она перестала, но дешёвой от этого не стала: длинные запросы дороже в обработке, длинные ответы дороже в генерации, а множество одновременных запросов давит и на планировщик, и на память.
Ключевые выводы
Генерация делится на две фазы с разной природой: разбор запроса грузит вычисления и хорошо распараллеливается, а генерация ответа последовательна и упирается в число шагов.
Главный потребитель памяти видеокарты — это кеш ключей и значений: для типовой конфигурации выходит около 128 КБ на каждый токен, и именно он ограничивает число одновременных запросов.
Один запрос пользователя к агенту превращается в десять, двадцать и больше обращений к модели, поэтому нагрузка получается неравномерной по памяти и по длительности.
Непрерывное пакетирование не даёт коротким запросам ждать длинных, а кеширование общего префикса убирает повторную обработку одного и того же системного промпта.
Расход токенов не является метрикой продуктивности: у неё тот же изъян, что у подсчёта строк кода, потому что она вознаграждает объём, а не результат.
Две фазы инференса, которые дорожают по-разному
Обслуживающая система делает две вещи: координирует запросы на стороне процессора и исполняет модель на ускорителе. Само исполнение распадается на две фазы, и путать их не стоит, потому что дорожают они от разных причин.
Разбор запроса (в англоязычной документации prefill). Модель обрабатывает все токены входного промпта. Их можно считать параллельно, поэтому фаза упирается в вычислительную мощность. Длинный промпт с историей диалога, найденными документами и описаниями инструментов увеличивает задержку до первого символа ответа.
Генерация (decode). Модель выдаёт по одному токену за раз, и каждый следующий зависит от предыдущих, поэтому распараллелить эту фазу нельзя. Длинный ответ означает много отдельных шагов исполнения модели.
Отсюда три коротких правила: длинные входы удорожают разбор, длинные ответы удорожают генерацию, а рост числа одновременных запросов давит одновременно на планирование и на память.
KV-кеш: где на самом деле кончается память
Внутри трансформера механизм внимания строит для каждого обработанного токена представления ключей и значений. Чтобы не пересчитывать их заново на каждом шаге генерации, модель хранит их в памяти. Это и есть кеш ключей и значений, без которого авторегрессивная генерация была бы непрактичной.
Платить за него приходится памятью видеокарты, и объём считается напрямую:
Для модели с 32 слоями, 8 KV-головами, размерностью головы 128 и половинной точностью получается примерно 128 КБ на один токен. У других моделей цифра будет своя, но зависимость одна: чем длиннее контекст, тем больше памяти занято. Именно поэтому длинные промпты, долгие диалоги и подтянутые из поиска документы делают инференс заметно дороже, а свободный объём этого кеша прямо определяет, сколько запросов сервер потянет одновременно.
Почему агентская нагрузка тяжелее обычной
Агент усиливает все перечисленные проблемы, потому что один запрос пользователя порождает множество обращений к модели: спланировать следующее действие, выбрать инструмент, разобрать его результат, свернуть найденное, восстановиться после ошибки, решить, нужна ли ещё работа, и наконец собрать ответ.
Одно взаимодействие легко превращается в десять или двадцать вызовов, а при сотне активных пользователей счёт идёт на тысячи. Вдобавок запросы неравноценны: в одном короткий вопрос, в другом длинный системный промпт с историей, документами и результатами инструментов. Получается динамическая нагрузка, где запросы приходят в разное время, занимают разный объём памяти и завершаются вразнобой.
Четыре механизма, которые возвращают пропускную способность
Именно на такую нагрузку рассчитаны специализированные движки обслуживания вроде vLLM. Вместо загрузки модели прямо в приложение и вызова метода генерации приложение обращается к серверу по HTTP, и логика агента отделяется от инфраструктуры под ней.
Страничное внимание, оно же PagedAttention
В наивной системе кеш каждой последовательности занимает один непрерывный участок памяти. Поскольку длины непредсказуемы, участки резервируются с запасом, и память утекает во фрагментацию. Страничная организация хранит кеш блоками фиксированного размера, которые выделяются по мере надобности и не обязаны лежать рядом. Завершился запрос — его блоки вернулись в общий пул и достались другому.
Непрерывное пакетирование
Обычное пакетирование работает раундами: сервер собирает группу запросов и держит её состав неизменным, пока цикл не отработает. Для генерации текста это плохо, потому что запросы заканчиваются в разное время.
Представьте: запросу A нужно сто токенов ответа, запросу B двадцать, а запрос C приходит, пока A ещё считается. При фиксированном пакетировании B освободится рано, но его место будет простаивать, а C дождётся конца всего цикла. Непрерывное пакетирование добавляет C в ближайший же шаг генерации, как только освободилось место. Ускоритель остаётся занят полезной работой.
Кеширование общего префикса
Агенты раз за разом отправляют один и тот же системный промпт, те же описания инструментов и ту же преамбулу рабочего процесса. Без кеширования этот префикс обрабатывается заново при каждом запросе:
С кешированием общая часть считается один раз и переиспользуется. Важное ограничение: выигрыш приходится только на фазу разбора запроса, генерацию новых токенов приём не ускоряет. Поэтому эффект тем заметнее, чем длиннее общий префикс.
Что здесь на самом деле новое:
Обычное кеширование ключей и значений есть в любой современной реализации авторегрессивной генерации, это не отличительная черта конкретного движка. Преимущество специализированных серверов в другом: в том, как они планируют запросы и как выделяют, переиспользуют и освобождают память кеша между конкурирующими нагрузками.
Совместимый интерфейс
Четвёртый пункт скучный, но на практике решающий: сервер выставляет интерфейс, совместимый с OpenAI. Существующие приложения и агентские фреймворки переключаются на него правкой конфигурации, а не переписыванием кода.
Когда всё это действительно нужно
Специализированный сервер инференса оправдан, когда вы держите модели с открытыми весами у себя и обслуживаете конкурентную нагрузку. Для одиночного прототипа на одного пользователя он избыточен: там хватит локального запуска модели, и вся описанная механика памяти просто не проявится.
Переломный момент наступает, когда прототип начинает принимать параллельный трафик, а агент проводит большую часть времени в ожидании модели. Тогда дешевле улучшить слой обслуживания под ним, чем переписывать логику самого агента.
Вторая половина задачи: считать деньги до запуска
Инфраструктура решает вопрос скорости. Вопрос стоимости она не решает, а иногда и обостряет, потому что удобная система располагает к длинным контекстам.
Дальше речь пойдёт и о тех, кто ходит в модель по чужому тарифу. Своя инфраструктура переводит счёт из токенов в часы работы видеокарт, но сама задача остаётся той же: понять цену задачи до запуска, а не после. Разница только в единице, в которой её считают.
Реестр токенов вместо пробного запуска
Пакетные задачи ломаются неприятным образом: счётчик прыгает по завершении, а не до старта; повтор удваивает расход без всякой обратной связи; системные промпты, длинные ответы и служебная разметка тоже считаются. Крошечный офлайновый реестр превращает всё это в проверку до первого обращения к модели.
Оценка строится на грубой эвристике: для английского текста один токен занимает примерно четыре символа. Точность здесь и не нужна, задача — понять порядок величины:
Проверим на задаче из 6000 сводок, где промпт занимает около 21 000 символов, а ответ ограничен 500. Выходит 5250 токенов на промпт плюс 125 на ответ, то есть 5375 на вызов, а на всю задачу 32 250 000 токенов при лимите в 30 000 000. Задача не помещается с запасом минус 7,5%, и узнать это удалось, не потратив ни одного токена.
Соседняя задача с промптом в 4000 символов, тем же ограничением ответа в 500 символов и 10 000 вызовов даёт 1125 токенов на вызов и 11 250 000 на всё, помещаясь с запасом в 62,5%. Живая пробная отправка ни того, ни другого не покажет: она расскажет о поведении конкретной ручки прямо сейчас, но не о сумме по всей пачке, и вдобавок сама потратит токены.
Почему расход токенов нельзя делать метрикой продуктивности
Тут стоит остановиться отдельно, потому что соблазн велик. Расход токенов удобно считается и потому легко попадает в отчёты, но как метрика продуктивности он повторяет ошибку подсчёта строк кода.
Возьмите двух разработчиков с одинаковой задачей и одним инструментом. Первый пишет размытые промпты, позволяет контексту разрастаться в длинной переписке и заставляет модель раз за разом восстанавливать одну и ту же вводную. Второй решает ту же задачу коротким настроенным сценарием. По расходу токенов первый выглядит продуктивнее, хотя на деле он просто шумнее.
Само по себе потребление токенов отвечает на вопрос, пользуется ли человек инструментом вообще. На раннем этапе внедрения этот сигнал полезен. Но он ничего не говорит о том, получилось ли в итоге что-то осмысленное, а «много ИИ» и «хорошо с ИИ» — разные вещи.
Стоимость на результат
Правильная постановка вопроса одна: что мы получили за то, что потратили. Оплата по токенам — совершенно разумная коммерческая модель со стороны поставщика вычислений. Ошибка возникает на стороне покупателя, когда единица биллинга поставщика переезжает во внутреннюю систему оценки работы.
Связь между расходом и результатом реальна, но не универсальна. Более способные модели действительно тратят больше токенов и на сложных задачах дают заметно лучший результат: расширенные рассуждения и многошаговые попытки стоят токенов и окупаются. При этом один и тот же расход при разных конфигурациях и стратегиях промптинга даёт совершенно разные результаты, а неудачная обвязка и небрежная работа с контекстом жгут токены без всякой отдачи.
Практический вывод: считайте отношение результата к затратам, а не сам объём. Для команды безопасности это стоимость одной подтверждённой находки, для продуктовой команды — доля задач, доведённых до готовой и проверенной функциональности.
Слишком жёсткая квота выталкивает работу за периметр
Обратная крайность встречается не реже. Когда организация выставляет квоты слишком консервативно, инженеры, которые видят реальную пользу от инструмента, упираются в искусственный потолок и начинают пользоваться личными аккаунтами и бесплатными тарифами.
Это рациональное поведение человека, которому нужно сдать работу. Но результат предсказуем: использование уходит из контролируемого контура, а вместе с ним пропадают наблюдаемость, возможность аудита и любая уверенность в том, что результат соответствует требованиям к качеству и безопасности. Осмысленная квота с измерением отдачи здесь работает лучше, чем экономная квота без измерений.
Часто задаваемые вопросы
Чем фаза разбора запроса отличается от фазы генерации?
При разборе модель обрабатывает все токены входного промпта, и делать это можно параллельно, поэтому фаза упирается в вычисления. При генерации модель выдаёт по одному токену за раз, каждый зависит от предыдущих, поэтому фаза последовательна и растёт с длиной ответа.
Что такое KV-кеш и почему он ограничивает нагрузку?
Это сохранённые представления ключей и значений для уже обработанных токенов, чтобы не пересчитывать их на каждом шаге генерации. Он занимает память видеокарты пропорционально длине контекста, поэтому свободный объём кеша прямо определяет, сколько запросов сервер обслужит одновременно.
Сколько памяти занимает KV-кеш на токен?
Объём считается как удвоенное произведение числа слоёв, числа KV-голов, размерности головы и размера значения в байтах. Для модели с 32 слоями, 8 KV-головами, размерностью 128 и половинной точностью получается около 128 КБ на токен.
Что даёт непрерывное пакетирование?
Оно позволяет добавлять новые запросы в ближайший шаг генерации, как только освободилось место, вместо ожидания конца всего пакетного цикла. Короткие запросы перестают простаивать в ожидании длинных, а ускоритель остаётся загружен полезной работой.
Стоит ли измерять продуктивность разработчиков расходом токенов?
Нет. Эта метрика повторяет изъян подсчёта строк кода: она вознаграждает объём, а не результат. Разработчик с размытыми промптами и разрастающимся контекстом потратит больше токенов на ту же задачу и по метрике окажется продуктивнее. Считать нужно отношение результата к затратам.
Как оценить стоимость пакетной задачи заранее?
Офлайновым расчётом по эвристике: для английского текста примерно четыре символа на токен. Умножьте оценку промпта и ответа на число вызовов и сравните с лимитом. Пробный живой запрос покажет поведение ручки, но не сумму по всей пачке, и сам потратит квоту.
Что забрать с собой
Слой обслуживания модели, то есть инференса, становится узким местом раньше, чем логика приложения, и лечится он не переписыванием агента, а работой с памятью и планированием запросов. Кеш ключей и значений задаёт потолок конкурентности, страничная организация возвращает потерянную на фрагментации память, непрерывное пакетирование убирает простой, а кеширование префикса снимает повторную обработку одинаковых преамбул.
Со стоимостью работает та же логика: считать нужно до запуска и в единицах результата, а не расхода. Офлайновый расчёт пакета занимает двадцать строк и ловит ошибку формы задачи раньше, чем она превратится в исчерпанный лимит посреди ночного прогона.
Материалы разбора: устройство обслуживания моделей под агентские нагрузки, почему расход токенов не метрика продуктивности и офлайновый расчёт бюджета пакетной задачи.
Посчитайте свой самый крупный пакетный прогон по эвристике четырёх символов на токен и сравните с остатком лимита. Если не сходится, вы только что сэкономили ночь и деньги.













