# Как объединить несколько ИИ-провайдеров в один API: настройка OpenAI-совместимого роутера

> AI Release · @ai_release1 · https://ai-release.org/guides/kak-obedinit-neskolko-ii-provayderov-v-odin-api-nastroy.html

Несколько ИИ-провайдеров сразу — это несколько API-ключей, несколько разных форматов запросов и несколько мест, где нужно править код при смене модели. OpenAI-совместимый роутер решает это так: вы поднимаете у себя один сервис, который принимает запросы в формате OpenAI API (стандартный JSON-формат чата: список сообщений с ролями system/user/assistant), а дальше сам перенаправляет их нужному провайдеру по вашим правилам. В результате все ваши клиенты — скрипты, чат-приложения, браузерные агенты, локальные ассистенты — ходят на один адрес вида `http://127.0.0.1:4000/v1` и используют один ключ, а вы меняете провайдера или модель правкой одного конфига, а не переписыванием кода. Гайд рассчитан на новичка: подойдёт для любой ОС, где вы умеете открыть терминал и запустить сервис (Linux, macOS, Windows 10/11), и для любой программы-клиента, у которой в настройках есть поля «base URL» и «API key».

## Требования и подготовка
![Иллюстрация к разделу «Требования и подготовка»](https://ai-release.org/guides/img/kak-obedinit-neskolko-ii-provayderov-v-odin-api-nastroy-ru-1.jpg)


- Компьютер или сервер, где будет работать роутер. Для локальных экспериментов хватит ноутбука, для постоянной работы — машина, которая не выключается.
- Терминал (Linux/macOS) или PowerShell (Windows) — командная строка, куда вы вводите команды.
- Runtime для запуска роутера: Python 3.10+ либо Docker — смотря в чём вы увереннее. Роутер — это обычный веб-сервис, ему нужен только один из этих вариантов.
- API-ключи тех провайдеров, которые вы хотите объединить. Ключ — это длинная строка вида `sk-...`, выдаётся в личном кабинете провайдера.
- Файл конфига роутера (обычно YAML или JSON) — текстовый файл, где перечислены провайдеры и их модели.
- Свободный TCP-порт на машине. В примерах ниже — `4000`. Порт — это «дверь», по которой клиенты стучатся в роутер.
- Клиент, который умеет OpenAI-совместимый API. Это может быть ваш скрипт, браузерный агент ([как подключить ИИ-агента к Chrome](https://ai-release.org/guides/kak-podklyuchit-ii-agenta-k-chrome-nastroyka-avtomatiza.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide)) или голосовой ассистент на компьютере ([обзор инструментов и настройка](https://ai-release.org/guides/kak-sdelat-golosovogo-ii-assistenta-na-kompyutere-obzor.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide)).
- Права: возможность запускать процессы и писать в свою папку проекта. Права администратора нужны только если вы слушаете порт ниже 1024 — мы этого делать не будем.

Чек-лист перед стартом:

- [ ] Терминал открывается, команда `python --version` (или `docker --version`) что-то отвечает.
- [ ] Есть минимум один рабочий API-ключ провайдера.
- [ ] Ключи сохранены в переменные окружения, а не в переписке.
- [ ] Порт `4000` свободен (проверяется на шаге 2).
- [ ] Есть один клиент для проверки — хотя бы `curl`.

## Пошаговая инструкция
![Иллюстрация к разделу «Пошаговая инструкция»](https://ai-release.org/guides/img/kak-obedinit-neskolko-ii-provayderov-v-odin-api-nastroy-ru-2.jpg)


1. Создайте папку проекта и файл конфига. Например, `~/ai-router` и внутри `router-config.yaml`. Ожидаемый результат: файл существует и открывается редактором. Почему так: весь смысл роутера в том, что состав провайдеров описан декларативно — вы не трогаете код клиентов.

**Пример:** содержимое `router-config.yaml` (это шаблон, подставьте свои значения):

```yaml
server:
  host: 0.0.0.0      # слушать все интерфейсы
  port: 4000

providers:
  - name: cloud_a                 # ваше внутреннее имя провайдера
    base_url: https://api.example-provider-a.com/v1
    api_key_env: PROVIDER_A_KEY   # ключ берём из переменной окружения
  - name: local_box
    base_url: http://127.0.0.1:8080/v1   # порт смотрите в настройках своего локального сервера
    api_key_env: PROVIDER_A_KEY

models:
  - alias: fast        # это имя будут запрашивать клиенты
    provider: cloud_a
    model: <имя-модели-у-провайдера>
  - alias: local
    provider: local_box
    model: <имя-локальной-модели>
```

2. Положите ключи в переменные окружения и запустите роутер. Ожидаемый результат: в терминале появляется строка вида `Listening on 0.0.0.0:4000` и процесс не завершается.

**Пример:**

```bash
export PROVIDER_A_KEY="sk-ваш-ключ"
python -m router --config ./router-config.yaml
```

Почему это работает: роутер читает конфиг, поднимает HTTP-сервер на порту 4000 и держит ключи в памяти процесса. Ключ из `api_key_env` не попадает в сам файл конфига — его безопаснее коммитить и передавать коллегам.

3. Проверьте, что порт слушается и роутер жив. Ожидаемый результат: команда печатает список моделей, среди них `fast` и `local` — это ваши алиасы, а не имена моделей провайдеров.

**Пример:**

```bash
curl http://127.0.0.1:4000/v1/models \
  -H "Authorization: Bearer sk-router-local-000"
```

Ключ `sk-router-local-000` — это ключ самого роутера, он придумывается вами и не равен ключам провайдеров. Почему так: клиент обязан передать заголовок `Authorization`, иначе роутер не поймёт, что запрос «свой».

4. Сделайте первый реальный запрос к чату. Ожидаемый результат: в ответе приходит JSON, где текст ответа лежит в `choices[0].message.content`.

**Пример:**

```bash
curl http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-router-local-000" \
  -H "Content-Type: application/json" \
  -d '{"model":"fast","messages":[{"role":"user","content":"Привет! Ответь одной строкой."}]}'
```

Почему это работает: вы отправили запрос в формате OpenAI, а роутер по алиасу `fast` нашёл нужного провайдера и перевёл запрос в его формат.

5. Повторите шаг 4, заменив `"model":"fast"` на `"model":"local"`. Ожидаемый результат: ответ приходит от второй модели, а адрес и ключ в команде не менялись. Это и есть главная выгода: один API — много провайдеров. Если вторым провайдером у вас локальная модель на телефоне или мини-ПК, пригодится гайд [про запуск ИИ-модели локально на Android](https://ai-release.org/guides/kak-zapustit-ii-model-lokalno-na-android-prilozheniya-i.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide) — такую модель тоже можно завернуть в роутер как отдельного провайдера.

6. Направьте на роутер своего постоянного клиента. В настройках приложения найдите поля base URL и API key и укажите `http://127.0.0.1:4000/v1` и ключ роутера. Для агента в браузере порядок действий описан в гайде [про подключение ИИ-агента к Chrome](https://ai-release.org/guides/kak-podklyuchit-ii-agenta-k-chrome-nastroyka-avtomatiza.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide). Ожидаемый результат: клиент перестаёт просить отдельные ключи и отвечает на запросы как обычно.

7. Добавьте правила выбора модели. Ожидаемый результат: у вас появляется логика «дешёвая модель по умолчанию, дорогая — по явному запросу», и это видно в конфиге, а не в коде.

**Пример:** добавьте в конфиг раздел с правилом по умолчанию:

```yaml
routing:
  default_alias: fast
  fallback_alias: local   # сюда уйдём, если основной провайдер недоступен
```

Почему это работает: роутер сначала смотрит на явный `model` в запросе, а если его нет — берёт alias из правил. Так один и тот же код клиента обслуживает и обычные, и «страховочные» сценарии.

8. Проверьте поведение при недоступном провайдере. Ожидаемый результат: запрос по-прежнему завершается ответом, но уже от fallback-провайдера.

**Пример:** временно испортите ключ основного провайдера на заведомо неверный, повторите команду из шага 4 и убедитесь, что ответ всё равно пришёл. После проверки верните ключ на место.

## Возможные проблемы и решения
![Иллюстрация к разделу «Возможные проблемы и решения»](https://ai-release.org/guides/img/kak-obedinit-neskolko-ii-provayderov-v-odin-api-nastroy-ru-3.jpg)


**Проблема 1: ошибка вида `401 Unauthorized`.** Симптом: в ответе JSON с полем `error` и текстом про invalid API key. Решение: сначала проверьте ключ роутера — он должен совпадать с тем, что вы придумали, и передаваться в заголовке `Authorization: Bearer ...`. Если ключ роутера верный, проблема в ключе провайдера: убедитесь, что переменная окружения (например, `PROVIDER_A_KEY`) реально задана в том же терминале, где запущен роутер — команда `echo $PROVIDER_A_KEY` должна вывести непустую строку. После правки переменной роутер нужно перезапустить, потому что старые значения он уже прочитал в память.

**Проблема 2: ошибка вида `model not found` или ответ «модель не поддерживается».** Симптом: запрос уходит, но провайдер отвечает, что такого имени модели у него нет. Решение: различайте alias и имя модели. В запросе клиента должно стоять ваше короткое имя (`fast`, `local`), а в конфиге в поле `model` — то имя, которое требует провайдер. Проверьте обе строки и перезапустите роутер. Быстрый тест — `curl http://127.0.0.1:4000/v1/models`: если в списке нет нужного alias, роутер просто не знает о нём.

**Проблема 3: клиент игнорирует роутер и ходит напрямую.** Симптом: в логах провайдера видно запросы, а в логах роутера — тишина. Решение: проверьте, что в настройках клиента адрес оканчивается на `/v1` и не содержит лишних пробелов или завершающего слэша; часть клиентов сохраняет старый адрес в скрытом профиле — перезапустите приложение. Если клиент запускается из скрипта, посмотрите, не переопределяет ли он base URL где-то ещё: частая причина — переменная окружения с базовым адресом, заданная раньше.

**Проблема 4: таймауты и `429 Too Many Requests`.** Симптом: ответ приходит не сразу, а через десятки секунд, либо с ошибкой про превышение лимита. Решение: это не сломанный роутер, а поведение провайдера — у него есть лимит запросов в минуту. Задайте fallback-алиас (шаг 7) и переводите лишние запросы на второй провайдер; заодно увеличьте таймаут в клиенте, если он у вас настраивается.

## FAQ

**Чем роутер отличается от того, чтобы просто подставить другой ключ в клиенте?**
Роутер даёт единый адрес и единый ключ для всех провайдеров сразу. Если подставлять ключ в клиенте, придётся для каждого провайдера менять настройки, а при смене модели — править код или настройки в нескольких местах. С роутером меняется одна строка в `router-config.yaml`.

**Обязателен ли отдельный сервер, или можно запустить на ноутбуке?**
Для обучения и личных задач ноутбука достаточно: роутер — обычный процесс на порту `4000`, он не требует много ресурсов. Отдельная машина нужна, только если клиентами пользуются другие люди или сервисы и роутер должен быть доступен круглосуточно.

**Что делать, если у провайдера не OpenAI-совместимый формат запросов?**
Проверьте, есть ли у провайдера отдельный OpenAI-совместимый эндпоинт — многие его предоставляют, тогда достаточно указать правильный `base_url` в конфиге. Если такого эндпоинта нет, провайдера нельзя подключить простым описанием в конфиге: нужен адаптер, который переводит формат OpenAI в формат провайдера.

**Безопасно ли хранить ключи в файле конфига?**
Ключи роутера (те, что клиенты передают в заголовке) — не секрет, их можно держать в конфиге. Ключи провайдеров лучше держать в переменных окружения и ссылаться на них через поле `api_key_env`, как в примере выше. Так файл конфига можно безопасно хранить в репозитории и передавать коллегам, а сами ключи остаются только на машине.
