запрос к модели идёт одним из двух путей. если формат запроса совпадает с форматом, на котором говорит конечный сервис, тело запроса передаётся ему без изменений. если не совпадает, включается перевод формата: по пути к сервису запрос приводится к нужному виду, а ответ переводится обратно.
рабочие оба. airakurs следит за актуальностью перевода и старается сохранить в нём все возможности модели. но перевод — всё же промежуточный слой, и там, где важна каждая деталь запроса, надёжнее обойтись без него.
как проходит перевод формата
разберём на запросе к anthropic/claude-sonnet-4.5 из библиотеки openai.
шаг 1. пришло на /v1/chat/completions в формате openai
{
"model": "anthropic/claude-sonnet-4.5",
"messages": [
{"role": "system", "content": "Отвечай коротко."},
{"role": "user", "content": "Назови столицу Австралии."}
],
"max_tokens": 100,
"temperature": 0.2
}
шаг 2. переведено в формат anthropic messages и отправлено в anthropic
{
"model": "claude-sonnet-4.5",
"system": [
{"type": "text", "text": "Отвечай коротко."}
],
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "Назови столицу Австралии."}
]
}
],
"max_tokens": 100,
"temperature": 0.2
}
изменились не только имена полей. инструкция, которая была сообщением с ролью system, стала отдельным полем system. строка в content стала списком блоков. идентификатор модели потерял приставку вендора: конечный сервис знает эту модель под именем claude-sonnet-4.5. а temperature и max_tokens совпали в обоих форматах и поехали как есть.
шаг 3. anthropic ответил в своём формате
{
"id": "msg_01abc",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-4.5",
"content": [
{"type": "text", "text": "Канберра."}
],
"stop_reason": "end_turn",
"usage": {"input_tokens": 18, "output_tokens": 4}
}
шаг 4. переведено обратно и отдано клиенту в формате openai
{
"id": "chatcmpl-6b9cf7a4-427d-404a-9c90-031b2ff36bc3",
"object": "chat.completion",
"model": "claude-sonnet-4.5",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Канберра."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 18,
"completion_tokens": 4,
"total_tokens": 22
}
}
текст оказался там, где его ждёт библиотека openai — в choices[0].message.content. stop_reason: "end_turn" стал finish_reason: "stop", а input_tokens и output_tokens — prompt_tokens и completion_tokens.
код при этом отправил и получил формат openai — о двух переводах по пути он не знает.
если формат для модели родной, шагов 2 и 4 нет: тело уходит сервису таким, каким пришло, и возвращается таким, каким сервис его сформировал.
что переводится, а что нет
| формат запроса | модель | что происходит |
|---|---|---|
openai, /v1/chat/completions | openai/gpt-5-mini | без перевода |
anthropic, /v1/messages | anthropic/claude-sonnet-4.5 | без перевода |
gemini, /v1beta/…:generateContent | google/gemini-3.5-flash | без перевода |
openai, /v1/chat/completions | anthropic/claude-sonnet-4.5 | перевод в формат anthropic messages |
anthropic, /v1/messages | google/gemini-3.5-flash | перевод в формат gemini |
правило простое: формат openai родной для моделей openai, формат anthropic messages — для моделей anthropic, формат gemini — для моделей google. формат responses на /v1/responses — тоже формат openai.
модели остальных вендоров каталога — xai и другие — обслуживает сервис, говорящий на формате openai, поэтому для них родные /v1/chat/completions и /v1/responses.
что даёт родной формат
- официальная библиотека вендора. клиент anthropic на
/v1/messages, клиент google на/v1beta— со своими типами и своими методами. - документация вендора применима буквально. передать можно любое поле из его описания, даже если в формате openai аналога нет.
- заголовки вендора работают. на
/v1/messagesпередаётсяanthropic-beta, которым включаются бета-возможности anthropic. - новые возможности доступны сразу. поле, которое вендор добавил только что, работает, не дожидаясь поддержки в переводе формата.
например, у anthropic бюджет на размышления задаётся точным числом токенов. в формате openai такого поля нет — там задаётся только уровень. на /v1/messages число передаётся как есть:
curl "https://api.airakurs.ru/v1/messages" \
-H "Content-Type: application/json" \
-H "X-API-Key: <КЛЮЧ>" \
-d '{
"model": "anthropic/claude-sonnet-4.5",
"max_tokens": 8192,
"thinking": {"type": "enabled", "budget_tokens": 4096},
"messages": [
{"role": "user", "content": "Сколько нулей в конце числа 100!?"}
]
}'
что теряется при переводе
перевод покрывает основное: сообщения и роли, параметры генерации, вызов функций, структурированный вывод. теряется то, чему в целевом формате нет соответствия.
- точность параметра. бюджет на размышления у anthropic задаётся числом токенов, а в формате openai — уровнем:
low,mediumилиhigh. при переводе одно заменяется другим. - поля ответа без аналога. подпись блоков размышлений у anthropic, метаданные поиска у google — в схеме openai для них не предусмотрено места. они возвращаются дополнительными полями рядом со стандартными, и официальная библиотека openai о них не знает.
что выбрать
формат openai — когда важно, чтобы один и тот же код обращался к любой модели: прототип, сравнение моделей, замена одной на другую без правок.
родной формат модели — когда задача требует возможностей конкретного сервиса: длинные диалоги с рассуждениями, точная разметка кэша, бета-возможности.
что дальше
частые вопросы
как понять, переводился ли запрос?
по таблице выше: всё определяют формат запроса и вендор модели. отдельного признака в ответе нет.
формат openai работает хуже родного?
для обычной генерации текста, вызова функций и структурированного вывода разницы нет. она появляется там, где нужны поля, которых в схеме openai не существует.
можно ли передать поле, которого нет в формате openai?
в родном формате — да, любое поле из документации вендора. при переводе формата рассчитывать на это не стоит: конечный сервис ответит ошибкой 400 на неизвестное ему поле.
стоит ли переписывать работающий код под родной формат?
нет, если задача решается. смысл в переходе появляется тогда, когда нужна конкретная возможность, недоступная через формат openai.