1. Главная
  2. Блог
  3. Как один клиент незаметно съедал большую часть нашего бюджета на GigaChat Pro

Как один клиент незаметно съедал большую часть нашего бюджета на GigaChat Pro

Как один клиент незаметно съедал большую часть нашего бюджета на GigaChat Pro

Понедельник, письмо от финдира заказчика: расходы на LLM за прошлый месяц выросли в разы к предыдущему, а выручка стоит. Он спрашивает, что происходит, и по тону понятно, что ответ нужен не завтра.

Мы полезли в Grafana. Дашборд показывал ровный график: RPS в норме, латентность в норме, ошибки в норме. Общее число токенов растёт — да, но по совокупности всех тенантов картина размазана. Кто именно жжёт бюджет — непонятно. Мы обслуживали несколько десятков клиентов на одном контуре, а метринг у нас был только глобальный. Классическая ошибка «средней температуры по больнице» — и я до этого дня был уверен, что у нас с наблюдаемостью всё в порядке.

К обеду того же дня мы уже знали ответ. Один тенант забирал львиную долю всех токенов GigaChat Pro. Ещё один — заметный кусок сверху. Остальные три десятка — то, что осталось. Ниже — как мы выкатили пер-тенантный биллинг за неделю с небольшим, что нашли внутри «пожирателя», и почему теперь ни одна настройка ИИ-агента у нас не начинается без разговора о квоте.

Почему стандартный дашборд не показал проблему

В нашей архитектуре FastAPI-приложение принимает запрос, обогащает его контекстом из pgvector и уходит в GigaChat Pro. Метрики уходят в Prometheus по стандартной схеме: llm_prompt_tokens_total, llm_completion_tokens_total, llm_latency_seconds. Всё это агрегировано по эндпоинту и модели.

Ярлыка tenant_id в метриках не было. Причина банальная: когда мы запускались, клиентов было трое, и «на глаз» было ясно, кто сколько потребляет. Через год стало сильно больше — и глаз перестал работать. Ты смотришь на общий счёт, он растёт, ты киваешь: «ну, платформа ИИ-агентов растёт, всё нормально» — и не замечаешь, что рост идёт от одного клиента, а не от всех.

Первое, что мы сделали — вытянули статистику из логов за месяц. У нас логировались request_id, tenant_id и usage из ответа GigaChat. Запрос в наш аналитический слой на ClickHouse (куда льём логи через Vector) дал разбивку по тенантам за месяц. Картина оказалась максимально несимметричной: один клиент выбирал столько же, сколько все остальные, вместе взятые, и ещё немного сверху. Второй по «прожорливости» — заметно меньше, но всё равно кратно выше среднего. Хвост из трёх десятков остальных — почти шум на фоне этих двух.

Мы позвонили в аккаунт-менеджмент. «Пожиратель» — это средний холдинг, который платит по стандартному тарифу, как и остальные. Экономика по нему стала отрицательной ещё пару месяцев назад. Никто этого не заметил, потому что суммарно контур приносил прибыль.

Что жёг тенант, о котором никто не знал

Мы проассоциировали токены с эндпоинтами. Внутри этого клиента обнаружилось три очага.

Первый — RAG-запросы по договорной базе. У них несколько тысяч договоров, средний размер PDF — под сорок страниц. Chunking был настроен на 800 токенов с overlap 200. При каждом вопросе система тянула top-15 чанков — это порядка двенадцати тысяч токенов только на контекст. Юристы клиента, распробовав инструмент, спрашивали в чате очень активно, десятки раз за день на каждого пользователя.

Второй — их сотрудники нашли в интерфейсе поле «сводка по документу» и полюбили его. Каждая сводка — это подача целого документа в LLM, до нескольких десятков тысяч токенов на запрос. Никакого map-reduce или иерархической суммаризации у нас в тот момент не было — сделали «в лоб», потому что на этапе создания ИИ-агента для юротдела казалось, что этого сценария почти не будет.

Третий — интеграция с их CRM гоняла ночью классификацию карточек клиентов, партия за партией. Классификация делалась через GigaChat Pro, хотя задача решалась моделью попроще, а честно говоря — и без LLM вообще.

Все три сценария технически работали. Продукт делал то, что задумано. Просто никто не считал, во что это обходится нам. Мы продавали «готовых ИИ-агентов» по фиксированному тарифу и подразумевали среднего клиента, а средних клиентов, как обычно, в природе нет.

Как выглядит per-tenant метринг в коде

Первую попытку я честно попробовал сделать «на скорую руку»: пробросить tenant_id в существующие метрики Prometheus как ещё один label. Быстро выяснилось, что cardinality разлетается, дашборды тупят, а Prometheus начинает жаловаться на memory. Пришлось отделить оперативные метрики от учётных.

В итоге сделали так: middleware в FastAPI прокидывает tenant_id через contextvars. Дальше — обёртка над клиентом GigaChat, которая после каждого ответа пишет usage в две точки: Prometheus (только агрегаты, без tenant-разбивки, для дашбордов) и таблицу в PostgreSQL (для биллинга и квот).

Схема таблицы:

create table llm_usage (
    id bigserial primary key,
    tenant_id text not null,
    ts timestamptz not null default now(),
    model text not null,
    endpoint text not null,
    prompt_tokens int not null,
    completion_tokens int not null,
    request_id text not null,
    embedding_calls int not null default 0
);
create index on llm_usage (tenant_id, ts desc);
create index on llm_usage (ts desc);

Отдельная материализованная вьюха обновляется раз в несколько минут и агрегирует по тенанту за текущий календарный месяц. На неё уже смотрят и админка, и алерты.

Дальше — квоты. Мы завели три уровня:

  • Soft cap. Улетает уведомление в Telegram аккаунт-менеджеру и внутренний тикет. Клиента ещё ничего не ограничивает, но мы уже начинаем разговор.
  • Hard cap. Дорогие эндпоинты (сводка документа, batch-классификация) начинают возвращать 429 с человеческим сообщением. Дешёвые операции — короткий вопрос-ответ — работают.
  • Kill switch. Клиент оплачивает overage или ждёт нового биллингового периода. Работают только read-only операции без LLM.

Проверка квоты — это одна SQL-функция, которую middleware дёргает до вызова модели. Оверхед незаметный, потому что мы кэшируем ответ в Redis по ключу quota:{tenant_id} на минуту. Первую версию сделали без кэша — и получили ощутимую прибавку к латентности на горячих эндпоинтах. Ошибка, за которую мы себя быстро побили и починили.

Что мы сделали с тенантом-пожирателем

С клиентом провели разговор. Показали разбивку. Объяснили, что три сценария из пяти — это работа тяжёлой модели там, где справится лёгкая или вообще алгоритм без LLM. Реакция была нормальная: «Ребята, мы не знали, почему нам никто раньше не сказал?». Ответить было особо нечего, кроме «потому что до этого момента мы сами не знали».

Дальше — техника.

Для RAG по договорам переключили chunking на семантический с размером 400 токенов и re-ranker по BM25 поверх top-30 → top-6. Средний размер контекста заметно уменьшился, при этом качество ответов проверили на нескольких десятках отложенных вопросов — просадка оказалась небольшой, для юристов терпимой. Здесь пришлось поспорить: продакт боялся, что снижение top-k убьёт полноту. По факту re-ranker вытащил именно те чанки, которые раньше терялись в шуме.

Для сводок собрали двухуровневый map-reduce: сначала суммаризация по разделам через YandexGPT Lite, потом финальная сборка через GigaChat Pro. Средняя стоимость сводки упала в разы, а качество на слепой проверке юристы даже сочли чуть лучше — просто потому, что промежуточная суммаризация вычистила воду.

Для ночной классификации CRM подняли self-hosted rubert-tiny2 на их же контуре. GigaChat Pro для этой задачи ушёл в прошлое. Это, кстати, поучительный момент: тяжёлая LLM попала туда не потому, что её кто-то выбрал осознанно, а потому что «уже подключена, работает, давайте на ней и сделаем».

Результат по клиенту: расход токенов за следующий месяц упал в несколько раз. Экономика по нему вернулась в плюс, тарифный план не меняли, функциональность не резали.

Что теперь встроено в каждый новый контракт

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

Общий счёт за GigaChat Pro по контуру за первый месяц после внедрения заметно просел. Ни один клиент не ушёл, потому что мы не резали функциональность — мы резали неоправданное потребление. От момента, когда сели за клавиатуру, до момента, когда квоты работали на всех тенантах, прошло чуть больше недели, включая миграцию исторических логов и обкатку на нескольких тестовых контурах.

Ещё один побочный эффект: разговоры с новыми клиентами стали проще. Когда обсуждаешь создание ИИ-агента под их процессы, и на столе сразу лежит модель потребления — «вот такие сценарии дешёвые, вот такие дорогие, вот здесь мы предложим квоту» — заказчик выдыхает. Он видит, что мы понимаем экономику, а не продаём чёрный ящик по подписке.

Что стоит проверить у себя сегодня

Если вы обслуживаете больше пяти клиентов на одном LLM-контуре и не имеете разбивки токенов по тенантам — вы почти наверняка субсидируете кого-то из них. Иногда — критично для маржи.

Три вопроса, на которые полезно ответить до конца квартала:

  • Могу ли я за пять минут показать, какие клиенты дают основную долю LLM-расхода?
  • Знаю ли я, какие эндпоинты в моей системе — самые дорогие в пересчёте на один вызов?
  • Что произойдёт, если один клиент завтра начнёт слать кратно больше запросов — увидит ли это кто-то, кроме бухгалтерии в конце месяца?

Метринг стоит дёшево. Отсутствие метринга — очень дорого, и вы узнаёте цену не сразу.

Похожие статьи

Хотите так же?

Обсудим, как это внедрить у вас

Опишите задачу — подберём ИИ-решение под ваш процесс и бюджет: что сделать агентом, какие нужны данные и интеграции, с чего начать пилот.