инструменты 2 мин

Пять ошибок в коде вызова LLM, которые сожрут ваш бюджет и нервы

Статья разбирает типичные ошибки при пакетной обработке запросов к LLM: неограниченная конкурентность вызывает лавину 429-ошибок, неправильные таймауты заставляют платить за несколько генераций одного ответа, слепые retry оплачивают невалидные запросы, а temperature=0 не гарантирует воспроизводимости из-за особенностей батчинга. Решение: семафоры по TPM, batch-режимы для фоновых задач, умные retry с фильтрацией ошибок и мониторинг usage.

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

Пять ошибок в коде вызова LLM, которые сожрут ваш бюджет и нервы

Вызов языковой модели давно стал рутиной: импортировал клиент, отправил промпт, получил ответ. Туториалы показывают, как это работает на одном запросе. Но никто не рассказывает, что происходит, когда запросов 500 тысяч.

Ошибка 1: asyncio.gather без ограничений

Первый порыв — завернуть всё в asyncio.gather(). Результат: 10 000 корутин стартуют одновременно, провайдер отвечает лавиной 429, неудачные запросы тоже съедают лимит. Задача на 10 минут не заканчивается никогда.

Решение: семафор на 20-50 одновременных запросов. Но считать надо не по RPM (requests per minute), а по TPM (tokens per minute) — с длинными промптами упрётесь в токены раньше.

Ошибка 2: Игнорирование rate limit заголовков

Вместо слепого sleep(5) читайте x-ratelimit-remaining-* и retry-after-ms из ответа — сервер сам говорит, когда повторить.

Ошибка 3: Использование онлайн-API для фоновых задач

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

Ошибка 4: Таймаут убивает запрос, но не генерацию

Поставили timeout=10.0 — клиент отвалился, пошёл на retry. Модель продолжила генерировать до конца, токены посчитали все. На тысяче документов платите за 4 генерации, используете одну.

Решение: таймаут по реальному времени генерации (2000 токенов физически не появятся за 10 секунд). Для раннего детекта зависания используйте стриминг и время до первого токена.

Ошибка 5: Retry без разбора типа ошибки

429 и 5xx — повторяем. 400 (невалидная схема, контекст не влез) — повторять бессмысленно, ответ будет тот же, но попытки оплатятся. @retry без фильтра исключений аккуратно оплачивает одинаковые неудачи.

Бонус-грабля: temperature=0 не гарантирует воспроизводимость. Ваш запрос батчится с чужими, размер батча меняет порядок сложения float, последний бит меняется, выбор токена тоже. seed не спасает — он управляет сэмплером, которого при нулевой температуре уже нет.

Что делать прямо сейчас: добавьте счётчик usage.prompt_tokens и usage.completion_tokens в метрики — скачок расхода увидите в тот же день, а не в конце месяца в счёте.

Ключевые выводы

  • Семафор нужно настраивать не по RPM, а по TPM (tokens per minute) — с длинными промптами упираешься в токены раньше, чем в запросы
  • Таймаут на клиенте не останавливает генерацию на сервере — модель дописывает до конца, токены списываются все, а вы платите за несколько попыток
  • Batch-режимы провайдеров для фоновых задач дешевле вдвое и избавляют от 429, но про них забывают и пишут костыли с retry
  • temperature=0 не даёт воспроизводимости из-за батчинга запросов: порядок сложения float меняется, последний бит плывёт, выбор токена тоже
  • Retry без фильтра по типу ошибки оплачивает невалидные запросы (400) многократно, хотя результат всегда будет одинаковым
LLMPythonAPIоптимизацияпродакшен

Автор: Никита Громов · Источник: habr.com

Мнение редакции

Это одна из тех статей, которые стоило бы выдавать каждому, кто пишет `await client.chat.completions.create()` во второй раз в жизни. Автор методично прошёлся по всем классическим граблям — от asyncio.gather без семафора до иллюзии воспроизводимости при temperature=0. Особенно ценно, что каждая ошибка показана с объяснением, *почему* она дорого стоит: не абстрактное «так делать плохо», а конкретное «вот тут ты платишь за четыре генерации вместо одной».

Особенно понравился момент про батчинг и недетерминированность: казалось бы, ставишь temperature=0 и получаешь чистую функцию, а на деле — порядок сложения float в батче меняет последний бит, и всё расползается. Это тот уровень детализации, которого не хватает в официальных туториалах. Единственное, чего не хватает — ссылок на конкретные библиотеки для мониторинга usage и примеров настройки batch API у разных провайдеров. Но как чеклист перед выкаткой в прод — отличный материал.

Комментарии