
Привет, Хабр! На связи команда Caila — это платформа от Just AI, объединяющая LLM и другие генеративные модели, включая модели для создания изображений и видео.
Мы хотим поговорить не про очередную только вышедшую нейросеть и не про то, какая модель сейчас лучше пишет код. Мы хотим поговорить про архитектурную проблему, с которой рано или поздно сталкивается любая команда, начавшая встраивать LLM в продакшен: код оказывается привязан к конкретному провайдеру, а значит, каждое появление более подходящей модели превращается в отдельную интеграционную задачу.
Пример
Полгода назад вы выбрали модель, подключили ее SDK, обвязали промптами, протестировали на своих кейсах и выкатили в прод. Дальше происходит один из вариантов:
у модели меняют цену — иногда в несколько раз, иногда в обе стороны;
провайдер режет лимиты или вводит очередь запросов в часы пик;
выходит модель нового поколения, и старая версия объявляется устаревшей с конкретной датой отключения;
конкурент выкатывает модель, которая на ваших задачах справляется заметно лучше и дешевле;
бывает и так, что сервис просто становится недоступен — из‑за региональных ограничений, изменений в инфраструктуре или других внешних причин.
Во всех этих случаях хочется сделать одно простое действие: выбрать другую модель и продолжить работу. Но новый провайдер — это новый SDK, другие способы авторизации, немного другой формат запросов и ответов, свои ограничения и особенности обработки ошибок. Вместо того чтобы заниматься продуктом, приходится снова разбираться с интеграцией.
Это знакомая боль: похожее уже было с базами данных, объектными хранилищами и очередями сообщений. У каждого вендора свой протокол, свой SDK, свои нюансы, и любая привязка к конкретной реализации рано или поздно оборачивается дорогой миграцией.
С LLM сегодня происходит то же самое. Только модели меняются настолько быстро, что такие миграции могут потребоваться не раз в несколько лет, а каждые несколько месяцев.
Дальше в статье покажем, как эту проблему решает Caila: код у вас остается прежним — меняются толькоbase_url и идентификатор модели. Причем неважно, каким SDK вы уже пользуетесь — OpenAI, Anthropic или Google.
Один SDK на провайдера не масштабируется
На словах все звучит несложно: сначала интегрировались с OpenAI, потом добавили Anthropic, затем Gemini. Но каждый новый провайдер — это отдельный набор мелких несовместимостей:
Что отличается |
OpenAI |
Anthropic |
|
Системный промпт |
сообщение с ролью system |
отдельное поле system вне массива сообщений |
часть system_instruction в конфиге |
Формат ответа |
choices[0].message.content |
content[0].text |
candidates[0].content.parts[0].text |
Вызов инструментов |
tools + tool_calls |
tools + content с типом tool_use |
functionCall внутри parts |
Стриминг |
SSE с chat.completion.chunk |
SSE с событиями content_block_delta |
отдельный gRPC/REST‑стриминг |
Авторизация |
заголовок Authorization: Bearer |
заголовок x‑api‑key |
API‑ключ в query‑параметре или OAuth |
Умножьте это на десяток провайдеров, добавьте разные ограничения на количество запросов, разные правила повторной отправки запросов, разную семантику ошибок и необходимость отдельно управлять биллингом и лимитами каждого сервиса — и станет понятно, почему инженерная команда, которая хочет держать в проде три‑четыре модели для подстраховки, быстро обрастает слоем самописного кода, который не делает ничего полезного для продукта, а просто сглаживает чужие несовместимости.
Такое уже было: S3, ODBC, API‑гейтвеи
Если посмотреть на инфраструктурные технологии последних десятилетий, окажется, что похожие ситуации уже случались.
Возьмем объектные хранилища. В марте 2006 года Amazon выкатила S3 с собственным REST API — по сути, просто способ загружать и читать файлы через HTTP, без ничего революционного в самой идее. Но API оказался настолько удобным, что спустя несколько лет его начали копировать конкуренты: MinIO, Wasabi, DigitalOcean Spaces, Cloudflare R2, а из знакомых российскому разработчику — Yandex Object Storage, Selectel, VK Cloud и другие. Внутри это совершенно разные системы, но снаружи они выглядят одинаково. Приложение, написанное под boto3 или любой другой S3 SDK, переезжает с одного облака на другое сменой endpoint и ключей доступа без единой строчки изменений в бизнес‑логике.
С базами данных похожая история случилась на 15 лет раньше. В 1992 году появился ODBC — стандартный интерфейс для доступа к SQL‑базам, который придумали для того, чтобы приложение могло работать с Oracle, SQL Server, PostgreSQL или MySQL одинаково, меняя только драйвер под конкретную СУБД. В 1997 году для мира Java вышел JDBC — тот же принцип, только на другом языке. Спустя три десятилетия оба стандарта все еще в строю: приложение, которое умеет говорить по ODBC или JDBC, переезжает на другую базу данных сменой драйвера, а не переписыванием слоя доступа к данным.
API‑гейтвеи выросли из той же потребности, только на уровне микросервисов. Когда за одним продуктом начинает стоять не пять сервисов, а полсотни, каждый клиент физически не может помнить адреса, протоколы авторизации и особенности каждого бэкенда. Поэтому перед всеми ними ставится один шлюз — Kong, Envoy, Nginx или облачный API Gateway. Клиент общается только с ним, а дальше гейтвей сам решает, куда маршрутизировать запрос, как его аутентифицировать и что делать, если конкретный бэкенд не ответил.
С LLM рынок пока в более ранней фазе: де‑факто стандартов уже несколько — OpenAI Chat Completions, Anthropic Messages, Gemini API — и у каждого своя экосистема SDK и инструментов. Полной унификации нет — слишком быстро появляются новые возможности (вызов функций, структурированный вывод, кэширование промптов и мультимодальность), и у каждого вендора они реализованы по‑своему.
Именно поэтому появляется потребность в отдельном инфраструктурном слое. Он не должен навязывать приложению один формат запросов. Наоборот, его задача — принимать запросы в том формате, под который уже написано ваше решение, и самостоятельно преобразовывать их для нужного провайдера.
Как устроена Caila внутри
Архитектура Caila разделена на два уровня:
Провайдер — сервис‑адаптер к источнику моделей: OpenAI, Anthropic, Google, DeepSeek, агрегатор OpenRouter и другие. Адаптер знает протокол, авторизацию и особенности своего вендора и отдает платформе весь его модельный ряд. Открытые модели, которые Caila хостит на собственной инфраструктуре (например, линейка Qwen или отечественные T‑lite/T‑pro), и модели, развернутые на выделенных серверах через vLLM, — такие же сервисы платформы, просто за ними стоит собственная инфраструктура для запуска моделей, а не внешнее облако.
Caila Gateway — компонент, который не привязан к конкретной модели. Он отвечает за авторизацию запроса, проверку прав и лимитов API‑ключа, биллинг, выбор маршрута до модели и повторную отправку запроса при сбое.
Приложение никогда не обращается к модели напрямую — оно обращается к гейтвею, а тот уже маршрутизирует запрос нужному провайдеру.
Каталог содержит множество моделей. Карточка модели — это вендор и каноническое имя, а за ней — один или несколько поставщиков, через которых модель реально доступна. Один и тот же deepseek‑v4-flash может быть обработан напрямую DeepSeek, через OpenRouter или в Yandex Cloud — для приложения это одна и та же модель с одним идентификатором.
Для разработчика все модели видны одинаково — как записи в каталоге с идентификатором, спецификацией запроса и примером вызова.
Один и тот же запрос напрямую и через Caila
Возьмем обычный запрос к OpenAI через официальный SDK:
from openai import OpenAI client = OpenAI(api_key="sk-...") response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Explain what a transformer is in two sentences"}], ) print(response.choices[0].message.content)
Через Caila код почти не меняется:
from openai import OpenAI client = OpenAI( api_key="<CAILA_API_KEY>", base_url="https://caila.io/api/openai/v1", ) response = client.chat.completions.create( model="gpt-5.5", messages=[{"role": "user", "content": "Explain what a transformer is in two sentences"}], ) print(response.choices[0].message.content)
Меняются только два параметра клиента: api_key и base_url. Сам вызов chat.completions.create, структура сообщений, разбор ответа — идентичны. Это работает потому, что Caila поддерживает OpenAI‑совместимый API.
А теперь представим, что сегодня вы используете новую модель GPT-5.6. Через месяц выходит другая модель, которая показывает такое же качество, но работает быстрее или стоит дешевле. Вместо новой интеграции меняется только идентификатор модели:
-
было
response = client.chat.completions.create( model="gpt-5.6", messages=messages, ) -
стало — тот же клиент, тот же код, модель другого вендора
response = client.chat.completions.create( model="claude-sonnet-5", messages=messages, )
*Точные идентификаторы моделей — от коммерческих флагманов до открытых моделей на выделенном хостинге — смотрите в каталоге Caila. Уже сегодня 29.07.26 там можно попробовать новую Kimi K3 и Claude Opus 5
Остальной код остается тем же. Авторизация, обработка ошибок, работа с SDK и формат ответов не меняются.
Для разработчиков из России это еще и упрощает доступ к зарубежным моделям: не нужен VPN и зарубежная банковская карта — все оплачивается в рублях.
Не только OpenAI: запрос в родном формате вашего SDK
До сих пор во всех примерах мы использовали OpenAI API. Но это лишь один из форматов, которые поддерживает Caila.
Платформа умеет принимать запросы в нескольких популярных форматах LLM API, при этом формат запроса никак не привязан к выбранной модели. Другими словами, любую модель из каталога можно использовать через любой из поддерживаемых API.
Сейчас доступны:
https://caila.io/api/openai/v1/... — OpenAI API: chat completions, embeddings и другие методы;
https://caila.io/api/anthropic/v1/... — Anthropic Messages API, включая подсчет токенов;
https://caila.io/api/google/v1beta/... — Google Gemini API: generateContent, работа с файлами, батчи.
Помимо этих трех форматов, для некоторых провайдеров доступны и собственные API по адресам вида https://caila.io/api/providers/<провайдер>/…
В этом случае запрос уходит в родном протоколе вендора без какой‑либо конвертации. Например, YandexGPT доступен через свой foundationModels‑API, GigaChat, DeepSeek и OpenRouter — через свои. При этом API‑ключ, биллинг и лимиты остаются общими.
Сохраняются все особенности каждого API: тот же способ авторизации (например, x‑api‑key для Anthropic или Authorization: Bearer для OpenAI), потоковая передача ответов, вызов инструментов и другие возможности.
Самое интересное, что формат запроса и модель независимы. Приложение, написанное под Anthropic SDK, может обращаться к GPT или Gemini, а приложение под OpenAI SDK — к Claude. Гейтвей сам на лету преобразует запросы, ответы и потоковые события между разными форматами API:
from anthropic import Anthropic client = Anthropic( api_key="<CAILA_API_KEY>", base_url="https://caila.io/api/anthropic", ) # Anthropic SDK, а модель — OpenAI response = client.messages.create( model="gpt-5.5", max_tokens=1024, messages=[{"role": "user", "content": "Explain what a transformer is in two sentences"}], ) print(response.content[0].text)
Таблица несовместимостей из начала статьи — системный промпт, формат ответа, вызов инструментов, стриминг — это ровно то, что конвертация закрывает за вас.
По этой же причине через Caila работают не только ваши приложения, но и готовые инструменты. Агенты, рассчитанные на OpenAI API, — Cursor, Continue — подключаются через OpenAI‑формат, Codex — через Responses API.
Claude Code достаточно переменной окружения ANTHROPIC_BASE_URL=https://caila.io/api/anthropic — и он сможет работать с любой моделью каталога, не только с Claude. Аналогично подключаются pi и другие агенты, умеющие работать с одним из этих форматов.
Несколько поставщиков одной модели
Есть еще один сценарий, о котором стоит сказать отдельно.
Иногда менять модель вообще не нужно. Нужно продолжать работать с той же самой моделью, но получать к ней доступ через другого поставщика. Например, если у текущего провайдера закончились лимиты, возникли региональные ограничения или сервис временно недоступен.
Поэтому в Caila разделены понятия модели и поставщика. Примерно у каждой пятой модели несколько поставщиков: сам вендор, агрегатор OpenRouter, сторонние облака. Например, deepseek‑v4-flash доступен напрямую от DeepSeek, через OpenRouter и из Yandex Cloud; gpt-5.5 и claude‑sonnet-5 — от родных вендоров и через агрегаторы.
Для приложения при этом ничего не меняется. Вы по‑прежнему указываете один и тот же идентификатор модели, а Caila сама выбирает, через какого поставщика выполнить запрос. Если один из них недоступен или возвращает ошибку, платформа автоматически попробует другой.
При необходимости можно зафиксировать конкретного поставщика — достаточно передать его в заголовке X‑Mlp‑Provider.
Если проводить аналогию, то по своей роли Caila во многом похожа на оригинальный OpenRouter, но с рядом дополнительных возможностей:
у Caila есть провайдеры и модели, которые не выходят за границы РФ;
нет проблем с доступом из России — OpenRouter с 27 июня 2026 года блокирует запросы из РФ;
потенциально больше поддерживаемых форматов и более гладкая работа с Claude Code, Codex и другими инструментами из коробки;
есть отдельная аналитика: расходы, токены, число запросов и процент ошибок в разбивке по моделям, сервисам и API‑ключам, с экспортом в CSV и журналом запросов для детального разбора;
доступ настраивается не только на уровне одного ключа, а на уровне всей организации: приглашение сотрудников, роли и разграничение прав, аудит действий пользователей — через единую панель Conversational Cloud.
персональные и чувствительные данные в запросах защищает встроенный шлюз Jay Guard, который маскирует их перед отправкой во внешнюю модель (подробнее расскажем дальше в разделе «Для бизнеса»).
Сравнение моделей одним циклом
Поскольку все модели используют один и тот же формат запросов и ответов, их становится проще не только менять, но и сравнивать.
Например, можно за один цикл отправить одинаковый запрос сразу нескольким моделям.
models_to_compare = ["gpt-5.5", "claude-sonnet-5", "deepseek-v4-pro", "qwen/qwen3-max"] prompt = "Summarize the CAP theorem in two sentences for a backend engineer" for model in models_to_compare: response = client.chat.completions.create( model=model, messages=[{"role": "user", "content": prompt}], ) print(f"--- {model} ---") print(response.choices[0].message.content, "\n")
Это удобно на этапе прототипирования, а если хочется сравнить ответы визуально, в Caila есть готовый инструмент — Multi Chat, который отправляет один и тот же запрос сразу нескольким моделям (или одной модели с разными настройками) и показывает ответы рядом друг с другом для удобства сравнения.

Реестр моделей и цены
Когда моделей уже несколько сотен, искать цены по сайтам разных провайдеров быстро надоедает. Обычно собственная таблица с тарифами устаревает примерно в тот момент, когда вы закончили ее обновлять.
Поэтому в Caila есть отдельный каталог моделей, где показывается стоимость каждой модели по типам токенов — входные, выходные, кэшированные — с сортировкой и поиском по названию модели или вендора. Это удобно, когда нужно понять, сколько будет стоить новая функция или оценить, есть ли смысл протестировать другую модель.

Что еще делает платформа
Когда LLM‑интеграция переезжает из пилота в продакшен, вокруг нее постепенно появляется инфраструктурный код. Часть этих задач можно вообще не писать самостоятельно.
Для любой модели в каталоге — независимо от того, проксирует ее Caila к внешнему провайдеру или хостит сама, — работает повторная отправка запроса при таймауте или ошибке: на другой доступный инстанс модели или на другого поставщика той же модели, с настраиваемым количеством попыток и таймаутов. Это базовая защита от сбоев без дополнительного кода в приложении.
Отдельная история — модели, которые Caila хостит на своей инфраструктуре или на выделенных серверах через vLLM (напомним, так можно развернуть и любую модель с Hugging Face). Для них платформа дополнительно берет на себя:
батчеризацию запросов, если сервис поддерживает пакетную обработку — платформа сама копит запросы до лимита времени или размера батча и одним вызовом отправляет их в инференс, что особенно ощутимо на GPU‑нагрузке;
кэширование повторяющихся запросов, чтобы не гонять инференс на одинаковых входах.
Ни один из этих пунктов не требует правок в коде приложения — это настройки на уровне сервиса или ключа доступа. Это ровно та обвязка, которую обычно дописывают вручную, когда LLM‑интеграция вырастает из пилота в продакшен с реальной нагрузкой.
Когда важнее не качество, а стоимость
Не каждой задаче нужна самая мощная LLM. Например, классификация обращений, извлечение сущностей или генерация типовых текстов часто отлично работают на открытых моделях вроде DeepSeek или Qwen. Во многих сценариях они дают сопоставимый результат, но стоят заметно дешевле.
Поэтому иногда выгоднее распределять нагрузку между несколькими моделями. Сложные запросы отправлять в более мощную LLM, а массовые и предсказуемые — в более экономичную.
Для бизнеса: доступ, квоты и защита данных
Если вы читаете это не только как разработчик, но и как человек, отвечающий за то, чтобы корпоративный трафик к LLM был управляемым, то у вас, скорее всего, два вопроса: сколько это будет стоить и не утечет ли куда‑то лишнее.
С расходами все достаточно просто: у каждого API‑ключа можно настроить свои лимиты и права, а пользователями, ролями и приглашениями в аккаунте управляют централизованно — в отдельной панели Conversational Cloud, общей для всех продуктов Just AI.
А с утечками работает отдельный сервис — Jay Guard, который анализирует содержимое запросов до того, как они попадут во внешнюю LLM и ищет там персональные и чувствительные данные. Он распознает больше 25 типов сущностей: ФИО, номера телефонов, данные банковских карт, ИНН, email, IP‑ и MAC‑адреса, геолокацию, даты и так далее, плюс можно завести свои сущности под конкретный проект.
Дальше, в зависимости от настроенного правила, Jay Guard либо блокирует запрос целиком, либо маскирует найденные данные плейсхолдерами перед тем, как отправить запрос дальше во внешнюю модель. Модель отвечает, уже не видя реальных данных, а на обратном пути Jay Guard восстанавливает оригинальные значения в ответе — так что для конечного пользователя вся эта прослойка не заметна. Это работает даже при стриминге: через демаскирование проходит каждый отдельный чанк ответа, а не только финальный текст целиком.

Jay Guard можно подключить на разных уровнях: для отдельного API‑ключа, для всего аккаунта (тогда маскирование будет работать не только в API‑запросах, но и в тестовых формах интерфейса) или сразу для всей инсталляции Caila, если платформа развернута в контуре вашей организации. Правила защиты данных достаточно настроить один раз, а не реализовывать их отдельно в каждом сервисе.
Что стоит учитывать
У такого подхода есть и свои ограничения.
Лишний шаг в сети. Запрос идет не сразу в OpenAI или Anthropic, а сначала через Caila, и только потом — к модели. Для большинства задач эта разница не заметна. Но если вы пишете что‑то с жесткими требованиями к скорости ответа — торгового бота, голосового ассистента — задержку стоит измерить самим, а не считать ее нулевой.
Не все фирменные фишки провайдера доедут без потерь. Кросс‑форматная конвертация покрывает основной контракт: сообщения, системный промпт, вызов инструментов, стриминг, подсчет токенов. Но у форматов есть возможности, которые не мапятся один в один. Если проект завязан на специфическую функцию конкретного API, используйте ее через родной формат этого вендора или через прямой путь /api/providers/<провайдер> и заранее сверьтесь с документацией Caila. Отдельные модели доступны только через нативный протокол своего провайдера и в кросс‑форматные маршруты не попадают.
Выбирать модель все равно придется самостоятельно. Caila не подскажет, какая модель лучше подойдет именно для вашей задачи. Она помогает быстро сравнить несколько вариантов — через Multi Chat или собственные бенчмарки, — но окончательное решение остается за разработчиком.
Заключение
Факт: модели меняются быстрее, чем архитектура приложений, которые их используют. Разумный инженерный ответ на это не привязываться к конкретному провайдеру, а вынести работу с LLM в отдельный слой со стабильным контрактом.
Caila — как раз такой слой: единая точка авторизации, маршрутизации и биллинга поверх сотен моделей, доступных в родном формате вашего SDK — OpenAI, Anthropic или Gemini — и с резервными маршрутами до одной и той же модели через разных поставщиков.
Будем рады обсудить в комментариях: как вы сейчас решаете проблему переключения между провайдерами. Используете одну модель? Поддерживаете собственный слой совместимости? Или уже работаете через единый API?
P. S. Если формат комментариев тесноват — заходите в наш Telegram‑канал для разработчиков. Там можно поделиться своим опытом, рассказать про кейс или прийти с конкретной проблемой.