Перейти к содержанию
Innovialabs

Как встроить LLM API в свой продукт

Практика4 мин

Коротко

Вызывайте LLM только с сервера, стримьте ответ пользователю, обрабатывайте лимиты через повторы с backoff, держите запасного провайдера и логируйте каждый запрос с затратами токенов.

Короткий ответ

Интеграция LLM API — это не один fetch к провайдеру, а небольшой слой на вашем сервере. Он хранит ключи, ограничивает нагрузку, повторяет неудачные запросы, переключается на запасную модель и пишет логи. Пользовательский интерфейс общается только с вашим бэкендом, а не с провайдером напрямую.

Минимальная надёжная схема:

  1. Клиент отправляет запрос на ваш API.
  2. Бэкенд проверяет пользователя и его лимиты, собирает промпт.
  3. LLM-шлюз (ваш модуль) вызывает провайдера с таймаутом, повторами и фолбэком.
  4. Ответ стримится обратно клиенту, а метаданные пишутся в лог.

Ключи API: только на сервере

  • Никогда не кладите ключ во фронтенд или мобильное приложение — его вытащат из бандла за минуты.
  • Храните ключи в переменных окружения или менеджере секретов, а не в репозитории.
  • Заведите отдельные ключи для dev, staging и production, чтобы утечка тестового ключа не трогала боевой.
  • Установите лимиты расходов в кабинете провайдера и настройте оповещения.
  • Регулярно ротируйте ключи и сразу отзывайте скомпрометированные.

Стриминг ответов

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

  • Большинство провайдеров отдают поток через Server-Sent Events (SSE).
  • Ваш бэкенд читает поток от провайдера и проксирует его клиенту, не дожидаясь конца.
  • Учитывайте обрыв соединения: если пользователь закрыл вкладку, прерывайте запрос к провайдеру, чтобы не платить за ненужные токены.
  • Для фоновых задач (суммаризация, классификация) стриминг не нужен — проще получить ответ целиком.

Лимиты и повторные запросы

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

Правила повторов:

  • Повторяйте только временные ошибки: 429, 5xx, таймауты сети. Ошибки 400 и 401 повторять бессмысленно.
  • Используйте экспоненциальный backoff с джиттером: пауза растёт с каждой попыткой и немного случайна.
  • Уважайте заголовок Retry-After, если провайдер его прислал.
  • Ограничьте число попыток и общий таймаут.
async function withRetry<T>(fn: () => Promise<T>, attempts = 4): Promise<T> {
  for (let i = 0; ; i++) {
    try {
      return await fn();
    } catch (err: any) {
      const retryable = err.status === 429 || err.status >= 500 || err.code === "ETIMEDOUT";
      if (!retryable || i >= attempts - 1) throw err;
      const delay = Math.min(1000 * 2 ** i, 15000) * (0.5 + Math.random() / 2);
      await new Promise((r) => setTimeout(r, delay));
    }
  }
}

Кроме того, ставьте собственные лимиты на пользователя и на тариф — иначе один активный клиент съест общий бюджет.

Фолбэк между провайдерами

Сбой одного провайдера не должен ронять ваш продукт.

  • Опишите единый интерфейс вызова модели внутри кода, а адаптеры под каждого провайдера держите отдельно.
  • Задайте цепочку: основная модель → запасная модель того же провайдера → модель другого провайдера.
  • Помните, что промпты ведут себя по-разному на разных моделях. Тестируйте запасной вариант на тех же сценариях.
  • Для критичных функций держите простой резервный ответ без LLM: шаблон, поиск по базе, сообщение «попробуйте позже».

Логирование и наблюдаемость

Без логов вы не поймёте, почему ответ плохой или откуда счёт.

Что записывать по каждому вызову:

ПолеЗачем
ID запроса и пользователяРазбор жалоб
Модель и версия промптаСравнение качества
Входные и выходные токеныКонтроль затрат
Задержка до первого токена и общаяСкорость
Статус, число повторов, фолбэкНадёжность

Тексты промптов и ответов могут содержать персональные данные. Маскируйте их или храните ограниченно, с доступом только у нужных людей.

Частые ошибки

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

Частые вопросы

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

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.