Зачем я собираю свой LLM gatewayНачалось всё с простой идеи: я хочу давать агентам один OpenAI-compatible endpoint и не заставлять их знать, какая модель и какой провайдер реально находятся за ним.
Вместо конкретных моделей и провайдеров агент видит условные stupid, standard, smart, а gateway уже решает, куда отправить запрос.
Но обычного маппинга моделей мне мало - хочется описывать полноценную маршрутизацию:
• несколько провайдеров одной модели
• retry с exponential backoff
• fallback на другую модель или провайдера
• parallel race между несколькими endpoint'ами
• кастомный /v1/models источник, если inference endpoint его не предоставляет
• скрытие реальных названий моделей от клиентов
В итоге конфиг постепенно свёлся к плоскому routing_rules.
Например, агент просит standard, а gateway может одновременно отправить запрос двум провайдерам, взять первый успешный ответ, при ошибке повторить запрос N раз, а после исчерпания retry уйти на fallback.
Самому агенту всю эту логику знать не нужно.
Это также позволяет быстро менять самих агентов, не настраивая в каждом из них отдельный список моделей и провайдеров, фоллбеки и роутинг. Агенту достаточно знать несколько стабильных логических имён, а всю инфраструктуру за ними можно менять централизованно - без обновления конфигов и образов каждого воркера.
Плюсом отдельный слой помогает бороться с нестабильными провайдерами. Если один endpoint периодически падает (gonka, привет), отвечает с ошибками или начинает работать слишком медленно, это можно компенсировать retry, fallback или параллельными запросами между несколькими провайдерами. При этом агент продолжает работать с тем же самым endpoint и той же логической моделью.
Мне такой подход особенно интересен для инфраструктуры с кучей изолированных воркеров: модель, ключи, лимиты и стратегия отказоустойчивости остаются снаружи контейнера, а внутри можно оставить максимально тупой OpenAI-клиент.
https://github.com/mytecor/lattice/tree/main/packages/llm-gateway