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 для чат-бота:
- Primary:
google/gemini-2.0-flash- быстрый, дешёвый, хорошее качество - Secondary:
deepseek/deepseek-chat- дешевле, чуть медленнее - 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.28 | 18x дешевле |
| Mistral Medium 3 | $0.40 | $2.00 | 5-6x дешевле |
| Gemini 2.5 Pro | $1.25 | $10.00 | 1-2x дешевле |
| GPT-4o | $2.50 | $10.00 | baseline |
| Claude Sonnet 4 | $3.00 | $15.00 | 1.2-1.5x дороже |
| Claude Opus 4 | $15.00 | $75.00 | 6-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 - не единственный вариант. Выбор зависит от приоритетов.
| Инструмент | Фокус | Модели | Цена | Подходит для |
|---|---|---|---|---|
| LiteLLM | SDK + proxy | 100+ | Open source | Разработчики, self-hosted |
| OpenRouter | Managed API | 500+ | 5.5% комиссия | Быстрый старт, доступ ко всем моделям |
| Portkey | Enterprise gateway | 1600+ | от $49/мес | Compliance, governance, команды |
| Helicone | Observability | Любые | 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-моделей.