API-вызовы (OpenAI-совместимый формат): запрос и ответ
При работе с LLM часто требуется знать верхнеуровневый формат «запрос-ответ», который обеспечивает полноценную коммуникацию между клиентом и LLM.
Единого официального стандарта нет: каждый вендор может использовать свои соглашения. Но для ~80% кейсов можно придерживаться OpenAI-совместимого формата.
Его используют локальные серверы и провайдеры:
- Ollama;
- vLLM;
- llama.cpp server;
- DeepSeek;
- Groq;
- Mistral AI;
- Anthropic через прокси/шлюз.
Важно: совместимость обычно частичная. Базовые
GET /v1/modelsиPOST /v1/chat/completionsвстречаются часто, а assistants, files, threads, vector stores — не везде.
Базовые правила
- Обычно используется HTTP + JSON.
- Основной рабочий метод —
POST. - Справочная информация —
GET. - Удаление объектов —
DELETE. - Путь обычно начинается с
/v1, например:
https://api.openai.com/v1
или локально:
http://localhost:11434/v1
- В ответах часто встречается поле
object. Оно показывает тип объекта:list,model,chat.completion,chat.completion.chunkи т.д. - Chat Completions API обычно stateless: сервер не хранит диалог автоматически. Историю нужно передавать в
messagesпри каждом запросе. - Если требуется авторизация:
Authorization: Bearer <API_KEY>
Content-Type: application/json
GET-запросы
GET используется для получения справочной информации.
Список всех моделей
GET /v1/models
Пример ответа:
{
"object": "list",
"data": [
{
"id": "qwen2.5:14b",
"object": "model",
"created": 1785339494,
"owned_by": "library"
},
{
"id": "hodza/cotype-nano-1.5-unofficial:latest",
"object": "model",
"created": 1772537295,
"owned_by": "hodza"
}
]
}
Информация по конкретной модели
GET /v1/models/qwen2.5:14b
Пример ответа:
{
"id": "qwen2.5:14b",
"object": "model",
"created": 1785339494,
"owned_by": "library"
}
Обратите внимание на ключ object. Это особенность формата: он указывает тип возвращаемого объекта.
Расширенные GET-endpoint'ы
Эти endpoint'ы относятся к расширенным возможностям, например Assistants API, и поддерживаются не всеми серверами:
GET /v1/assistants
GET /v1/threads/{thread_id}/messages
GET /v1/vector_stores
Например:
GET /v1/assistants— список созданных ИИ-агентов.GET /v1/threads/{thread_id}/messages— история сообщений из сохранённого потока.GET /v1/vector_stores— список векторных хранилищ для RAG.
DELETE-запросы
Так как API работает с объектами, некоторые из них можно удалить, если сервер это поддерживает.
Важно: удаление обычно необратимо.
Удаление модели
Если сервер поддерживает удаление моделей:
DELETE /v1/models/qwen2.5:14b
Пример ответа:
{
"id": "qwen2.5:14b",
"object": "model",
"deleted": true
}
В OpenAI удаление моделей обычно доступно только для отдельных owned/fine-tuned моделей. В локальных серверах поддержка зависит от реализации.
Удаление файлов
Файлы удаляются отдельно:
DELETE /v1/files/{file_id}
Пример:
DELETE /v1/files/file-abc123
Пример ответа:
{
"id": "file-abc123",
"object": "file",
"deleted": true
}
Не путайте файлы и модели: модель не удаляется через /v1/files/....
Удаление расширенных объектов
Если сервер поддерживает Assistants/Threads/Vector Stores:
DELETE /v1/assistants/{assistant_id}
DELETE /v1/threads/{thread_id}
DELETE /v1/vector_stores/{vector_store_id}
Назначение:
- удаление созданного ИИ-агента;
- удаление сохранённого потока сообщений;
- удаление векторного хранилища с документами.
Это важно в том числе для конфиденциальности пользователей.
POST-запросы
Основной рабочий метод — POST.
Главный endpoint для чата:
POST /v1/chat/completions
Основные поля запроса
| Поле | Тип | Назначение |
|---|---|---|
model |
string | имя модели |
messages |
array | диалог/контекст |
temperature |
float | случайность генерации |
stream |
boolean | потоковый режим ответа |
tools |
array | доступные инструменты |
tool_choice |
string/object | режим использования инструментов |
max_tokens |
integer | лимит токенов ответа |
Минимальный запрос:
{
"model": "qwen2.5:14b",
"messages": [
{
"role": "user",
"content": "Привет"
}
]
}
Поле stream
"stream": true
Если stream: true, сервер будет отправлять ответ частями, по мере генерации, а не одним целым сообщением.
Если stream: false или поле не указано, обычно возвращается один полный JSON-ответ.
Поле tools
tools — список инструментов, которые модель может предложить вызвать.
Пример:
{
"tools": [
{
"type": "function",
"function": {
"name": "bash",
"description": "Execute a shell command and return the output.",
"parameters": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The bash command to execute."
}
},
"required": [
"command"
]
}
}
}
]
}
Здесь передаются:
- имя функции;
- описание;
- входные параметры.
Поле tool_choice
tool_choice задаёт режим работы с инструментами.
Примеры:
"tool_choice": "auto"
Модель сама решает, что делать. Если для ответа нужны инструменты — генерирует вызов. Если нет — отвечает текстом.
"tool_choice": "none"
Модель игнорирует инструменты и обязана ответить обычным текстом.
"tool_choice": "required"
Модель обязана вызвать хотя бы один инструмент из списка tools. Конкретную функцию она выбирает сама на основе контекста.
Поле max_tokens
"max_tokens": 4096
Это максимальное количество токенов, которое мы ожидаем от модели в ответе.
Важно:
max_tokensограничивает ответ модели;- prompt-токены обычно считаются отдельно;
- суммарный объём prompt + completion должен укладываться в контекстное окно модели;
- в некоторых API вместо или вместе с
max_tokensможет использоватьсяmax_completion_tokens.
Поле messages
messages — основной блок запроса. Это массив сообщений, который формирует контекст для модели.
Пример:
{
"messages": [
{
"role": "system",
"content": "You are a coding agent. Your job is to help the user with programming tasks.\n\nYou have access to ONE tool: `bash` — which executes shell commands and returns stdout/stderr.\n\nWorkflow:\n1. Plan what needs to be done.\n2. Use `bash` to read files, run commands, write code, etc.\n3. After gathering enough information or completing the task, give your final answer in natural language.\n4. To finish, reply with a regular message (no tool call).\n\nBe concise. Explain what you're doing before each command."
},
{
"role": "user",
"content": "Посчитай общее количество файлов в директории code-rag"
},
{
"role": "assistant",
"content": "Для подсчета общего количества файлов в директории `code-rag`, я выполню команду `find` с флагом `-type f`, чтобы найти только файлы, и посчитаю их количество.",
"tool_calls": [
{
"id": "call_a5w0a9jy",
"index": 0,
"type": "function",
"function": {
"name": "bash",
"arguments": "{\"command\":\"find code-rag -type f | wc -l\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_a5w0a9jy",
"content": "Exit code: 0\n442\n"
}
]
}
Роли сообщений
| Роль | Назначение |
|---|---|
system |
инструкции для модели |
user |
сообщение пользователя |
assistant |
ответ модели |
tool |
результат выполнения инструмента |
Как работает вызов инструмента
В примере модель предложила вызвать инструмент bash:
"tool_calls": [
{
"id": "call_a5w0a9jy",
"index": 0,
"type": "function",
"function": {
"name": "bash",
"arguments": "{\"command\":\"find code-rag -type f | wc -l\"}"
}
}
]
Здесь:
id— уникальный идентификатор вызова;index— порядковый номер вызова внутри ответа;function.name— имя функции;function.arguments— аргументы в виде JSON-строки.
Поле id, например call_a5w0a9jy, нужно, чтобы связать вызов инструмента с его результатом.
Ключ index можно считать счётчиком или порядковым номером вызова внутри конкретного ответа.
Если модель за раз хочет узнать погоду и курс доллара, она может вернуть массив из двух элементов:
- у первой функции, например погоды,
indexбудет0; - у второй функции, например курса валюты,
indexбудет1.
Важно: модель не выполняет функцию сама. Она только возвращает предложение вызвать функцию. Выполняет функцию ваш клиент, агент или runtime. Затем результат нужно отправить обратно в
messagesс рольюtool.
Для корректной работы tool calling функция bash должна быть доступна на стороне клиента и должна быть описана в tools с соответствующими входными параметрами.
Также важно валидировать аргументы и не выполнять небезопасные команды автоматически.
Ответ модели
Структура ответа содержит похожие логические блоки и требует понимания нюансов работы LLM.
Основные поля ответа
| Поле | Назначение |
|---|---|
id |
уникальный идентификатор ответа |
object |
тип ответа |
created |
Unix Timestamp |
model |
имя модели |
system_fingerprint |
идентификатор конфигурации/окружения |
choices |
список вариантов ответа |
usage |
информация о токенах |
Пример:
"object": "chat.completion"
Это означает ответ в режиме чата.
created
Содержит временную метку в формате Unix Timestamp, также известную как POSIX time.
system_fingerprint
Уникальный идентификатор, который показывает, какая конфигурация сервера и инференса использовалась при генерации ответа.
Нужен, чтобы понимать, не изменились ли:
- серверное оборудование;
- конфигурация;
- параметры инференса;
- окружение модели.
Поле может отсутствовать или быть null в некоторых реализациях.
Поле choices
choices — список вариантов ответа.
Некоторые LLM могут генерировать несколько вариантов ответа, если запросить это параметром n.
Чаще всего для экономии токенов и повышения производительности используется один вариант ответа:
choices[0]
Пример:
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Для подсчета общего количества файлов в директории `code-rag`, я выполню команду `find` с флагом `-type f`, чтобы найти только файлы, и посчитаю их количество.\n\n",
"tool_calls": [
{
"id": "call_a5w0a9jy",
"index": 0,
"type": "function",
"function": {
"name": "bash",
"arguments": "{\"command\":\"find code-rag -type f | wc -l\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
Если вариант ответа один, index обычно равен 0, так как нумерация начинается с нуля.
Поле message похоже на элемент messages из запроса, но в ответе рядом с ним также находится finish_reason.
Поле finish_reason
finish_reason показывает, почему модель остановила генерацию.
Основные значения:
stop
Успешное завершение.
Модель закончила мысль, ответила на вопрос или встретила специальный токен конца текста, например <|endoftext|>.
tool_calls
Модель поняла, что для продолжения нужны внешние данные или действие.
Она остановилась и сформировала JSON в массиве tool_calls.
length
Превышен лимит токенов.
Ответ был принудительно оборван, потому что был достигнут лимит:
max_tokens;max_completion_tokens;- общий лимит контекста или серверные ограничения.
content_filter
Сработал фильтр контента.
Генерация была заблокирована системой безопасности провайдера, например из-за политик по токсичному контенту, оружию, персональным данным и т.п.
Значение может зависеть от провайдера.
null
Статус может встречаться при стриминге в промежуточных чанках.
Он означает, что модель ещё генерирует ответ. В последнем чанке статус изменится, например на stop или tool_calls.
Поле usage
usage показывает расход токенов.
Пример:
"usage": {
"prompt_tokens": 272,
"completion_tokens": 82,
"total_tokens": 354
}
Где:
prompt_tokens— объём данных, отправленных на сервер;completion_tokens— объём данных, сгенерированных моделью;total_tokens— сумма двух предыдущих.
Это нужно для мониторинга лимитов, стоимости и производительности.
Streaming
Если в запросе передано:
"stream": true
сервер переключает заголовок ответа в:
Content-Type: text/event-stream
Вместо одного большого JSON сервер отправляет данные порциями, чанками, по мере генерации.
Пример:
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"TCP"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":" — надежный"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
В стриминге обычно используется тип:
"object": "chat.completion.chunk"
Вместо полного message может приходить delta, например:
"delta": {
"content": "TCP"
}
Клиент должен накапливать эти части и собирать финальный ответ.
Ошибки и надёжность
При реальной интеграции обязательно обрабатывайте ошибки.
Пример ошибки:
{
"error": {
"message": "Invalid value for 'messages'",
"type": "invalid_request_error",
"param": "messages",
"code": null
}
}
Типовые HTTP-коды:
| Код | Значение |
|---|---|
400 |
некорректный запрос |
401 |
отсутствует или неверный API-ключ |
403 |
нет доступа |
404 |
модель или объект не найдены |
429 |
rate limit |
500 |
ошибка сервера |
503 |
сервис временно недоступен |
Практические рекомендации:
- устанавливайте timeout;
- используйте retry для 429 и 5xx;
- применяйте exponential backoff;
- обрабатывайте обрыв стриминга;
- не выполняйте инструменты без проверки;
- проверяйте
finish_reason; - логируйте
usageдля контроля лимитов.
Краткий итог
- Единого официального стандарта API для LLM нет, но часто используют OpenAI-совместимый формат.
- Данные обычно передаются в JSON; при стриминге используется SSE.
- Основной рабочий endpoint:
POST /v1/chat/completions
- LLM ожидает ключ
messagesс полямиroleиcontent. - Роли:
system,user,assistant,tool. - Инструменты описываются в
tools. - Модель не выполняет инструменты сама: она только возвращает
tool_calls, а выполняет их клиент. - Ответ содержит
choices,message,finish_reasonиusage. finish_reasonпоказывает причину завершения генерации.- При
stream: trueответ приходит частями в виде чанков. - Расширенные объекты — files, assistants, threads, vector stores — поддерживаются не везде и должны рассматриваться отдельно.
Оставить отзыв
Комментарии
Загрузка комментариев...
★ Оставить отзыв