«Поддерживает ли эта модель вызовы инструментов?» — звучит как вопрос о модели. Но это не так. Это вопрос о модели и об эндпоинте, на котором вы её вызываете — и одна и та же модель может ответить «да» на одном и «нет» на другом. На этой странице объясняется, почему так и каким образом Entity Enricher решает этот вопрос: измеряя, а не доверяя каталогу.
Отправьте семейству gpt-6 от OpenAI запрос с функциями-инструментами на классический эндпоинт /v1/chat/completions — и запрос будет отклонён сразу же:
400 Bad Request Function tools with reasoning_effort are not supported. Use /v1/responses or set reasoning_effort to 'none'.
Отправьте ту же модель, те же инструменты и тот же промпт на /v1/responses — и она ответит нормально: по обоим каналам структурированного вывода, с усилием рассуждения или без него. В самой модели ничего не изменилось. Изменился только адрес.
Следствие
Возможность — это свойство не одной модели. Это свойство пары (модель, эндпоинт). Поэтому любой каталог, который хранит одно значение true/false на модель, отвечает на вопрос, которого он на самом деле не видит.
Маршрут — это просто эндпоинт, на который отправляется запрос. Тот же провайдер, та же модель, тот же ключ API — но другой формат передачи, другая структура запроса и другой набор правил о том, что можно совмещать. OpenAI предоставляет два таких эндпоинта, и большая часть отрасли скопировала более старый.
| Маршрут | Форма обмена | Кто его поддерживает |
|---|---|---|
Chat Completions /v1/chat/completions2023 | Без сохранения состояния: вы отправляете весь список сообщений и получаете обратно следующее сообщение. Между вызовами на сервере ничего не хранится. | OpenAI и де-факто отраслевой стандарт, скопированный почти всеми остальными — xAI, DeepSeek, Alibaba, Groq, Together, Ollama и большинством OpenAI-совместимых шлюзов. |
Responses API /v1/responses2025 | Возвращает список типизированных элементов — элемент рассуждения, сообщение, вызов функции-инструмента, вызов размещённого инструмента, — хранит состояние на сервере и может переносить рассуждение из одного хода в следующий. | Только OpenAI. Похожий эндпоинт, говорящий в формате Chat Completions, не обслуживает автоматически и этот. |
Именно поэтому выражение «OpenAI API» сегодня неоднозначно: их два, они пересекаются и действуют по разным правилам.
Модели с рассуждением сломали модель сообщений. Внутреннее рассуждение модели — не сообщение: это не то, что она сказала вам, и не то, что вы сказали ей. В Chat Completions есть только список сообщений, поэтому рассуждению там негде разместиться — а то, что он не может представить, он теряет между ходами.
Responses API создавался именно вокруг этого пробела. Рассуждение здесь — полноценный элемент возвращаемого списка; оно может прийти в зашифрованном виде и быть воспроизведено на следующем шаге, так что модель продолжает собственные размышления, а не восстанавливает их заново.
Вызов функции-инструмента — это обмен в два хода: модель запрашивает инструмент, ваш код отвечает, модель продолжает. В Chat Completions рассуждение негде сохранить между этими ходами, поэтому продолжение молча возобновилось бы без него — модели пришлось бы заново проделать то размышление, за которое вы уже заплатили, и, скорее всего, она проделала бы его иначе.
Поэтому провайдер отклоняет такую комбинацию, а не ухудшает результат молча. Сообщение об ошибке — не ограничение модели; это старый API признаёт, что у него нет места для того, что производит эта модель.
Если бы все возможности предпочитали один и тот же эндпоинт, маршрут был бы деталью реализации, о которой никому не нужно думать. Но это не так.
Структурированный вывод через канал инструментов — это ровно та комбинация, которую Chat Completions отклоняет на модели с рассуждением, а размещённый провайдером инструмент веб-поиска принимается только там.
Передача «сырых» аудиоданных на пути через Responses API невозможна, поэтому модель с поддержкой аудио доступна только через более старый эндпоинт.
Одна модель, две возможности, два разных эндпоинта. Нет единого true/false, которое было бы честным для обоих, — поэтому Entity Enricher ведёт карту возможностей для каждого маршрута, и поэтому запуск, которому нужны две возможности, живущие на двух разных маршрутах, отклоняется явно, а не теряет одну из них незаметно.
Проверка возможностей не отправляет синтетический запрос, написанный вручную. Она задействует реальный путь агента — тот же код, что и при запуске обогащения, — с принудительно включённой проверяемой возможностью, и решение принимает ответ провайдера. Поэтому измеряется то, что приложение действительно может сделать, а не то, что API теоретически мог бы принять.
Вся эта цепочка — каналы структурированного вывода, зрение, PDF, аудио, веб-поиск, рассуждение и управление его глубиной для каждого запроса — выполняется по одному разу на каждый маршрут, который может выбрать маршрутизатор. Почти у любого провайдера маршрут один, и проверка стоит ровно столько же, сколько и раньше; дважды измеряются только провайдеры с интерфейсом OpenAI, к которым действительно можно обратиться двумя способами.
Маршрут, который не отвечает на первый же запрос в обычном тексте, просто не измеряется, и проверенная модель затем направляется только по тем маршрутам, которые ответили — отката на другой эндпоинт нет ни при выборе, ни при сбое запуска. Модель, за которую не отвечает ни один маршрут, выводится из эксплуатации; то же происходит с моделью, чьи маршруты не дают ни одного канала структурированного вывода: текст никогда не служит заменой, поэтому такая модель не смогла бы выполнить ни одного обогащения. Вернуть её может только последующая проверка, подтвердившая наличие канала. Возможности никогда не меняются из-за сбоя запуска — измеряет их только проверка.
Запрос был принят, и модель вернула ответ ожидаемого вида.
Записывается как измеренное true для этого маршрута — и перевешивает любое заявление, собранное из внешних каталогов.
Провайдер явно отклонил эту возможность, либо клиент вообще отказался формировать запрос.
Единственный исход, которому разрешено записать значение false. Отказ к тому же бесплатен: он никогда не тарифицируется.
Что-либо ещё: ограничение частоты запросов, отсутствующий уровень доступа, тайм-аут, ошибка 5xx у провайдера, нераспознанная ошибка.
Ничего не записывается, и следующая проверка сможет это прояснить.
Эти две ошибки стоят по-разному. Ошибочное «неизвестно» обходится в одну повторную проверку: возможность просто измеряется заново позже. Ошибочное «нет» действует незаметно и навсегда — оно понижает модель повсюду, убирая её из списков выбора и из решения о маршрутизации, и никто так и не узнает почему. Поэтому нераспознанный сбой ничего не доказывает и записывается как ничего не доказывающий.
Ответ HTTP 200 подтверждает, что запрос был принят. Он не подтверждает, что модель действительно прочитала вложение — провайдер может спокойно принять изображение и передать его модели, которая на него даже не смотрит. Поэтому каждая проверка с полезной нагрузкой несёт такую нагрузку, ответ по которой можно сверить:
| Возможность | Отправленная полезная нагрузка | Заданный вопрос |
|---|---|---|
| Зрение | Однотонное красное изображение | «Какой цвет в нём преобладает?» |
| Входные данные PDF | PDF, содержащий ровно одно слово | «Какое слово есть в документе?» |
| Аудиовход | Аудиофайл с поддающимся подсчёту количеством тонов | «Сколько отдельных сигналов?» |
Два отдельных результата — и это намеренно
Ответ по содержанию записывается рядом с вердиктом и никогда не меняет флаг возможности. Небольшая модель, неверно распознавшая красный квадрат, всё равно поддерживает зрение: передача сработала, модальность была доставлена, модель просто оказалась слабой. Смешение «не может» и «сделала плохо» тихо удалило бы работающую возможность из-за одного неверного слова; их разделение сообщает вам обе вещи сразу.
Публичные каталоги моделей, такие как LiteLLM или PricePerToken, указывают возможности как простой факт о модели, без привязки к эндпоинту. Это не небрежность — для этого просто нет поля. Но это значит, что заявление может быть совершенно верным в целом и всё же неверным для того эндпоинта, который использует ваш запуск.
«Эта модель поддерживает вызовы инструментов.» Где-то это правда. Где именно — не сказано. Годится как отправная точка для модели, которую ещё никто не проверял.
«На этом эндпоинте в этот момент модель приняла вызовы инструментов.» Здесь назван маршрут, поэтому его можно сравнить с тем маршрутом, который действительно будет использован при запуске.
Указание маршрута также сохраняет честность измерения со временем. Вердикт, вынесенный на эндпоинте, который модель больше не использует, — не более надёжное свидетельство, чем заявление: это свидетельство о чём-то другом. Без записанного рядом маршрута старое измерение было бы неотличимо от текущего и перевесило бы каталог для неподходящего эндпоинта. Когда маршрут записан, такое измерение просто не применяется, и заявление остаётся в силе, пока его не заменит измерение на нужном эндпоинте.
И каталог моделей внутри приложения, и публичный каталог моделей содержат селектор Route рядом с фильтрами. Он определяет, за какой маршрут отвечают значки возможностей в каждой строке.
Каждая строка отвечает за тот эндпоинт, на котором модель действительно обслуживается. Это представление соответствует тому, что вы получаете при запуске обогащения, и именно его стоит смотреть, если вы не исследуете конкретный эндпоинт.
Закрепите всю таблицу за одним эндпоинтом — и значки будут отвечать только за него: так вы увидите, как возможность появляется или исчезает при переключении между эндпоинтами, и сможете сравнить две модели на равных.
Модель, у которой доступен только один маршрут, вообще не показывает селектор: выбирать нечего, а её значки уже отвечают за единственный имеющийся эндпоинт. Селектор появляется именно тогда, когда неоднозначность реальна.
Чаще всего ничего — и в этом весь смысл. Вы никогда не выбираете маршрут. Вы выбираете модель и описываете свой запуск, а маршрутизатор подбирает эндпоинт, способный обеспечить то, что нужно запуску: инструменты и веб-поиск идут туда, где они принимаются, а аудиовложение — туда, где передаётся аудио.
Единственный случай, который вы заметите, — это настоящий конфликт: например, запрос веб-поиска с одновременным прикреплением аудиофайла к одной и той же модели OpenAI, поскольку ни один эндпоинт не обслуживает и то и другое. Такой запуск отклоняется явно, с указанием обеих возможностей и того, что способен передать каждый маршрут, — вместо того чтобы выполниться и молча отбросить одну из них. Уберите одно из требований или выберите модель, которая поддерживает оба, — и запуск продолжится.
А если значок возможности вас удивил, объяснение даёт селектор маршрута: почти всегда перед вами модель, чей ответ зависит от того, где был задан вопрос.