Вызов инструментов (Function Calling)
Используйте tool calling в GonkaGate chat completions, когда модель должна запросить инструмент или функцию у вашего приложения.
Используйте tool calling (function calling), когда модель должна запросить работу у вашего backend или другой внешней системы. Сама модель инструмент не запускает. Она возвращает tool_calls; ваше приложение валидирует аргументы, выполняет подходящий handler, добавляет по одному сообщению role: "tool" на каждый tool_call_id и отправляет обновлённый диалог обратно, пока модель не вернёт обычное сообщение.
Держите собственный лимит итераций, валидируйте каждый аргумент до выполнения и заново проверяйте права внутри каждого handler. Если нужен только машиночитаемый JSON, используйте Structured Outputs. Если этим поведением должен управлять сам GonkaGate, используйте Плагины в chat completions.
Перед стартом
- Рабочий
chat.completionsзапрос кhttps://api.gonkagate.com/v1. - Пара model/provider, которая поддерживает tool calling для вашего сценария.
- По одному понятному server-side handler на каждый инструмент, который вы показываете модели.
- Проверка прав текущего пользователя или сессии внутри каждого handler. Tool call от модели — это запрос, а не подтверждение доступа.
- Валидация аргументов до выполнения и
JSON.stringify(...), когда вы возвращаете структурированный результат инструмента.
Как проходит цикл
- Отправьте
messages,toolsи при необходимостиtool_choiceв/v1/chat/completions. - Если модель вернула
tool_calls, провалидируйте аргументы и запустите соответствующий handler в своём приложении. - Добавьте assistant message с
tool_calls, затем по одному сообщениюrole: "tool"на каждыйtool_call_id, и снова отправьте диалог, обычно с тем жеtools, если в следующих ходах могут понадобиться новые вызовы инструмента.
Повторяйте цикл, пока модель не вернёт сообщение без tool_calls, и останавливайтесь на своём лимите итераций.
GonkaGate пробрасывает tools и tool_choice через тот же OpenAI-совместимый /v1/chat/completions. Ваше приложение отвечает за валидацию аргументов, выполнение handler, таймауты, ретраи, follow-up запрос и авторизацию. Tool calls остаются недоверенным model output.
Отправьте первый запрос
Форма запроса — это обычный OpenAI-совместимый chat.completions payload плюс tools и при необходимости tool_choice.
{
"model": "qwen/qwen3-235b-a22b-instruct-2507-fp8",
"messages": [
{
"role": "user",
"content": "Check whether api-gateway is healthy in prod and summarize the result."
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_deployment_status",
"description": "Return the current deployment status for a service",
"parameters": {
"type": "object",
"properties": {
"service": { "type": "string" },
"environment": {
"type": "string",
"enum": ["prod", "staging"]
}
},
"required": ["service", "environment"]
}
}
}
],
"tool_choice": "auto"
}Добавьте результат инструмента
Если модель хочет, чтобы действие выполнило ваше приложение, она может вернуть такой tool call:
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_deployment_status",
"arguments": "{\"service\":\"api-gateway\",\"environment\":\"prod\"}"
}
}
]
}После локального вызова handler добавьте одно tool message для этого tool_call_id:
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"service\":\"api-gateway\",\"environment\":\"prod\",\"status\":\"healthy\"}"
}После этого снова отправьте обновлённый диалог модели. В большинстве интеграций тот же массив tools передают и в следующем запросе, чтобы модель при необходимости могла запросить ещё один инструмент.
Ожидаемый результат: после одного или нескольких раундов инструментов финальный assistant message содержит обычный content и не содержит tool_calls.
Сквозной пример
Это минимальный безопасный паттерн: провалидировать аргументы, отклонить неизвестные инструменты, превратить ошибки парсинга и handler в явные ошибки инструмента, повторно передавать tools на follow-up шагах и остановиться на собственном лимите цикла.
import OpenAI from "openai";
type Environment = "prod" | "staging";
const client = new OpenAI({
baseURL: "https://api.gonkagate.com/v1",
apiKey: "gp-your-api-key"
});
const model = "qwen/qwen3-235b-a22b-instruct-2507-fp8";
const tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [
{
type: "function",
function: {
name: "get_deployment_status",
description: "Return the current deployment status for a service",
parameters: {
type: "object",
properties: {
service: { type: "string" },
environment: {
type: "string",
enum: ["prod", "staging"]
}
},
required: ["service", "environment"]
}
}
}
];
async function getDeploymentStatus(service: string, environment: Environment) {
return {
service,
environment,
status: "healthy",
updated_at: "2026-03-14T09:30:00Z"
};
}
type ToolCall = {
id: string;
function: {
name: string;
arguments: string | null;
};
};
function parseArgs(raw: string): { service: string; environment: Environment } {
const parsed = JSON.parse(raw || "{}") as {
service?: unknown;
environment?: unknown;
};
if (typeof parsed.service !== "string" || parsed.service.trim() === "") {
throw new Error("service must be a non-empty string");
}
if (parsed.environment !== "prod" && parsed.environment !== "staging") {
throw new Error("environment must be prod or staging");
}
return {
service: parsed.service,
environment: parsed.environment
};
}
const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [
{
role: "user",
content: "Check whether api-gateway is healthy in prod and summarize the result."
}
];
async function executeToolCall(
call: ToolCall
): Promise<OpenAI.Chat.Completions.ChatCompletionToolMessageParam> {
if (call.function.name !== "get_deployment_status") {
return {
role: "tool",
tool_call_id: call.id,
content: JSON.stringify({
ok: false,
error: `unknown tool: ${call.function.name}`
})
};
}
try {
const args = parseArgs(call.function.arguments || "{}");
const result = await getDeploymentStatus(args.service, args.environment);
return {
role: "tool",
tool_call_id: call.id,
content: JSON.stringify({
ok: true,
result
})
};
} catch (error) {
return {
role: "tool",
tool_call_id: call.id,
content: JSON.stringify({
ok: false,
error:
error instanceof Error ? error.message : "tool_execution_failed"
})
};
}
}
const maxToolRounds = 5;
let finalText: string | null = null;
for (let round = 0; round < maxToolRounds; round++) {
const response = await client.chat.completions.create({
model,
messages,
tools,
tool_choice: "auto"
});
const assistantMessage = response.choices[0]?.message;
const toolCalls = assistantMessage?.tool_calls ?? [];
if (!assistantMessage) {
throw new Error("Model returned no message");
}
messages.push(assistantMessage);
if (!toolCalls.length) {
finalText = assistantMessage.content ?? "";
break;
}
const toolMessages = await Promise.all(
toolCalls.map(executeToolCall)
);
messages.push(...toolMessages);
}
if (finalText === null) {
throw new Error(`Tool loop exceeded ${maxToolRounds} rounds`);
}
console.log(finalText);Частые сбои
tool_callsне появляются: модель может ответить напрямую. Уточните промпт или задайтеtool_choiceкакrequiredили конкретную функцию, если действие обязательно.function.argumentsпарсятся, но всё равно неверные: считайте их недоверенным JSON. Валидируйтеenum, обязательные поля и пустые строки до вызова backend.function.argumentsне парсятся, handler падает или инструмент упирается в timeout: верните явную ошибку в tool result или аккуратно оборвите цикл, но не делайте вид, что инструмент отработал успешно.- Кажется, что модель игнорирует результат инструмента: сначала добавьте assistant message с
tool_calls, потомrole: "tool"сообщения, и только после этого делайте follow-up запрос. - Tool call пришёл для привилегированного действия: не считайте запрос модели авторизацией. Перед выполнением handler заново проверьте права текущего пользователя или сессии.
- В одном ходе пришло несколько tool calls: верните по одному
role: "tool"сообщению на каждыйtool_call_idи не меняйте идентификаторы. - В follow-up запросе забыли снова передать
tools: если в следующих ходах модель ещё может запросить инструмент, отправляйте тот же массивtoolsна каждом раунде. - Стриминговые tool calls приходят кусками: сначала соберите полную строку
arguments, а уже потом парсьте JSON и вызывайте handler. - Результат инструмента не проходит проверки дальше по цепочке:
contentв tool message должен быть строкой. Если handler вернул JSON, отправляйте его черезJSON.stringify(...).
См. также
- Параметры chat completions для точного контракта полей
toolsиtool_choice. - Structured Outputs для JSON по схеме без действий со стороны приложения.
- Плагины в chat completions для поведения, которым управляет GonkaGate, например built-in web search или response healing.