Как проверить OpenAI-совместимый API перед подключением к агенту

Пять проверок: чат, JSON по схеме, полный цикл вызова инструмента, завершение SSE и ошибки параметров. Готовый Python-скрипт, локальные тесты сбоев и результат реального запуска.

Контрольный калибр показывает, что деталь подходит к одному отверстию, но не ко всем

Успешный запрос в чат ещё не означает, что OpenAI-совместимый API сможет обслуживать вашего агента. Отдельно проверьте структурированный ответ, полный цикл вызова инструмента, завершение потока и обработку ошибок. Для каждого запуска сохраните адрес endpoint и точный идентификатор модели. Совместимость относится к конкретному маршруту, запросу и ответу, а не ко всем возможностям провайдера навсегда.

В статье используется небольшой Python-скрипт для формата Chat Completions. Он отправляет не более шести запросов и не повторяет их автоматически. Это отдельный этап перед настройкой OpenCode, Copilot или n8n: сначала проверьте маршрут напрямую, затем подключите клиент и испытайте сам клиент. Успех скрипта не подтверждает поддержку Responses API, работу под нагрузкой или всех параметров приложения.

Скачайте скрипт и определите границы проверки

Скачайте check_api.py. Пять проверок требуют шести запросов: для инструмента нужен первый вызов модели и второй запрос с результатом выполнения.

ПроверкаЧто требуется для PASSЧего PASS не доказывает
Обычный чатРазобранный ответ Chat Completions с заданным коротким ответомВызов инструментов, поток или обработку изображений
Структурированный ответЗапрос со строгой схемой принят; JSON соответствует схеме и известным значениямВсе ключевые слова JSON Schema и надёжное извлечение из произвольных данных
Полный цикл инструментаВерные имя функции, аргументы и ID; локальный результат возвращён модели; итоговый ответ точныйПотоковые аргументы, параллельные инструменты и безопасность удалённых действий
ПотокТип содержимого SSE, разобранные события, ожидаемый текст, stop и [DONE]Совместимость событий Responses и восстановление соединения
Ошибка валидацииНеверный тип поля возвращает HTTP 400/422 и JSON-объект ошибкиВсе виды ошибок, правила повторов и тарификацию

Локальный набор регрессионных тестов подменяет HTTP-вызовы фиксированными ответами. Он не открывает сетевые соединения и не требует ключа. Положите его рядом с check_api.py, установите зависимости из следующего раздела и запустите:

python test_check_api.py
python -O test_check_api.py

7 ноября 2026 года все девять сценариев дали ожидаемые результаты в обоих режимах: корректный обмен, оборванный поток, неверный ответ инструмента, неверный тип инструмента, неправильные значения JSON, ошибочный ответ чата, null вместо содержимого, ошибка авторизации и ограничение частоты запросов. Это реальные локальные тесты самого проверяющего скрипта, а не обращения к размещённой модели. Успех сценария означает совпадение фактического статуса с ожидаемым: намеренно сломанные ответы не должны все получать PASS. Для результата провайдера нужен отдельный реальный запуск с датой, маршрутом, моделью и данными об использовании.

Уточните endpoint и ограничьте расходы

Скопируйте корневой адрес API из документации выбранного провайдера. Корень с окончанием /v1 скрипт преобразует в /v1/chat/completions. Не передавайте уже полный путь /chat/completions или адрес страницы с описанием модели. До проверки ключа убедитесь, что модель поддерживает нужный протокол.

Для Ofox начните с актуальной документации API и документации конкретной модели. Обозначение «OpenAI-совместимый» не означает поддержку всех endpoint OpenAI. Если инструменты модели требуют Responses, используйте инструкцию по переходу на Responses, а не пытайтесь исправить этот тест одной заменой URL.

Выберите недорогую модель, к которой у вас есть доступ, проверьте текущий тариф и, если сервис позволяет, установите лимит расходов на уровне аккаунта. Скрипт использует синтетические задания и небольшой лимит выходных токенов, но параметр токенов не гарантирует денежный потолок. Провайдер может отклонить поле или иначе его обработать; учёт токенов рассуждений тоже различается. Короткий пример сам по себе не повод запускать дорогую модель.

По умолчанию используется max_completion_tokens. Если документация модели требует max_tokens, выберите это поле соответствующим аргументом. Отказ из-за поля ограничения означает, что запрос нужно привести к документированному формату, а не то, что провайдер вообще не умеет генерировать текст.

Установите зависимости и выполните один запуск

Используйте актуальное окружение Python 3 с поддерживаемой TLS-библиотекой. Виртуальное окружение позволит не менять зависимости основного приложения:

python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests jsonschema

Задайте LLM_API_KEY привычным локальным способом управления секретами. Не помещайте ключ в аргументы команды, публичный ноутбук, скриншот или скачанный скрипт. Если провайдер поддерживает временные ключи с ограниченными правами, используйте такой ключ.

python check_api.py \
  --base-url https://api.example.com/v1 \
  --model YOUR_EXACT_MODEL_ID \
  --token-field max_completion_tokens \
  --max-output 256 \
  --out first-run.json

api.example.com и YOUR_EXACT_MODEL_ID — заполнители. Замените их документированным адресом и реальной моделью. Скрипт требует HTTPS, кроме явно включённой проверки локального loopback-адреса. Перенаправления отключены, чтобы ошибка адреса не переслала bearer-ключ другому получателю. Существующий файл отчёта не перезаписывается: для повторного запуска укажите другое имя.

Скрипт задаёт тайм-ауты подключения и чтения, а между полными строками SSE проверяет ориентировочный предел времени. Это не жёсткий предел общей длительности: сервер может медленно передавать незавершённую строку и поддерживать чтение активным. Запускайте пример под наблюдением и прерывайте зависший запрос. Для фонового производственного мониторинга нужны отдельный предел времени процесса и ограничения размера ответа.

Читайте результат как свидетельство, а не оценку в процентах

PASS означает, что ответ удовлетворил этой конкретной проверке. FAIL — запрос отклонён или ожидаемое условие не выполнено. UNKNOWN — транспортная проблема либо неожиданная структура не позволила сделать надёжный вывод. Перед исправлениями прочитайте подробности и фактический HTTP-статус.

Сбой сети не доказывает отсутствие вызова инструментов. Ошибка 401 не является ошибкой JSON Schema. Ответ 429 на намеренно неправильный запрос не доказывает проверку типов: сервер мог остановить запрос ещё до валидации. Поэтому ошибки авторизации и лимита запросов не принимаются за успешный тест ошибки параметров.

Не превращайте пять статусов в «совместимость 80%». Агенту, которому нужны инструменты, не подходит маршрут только потому, что четыре другие проверки прошли. Составьте таблицу требований приложения и отметьте обязательные и необязательные операции.

Реальный запуск шести запросов через Ofox

7 ноября 2026 года мы запустили скачиваемый скрипт с адресом https://api.ofox.ai/v1, доступной аккаунту моделью openai/gpt-6-luna и параметром max_completion_tokens: 256. Всего было шесть запросов, включая намеренно некорректный; повторов не было. Фактический JSON-отчёт содержит время ответов и возвращённые данные об использовании токенов.

ПроверкаНаблюдаемый результат
Обычный чатPASS: требуемый ответ и нормальное завершение
Структурированный ответPASS: схема, SKU и количество соответствовали заданию
Цикл инструментаPASS: второй ответ был ровно 17
ПотокPASS: получены текст, причина завершения и [DONE]
Неверный параметрPASS: HTTP 400 с JSON-объектом ошибки

Скриншот локального просмотрщика фактического отчёта API; интерфейс на английском языке.

На скриншоте показан локально отображённый сохранённый JSON. Это не панель Ofox и не дополнительный онлайн-тест. Английские названия проверок сохранены, чтобы их можно было сопоставить с JSON и исходным кодом.

Результаты относятся к выбранной модели, маршруту и тестовым заданиям в момент запуска. Они не сертифицируют все модели, не доказывают внутреннее принудительное применение строгой схемы и не подтверждают Responses, потоковые аргументы инструментов, fallback или дополнительные поля вашего клиента. Ответ 400 в последней строке — ожидаемый результат проверки, а не авария сервиса.

Зачем возвращать результат инструмента модели

Наличие tool_calls подтверждает только первую половину взаимодействия. Приложение должно проверить вызов, выполнить разрешённый инструмент и вернуть результат с соответствующим ID. Во втором ответе модель должна правильно использовать этот результат.

В тесте доступна одна локальная операция чтения — lookup_stock. Для синтетического SKU 000102 она возвращает фиксированный остаток 17. Скрипт проверяет тип инструмента, имя, JSON-аргументы и ID вызова, затем передаёт результат обратно. Итоговый ответ должен быть ровно 17. Команды оболочки и произвольные функции, предложенные моделью, не выполняются.

Такой узкий сценарий упрощает диагностику. Если первый вызов правильный, а второй запрос отклонён, сначала проверьте формат сообщения с результатом и ID вызова. Если модель отвечает 170, это ошибка: наличия подстроки «17» недостаточно. Проверки реализованы явными исключениями и остаются активными при запуске Python с оптимизацией.

В рабочем приложении проверяйте аргументы по схеме, ограничивайте разрешённые операции и учитывайте права до выполнения. Корректный вызов инструмента сам по себе не разрешает отправить письмо, провести платёж или изменить базу данных.

Отличайте правильный JSON от выполненного задания

Сценарий структурированного ответа запрашивает {"sku":"000102","quantity":3}. Схема требует строковый SKU, целое количество, оба поля и отсутствие дополнительных свойств. Скрипт сначала проверяет схему, затем сравнивает значения с известным ответом. Один корректный результат не доказывает, что сервер применил строгий режим: модель могла выдать такой JSON и при проигнорированном параметре. Это наблюдение для конкретного задания, а не доказательство внутренней реализации.

JSON может успешно разбираться, но потерять нули в SKU. Значение может иметь правильный тип, но быть неверным. Сервер может поддерживать обычный JSON-режим и отклонять строгую схему. Сохраняйте точный запрос, а не удаляйте strict ради зелёного статуса.

Документация OpenAI по структурированным ответам (английский) разделяет структурированные ответы и вызов функций. Используйте контракт выбранного endpoint: перенос поля из другого API не является проверкой совместимости.

Если ваша задача — извлечение информации, после этого используйте документ с известным ответом и проверку преобразования текста в JSON. Маленькая фиксированная схема — лишь первый тест.

Проверяйте завершение потока

Поток, который вывел часть текста, может оставаться незавершённым. Скрипт читает события SSE, соединяет текстовые фрагменты, записывает причину завершения и требует маркер Chat Completions [DONE]. Оборванный поток считается ошибкой, даже если часть ответа выглядит правдоподобно.

Это требование выбранного профиля, а не универсальное правило всех API. У Responses другой формат событий. Провайдеры также по-разному возвращают использование токенов в потоке. Скрипт не запрашивает stream_options; отсутствие сведений об использовании записывается, но само по себе не считается сбоем. Если учёт расходов зависит от этих данных, отдельно проверьте документированный параметр провайдера.

После обрыва не повторяйте автоматически всю цепочку, если она могла уже выполнить внешнее действие. Сначала установите, запускался ли инструмент или последующий шаг. Разбор обрыва потока Codex касается конкретного клиента; обычный сетевой повтор не гарантирует безопасного продолжения агента.

Исправляйте проблему на нужном уровне

НаблюдениеЧто проверить сначала
Все запросы возвращают 401/403Ключ, доступ аккаунта и авторизацию endpoint
404 или model-not-foundАдрес, точный ID и доступность модели; диагностика model-not-found (английский)
Чат проходит, строгая схема нетПоддержку response-format и допустимое подмножество схем
Запрос инструмента сразу отклоняетсяПротокол, возможность модели и поля запроса
Не принимается результат инструментаID вызова, историю сообщений и формат результата
Есть текст, но нет завершенияSSE, буферизацию прокси, разрыв соединения и соответствие профилю
Учётные данные n8n проходят, выполнение нетРеальный endpoint операции; диагностика n8n

Когда обязательные проверки пройдены, настройте собственного провайдера в OpenCode (английский) или нужном клиенте и повторите небольшое задание через него. Клиент может отправлять поля, которых нет в этом тесте. Сохраните оба результата: прямой вызов API не гарантирует работу приложения.

Перед передачей отчёта удалите закрытые адреса, request ID и данные аккаунта, если они присутствуют. Пример сохраняет только синтетические результаты, модель, время, статусы и использование токенов; ключ в файл не записывается. При смене версии модели или маршрута сохраняйте новый датированный результат. Он полезнее общего ярлыка «совместимый».