Архитектура Middleware для AI: Как спрятать зоопарк моделей за одним интерфейсом

13.06.2026 09:00

Каждый стартап начинает интеграцию AI по одному сценарию. Вы устанавливаете пакет openai, пишете десять строк кода для вызова эндпоинта /v1/chat/completions, и в приложении появляется умный ассистент. Фича быстро улетает в продакшен, метрики растут. Через пару месяцев приходят продакт-менеджеры: «Говорят, Claude 3.5 Sonnet пишет код лучше, давай переключим генерацию компонентов на него. А для парсинга гигантских логов нам нужен Gemini 1.5 Pro с его окном в два миллиона токенов. Еще Llama 3 вышла, она дешевле, пустим на нее фоновые задачи».

Разработчик открывает терминал и добавляет @anthropic-ai/sdk, @google/generative-ai и клиент для локальных моделей. В этот момент архитектура дает трещину. Элегантный код превращается в непробиваемую стену из адаптеров, if/else ветвлений и мапперов.

Провайдеры нейросетей проектируют API по-своему. OpenAI позволяет ставить сообщения с ролью system в любом месте массива. Anthropic требует передавать системный промпт отдельным параметром на верхнем уровне, а сообщения в массиве обязаны строго чередоваться — user, затем assistant. Если отправить два сообщения от user подряд, API Anthropic вернет ошибку валидации. Форматы передачи изображений отличаются: один ждет Base64 прямо в текстовом поле, другой требует сложный объект с указанием MIME-типа.

Обработка потоковой передачи (streaming) создает еще больше проблем. Server-Sent Events (SSE) у каждого провайдера отдают разные структуры JSON. Вызов инструментов (function calling) заставляет писать отдельные парсеры под каждую модель: форматы описания схем и возврата результатов инструментов критически расходятся.

Команда бэкенда перестает заниматься продуктом. Разработчики перепаковывают JSON из одного формата в другой. Разнородные SDK усложняют процессы деплоя и ломают сборку метрик.

Архитектурный хаос наступает поэтапно. Сначала появляется фабрика для выбора нужного SDK. Потом добавляется слой для стандартизации ошибок: когда Anthropic возвращает статус 429, а OpenAI — кастомную ошибку о превышении квот, бэкенду нужно привести это к единому коду для фронтенда. Затем выясняется, что логировать затраты на токены нужно разными путями. У каждого вендора свои правила тарификации кэшированных токенов (prompt caching) и разные цены на входные и выходные данные. Когда один из API падает под нагрузкой, инженеры на коленке прикручивают логику повторных вызовов.

Индустрия знает решение этой проблемы — паттерн API Gateway. При работе с LLM этот подход трансформировался в AI Middleware.

Вместо внедрения десятка разных SDK в основной монолит, архитектура разделяется. Приложение общается с единым интерфейсом, а трансляция форматов уходит в шлюз.

Разработчики сделали формат API от OpenAI универсальным контрактом для общения с языковыми моделями. Никто не стал ждать единого стандарта от консорциумов.

Полноценный AI-шлюз выполняет четыре функции.

Первая — нормализация запросов и ответов (Payload Translation Layer). Шлюз принимает стандартный OpenAI payload и на лету компилирует его под нужного вендора. Если клиент отправляет два user сообщения подряд в Claude, шлюз склеивает их содержимое. Если приходит запрос к Gemini с функцией, шлюз переводит JSON Schema из формата OpenAI в структуру tools для Google. Обратно клиент получает стандартный поток чанков. Вызывающий сервис взаимодействует с интерфейсом, который ведет себя как оригинальный OpenAI.

Вторая функция — обеспечение отказоустойчивости (Resiliency & Fallbacks). Провайдеры регулярно испытывают перегрузки, отваливаются по таймауту или блокируют вызовы из-за лимитов. Middleware реализует паттерны Circuit Breaker и Retry. Если первичная модель недоступна, шлюз перенаправляет запрос на эквивалентную модель. Клиент запрашивает gpt-4o, получает ошибку сети, и шлюз отправляет запрос к claude-3-5-sonnet. Пользователь видит небольшую задержку вместо ошибки 500.

Третья — выравнивание стриминга. Парсинг SSE-потоков требует точной работы с буфером. Middleware читает сырые байты от провайдера, разбивает их на фреймы, трансформирует структуру данных внутри чанка в OpenAI-совместимый вид и транслирует дальше. Фронтенд использует стандартные хуки вроде useChat из Vercel AI SDK, не зная, какая модель реально генерирует ответ.

Четвертая — единая телеметрия и биллинг. Считать токены на стороне приложения дорого и сложно. Запросы обрываются, стриминг требует аккуратного агрегирования, модели используют разные токенизаторы. Middleware перехватывает информацию об использовании из финальных чанков ответа, вычисляет стоимость по актуальному прайс-листу провайдера и сохраняет в базу для аналитики.

Реализовывать такой шлюз самостоятельно — значит обречь команду на вечную поддержку чужих API. Форматы меняются часто.

Использование готовых AI Middleware, таких как RouterAPI, решает эту проблему. Интеграция сводится к тому, чтобы оставить в проекте только клиент OpenAI. Разработчик меняет базовый URL и ключ доступа.

import OpenAI from 'openai';

const client = new OpenAI({
 baseURL: 'https://routerapi.net/v1',
 apiKey: process.env.ROUTERAPI_KEY
});

// Прямой вызов модели OpenAI
await client.chat.completions.create({
 model: 'gpt-4o',
 messages: [{ role: 'user', content: 'Опиши архитектуру' }]
});

// Вызов Anthropic через тот же интерфейс
await client.chat.completions.create({
 model: 'anthropic/claude-3-5-sonnet',
 messages: [{ role: 'user', content: 'Опиши архитектуру' }]
});

Архитектура становится плоской. За маршрутизацию отвечает префикс в имени модели. Логика переупаковки сообщений, извлечения системных промптов, конвертации форматов изображений и обработки вызовов функций изолирована на стороне RouterAPI.

Если появится новый игрок на рынке LLM, вам не потребуется добавлять новую библиотеку или переписывать обработчики ошибок. Вы просто укажете новое имя модели. Сложность нужно выносить за пределы бизнес-логики. Единый контракт в виде OpenAI API и надежный шлюз позволяют разработчикам писать продукт, а не интеграцию.

Теги

Ещё по теме