Туториалы

Multi-provider LLM: как не зависеть от одного API

Что такое мульти-провайдерная архитектура LLM?

Мульти-провайдерная архитектура LLM — это инфраструктурный паттерн, при котором приложение маршрутизирует AI-запросы через несколько провайдеров языковых моделей (OpenAI, Anthropic, DeepSeek и другие) через единый прокси-слой, обеспечивая автоматический failover, роутинг по стоимости и смену моделей без изменений кода. Устраняет зависимость от одного провайдера, которая приводит к простоям и ценовой блокировке.

TL;DR

  • -В декабре 2025: OpenAI — 22 инцидента, Anthropic — 20. Даже 99.7% uptime — это ~26 часов простоя в год
  • -DeepSeek-V3 стоит $0.14 за 1M токенов на входе, GPT-4o — $2.50. Разброс в 18 раз: роутинг по типу задачи резко снижает расходы
  • -LiteLLM: 36 700 звёзд, 100+ провайдеров, добавляет ~8 мс на P95, единый OpenAI-совместимый эндпоинт для всех провайдеров
  • -Смена модели — одна строка в конфиге: нет миграции SDK, нет рефакторинга кода приложения
  • -Circuit breaker предотвращает каскадные отказы: после N ошибок провайдер помечается как unhealthy и трафик уходит к другому

В декабре 2025 у OpenAI было 22 инцидента, у Anthropic - 20. Среднее время восстановления - 8-9 часов на инцидент. При этом пять других провайдеров (Cohere, Google Gemini, Groq, Replicate, xAI) за тот же месяц не упали ни разу.

99.7% uptime звучит надёжно. В пересчёте - ~26 часов простоя в год. Для приложения, обрабатывающего тысячи LLM-запросов в день, один плохой месяц у одного провайдера означает потерю пользователей.

Статья о том, как построить LLM-инфраструктуру, которая переживает аварии, использует разницу в ценах и даёт менять модели без правок в коде.

Зачем несколько провайдеров

Четыре причины. Любой одной хватает.

Отказоустойчивость. Провайдеры падают. Не “иногда” - регулярно. Декабрь 2025: два лидера рынка получили суммарно 42 инцидента за один месяц. Когда единственный провайдер лежит, приложение лежит. Когда есть fallback - запросы уходят на другого провайдера, пользователь не замечает.

Rate limits. Провайдеры меняют лимиты в одностороннем порядке. Летом 2025 Anthropic ввёл еженедельные лимиты для активных пользователей Claude Code. OpenAI распределяет доступ по “тирам” расходов. С одним провайдером внезапное снижение лимита - каскадный сбой без запасного варианта.

Стоимость. Разброс цен - порядки, не проценты. DeepSeek-V3 стоит $0.14 за миллион входных токенов. GPT-4o - $2.50. Это разница в 18 раз на входе и 36 раз на выходе. Не каждая задача требует самой дорогой модели. Классификация, извлечение данных из текста, генерация эмбеддингов - всё это можно маршрутизировать на дешёвые модели без потери качества.

Deprecation. Модели снимают с поддержки. Snapshot chatgpt-4o-latest удалён из API 17 февраля 2026, а из ChatGPT модель убрали 13 февраля - предупредили за три месяца. GPT-4.5, запущенный в феврале 2025 по цене $75/$150 за миллион токенов, тоже отправлен на пенсию. Цикл жизни флагманской модели - 12-24 месяца. Приложение, завязанное на конкретную модель, каждые 1-2 года попадает в вынужденную миграцию.

LiteLLM: единая точка входа

LiteLLM - open-source proxy, который превращает вызовы к разным LLM-провайдерам в единый OpenAI-совместимый API. 36 700 звёзд на GitHub, поддержка 100+ провайдеров. Overhead самого прокси - P95 около 8 мс (по данным LiteLLM).

Вместо прямых вызовов к API провайдеров все запросы идут через LiteLLM. Он принимает стандартный /v1/chat/completions, маршрутизирует на нужного провайдера, возвращает ответ в едином формате.

Приложение

    │  POST /v1/chat/completions
    │  model: "deepseek/deepseek-chat"

┌──────────┐
│  LiteLLM │ → routing → DeepSeek API
│  Proxy   │ → fallback → Google Gemini API
│          │ → fallback → Anthropic API
└──────────┘

    │  OpenAI-compatible response

Приложение

На практике:

  • Смена модели - одна строка. Поменять deepseek/deepseek-chat на google/gemini-2.0-flash - это изменение параметра model в запросе. Не рефакторинг, не миграция SDK.
  • Единый формат. Независимо от провайдера, ответ приходит в формате OpenAI Chat Completion. Клиентский код не знает, какой провайдер обработал запрос.
  • Centralized auth. API-ключи провайдеров хранятся в конфигурации LiteLLM, а не в каждой edge function. Один LiteLLM-ключ для клиента, десяток ключей провайдеров за кулисами.
  • Rate limiting на уровне прокси. RPM/TPM лимиты, per-user квоты, бюджетные ограничения - всё в одном месте.

Настройка

LiteLLM конфигурируется YAML-файлом. Минимальная конфигурация для двух провайдеров:

model_list:
  - model_name: fast-chat
    litellm_params:
      model: google/gemini-2.0-flash
      api_key: os.environ/GOOGLE_API_KEY
  - model_name: fast-chat           # тот же model_name = fallback
    litellm_params:
      model: deepseek/deepseek-chat
      api_key: os.environ/DEEPSEEK_API_KEY
  - model_name: deep-analysis
    litellm_params:
      model: anthropic/claude-sonnet-4
      api_key: os.environ/ANTHROPIC_API_KEY

router_settings:
  routing_strategy: usage-based-routing
  enable_pre_call_checks: true       # проверка лимитов до вызова

Два deployment с одним model_name - LiteLLM автоматически маршрутизирует между ними и использует второй как fallback при сбое первого.

Стратегии маршрутизации

LiteLLM поддерживает четыре стратегии:

СтратегияКак работаетКогда использовать
simple-shuffleСлучайный выборПо умолчанию, когда всё равно
least-busyНа наименее загруженныйБалансировка нагрузки
usage-based-routingФильтрует по TPM/RPM лимитамНе превышать квоты провайдера
latency-based-routingНа самый быстрыйМинимизация времени ответа

usage-based-routing - самая полезная для production. LiteLLM отслеживает текущий расход TPM/RPM через Redis и исключает deployment, который близок к лимиту. Запрос уходит на deployment с наименьшим текущим потреблением.

Fallback chains: primary → secondary → emergency

Fallback chain - цепочка провайдеров, которая срабатывает автоматически при сбое. Первый провайдер упал - запрос уходит на второй. Второй перегружен - на третий.

Какие ошибки триггерят fallback:

  • 429 - rate limit exceeded (провайдер перегружен)
  • 500, 502, 503, 504 - серверные ошибки (провайдер лежит)

Что НЕ триггерит:

  • 400 - невалидный запрос (проблема в нашем коде, не в провайдере)
  • 401, 403 - проблема с ключом (fallback не поможет)

В LiteLLM это работает автоматически: несколько deployment с одним model_name - встроенный fallback. Для разных model_name можно настроить fallback-список:

router_settings:
  fallbacks: [
    {"fast-chat": ["backup-chat"]},
    {"deep-analysis": ["backup-analysis"]}
  ]

Практический пример

Три уровня fallback для чат-бота:

  1. Primary: google/gemini-2.0-flash - быстрый, дешёвый, хорошее качество
  2. Secondary: deepseek/deepseek-chat - дешевле, чуть медленнее
  3. Emergency: anthropic/claude-3-haiku - дороже, но стабильный

Gemini вернул 503 - запрос ушёл на DeepSeek. DeepSeek вернул 429 (rate limit) - запрос ушёл на Claude Haiku. Пользователь получил ответ, возможно чуть медленнее.

Но разные модели генерируют разные ответы. Для чата это приемлемо - пользователь не сравнивает ответы двух моделей. Для пайплайна, где важна консистентность формата (JSON-схема, структурированный вывод), fallback между моделями требует дополнительной валидации.

Task-based routing: разные задачи → разные модели

Не все задачи одинаковы. Генерация маршрута путешествия требует рассуждений и большого контекста. Генерация заголовка для чата - 10 токенов на входе, 5 на выходе. Обогащение POI-данных - парсинг структурированного текста.

Отправлять всё на одну модель - переплата или потеря качества.

Паттерн: определить тип задачи → выбрать оптимальную модель.

ЗадачаМодельПочему
AI-чат (быстрые ответы)Gemini 2.0 FlashБыстрый, дешёвый, хороший для диалога
Анализ поездки, извлечение данныхDeepSeek ChatДёшево, хорошо работает со структурированным выводом
Генерация маршрута (пайплайн)DeepSeek Chat + валидацияСложная задача, но DeepSeek справляется при правильных промптах
Генерация заголовковGemini 2.0 FlashТривиальная задача, не стоит дорогой модели
Оркестрация (мульти-шаговые агенты)Claude HaikuХорошо следует инструкциям, предсказуемый

Модель указывается в каждом запросе через параметр model. Поскольку все вызовы идут через LiteLLM, переключение модели - замена строки.

Управление моделями через Langfuse

Модель можно вынести из кода в конфигурацию промпта. В Langfuse каждый промпт хранит config.model:

{
  "name": "ai-chat-travel-assistant",
  "config": {
    "model": "google/gemini-2.0-flash",
    "temperature": 0.7,
    "max_tokens": 4096
  }
}

Edge function получает промпт из Langfuse, берёт модель из конфига и передаёт в LiteLLM:

const promptTemplate = await getLangfusePrompt('ai-chat-travel-assistant', langfuseConfig);
const model = promptTemplate.config?.model || 'google/gemini-2.0-flash';

const response = await fetch(`${LITELLM_URL}/v1/chat/completions`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${LITELLM_KEY}`,
  },
  body: JSON.stringify({
    model,
    messages: compiledMessages,
    temperature: promptTemplate.config?.temperature ?? 0.7,
  }),
});

Смена модели для любого промпта - через UI Langfuse, без деплоя кода. Промпт тоже можно отредактировать и сразу пометить как production.

Подробнее о Langfuse - в отдельной статье про LLM observability.

Стоимость: порядок имеет значение

Разница в ценах между провайдерами - на порядки.

МодельВход ($/1M)Выход ($/1M)Относительно GPT-4o
DeepSeek-V3$0.14$0.2818x дешевле
Mistral Medium 3$0.40$2.005-6x дешевле
Gemini 2.5 Pro$1.25$10.001-2x дешевле
GPT-4o$2.50$10.00baseline
Claude Sonnet 4$3.00$15.001.2-1.5x дороже
Claude Opus 4$15.00$75.006-7x дороже

Исследователи из LMSYS (RouteLLM) показали, что грамотная маршрутизация сокращает расходы на 85%+ на бенчмарке MT Bench без заметной потери качества. Суть подхода: 90% “простых” запросов обрабатываются дешёвой моделью, 10% “сложных” - дорогой.

В production это выглядит проще: выбор модели по типу задачи. Чат, генерация заголовков, извлечение данных - дешёвые модели. Сложный анализ, рассуждения, мульти-шаговые агенты - дорогие.

Мониторинг: как понять, что провайдер деградирует

Провайдер может деградировать без полного падения. Латентность растёт с 200 мс до 5 секунд. Процент ошибок поднимается с 0.1% до 3%. Качество ответов падает (модель начинает “галлюцинировать” чаще).

Что мониторить:

МетрикаПорог тревогиЧто делать
P95 латентность> 2x от baselineВключить fallback
Error rate> 2%Включить fallback
Timeout rate> 1%Снизить timeout, включить fallback
Token costВыход за бюджетПереключить на дешёвую модель

LiteLLM пишет логи каждого вызова: провайдер, модель, латентность, статус, количество токенов. Для визуализации - любой стек: Grafana, Datadog, собственный дашборд. Langfuse добавляет трейсинг на уровне промптов: какой промпт, какая версия, какой результат.

Самая полезная метрика - отношение fallback-вызовов к общему количеству. Если больше 10% запросов уходят на fallback - основной провайдер деградирует. Если больше 30% - пора менять primary.

Circuit Breaker для LLM-вызовов

Circuit Breaker - паттерн, который предотвращает каскадные сбои. Когда внешний сервис начинает стабильно отвечать ошибками, circuit breaker “размыкает цепь” и перестаёт отправлять запросы. Вместо того чтобы ждать timeout по 60 секунд на каждый запрос к лежащему провайдеру, система моментально отвечает ошибкой.

Три состояния:

CLOSED (норма)          OPEN (сервис лежит)       HALF-OPEN (проверка)
    │                       │                          │
    │  3 ошибки подряд      │  60 секунд прошло        │  1 успех
    │─────────────────►     │──────────────────►       │──────────────►  CLOSED
    │                       │                          │
    │                       │  запросы отклоняются     │  1 ошибка
    │                       │  мгновенно               │──────────────►  OPEN

Конфигурация для LLM-вызовов отличается от обычных API. Модели отвечают медленнее - timeout 60 секунд вместо 10. Порог ошибок ниже - 3 сбоя вместо 5, потому что каждый LLM-запрос дорогой. Период восстановления длиннее - 60 секунд вместо 30.

const LLM_CIRCUIT_CONFIG = {
  failureThreshold: 3,       // 3 сбоя → circuit open
  resetTimeoutMs: 60_000,    // 60 секунд в состоянии open
  successThreshold: 1,       // 1 успех в half-open → closed
  ignoredStatusCodes: [400, 404],  // клиентские ошибки не считаются
};

В serverless-окружении (Deno Edge Functions, AWS Lambda) каждый вызов потенциально запускается в новом изоляте. Circuit breaker, хранящий состояние в памяти, теряет его при создании нового изолята. Для распределённого circuit breaking нужно внешнее хранилище - Redis или таблица в базе.

Подробнее о реализации - в статье про Circuit Breaker в Edge Functions.

Альтернативы LiteLLM

LiteLLM - не единственный вариант. Выбор зависит от приоритетов.

ИнструментФокусМоделиЦенаПодходит для
LiteLLMSDK + proxy100+Open sourceРазработчики, self-hosted
OpenRouterManaged API500+5.5% комиссияБыстрый старт, доступ ко всем моделям
PortkeyEnterprise gateway1600+от $49/месCompliance, governance, команды
HeliconeObservabilityЛюбыеFree tier / $49Мониторинг, кеширование

OpenRouter - managed-альтернатива. Не нужно поднимать свой прокси. 500+ моделей от 60+ провайдеров. Комиссия 5.5% при покупке кредитов, цены моделей - pass-through без наценки. Привлёк $40 млн инвестиций в июне 2025, run-rate расходов клиентов на инференс превысил $100 млн. Удобен для прототипирования и проектов, где self-hosted инфраструктура избыточна.

Portkey - для команд с требованиями compliance. PII-редакция, обнаружение jailbreak, аудит-трейлы, SSO. Если в проекте нужна корпоративная безопасность - стоит смотреть сюда.

Helicone - open-source, фокус на observability. Gateway на Rust с P50 латентностью 8 мс. Встроенное кеширование ответов снижает расходы на повторных запросах. Хорош как дополнение к LiteLLM, не как замена.

LiteLLM выигрывает по контролю: self-hosted, полный доступ к конфигурации, бесплатный. Для production-приложения с несколькими провайдерами - лучшее соотношение контроля и трудозатрат.

Где это не работает

Multi-provider не бесплатен. За гибкость приходится платить.

Prompt caching ломается при fallback. Anthropic и OpenAI кешируют промпты для ускорения повторных вызовов. Если запрос ушёл на fallback-провайдера, кеш первого не используется. Для длинных system-промптов это заметная потеря в латентности и стоимости. Продвинутые реализации используют project-level affinity - запросы одного проекта по возможности идут на одного провайдера.

Консистентность ответов. Разные модели генерируют разный текст. Для чат-бота это нормально. Для пайплайна со строгой JSON-схемой - риск. DeepSeek может вернуть "rating": 4.5, а Gemini - "rating": "4.5". Валидация на выходе обязательна.

Дополнительная инфраструктура. LiteLLM - это сервер, который нужно поднять, мониторить и обновлять. Для одного провайдера достаточно API-ключа. Для пяти провайдеров через LiteLLM - Docker-контейнер, Redis для rate limiting, мониторинг. Операционная сложность растёт.

Отладка усложняется. “Запрос упал” - на каком провайдере? На каком fallback-уровне? С какой ошибкой? Логирование каждого шага обязательно: провайдер, модель, латентность, статус, attempt number. Без этого - отладка вслепую.

Не все API одинаковы. OpenAI-совместимый формат покрывает /chat/completions. Специфичные фичи провайдеров (vision API, function calling с конкретными форматами, streaming с tool use) могут работать по-разному через прокси. Перед включением нового провайдера в fallback chain - тестирование конкретных сценариев.

С чего начать

Если приложение сейчас работает с одним провайдером, миграция на multi-provider не требует переписывания.

Шаг 1: LiteLLM proxy. Поднять Docker-контейнер. Подключить текущего провайдера. Все вызовы перенаправить через прокси. На этом этапе ничего не меняется - тот же провайдер, тот же результат. Но появляется единая точка, через которую проходят все LLM-вызовы.

Шаг 2: Второй провайдер как fallback. Добавить DeepSeek или Gemini Flash как второй deployment с тем же model_name. LiteLLM автоматически переключит на него при сбое основного. Протестировать fallback - вручную отключить основной провайдер.

Шаг 3: Task-based routing. Проанализировать вызовы: какие задачи дорогие, какие дешёвые. Перевести дешёвые задачи на дешёвую модель. Генерация заголовков, классификация, извлечение данных - DeepSeek. Чат, рассуждения - Gemini или Claude.

Шаг 4: Мониторинг. Подключить Langfuse или аналог. Трейсить каждый вызов: провайдер, модель, латентность, стоимость. Настроить алерты на деградацию.

Весь процесс - от нуля до production multi-provider - занимает пару дней. LiteLLM proxy поднимается за 30 минут. Добавление провайдера - строка в конфиге. Основные затраты - тестирование fallback-сценариев и настройка мониторинга.

FAQ

Как LiteLLM обрабатывает сбой провайдера в середине стримингового ответа?

LiteLLM не может прозрачно сделать retry для стримингового ответа, который уже начался — как только токены пошли к клиенту, сбой посередине проявится как оборванный стрим, а не бесшовный переход на fallback. Механизм fallback срабатывает только до отправки первого токена. Для устойчивого стриминга рабочий паттерн — агрессивные таймауты (timeout: 10), чтобы поймать медленных провайдеров заранее, и логика reconnect на стороне клиента, которая воспроизводит запрос с нуля при ошибке стрима. Как вариант: отключить стриминг для критических запросов, где консистентность важнее времени до первого токена.

Каков реальный P99 latency-оверхед при роутинге через LiteLLM proxy по сравнению с прямыми вызовами провайдеров?

Собственные бенчмарки LiteLLM показывают ~8 мс оверхед на P95. На P99 в реальных production-окружениях типичный оверхед — 15–30 мс, в основном за счёт управления connection pool и JSON-сериализации запроса/ответа. Это незаметно на фоне inference-латентности LLM (обычно 500 мс–3 с P50). Оверхед накапливается при cold-start: когда Docker-контейнер LiteLLM перезапускается, первые несколько запросов занимают на 200–400 мс больше, пока прогреваются connection pool.

Можно ли использовать provider-specific функции (extended thinking Anthropic или reasoning-токены OpenAI) через LiteLLM?

Специфические параметры провайдера можно передавать через параметр extra_body, который LiteLLM проксирует как есть в API провайдера. Для extended thinking Anthropic: extra_body={"thinking": {"type": "enabled", "budget_tokens": 5000}}. Однако эти параметры не входят в унифицированный OpenAI-совместимый формат, поэтому работают только при обращении к конкретному провайдеру. Если запрос упадёт на fallback-провайдера, provider-specific параметры будут проигнорированы без ошибки. Отдельно тестируйте поведение fallback при использовании extended thinking или reasoning-моделей.