Открыт к новым проектам и коллаборациям. Связаться →

2026.07.10 · llm · agents

Каталог моделей у gateway — это не список доступного

GET /v1/models отвечает не на тот вопрос: он перечисляет, что gateway знает, а не что ответит твоему ключу и твоему протоколу. История про то, как одна модель молчит на одном эндпоинте и оживает на другом за тем же адресом.

Когда подключаешь OpenAI-совместимый gateway — агрегатор, который прячет несколько провайдеров за одним адресом, — первое движение руки предсказуемо: дёрнуть GET /v1/models и поверить списку. Список врёт. Точнее, он честно отвечает не на тот вопрос. Он перечисляет модели, которые gateway умеет маршрутизировать в принципе, а не те, что ответят именно твоему ключу и именно твоему способу вызова. Между «есть в каталоге» и «вернёт ответ на мой запрос» стоят две отдельные стены, и /v1/models не знает ни про одну.

Первая стена — ключ. Каталог у такого gateway общий на всех, а привязка живых провайдеров идёт per-key: за моделью в списке может не стоять ни одного включённого для тебя апстрима. На вызове это выглядит как no_available_providers — не «модель сломана», а «под твоим ключом за ней пусто». Каталог показывает витрину, ключ определяет, что из витрины реально отпустят.

Вторая стена тоньше и интереснее. Даже среди моделей, которые ключу доступны, не все отвечают на одном и том же пути. Одна отдаёт результат только на классическом /v1/chat/completions. Другая там молчит той же ошибкой no_available_providers — но оживает на /v1/responses, эндпоинте протокола, на котором работают агентные CLI. Та же строка ошибки на одном пути превращалась в нормальный ответ на другом — для соседней модели. Значит дело было не в теле запроса и не в ключе, а в том, что модель маршрутизируется через другой протокол за тем же base URL. Ошибка говорила «не здесь», а звучала как «сломано».

/chat/completions /v1/responses /v1/messages

модель A модель B

Один base URL, три протокол-эндпоинта. Модель не сломана — она просто отвечает не на том пути, где её ждут по привычке.

Диагностика после этого стала механической: не доверять одному пробнику, а прогнать матрицу «модель × эндпоинт» — каждую модель по каждому пути. Картина вышла зеркальной: то, что мертво на одном протоколе, живёт на другом, и наоборот. Пять минут curl в цикле дали карту, которую каталог не отдал бы никогда, потому что каталог про неё и не знает — он одномерный, а доступность трёхмерная.

Отсюда урок, который я теперь применяю до того, как вписывать модель в оркестратор или строить на ней fallback-цепочку. no_available_providers и родственные ей ошибки почти никогда не значат «сломано» — они значат «не тем путём». За одним адресом прячутся три независимые оси: ключ (что тебе провиженено), протокол-эндпоинт (chat против responses), формат тела запроса. Прежде чем чинить формат — а именно туда тянется рука по привычке — стоит проверить первые две оси. GET /v1/models не ответит ни на одну из трёх; на них отвечает только матрица.