Вызов инструментов
Как передать модели описание функции, безопасно выполнить вызов в приложении и вернуть результат для итогового ответа.
Вызов инструментов через AI Gateway позволяет модели предложить вашему приложению функцию и аргументы, но не выполняет эту функцию вместо приложения. Вы отправляете описание доступных функций, разбираете choices[0].message.tool_calls, запускаете разрешённый код у себя и возвращаете результат сообщением с role: "tool".
В этом руководстве разобран цикл для POST https://route.smartaipack.ru/v1/chat/completions. Точный request_model_id, этот путь и признак «Инструменты» сначала проверьте в разделе Модели: набор моделей, поставщиков и возможностей меняется. Для начала работы понадобятся учётная запись в AI Gateway, API-ключ из переменной AI_GATEWAY_API_KEY и положительный баланс по актуальным ценам.
Как устроен вызов инструмента
Модель не получает прямого доступа к функции. Она видит только её имя, описание и JSON-схему аргументов. В ответ модель может предложить вызов примерно такого вида:
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_order_status",
"arguments": "{\"order_id\":\"ORD-1042\"}"
}
}Поле arguments здесь — строка с JSON, а не доверенный объект. Приложение должно разобрать её, проверить имя функции и аргументы, выполнить функцию и сформировать новое сообщение. Только после второго запроса модель сможет использовать результат в обычном ответе пользователю.
AI Gateway маршрутизирует запрос к выбранной модели, но не даёт ей доступ к операционной системе, базе данных или платежам. Шлюз также не выполняет встроенный веб-поиск, MCP-инструменты и автоматический цикл за ваше приложение. Все права, вызовы, повторы и остановка цикла остаются в вашем коде.
Если вы используете /v1/responses, откройте отдельное руководство по инструментам в Responses API. У этого протокола другая, зависящая от поставщика форма входа и выхода; смешивать её с messages и choices[0].message.tool_calls из Chat Completions нельзя.
Примеры тела запроса
Ниже показан минимальный цикл с функцией только для чтения. Значение <REQUEST_MODEL_ID> замените точным идентификатором варианта из каталога моделей.
Шаг 1. Отправить запрос с tools
curl https://route.smartaipack.ru/v1/chat/completions \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "<REQUEST_MODEL_ID>",
"messages": [
{
"role": "user",
"content": "Какой статус у заказа ORD-1042?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_order_status",
"description": "Возвращает текущий статус заказа по его идентификатору.",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "Идентификатор вида ORD-1042"
}
},
"required": ["order_id"],
"additionalProperties": false
}
}
}
],
"tool_choice": {
"type": "function",
"function": { "name": "get_order_status" }
},
"stream": false
}'Для наглядности пример принудительно выбирает одну функцию — такая форма проверяется внутренней проверкой маршрута AI Gateway. Поддержка принудительного tool_choice зависит от модели и поставщика, поэтому для рабочего сценария сначала проверьте выбранный вариант; обычно безопаснее начать с "auto".
При выборе инструмента ответ содержит choices[0].message.tool_calls. Сохраните всё сообщение помощника, включая content и полный массив tool_calls: оно понадобится в истории следующего запроса.
Шаг 2. Выполнить функцию в приложении
Приложение берёт function.name, сверяет его со списком разрешённых функций, разбирает function.arguments и проверяет данные по схеме. В нашем безопасном примере функция читает локальную заглушку:
const orders = Object.freeze({
"ORD-1042": { status: "ready_for_pickup", updated_at: "2026-08-22T10:30:00Z" }
});
function getOrderStatus({ order_id }) {
if (!/^ORD-[0-9]{4}$/.test(order_id)) {
throw new Error("Некорректный order_id");
}
return orders[order_id] ?? { status: "not_found" };
}Это обычная функция вашего приложения. Она не обращается к внешнему API, ничего не меняет и не передаёт модели лишние данные.
Шаг 3. Вернуть результат и получить итог
Добавьте в messages исходный вопрос, полученное сообщение помощника и результат функции. tool_call_id должен точно совпасть с id предложенного вызова:
{
"model": "<REQUEST_MODEL_ID>",
"messages": [
{
"role": "user",
"content": "Какой статус у заказа ORD-1042?"
},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_order_status",
"arguments": "{\"order_id\":\"ORD-1042\"}"
}
}
]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"status\":\"ready_for_pickup\",\"updated_at\":\"2026-08-22T10:30:00Z\"}"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_order_status",
"description": "Возвращает текущий статус заказа по его идентификатору.",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string" }
},
"required": ["order_id"],
"additionalProperties": false
}
}
}
],
"tool_choice": "auto",
"stream": false
}Отправьте это тело повторным POST на тот же /v1/chat/completions. Если дополнительных вызовов нет, текст итогового ответа находится в choices[0].message.content.
Практический пример
Этот самодостаточный пример на Node.js использует только встроенный fetch и локальный объект. Он допускает ровно одну функцию, проверяет JSON и ограничивает цикл четырьмя шагами и 20 секундами.
const apiKey = process.env.AI_GATEWAY_API_KEY;
const model = process.env.AI_GATEWAY_MODEL_ID;
if (!apiKey || !model) {
throw new Error("Задайте AI_GATEWAY_API_KEY и AI_GATEWAY_MODEL_ID");
}
const url = "https://route.smartaipack.ru/v1/chat/completions";
const orders = Object.freeze({
"ORD-1042": { status: "ready_for_pickup", updated_at: "2026-08-22T10:30:00Z" }
});
const tools = [
{
type: "function",
function: {
name: "get_order_status",
description: "Возвращает текущий статус заказа по его идентификатору.",
parameters: {
type: "object",
properties: {
order_id: {
type: "string",
description: "Идентификатор вида ORD-1042"
}
},
required: ["order_id"],
additionalProperties: false
}
}
}
];
function runAllowedTool(toolCall) {
if (toolCall?.function?.name !== "get_order_status") {
throw new Error("Запрошена неизвестная функция");
}
let args;
try {
args = JSON.parse(toolCall.function.arguments);
} catch {
throw new Error("Аргументы функции не являются JSON");
}
const validKeys =
args && typeof args === "object" && !Array.isArray(args)
? Object.keys(args)
: [];
if (
validKeys.length !== 1 ||
validKeys[0] !== "order_id" ||
typeof args.order_id !== "string" ||
!/^ORD-[0-9]{4}$/.test(args.order_id)
) {
throw new Error("Аргументы функции не прошли проверку");
}
return orders[args.order_id] ?? { status: "not_found" };
}
async function complete(messages, signal) {
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model,
messages,
tools,
tool_choice: "auto",
max_tokens: 256,
stream: false
}),
signal
});
if (!response.ok) {
throw new Error(`AI Gateway вернул HTTP ${response.status}`);
}
const data = await response.json();
const message = data?.choices?.[0]?.message;
if (!message) throw new Error("В ответе нет сообщения модели");
return message;
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 20_000);
const messages = [
{ role: "user", content: "Какой статус у заказа ORD-1042?" }
];
try {
for (let step = 0; step < 4; step += 1) {
const assistant = await complete(messages, controller.signal);
messages.push({
role: "assistant",
content: assistant.content ?? null,
...(assistant.tool_calls ? { tool_calls: assistant.tool_calls } : {})
});
const calls = assistant.tool_calls ?? [];
if (calls.length === 0) {
console.log(assistant.content);
break;
}
for (const call of calls) {
const result = runAllowedTool(call);
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(result)
});
}
if (step === 3) throw new Error("Превышен предел шагов");
}
} finally {
clearTimeout(timer);
}Перед запуском скопируйте точный request_model_id из каталога и задайте переменные окружения. Ключ храните только на сервере; порядок выпуска и отзыва описан в руководстве по авторизации.
Определение инструмента
Каждый элемент tools состоит из type: "function" и объекта function:
name— стабильное имя, которое приложение сверяет со своим списком разрешений;description— конкретное объяснение, когда функцию можно применять;parameters— JSON Schema для аргументов;required— обязательные поля;additionalProperties: false— запрет неожиданных полей на уровне схемы.
Делайте одну функцию для одной понятной операции. Имя get_order_status точнее, чем orders, а узкий параметр order_id безопаснее свободного объекта. Схема помогает модели сформировать аргументы, но не заменяет проверку на стороне приложения.
Использование инструмента и результата
Обработчик ответа должен различать обычный текст и запрос функции. Если есть tool_calls, для каждого элемента он проверяет id, type, имя и строку arguments. Затем он выполняет разрешённую функцию и возвращает короткий сериализованный результат с тем же tool_call_id.
Не подставляйте результат функции в сообщение пользователя или помощника. Роль tool явно связывает данные с конкретным вызовом и сохраняет корректную историю. Не пересказывайте самостоятельно сообщение помощника с tool_calls: передавайте его в следующем запросе в полученной форме.
После завершения проверьте запросы в разделе Использование, а расход по моделям и ключам — в Аналитике. Диагностику кодов ответа удобно вести по справочнику ошибок.
Многошаговый цикл
Иногда результат первой функции приводит к следующему предложению инструмента. Тогда приложение повторяет один и тот же порядок: запрос к модели, проверка tool_calls, локальное выполнение, добавление сообщений tool, новый запрос.
Это цикл приложения, а не встроенная автоматика AI Gateway. Обязательно задайте предел числа шагов, общее время выполнения и ограничение времени каждой функции. Также ограничьте число вызовов в одном ответе и общий размер возвращаемых данных. При достижении предела завершайте операцию контролируемой ошибкой, а не отправляйте запросы бесконечно.
Некоторые модели могут рассуждать между последовательными результатами инструментов. Это зависит от выбранной модели и поставщика, увеличивает задержку и расход токенов и не является отдельной функцией AI Gateway. Приложению не нужно запрашивать или раскрывать скрытую цепочку рассуждений; для доступных режимов смотрите отдельный материал о рассуждении в Responses API.
Рекомендации и расширенные схемы
Правила функций
Используйте короткие однозначные имена и описания. Ограничивайте строки по формату и длине, числа — диапазоном, а варианты — enum. Возвращайте только поля, необходимые для следующего решения модели. Большой внутренний объект повышает стоимость и риск утечки данных.
Выбор инструмента
Значение tool_choice: "auto" оставляет выбор модели. Формат принудительного выбора конкретной функции также существует в OpenAI-совместимом протоколе и используется проектной проверкой маршрута, но поддержка может отличаться у поставщиков. До применения проверьте выбранный вариант в каталоге моделей и коротким тестовым запросом. Не стройте рабочую логику на принудительном выборе без такой проверки.
Параллельные вызовы
Модель может предложить несколько элементов в tool_calls. Выполняйте их параллельно только тогда, когда операции независимы, безопасны и не меняют общий ресурс. Сохраните отдельный результат для каждого id. Параметр parallel_tool_calls поддерживается не каждым поставщиком, поэтому его нельзя считать универсальной возможностью AI Gateway.
Несколько инструментов
Передавайте только нужный для текущего сценария набор. Например, чтение статуса и чтение состава заказа можно разделить на две узкие функции. Не добавляйте универсальную функцию выполнения SQL, команд оболочки или произвольного HTTP-запроса: модельные аргументы нельзя считать разрешением на доступ к инфраструктуре.
Надёжность и безопасность
- Разрешайте только имена функций из заранее заданного списка.
- Разбирайте
argumentsкак JSON и повторно проверяйте их собственной схемой. - Проверяйте авторизацию, организацию пользователя и принадлежность объекта до чтения данных.
- Выдавайте функции минимальные права и отдельные учётные данные, если они нужны.
- Ограничивайте время, число шагов, число вызовов и размер результата.
- Удаляйте секреты и лишние персональные данные из результата и журналов.
- Требуйте явного подтверждения человека для удаления, платежа, отправки сообщения и другого опасного действия.
- Используйте ключ идемпотентности или собственную защиту от повторов для операций, которые нельзя выполнить дважды.
- Логируйте имя функции, безопасные идентификаторы, длительность и исход без полного API-ключа и чувствительных аргументов.
Учитывайте общие лимиты API: повтор после сетевой ошибки не должен незаметно повторять действие. Если не удаётся определить причину отказа или поведение выбранного маршрута, передайте в поддержку время запроса, путь, request_model_id и код ошибки — без ключа и секретных данных.