LLM API и интеграция

Переход с OpenAI API на совместимый: инструкция

Переход с OpenAI API на совместимый: инструкция

Перенос кода на OpenAI-совместимый API в большинстве проектов сводится к двум значениям: базовому адресу и ключу. Официальные SDK принимают адрес параметром, так что правка укладывается в одну строку, и дальше всё продолжает работать — тот же метод, те же поля запроса, тот же формат ответа. Это и есть смысл совместимости. Но остаётся класс случаев, где перенос ломается, и ломается тихо: код не падает, а начинает вести себя иначе. Разбираем, что именно проверить, как убедиться, что перенос прошёл без потерь, и какие ожидания от слова «совместимый» заведомо не оправдаются.

Что меняется

Минимальная правка выглядит одинаково почти во всех языках: у клиента задаётся другой базовый адрес и другой ключ.

Всё остальное в коде остаётся: структура сообщений, роли, параметры генерации, разбор ответа, обработка потоковой передачи. Именно поэтому совместимый интерфейс и стал стандартом де-факто — он позволяет менять поставщика, не переписывая приложение.

Проверить, что перенос состоялся, проще всего одним запросом к списку моделей: если он вернул каталог, адрес и ключ настроены верно.

Что ломается чаще всего

Четыре вещи, отсортированные по частоте.

Идентификаторы моделей. Публичные названия у разных поставщиков свои. Строка gpt-4o из старого кода в другом каталоге может отсутствовать или называться иначе. Это самая частая причина ошибки model_not_found сразу после переезда.

Неподдерживаемые параметры. Некоторые модели отклоняют параметры, которые у другого вендора работали. Показательный случай: у Claude начиная с Opus 4.7 передача temperature, top_p или top_k с недефолтным значением возвращает ошибку. Код, который всегда подставлял temperature, сломается не на всех моделях, а только на части — и это хуже, чем если бы он ломался везде.

Предположения о формате ответа. Если ваш парсер рассчитывает, что модель всегда возвращает JSON без обрамления или всегда начинает со списка, то на другой модели он споткнётся. Формально совместимость соблюдена — меняется поведение модели, а не контракт API.

Специфичные для вендора возможности. Всё, чего нет в общем интерфейсе, переносу не подлежит по определению.

Чего в совместимости не бывает

Важное разграничение, экономящее время на выяснении.

Совместимость — это про форму запроса, а не про поведение модели. Один и тот же запрос к разным моделям даст разные ответы, и это не дефект.

Не все маршруты открыты у всех моделей. В нашем каталоге у каждой позиции есть поле protocols, и chat_completions может быть открыт, а другой протокол — нет. Читать его нужно перед запросом; разбор всех кодов отказа — в материале об отказах маршрута.

Кэш промптов не переносится. У нас prompt_cache помечен unsupported у всех текстовых моделей. Если ваша экономика строилась на скидке за повторяющийся контекст, её нужно пересчитать заново.

Родной протокол вендора — отдельная история. Claude Code, например, ходит в /v1/messages, а не в совместимый маршрут. Мы разбирали это, когда протокол открылся у восьми моделей.

Порядок переноса

Шесть шагов, которые закрывают подавляющее большинство проблем.

  1. Соберите список моделей, которые вы реально вызываете. Обычно их меньше, чем кажется: две-три несут почти весь трафик.
  2. Найдите соответствия в новом каталоге. Не по названию на глаз, а по GET /v1/models. Заодно проверьте protocols.chat_completions у каждой.
  3. Вынесите идентификатор модели в конфигурацию. Если он зашит в вызов, сделайте это сейчас — пригодится не только при переносе.
  4. Отключите параметры, в которых не уверены. Проще добавить обратно то, что нужно, чем ловить отказы по одному.
  5. Прогоните набор реальных запросов до и после. Полсотни штук, сравнение глазами. Это единственный способ поймать тихое изменение поведения.
  6. Проверьте обработку ошибок. Коды отказов у нового поставщика свои, и ветка «повторить» не должна срабатывать там, где повтор бессмыслен.

Пятый пункт — тот, который пропускают чаще всего и о котором потом жалеют. Код, который запустился и не упал, ещё не означает, что он делает то же самое.

Как проверить перенос по-настоящему

Пятый шаг заслуживает развёртывания, потому что «прогнать запросы и сравнить» звучит очевидно, а делается почти никогда.

Соберите набор заранее. Не придумывайте примеры — возьмите настоящие запросы из логов за неделю. Пятьдесят штук достаточно, если они покрывают разные типы: короткие и длинные, простые и пограничные, с инструментами и без.

Сохраните ответы до переноса. Это база сравнения, и получить её после переезда уже нельзя.

Сравнивайте глазами, а не метрикой. На выборке в полсотни любая автоматическая оценка даёт шум, который легко принять за сигнал. Человек за полчаса увидит то, чего метрика не покажет: изменившийся тон, другую структуру, пропавшую деталь.

Смотрите на пограничные случаи отдельно. Обычные запросы почти наверняка отработают одинаково. Разница проявляется там, где модель раньше справлялась с трудом, — и именно эти случаи определяют, стоит ли переносить.

Проверьте потоковую передачу отдельно от обычной. Это разные пути в коде, и ломаются они независимо.

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

Частая ошибка: переносить всё сразу

Практическое замечание, которое экономит нервы.

Соблазн понятен: раз меняется одна строка, поменяем везде и сразу. Проблема в том, что если после переноса что-то пойдёт не так, вы не будете знать, что именно — изменился поставщик, модель, версия библиотеки или у вас просто был плохой день по трафику.

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

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

Как посчитать, что изменится в счёте

Отдельно про деньги, потому что ставки устроены по-разному.

У официальных вендоров выход обычно дороже входа в пять-шесть раз. В нашем каталоге ставки симметричны почти у всех позиций: вход и выход стоят одинаково. Это меняет арифметику в зависимости от профиля вашей нагрузки — на длинных ответах симметричная схема выгоднее, на огромном контексте с короткими ответами наоборот. Механику разбирали на единственной асимметричной позиции.

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

Третье: считать надо по фактическим токенам, а не по отправленным. В prompt_tokens попадает больше, чем вы послали, и величина различается между семействами на порядок — разбор служебного контекста.

Чего мы не утверждаем

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

Не утверждаем, что качество ответов сохранится. Совместимость касается интерфейса. Если вы меняете и поставщика, и модель, меняется и результат — это надо проверять.

Не все модели каталога открыты по совместимому маршруту. Статус читается из живой выдачи и меняется без предупреждения.

Ставки и состав каталога актуальны на дату публикации. Текущие значения — только из GET /v1/models.

Что держать в голове

Технически перенос — это адрес и ключ. Практически — это проверка трёх вещей: идентификаторов моделей, параметров, которые новая модель может не принять, и предположений вашего кода о формате ответа.

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

Как мы проверяем факты и почему у каждого стоит источник — на странице о проекте. Ключ для проверки переноса заводится за минуту: keydealer.ru/login.

Частые вопросы

Что нужно поменять в коде?

Базовый адрес и ключ. Официальные SDK OpenAI принимают base_url параметром, поэтому в большинстве проектов правка занимает одну строку.

Останутся ли работать библиотеки?

Да, если они используют стандартный OpenAI-совместимый интерфейс. Проблемы возникают у обёрток, которые обращаются к специфичным для вендора эндпоинтам.

Что чаще всего ломается при переносе?

Идентификаторы моделей, неподдерживаемые параметры и предположения о формате ответа, унаследованные от конкретной модели.

Как проверить перенос?

Прогнать набор реальных запросов до и после и сравнить ответы вручную. Автоматическая сверка на малой выборке вводит в заблуждение чаще, чем помогает.

Все ли модели каталога доступны по этому маршруту?

Нет. Проверять нужно поле protocols.chat_completions в ответе GET /v1/models: значение available означает, что запрос пройдёт.

Источники