Перейти к содержимому
12 сент. 2024 г.·7 мин чтения

Сравнение провайдеров LLM по стабильности за неделю

Сравнение провайдеров LLM по стабильности: как за 7 дней собрать p95, 429, TTFT, долю битых JSON и фактическую цену для честного выбора.

Сравнение провайдеров LLM по стабильности за неделю

Почему демо не помогает выбрать провайдера

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

В рабочей системе все сложнее. Есть утренние пики, ночные батчи, длинные промпты, повторы после таймаутов и ответы в JSON, которые приложение должно разбирать без ручной правки. Один и тот же провайдер может вести себя ровно в спокойный час и заметно хуже, когда растет очередь.

Отсюда и частая ошибка. Команда делает 20 удачных вызовов на демо, видит приятную цену за токены и считает вопрос закрытым. Через неделю в проде оказывается, что часть запросов упирается в лимиты, часть уходит в ретраи, а JSON периодически ломается. Формально тариф тот же. Реальная цена каждого полезного ответа уже выше.

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

Поэтому цена на демо почти всегда занижена. На слайде есть только стоимость входных и выходных токенов. В счете потом появляются ретраи, дубли, запас по таймаутам и fallback на другую модель. Даже 2-3% неудачных ответов быстро съедают экономию, если сервис обрабатывает тысячи запросов в день.

Есть и другая ловушка. Многие провайдеры не показывают мягкие лимиты и внутренние очереди. API формально доступен, но в часы пика time to first token растет, а длинный хвост задержек ломает пользовательский сценарий. На демо этого не видно, потому что запрос идет без конкуренции за ресурсы.

Быстрый тест все равно нужен, но только как первый фильтр. Финальный выбор лучше делать после недели одинаковых запросов, одинаковых настроек и реального объема трафика.

Какие метрики считать каждую неделю

Одной цифры здесь мало. Средняя задержка красиво смотрится в отчете, но пользователям она мало что говорит. Прод ломают редкие медленные ответы, ретраи и невалидный JSON. Поэтому полезнее смотреть на набор метрик, где каждая отвечает за свой риск.

Обычно хватает пяти показателей.

  • p95 полной длительности ответа. Считайте время от отправки запроса до последнего токена, а не только среднее и не только серверное время. Если p95 высокий, часть пользователей будет ждать слишком долго даже при нормальной медиане.
  • TTFT отдельно. Time to first token показывает паузу до первого токена. Для чата и внутренних copilot-сценариев она заметнее, чем полная длина ответа. Бывает, что общий ответ нормальный, а TTFT плохой из-за очередей или холодного старта.
  • Доля 429 от всех попыток. Считайте именно от всех запросов, включая те, что потом прошли после ретрая. Иначе проблема исчезает из отчета, хотя приложение уже тратит лишние секунды и деньги.
  • Доля битых JSON на реальных схемах. Проверяйте не абстрактный JSON, а те структуры, которые ваше приложение реально ждет: обязательные поля, типы, enum, вложенные объекты. Лишний текст вокруг JSON и неверный тип поля тоже считайте ошибкой.
  • Фактическая цена за успешный ответ. Берите общий расход за неделю и делите на число ответов, которые дошли, не упали в 429 и прошли валидацию.

Простой пример: один провайдер дешевле по прайсу на 15%, но чаще дает 429 и иногда ломает JSON-схему. В итоге команда платит не только за токены, но и за повторы, таймауты и лишние проверки. На бумаге он дешевле. В работе - нет.

Сводите метрики по одинаковым сценариям, а не в одну общую корзину. Короткий чат, длинный контекст, извлечение в JSON и batch-задачи ведут себя по-разному. Если смешать все вместе, провайдер с плохим структурированным выводом может выглядеть нормально за счет простых запросов.

Если прогонять несколько провайдеров через один OpenAI-совместимый шлюз, сравнение получается чище. Один и тот же SDK, одинаковые промпты и единые логи убирают лишний шум.

Хорошая недельная сводка отвечает на один вопрос: сколько стоит один нормальный ответ без сбоев и лишнего ожидания.

Как собрать честный тест за 7 дней

Один удачный демо-запрос ничего не доказывает. Провайдер может быстро ответить утром во вторник и сыпать ошибками в вечерний пик. Нужен один и тот же тест, который живет полную неделю без ручных правок.

Сначала зафиксируйте 5-10 типовых задач. Берите не "красивые" примеры, а то, что реально идет в проде. Обычно достаточно короткого вопроса-ответа, извлечения полей в JSON, суммаризации длинного текста, классификации обращения и RAG-запроса с вашим контекстом.

Если у вас поток разной сложности, смешайте простые и тяжелые запросы. Иначе один провайдер выиграет просто потому, что ему достались легкие кейсы.

Дальше заморозьте условия на все 7 дней. Не меняйте промпт, модель, temperature, max_tokens, формат ответа и правила ретраев. Даже небольшая правка в середине недели ломает сравнение.

Распределяйте запросы по времени, а не запускайте весь тест за один час. Нагрузка в понедельник днем и в субботу ночью дает разную картину. Практичная схема - отправлять одинаковые пачки утром, днем, вечером и ночью, чтобы увидеть просадки под разной нагрузкой.

У каждого провайдера должен быть одинаковый объем. Если на задачу "извлечение JSON" одному отправили 2000 запросов, остальные должны получить столько же в те же временные окна. Лучше заранее собрать простую таблицу: задача, слот по времени, число запросов, целевая модель.

Чтобы потом не спорить с цифрами, сохраняйте сырые поля по каждому вызову:

  • request_id
  • провайдер и модель
  • время отправки и время ответа
  • входные и выходные токены
  • HTTP-статус и тип ошибки

Если тест идет через один совместимый слой, сбор данных становится проще. Код, SDK и промпты остаются теми же, а меняется только маршрут к провайдеру. Так меньше шансов перепутать проблемы API с проблемами своей обвязки.

После недели у вас будет не набор впечатлений, а журнал реального поведения. С ним уже видно, кто держит нагрузку ровно, а кто хорошо выглядит только в демо.

Как считать p95, TTFT и 429 без путаницы

Средняя задержка почти всегда скрывает проблему. Если один провайдер отвечает за 2 секунды почти всегда, а другой раз в час подвисает на 25 секунд, пользователи заметят именно второе.

p95 считайте по полному времени запроса: от момента отправки до получения последнего токена или полного JSON-ответа. Не обрезайте замер на первом байте и не смешивайте стриминг с обычными ответами. Иначе цифра получится красивой, но бесполезной.

TTFT, или time to first token, имеет смысл считать только для стриминга. Это время от отправки запроса до первого токена в потоке. Для нестриминговых вызовов TTFT часто подменяют общей задержкой, и после этого команды сравнивают разные вещи под одним названием.

Что писать в логах

Чтобы метрики не спорили друг с другом, сохраняйте по каждому запросу timestamp начала и конца, режим ответа, HTTP-код провайдера, тип сбоя, модель и название провайдера.

429 делите на все запросы за период, а не только на ошибки. Если у вас 300 ответов 429 на 30 000 вызовов, это 1%, а не 12% от всех неуспешных ответов. Вторая цифра звучит громче, но не показывает реальную частоту отказа.

Сетевой сбой и ответ провайдера надо разносить в разные корзины. Если упал DNS, сломался прокси или клиент сам оборвал соединение, провайдер тут ни при чем. Если провайдер вернул 429, 500 или оборвал стрим посередине, это уже его поведение.

Как смотреть на график

Недельный отчет без разбивки по часу часто врет. Провайдер может выглядеть ровно по среднему за 7 дней, но каждый будний день с 11:00 до 15:00 ловить всплеск p95 и рост 429. Для поддержки, чатов и внутренних copilot-сценариев это важнее, чем красивая недельная медиана.

Стройте p95 по часу, отдельно для streaming и non-streaming, и рядом показывайте TTFT и долю 429 за тот же час. Тогда спор будет не о методике, а о фактах.

Как считать долю битых JSON

Запустите пилот по 152-ФЗ
Для российских команд проще держать биллинг, логи и поддержку внутри страны.

Доля битых JSON показывает, сколько ответов ваш код реально сможет обработать без правок. Для такого теста она полезнее, чем удачное демо с одним коротким запросом.

Начните с одного и того же schema prompt для всех участников. Не меняйте текст инструкции, набор обязательных полей, примеры и порядок полей. Если у одного провайдера схема проще, а у другого строже, итоговая цифра уже ничего не значит.

Проверяйте ответ так, как его увидит приложение. Берите сырой текст модели и сразу отправляйте его в обычный парсер и валидатор схемы. Не чините ответ вручную и не скрывайте ошибки через regex, автозакрытие скобок или подстановку кавычек. Если в проде у вас есть repair-слой, считайте его отдельно.

Что разделять в отчете

Одна общая цифра скрывает разные сбои, поэтому ошибки лучше раскладывать по типам:

  • ответ обрезался и модель не закрыла JSON
  • парсер не смог прочитать строку как JSON
  • модель пропустила обязательные поля
  • модель добавила лишние поля
  • повторный запрос дал валидный результат

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

Лишние и пропущенные поля тоже не стоит смешивать. Пропуск обязательного поля ломает бизнес-логику сразу. Лишнее поле иногда не мешает, но показывает, что модель не держит контракт и может вести себя по-разному на одних и тех же данных.

Повторный запрос помечайте отдельно. Если первый ответ сломан, а второй уже нормальный, ошибка все равно была. В отчете полезны две цифры: доля битых JSON на первой попытке и доля случаев, где retry спас результат.

Простой пример: вы отправили 1000 запросов и получили 38 невалидных ответов. Из них 12 обрезались, 9 не распарсились, в 11 не хватало полей, а в 6 были лишние поля. Потом вы повторили только неудачные запросы, и 20 из 38 вернулись в норму. Значит, основная цифра - 3,8% битых JSON, а recovery after retry - 52,6%.

Такой расклад дает честную картину. Один провайдер редко падает, но часто теряет поля. Другой почти всегда держит схему, но иногда обрывает ответ на длинных генерациях. Для продакшена это разные риски.

Как посчитать фактическую цену

Считать цену по прайсу за 1 млн токенов мало. На практике команда платит не за "идеальный запрос", а за весь поток: удачные ответы, ретраи, таймауты и результаты, которые потом нельзя использовать. Если смотреть только на тариф, дешевый провайдер легко окажется самым дорогим.

Начните с базы: за неделю сложите все входные и выходные токены по каждому провайдеру. Не берите средние значения по одному удачному прогону. Берите реальные логи продакшена или честного 7-дневного теста, где видны все попытки, а не только успешные ответы.

В расчет должны войти цена входных токенов, цена выходных токенов, стоимость всех ретраев после 429 и 5xx, стоимость запросов, которые уперлись в таймаут, и стоимость ответов, которые пришлось выбросить.

Последний пункт часто ломает экономику сильнее, чем кажется. Если модель вернула битый JSON, лишний текст вокруг JSON или оборвала ответ на середине, токены уже потрачены. Потом вы платите еще раз за повторный вызов. Для финансового расчета такой ответ нельзя считать полезным.

Поэтому сравнивайте не "цену запроса", а цену успешного валидного ответа. Формула простая:

Фактическая цена =
(все затраты на токены за неделю + затраты на ретраи + затраты на таймауты)
/ число успешных валидных ответов

Небольшой пример. Провайдер A потратил 18 000 рублей по тарифу и дал 9000 валидных ответов. Провайдер B потратил 15 000 рублей, но из-за 429, таймаутов и битых JSON команде пришлось выбросить часть результатов, и осталось только 6500 валидных ответов. Цена полезного ответа у A будет 2 рубля, у B - около 2,31 рубля. На бумаге B дешевле, в работе - нет.

Еще одна частая ошибка - смешивать тарифы и курс валют из разных дат. Если один провайдер выставляет цены в долларах, а другой в рублях, зафиксируйте один день среза и пересчитайте все по одному курсу. Иначе сравнение расползется даже без изменений в качестве.

Если вы гоняете нескольких провайдеров через единый OpenAI-совместимый слой, такой расчет делать проще: запросы, токены и ошибки лежат в одном формате. Но даже в этом случае считайте только то, что дошло до пользователя и прошло вашу проверку. Это и есть реальная цена.

Пример недельного сравнения

Сведите счета к рублям
Работайте с B2B-инвойсингом в рублях и тарифами провайдеров без наценки на API.

Банк гонял тест 7 дней на двух задачах: определить тип обращения и вернуть короткий ответ клиенту для оператора. Каждый провайдер получал один и тот же шаблон JSON с тремя полями: category, priority, reply. Температура, лимит токенов, системный промпт и число ретраев были одинаковыми.

Банк засчитывал успехом только тот ответ, который пришел без 429, уложился во внутренний лимит по времени и прошел JSON-парсер с первой или второй попытки. Такой фильтр быстро меняет картину: красивое демо за 10 минут почти всегда выглядит лучше, чем неделя под живой нагрузкой.

ПровайдерTTFT, cp95, c429 за неделюБитый JSONЦена по тарифуФактическая цена за валидный ответ
A0.828.92.7%0.5%0.93 ₽1.05 ₽
B0.6810.79.4%3.9%0.71 ₽1.29 ₽
C0.975.61.1%0.2%1.01 ₽1.08 ₽

На демо первым выглядел провайдер B. Он быстрее отдавал первый токен и стоил дешевле всех. Но с 9:00 до 11:00, когда банк получал пик входящих обращений, доля 429 у него местами доходила до 14%. Ретраи съедали время, а часть ответов приходила уже после лимита, поэтому оператор их не видел вовремя.

У провайдера A была другая проблема. Утром он держался ровнее, зато после 18:00 p95 заметно рос. В некоторые дни хвост доходил до 12 секунд. Для внутреннего чата это терпимо, а для окна оператора уже нет: человек ждет, очередь копится, среднее время обработки растет.

Провайдер C не выиграл бы короткое демо. Он не самый быстрый и не самый дешевый по прайсу. Но за неделю он дал больше валидных ответов, почти не ломал JSON и реже упирался в 429. Если считать не цену токена, а цену принятого системой результата, лидер уже другой.

Для банка победитель - не тот, кто один раз красиво ответил на стенде, а тот, кто семь дней подряд отдает предсказуемый JSON в нужное время и без лишних ретраев.

Где команды ошибаются чаще всего

Смените только base_url
Оставьте SDK, код и промпты как есть и сравните маршруты без переписывания.

Больше всего ошибок появляется не в таблице, а в правилах теста. Команда думает, что сравнивает провайдеров честно, но на деле меняет сразу несколько условий. После этого выводы выглядят аккуратно только на бумаге.

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

Не лучше и ситуация, когда команда в среду меняет промпт, потому что "так ответ стал лучше". После этого неделя распадается на две разные выборки. Даже небольшая правка в системном сообщении или в формате ответа легко меняет TTFT, длину вывода и частоту ошибок JSON.

Чаще всего результат портят такие вещи:

  • сравнивают 8B и 70B модели как будто это один уровень качества
  • меняют промпт, temperature или max_tokens после старта замера
  • смотрят на среднюю задержку и не видят провалы в пиковые часы
  • убирают неуспешные запросы из цены, хотя retries тоже стоят денег
  • делают вывод по одному "хорошему" дню, когда нагрузка случайно была низкой

Со средней задержкой команды ошибаются особенно часто. Среднее число приятно выглядит в отчете, но пользователи замечают не его, а длинные зависания. Их и ловит p95. Если у провайдера средняя задержка 2,1 секунды, а p95 прыгает до 11, это уже риск для продакшена.

С ценой та же история. Если вы вычли запросы с 429, таймаутами и битым JSON, то получили не фактическую цену, а цену "в идеальном мире". В реальной системе вы платите и за повторные попытки, и за лишние токены после неудачного маршрута.

Неделя почти всегда честнее одного удачного дня. В понедельник утром, в пятницу вечером и во время пиков картина часто разная. Если провайдер хорошо выглядел только во вторник с 11 до 13, это не победа, а удачное окно.

Хороший тест обычно скучный по форме: одна и та же модель, один и тот же промпт, одинаковые параметры, полные логи за 7 дней. Зато вывод из такого теста намного полезнее.

Что проверить перед финальным выбором

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

Сведите в один отчет все, что влияет на запуск: TTFT, p95, долю ответов с 429 и долю битых JSON. Когда p95 лежит в одной таблице, 429 в другой, а JSON считают отдельно, люди начинают сравнивать цифры вне контекста. Провайдер может быстро отдавать первый токен, но чаще ломать структурированный ответ на длинных запросах.

Перед решением полезно пройти короткий чек:

  • есть почасовой отчет за все 7 дней, включая утренние и вечерние пики
  • TTFT, p95, 429 и JSON собраны по одному и тому же набору запросов
  • цена считается только за валидный результат, который дошел до приложения и прошел проверку
  • тестовые сценарии похожи на продакшен по длине промпта, формату ответа и ожидаемой нагрузке
  • на следующей неделе вы готовы повторить тот же набор без изменений

С ценой ошибаются чаще всего. Если один провайдер дешевле на бумаге, но 8% ответов у него приходят с 429 или ломают JSON, итоговая стоимость полезного ответа уже другая. Внутри команды лучше считать не цену за миллион токенов, а цену за валидный результат.

Сами сценарии тоже должны быть честными. Если в продакшене вы ждете JSON по схеме, вызываете tool calling или отправляете длинные документы, именно это и нужно гонять в тесте. Короткий вопрос-ответ почти всегда рисует слишком приятную картину.

Если хотите убрать лишние различия в интеграции, тот же недельный прогон удобно делать через единый совместимый слой вроде RU LLM. В таком случае можно сохранить те же SDK, код и промпты, а для команд с российским контекстом это еще и упрощает сбор логов и сравнение провайдеров в одинаковых условиях внутри РФ.

Часто задаваемые вопросы

Почему демо почти ничего не говорит о стабильности провайдера?

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

Сколько должен идти нормальный тест провайдера?

Обычно хватает 7 дней. За неделю вы успеваете увидеть утренние пики, вечерние просадки, ночные батчи и редкие сбои, которые не всплывают за один час теста.

Какие метрики нужно считать каждую неделю?

Смотрите на p95 полной длительности ответа, TTFT для стриминга, долю 429, долю битого JSON и фактическую цену за валидный ответ. Эти цифры вместе показывают, сколько вы реально ждете, сколько теряете на ошибках и сколько платите за полезный результат.

Как правильно считать p95?

Берите время от отправки запроса до последнего токена или до полного JSON-ответа. Не смешивайте стриминг и обычные ответы в одной выборке, иначе цифра получится красивой, но бесполезной.

Когда TTFT важнее общей задержки?

TTFT полезен там, где человек видит поток ответа: в чатах, copilot-сценариях и операторских окнах. Если первый токен приходит поздно, интерфейс кажется медленным даже тогда, когда общий ответ заканчивается быстро.

Как честно считать 429?

Делите число ответов с 429 на все попытки за период, включая те запросы, которые потом прошли после ретрая. Так вы увидите настоящую частоту отказов, а не отчет, где проблема спряталась за повторными вызовами.

Что считать битым JSON, а что нет?

Считайте битым JSON любой ответ, который ваш код не может сразу разобрать и провалидировать по схеме. Сюда входят обрезанный JSON, лишний текст вокруг объекта, неверные типы, пропущенные обязательные поля и лишние поля, если схема их запрещает.

Как посчитать фактическую цену, а не цену по прайсу?

Сложите все расходы на токены за неделю, добавьте цену ретраев и таймаутов, а потом разделите сумму на число валидных ответов, которые дошли до приложения и прошли проверку. Если ответ сломался и вы его выбросили, деньги уже ушли, так что исключать его из затрат нельзя.

Какие ошибки чаще всего портят сравнение провайдеров?

Чаще всего тест ломают сами правила: команда меняет промпт посреди недели, сравнивает модели разного класса или смотрит только на среднюю задержку. Еще одна частая ошибка — считать цену только по успешным запросам и не включать туда повторы после 429, 5xx и битого JSON.

Зачем запускать недельный тест через единый совместимый шлюз вроде RU LLM?

Если вы гоняете всех через один OpenAI-совместимый слой, сравнение выходит чище: один SDK, те же промпты и один формат логов. Для российских команд RU LLM еще убирает лишние различия в интеграции и помогает собрать данные по разным провайдерам в одинаковых условиях внутри РФ.