Skip to main content

Вызов инструментов (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(...), когда вы возвращаете структурированный результат инструмента.

Как проходит цикл

  1. Отправьте messages, tools и при необходимости tool_choice в /v1/chat/completions.
  2. Если модель вернула tool_calls, провалидируйте аргументы и запустите соответствующий handler в своём приложении.
  3. Добавьте 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.

request.json
{
  "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:

assistant-message.json
{
  "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:

tool-message.json
{
  "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(...).

См. также

Была ли эта страница полезной?