Skip to content

Tool Calling (вызов функций)

Tool Calling (также называемый Function Calling) позволяет моделям выходить за рамки генерации текста, возвращая структурированный «запрос вызова функции» при необходимости. Ваша программа выполняет реальную логику (проверить погоду, запросить базу данных, оформить заказ), возвращает результат модели, и модель генерирует окончательный ответ на его основе. RouteAPI поддерживает Tool Calling как на /v1/chat/completions (формат OpenAI), так и на /v1/messages (формат Claude).

Tool Calling — это многораундовый обмен:

  1. Вы объявляете набор «инструментов» в запросе (каждый инструмент имеет имя, описание и схему параметров).
  2. Модель определяет, нужен ли инструмент; если да, она возвращает запрос вызова с параметрами вместо прямого ответа.
  3. Ваш код разбирает параметры, выполняет реальную логику и получает результат.
  4. Вы передаёте результат обратно модели, которая генерирует ответ на естественном языке для пользователя.

Сама модель не выполняет никакой код; она только решает «какой инструмент вызвать» и «какие параметры передать». Фактическое выполнение всегда происходит на вашей стороне, что означает, что границы безопасности (проверки прав доступа, фильтрация параметров) находятся под вашим контролем.

СценарийОписание
Запросы данных в реальном времениПогода, обменные курсы, котировки акций, запасы — информация, отсутствующая в обучающих данных модели
Интеграция внутренних системЗапросы к базам данных, вызовы внутренних API, чтение статуса заказов
Выполнение действийОформление заказов, отправка электронных писем, создание тикетов — операции с побочными эффектами
Структурированное извлечениеПринудить модель выводить данные согласно фиксированной схеме, эквивалентно методу структурированного вывода
Оркестровка агентовФреймворки агентов используют Tool Calling для управления многошаговыми задачами

Способность Tool Calling зависит от выбранной модели. Большинство основных моделей (серии OpenAI GPT, Claude, Gemini и др.) поддерживают её, но детали, такие как максимальное количество инструментов и поддержка параллельных вызовов, различаются. Конечная точка списка моделей не возвращает поле с признаком поддержки Tool Calling: сверяйтесь с документацией поставщика модели либо отправьте целевой модели один реальный запрос с tools для проверки, подробнее см. Models.

2. Формат определения инструмента (OpenAI)

Section titled “2. Формат определения инструмента (OpenAI)”

В /v1/chat/completions инструменты объявляются через массив верхнего уровня tools, где каждый инструмент является вложенным объектом с type: "function".

{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Запросить текущую погоду для указанного города. Использовать полное название города на китайском языке.",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "Название города, например, Пекин" },
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Единица температуры, по умолчанию градусы Цельсия"
}
},
"required": ["city"]
}
}
}
]
}
ПолеТипОбязательноОписание
typestringДаФиксированное значение "function"
function.namestringДаИмя инструмента, может содержать только буквы, цифры, подчёркивания и дефисы
function.descriptionstringРекомендуетсяОписание назначения инструмента, модель использует это для принятия решения о вызове
function.parametersobjectНетОпределение параметров, стандартная JSON Schema

Качество description напрямую определяет, правильно ли модель выберет инструмент. Чётко описать назначение, значение каждого параметра, диапазон значений и граничные условия эффективнее, чем настройка любых параметров сэмплирования.

parameters использует стандартную JSON Schema для описания структуры параметров:

  • type: Обычно "object".
  • properties: Тип, описание, перечисляемые значения для каждого параметра.
  • required: Список обязательных имён параметров.
  • enum: Ограничить диапазон значений, значительно снижает вероятность того, что модель передаст неверные значения.

Инструменты без параметров всё равно должны предоставить пустую схему: "parameters": { "type": "object", "properties": {} }.

3. Формат определения инструмента (Claude)

Section titled “3. Формат определения инструмента (Claude)”

В /v1/messages определения инструментов имеют плоскую структуру без внешних оболочек type и function, а поле параметров называется input_schema.

{
"tools": [
{
"name": "get_weather",
"description": "Запросить текущую погоду для указанного города. Использовать полное название города на китайском языке.",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "Название города, например, Пекин" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
]
}

Внутренняя структура input_schema идентична parameters OpenAI, обе являются стандартной JSON Schema. Различие только во внешней оболочке.

Сравнение двух форматов

Section titled “Сравнение двух форматов”
ЭлементOpenAI (/v1/chat/completions)Claude (/v1/messages)
Внешняя оболочка{ "type": "function", "function": {...} }Прямая плоская структура, без оболочки
Поле имени инструментаfunction.namename
Поле описанияfunction.descriptiondescription
Поле параметраfunction.parametersinput_schema
Схема параметраСтандартная JSON SchemaСтандартная JSON Schema (идентично)
Возврат моделиМассив message.tool_callsБлок tool_use в content
Роль передачи результатаНезависимое сообщение role: "tool"Блок tool_result в сообщении user
Поле ассоциации результатаtool_call_idtool_use_id

Полные детали протокола Claude (включая is_error, потоковый input_json_delta и т. д.) см. в Протокол Claude Messages. Последующие примеры на этой странице по умолчанию используют формат OpenAI.

tool_choice контролирует, как модель выбирает инструменты.

ЗначениеПоведение
"auto"Модель сама решает, вызывать ли инструмент и какой. Значение по умолчанию при наличии tools
"none"Запретить вызов любого инструмента, модель выводит только текст
"required"Должен вызвать хотя бы один инструмент, но модель выбирает какой
{ "type": "function", "function": { "name": "get_weather" } }Принудительно вызвать указанный инструмент
{ "tool_choice": "auto" }

Наиболее распространённый. Подходит для сценариев, где «вопросы пользователя иногда требуют инструментов, иногда прямых ответов».

{ "tool_choice": "none" }

Временно отключить инструменты, сохраняя определения. Часто используется на заключительном этапе «пусть модель подведёт итог, не вызывать инструменты снова».

{ "tool_choice": "required" }

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

Указать конкретный инструмент

Section titled “Указать конкретный инструмент”
{
"tool_choice": {
"type": "function",
"function": { "name": "get_weather" }
}
}

Эквивалент в формате Claude: { "type": "tool", "name": "get_weather" }, required соответствует { "type": "any" }, none соответствует { "type": "none" }.

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

Первый раунд: модель возвращает tool_calls

Section titled “Первый раунд: модель возвращает tool_calls”

Отправить запрос с tools:

Terminal window
curl https://api.routeapi.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "Какая сейчас погода в Пекине?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Запросить текущую погоду для указанного города.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
]
}'

Когда модель определяет, что нужен инструмент, finish_reason будет tool_calls, message.content будет null, а tool_calls содержит запрос вызова:

{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"北京\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}

Обратите внимание, что arguments — это JSON строка, а не объект; вам нужно самостоятельно разобрать её с помощью JSON.parse / json.loads. Модель может иногда генерировать недействительный JSON, поэтому разбор должен быть в try/catch.

Отправить по function.name к вашей реальной логике, выполнить с разобранными параметрами:

import json
args = json.loads(tool_call["function"]["arguments"])
result = get_weather(**args) # "Пекин, ясно, 23 градуса"

Второй раунд: передать результат инструмента

Section titled “Второй раунд: передать результат инструмента”

Добавить сообщение assistant первого раунда (с tool_calls) обратно в messages как есть, затем добавить сообщение role: "tool", несущее результат. tool_call_id должен точно совпадать с id из первого раунда:

{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "Какая сейчас погода в Пекине?" },
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\": \"北京\"}" }
}
]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "Пекин, ясно, температура 23 градуса Цельсия, влажность 45%."
}
],
"tools": []
}

Модель генерирует окончательный ответ

Section titled “Модель генерирует окончательный ответ”

Запрос второго раунда возвращает результат на естественном языке, finish_reason возвращается к stop:

{
"choices": [
{
"message": {
"role": "assistant",
"content": "В Пекине сейчас ясно, температура 23 градуса Цельсия, влажность 45%, довольно комфортно."
},
"finish_reason": "stop"
}
]
}

6. Многораундовый Tool Calling

Section titled “6. Многораундовый Tool Calling”

Последовательные вызовы нескольких инструментов

Section titled “Последовательные вызовы нескольких инструментов”

Модели может потребоваться несколько раундов вызовов инструментов для выполнения задачи: сначала проверить номер заказа, затем использовать номер заказа для проверки логистики. Каждый раунд следует циклу «модель возвращает tool_calls → выполнить → передать обратно результат», пока finish_reason не вернётся к stop. Производственный код должен быть написан в виде цикла с установленным максимальным лимитом раундов для предотвращения бесконечных циклов:

MAX_TURNS = 5
for _ in range(MAX_TURNS):
resp = call_model(messages)
msg = resp["choices"][0]["message"]
messages.append(msg)
if not msg.get("tool_calls"):
break # Модель даёт окончательный ответ
for tc in msg["tool_calls"]:
result = dispatch(tc) # Выполнить и вернуть строку
messages.append({
"role": "tool",
"tool_call_id": tc["id"],
"content": result,
})

Параллельные вызовы инструментов

Section titled “Параллельные вызовы инструментов”

В одном раунде модель может одновременно запросить несколько независимых инструментов (например, проверить погоду в Пекине и Шанхае одновременно), в результате чего tool_calls будет многоэлементным массивом. Вам нужно добавить соответствующее сообщение role: "tool" для каждого tool_call, с tool_call_id, выровненным один к одному; пропуск одного вызовет ошибку в следующем раунде.

Чтобы отключить параллелизм и заставить модель вызывать только один инструмент за раз, добавьте "parallel_tool_calls": false в формате OpenAI или добавьте "disable_parallel_tool_use": true в tool_choice для формата Claude.

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

tool_calls в потоковом выводе

Section titled “tool_calls в потоковом выводе”

В delta.tool_calls каждого SSE chunk содержится index (идентифицирует, какой вызов инструмента), а function.arguments — это фрагмент JSON параметров:

data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_abc123","type":"function","function":{"name":"get_weather","arguments":""}}]}}]}
data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]}}]}
data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"北京\"}"}}]}}]}
data: {"choices":[{"finish_reason":"tool_calls"}]}
data: [DONE]

Как обрабатывать инкрементный delta

Section titled “Как обрабатывать инкрементный delta”

Накапливать по группам index: id и name обычно появляются только в первом фрагменте, arguments нужно конкатенировать все фрагменты перед разбором. Не пытайтесь разбирать промежуточные состояния во время конкатенации (это неполный JSON).

buffers = {} # index -> {"id", "name", "args"}
for chunk in stream:
for tc in chunk["choices"][0]["delta"].get("tool_calls", []):
i = tc["index"]
buf = buffers.setdefault(i, {"id": "", "name": "", "args": ""})
if tc.get("id"):
buf["id"] = tc["id"]
fn = tc.get("function", {})
if fn.get("name"):
buf["name"] = fn["name"]
if fn.get("arguments"):
buf["args"] += fn["arguments"]
# Разобрать после завершения потока
for buf in buffers.values():
args = json.loads(buf["args"])

Потоковые параметры инструмента в формате Claude возвращаются инкрементально через partial_json события input_json_delta, накапливаются по index, затем разбираются; детали в Протокол Claude Messages.

8. Статус поддержки моделей

Section titled “8. Статус поддержки моделей”

Модели, поддерживающие Tool Calling

Section titled “Модели, поддерживающие Tool Calling”

Большинство основных моделей, агрегированных RouteAPI, поддерживают Tool Calling, включая серии OpenAI GPT, Claude, Gemini и др. Сначала убедитесь через конечную точку списка моделей, что модель доступна в вашем аккаунте:

Terminal window
curl https://api.routeapi.ai/v1/models \
-H "Authorization: Bearer $ROUTEAPI_KEY"

Эта конечная точка возвращает только доступность модели и доступные эндпоинты, но не способность Tool Calling. Наличие поддержки Tool Calling определяйте по документации поставщика либо по одному реальному запросу с tools.

Разные модели различаются в следующих измерениях; проверьте в тестовой среде перед переходом в производство:

ИзмерениеОписание
Максимальное количество инструментовВерхний лимит инструментов, которые можно объявить в одном запросе, зависит от модели
Параллельные вызовыНекоторые модели не поддерживают возврат нескольких tool_calls в одном раунде
Поддержка tool_choiceНе все модели поддерживают required / указание конкретных инструментов
Сложность параметровГлубоко вложенные или очень большие JSON Schema могут быть обрезаны или проигнорированы некоторыми моделями
Гранулярность потокового инкрементаСпособ фрагментации arguments непоследователен между моделями; необходимо накапливать по index

Пример инструмента запроса погоды (от начала до конца)

Section titled “Пример инструмента запроса погоды (от начала до конца)”

Следующие три фрагмента кода функционально эквивалентны: определить инструмент → первый раунд получить tool_calls → выполнить → второй раунд передать обратно результат → получить окончательный ответ.

curl (вручную завершить два раунда):

Terminal window
# Первый раунд: отправить запрос с инструментами
curl https://api.routeapi.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [{ "role": "user", "content": "Какая сейчас погода в Пекине?" }],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Запросить текущую погоду для указанного города.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}]
}'
# Второй раунд: передать обратно результат инструмента (tool_call_id использует значение из первого раунда)
curl https://api.routeapi.ai/v1/chat/completions \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "Какая сейчас погода в Пекине?" },
{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\": \"北京\"}" }
}]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "Пекин, ясно, температура 23 градуса Цельсия, влажность 45%."
}
]
}'

Python (SDK OpenAI, автоматически завершает два раунда):

import json
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["ROUTEAPI_KEY"],
base_url="https://api.routeapi.ai/v1",
)
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Запросить текущую погоду для указанного города. Использовать полное название города на китайском языке.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "Название города, например, Пекин"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
},
},
}
]
def get_weather(city: str, unit: str = "celsius") -> str:
# Заменить на реальный вызов сервиса погоды
return f"{city}, ясно, температура 23 градуса Цельсия, влажность 45%."
messages = [{"role": "user", "content": "Какая сейчас погода в Пекине?"}]
# Первый раунд
resp = client.chat.completions.create(
model="gpt-5.5", messages=messages, tools=tools
)
msg = resp.choices[0].message
if msg.tool_calls:
# Сообщение assistant должно быть добавлено обратно как есть, иначе tool_call_id не может выровняться
messages.append(msg)
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
result = get_weather(**args)
messages.append(
{"role": "tool", "tool_call_id": tc.id, "content": result}
)
# Второй раунд
resp = client.chat.completions.create(
model="gpt-5.5", messages=messages, tools=tools
)
print(resp.choices[0].message.content)

Node.js (пакет openai, автоматически завершает два раунда):

import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.ROUTEAPI_KEY,
baseURL: 'https://api.routeapi.ai/v1',
});
const tools = [
{
type: 'function',
function: {
name: 'get_weather',
description: 'Запросить текущую погоду для указанного города. Использовать полное название города на китайском языке.',
parameters: {
type: 'object',
properties: {
city: { type: 'string', description: 'Название города, например, Пекин' },
unit: { type: 'string', enum: ['celsius', 'fahrenheit'] },
},
required: ['city'],
},
},
},
];
function getWeather(city, unit = 'celsius') {
// Заменить на реальный вызов сервиса погоды
return `${city}, ясно, температура 23 градуса Цельсия, влажность 45%.`;
}
const messages = [{ role: 'user', content: 'Какая сейчас погода в Пекине?' }];
// Первый раунд
let resp = await client.chat.completions.create({
model: 'gpt-5.5',
messages,
tools,
});
const msg = resp.choices[0].message;
if (msg.tool_calls) {
messages.push(msg); // Добавить сообщение assistant обратно как есть
for (const tc of msg.tool_calls) {
const args = JSON.parse(tc.function.arguments);
const result = getWeather(args.city, args.unit);
messages.push({ role: 'tool', tool_call_id: tc.id, content: result });
}
// Второй раунд
resp = await client.chat.completions.create({
model: 'gpt-5.5',
messages,
tools,
});
}
console.log(resp.choices[0].message.content);

Пример инструмента запроса к базе данных

Section titled “Пример инструмента запроса к базе данных”

Используйте инструменты как безопасные обёртки для внутренних систем. Ключевое: SQL не должен генерироваться моделью напрямую; вместо этого модель выбирает параметры, а ваш код создаёт параметризованные запросы, чтобы избежать инъекции.

tools = [
{
"type": "function",
"function": {
"name": "query_order",
"description": "Запросить статус заказа и сумму по номеру заказа.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "Номер заказа, например ORD-20260917-001",
}
},
"required": ["order_id"],
},
},
}
]
def query_order(order_id: str) -> str:
# Использовать параметризованный запрос, никогда не объединять вывод модели напрямую в SQL
row = db.execute(
"SELECT status, amount FROM orders WHERE order_id = %s",
(order_id,),
).fetchone()
if row is None:
return json.dumps({"found": False})
return json.dumps({"found": True, "status": row[0], "amount": row[1]})

Результаты инструментов рекомендуется передавать обратно в виде JSON-строк; модели могут более надёжно разбирать структурированные поля.

Пример оркестровки нескольких инструментов

Section titled “Пример оркестровки нескольких инструментов”

Объявить несколько инструментов одновременно; модель выбирает по мере необходимости или даже вызывает параллельно. Здесь используются два инструмента: погода + обменный курс:

tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Запросить текущую погоду для указанного города.",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
},
{
"type": "function",
"function": {
"name": "get_exchange_rate",
"description": "Запросить обменный курс между двумя валютами.",
"parameters": {
"type": "object",
"properties": {
"from_currency": {"type": "string", "description": "Например USD"},
"to_currency": {"type": "string", "description": "Например CNY"},
},
"required": ["from_currency", "to_currency"],
},
},
},
]
dispatch = {
"get_weather": lambda city: f"{city}, ясно, 23 градуса.",
"get_exchange_rate": lambda from_currency, to_currency: f"1 {from_currency} = 7.2 {to_currency}",
}
messages = [{"role": "user", "content": "Какая погода в Пекине? Также скажите мне обменный курс USD к CNY."}]
# Цикл для обработки многораундовых / параллельных вызовов инструментов, установить лимит для предотвращения бесконечных циклов
for _ in range(5):
resp = client.chat.completions.create(
model="gpt-5.5", messages=messages, tools=tools
)
msg = resp.choices[0].message
messages.append(msg)
if not msg.tool_calls:
break
# Во время параллельных вызовов tool_calls — многоэлементный массив; каждый должен быть передан обратно
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
result = dispatch[tc.function.name](**args)
messages.append(
{"role": "tool", "tool_call_id": tc.id, "content": result}
)
print(messages[-1]["content"])

Tool Calling вводит три потенциальные точки ошибки: со стороны модели, со стороны вашего кода и со стороны восходящего сервиса.

Сбой проверки параметров инструмента

Section titled “Сбой проверки параметров инструмента”

Модель может вернуть недействительный JSON, или пропустить обязательные параметры, или передать значения вне диапазона enum. Обязательно:

  • Обернуть JSON.parse / json.loads в try/catch.
  • После разбора проверить обязательные поля и диапазоны значений согласно схеме.
  • Когда проверка не удалась, передать сообщение об ошибке обратно как результат инструмента, чтобы позволить модели самокорректироваться, вместо того чтобы напрямую выбрасывать исключение для завершения разговора:
try:
args = json.loads(tc.function.arguments)
city = args["city"] # Проверить обязательное поле
except (json.JSONDecodeError, KeyError) as exc:
result = f"Сбой разбора параметров: {exc}. Пожалуйста, предоставьте действительный параметр city снова."
else:
result = get_weather(city)
messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Тайм-аут выполнения инструмента

Section titled “Тайм-аут выполнения инструмента”

Инструменты поддерживаются реальными сервисами, которые могут истечь по времени или быть недоступными. Установите лимит тайм-аута для каждого вызова инструмента; если тайм-аут, передайте результат обратно как чёткое описание ошибки, чтобы позволить модели переключиться на другую стратегию:

try:
result = call_service(args, timeout=5)
except TimeoutError:
result = "Тайм-аут сервиса, данные не получены, пожалуйста, повторите попытку позже или используйте другой метод."

Не выполняйте автоматические повторные попытки для инструментов с побочными эффектами (оформление заказов, отправка электронных писем), так как они могут выполняться повторно. Идемпотентный дизайн или проверка перед записью безопаснее.

Инструмент возвращает ошибку

Section titled “Инструмент возвращает ошибку”

Ошибки бизнес-уровня (заказ не найден, нет разрешения) также должны быть переданы обратно модели, вместо молчаливого возврата пустых значений. Передайте обратно структурированную информацию об ошибке; модель может предоставить разумные объяснения пользователям:

messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps({"error": "order_not_found", "order_id": order_id}),
})

Формат Claude соответственно устанавливает "is_error": true в блоке tool_result, см. Протокол Claude Messages.

Напоминания о совместимости

Section titled “Напоминания о совместимости”
  • Способность Tool Calling, максимальное количество инструментов, поддержка параллельных вызовов зависят от выбранной модели; проверьте в тестовой среде перед переходом в производство.
  • arguments всегда является JSON-строкой, а не объектом; необходимо явно разбирать.
  • В потоковых сценариях необходимо накапливать фрагменты arguments по index, разбирать после полной конкатенации.
  • Необязательные параметры, явно переданные как 0 или false, считаются установленными пользователем, не рассматриваются как значения по умолчанию и отбрасываются.
  • Записывайте request ID, ID модели, код статуса и использование токенов для каждого запроса для более лёгкого устранения неполадок. Подробности структуры ошибок в Ошибки и отладка.