Перейти к содержимому

Вызов инструментов

Модель не выполняет функции сама. Она получает их описания и возвращает намерение: имя функции и аргументы в JSON. Код выполняете вы, результат отправляете следующим запросом. Дальше модель либо просит ещё один вызов, либо отвечает текстом.

Инструменты поддерживает почти весь каталог. Проверить конкретную модель можно по GET /v1/models: в capabilities.features должно быть tools. См. Модели.

  1. Запрос с полем tools.
  2. Ответ с finish_reason: "tool_calls" и массивом tool_calls. Вы выполняете функции у себя.
  3. Запрос с той же историей плюс сообщение ассистента и результаты в сообщениях role: "tool".

История не хранится на стороне роутера: каждый запрос отправляется целиком, включая предыдущие вызовы и их результаты.

step-1.sh
curl https://api.waibee.com/v1/chat/completions \
-H "Authorization: Bearer $WAIBEE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemini-3.6-flash",
"messages": [{ "role": "user", "content": "Какая погода в Берлине?" }],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Текущая погода в городе",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "Город" },
"unit": { "type": "string", "enum": ["c", "f"] }
},
"required": ["city"]
}
}
}]
}'
response.json
{
"choices": [{
"index": 0,
"finish_reason": "tool_calls",
"message": {
"role": "assistant",
"tool_calls": [{
"id": "29veklzD",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"Берлин\"}" }
}]
}
}],
"usage": { "prompt_tokens": 79, "completion_tokens": 129, "total_tokens": 208 }
}

Два момента, на которых спотыкаются чаще всего:

  • arguments это строка с JSON, а не объект. Её нужно распарсить, и она может не пройти валидацию: обрабатывайте ошибку разбора как обычный сбой, а не как исключительную ситуацию.
  • id вызова обязателен на третьем шаге. По нему модель сопоставляет результат с вызовом.
step-3.sh
curl https://api.waibee.com/v1/chat/completions \
-H "Authorization: Bearer $WAIBEE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemini-3.6-flash",
"messages": [
{ "role": "user", "content": "Какая погода в Берлине?" },
{ "role": "assistant", "tool_calls": [{
"id": "29veklzD", "type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"Берлин\"}" } }] },
{ "role": "tool", "tool_call_id": "29veklzD",
"content": "{\"temp_c\": 19, \"cond\": \"облачно\"}" }
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Текущая погода в городе",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "Город" },
"unit": { "type": "string", "enum": ["c", "f"] }
},
"required": ["city"]
}
}
}]
}'

Ответ приходит обычным текстом с finish_reason: "stop". Определения tools стоит передавать и в этом запросе: без них модель не сможет вызвать инструмент повторно, если ей понадобятся ещё данные.

Содержимое role: "tool" это произвольная строка. JSON удобнее свободного текста: он однозначнее и экономит токены на пояснениях.

Модель может вернуть сразу несколько элементов в tool_calls. Выполните их все и добавьте отдельное сообщение role: "tool" на каждый tool_call_id. Порядок сообщений с результатами не важен, важно, чтобы ни один вызов не остался без ответа: пропущенный результат обычно приводит к ошибке валидации на стороне модели.

У моделей OpenAI параллельные вызовы отключаются параметром parallel_tool_calls: false.

Значение Поведение
"auto" Модель сама решает, вызывать инструмент или ответить текстом. Поведение по умолчанию
"none" Инструменты видны модели, но вызывать их нельзя
"required" Обязательно вызвать хотя бы один инструмент
{"type": "function", "function": {"name": "get_weather"}} Вызвать именно эту функцию

Значения проходят к модели как есть, поэтому набор поддерживаемых вариантов зависит от семейства модели. Универсально работают "auto" и "none".

На POST /v1/messages инструменты описываются по-своему: поля name, description и input_schema на верхнем уровне, без обёртки function.

Окно терминала
curl https://api.waibee.com/v1/messages \
-H "x-api-key: $WAIBEE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"max_tokens": 512,
"tools": [{
"name": "get_weather",
"description": "Текущая погода в городе",
"input_schema": {
"type": "object",
"properties": { "city": { "type": "string", "description": "Город" } },
"required": ["city"]
}
}],
"messages": [{ "role": "user", "content": "Какая погода в Берлине?" }]
}'

Вызов приходит блоком tool_use со stop_reason: "tool_use", а аргументы лежат в input уже разобранным объектом:

{
"type": "message",
"stop_reason": "tool_use",
"content": [{
"type": "tool_use",
"id": "toolu_011Q2fbw78h7hDJT2mBJjEfx",
"name": "get_weather",
"input": { "city": "Берлин" }
}]
}

Результат возвращается блоком tool_result внутри сообщения пользователя:

{
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": "toolu_011Q2fbw78h7hDJT2mBJjEfx",
"content": "{\"temp_c\": 19, \"cond\": \"облачно\"}"
}]
}

Модель может искать в интернете сама, и поиск за неё выполняет роутер. Просят его через tools, поэтому он и описан рядом, но три шага выше к нему не относятся: принимать вызов, выполнять поиск и отправлять результат обратно вам не нужно.

Отдельная страница: Веб-поиск.

При "stream": true вызов приходит по частям. В delta.tool_calls первый фрагмент несёт id и name, следующие только куски arguments. Собирайте их по полю index, пока не получите валидный JSON.

data: {"choices":[{"index":0,"delta":{"role":"assistant","tool_calls":[
{"index":0,"id":"zmahjJWU","type":"function",
"function":{"name":"get_weather","arguments":""}}]}}]}
data: {"choices":[{"index":0,"delta":{"tool_calls":[
{"index":0,"function":{"arguments":"{\"city\":\"Берлин\"}"}}]}}]}
data: {"choices":[{"index":0,"delta":{},"finish_reason":"tool_calls"}],"usage":{...}}
data: [DONE]

Последний содержательный чанк несёт finish_reason и usage. Подробнее про формат потока: Стриминг.

  • До 256 инструментов в одном запросе, см. Лимиты.
  • Из серверных инструментов доступен только веб-поиск, на всех трёх эндпоинтах, см. выше. Остальные, web_fetch_* и code_execution_*, тарифицируются вне токенов и отклоняются с feature_not_supported (400). Эквивалент реализуется своей функцией на стороне клиента.
  • Схемы аргументов передаются провайдеру без изменений. Насколько строго модель следует схеме, зависит от модели; если нужна гарантия структуры для ответа, а не для аргументов, смотрите Structured outputs.
  • Описания пишите для человека. Имя, описание функции и описания полей это единственная инструкция, по которой модель решает, когда и с чем вызывать инструмент. Одно точное предложение работает лучше перечисления деталей реализации.
  • Сужайте схему. enum вместо свободной строки, обязательные поля в required, никаких «опциональных на всякий случай» параметров.
  • Ставьте предел итераций. Цикл «вызов, результат, вызов» может не сходиться: ограничьте число проходов и завершайте разговор понятной ошибкой.
  • Проверяйте finish_reason. Инструменты нужно выполнять только при значении tool_calls, иначе в ответе обычный текст.
  • Держите инструментов немного. Десятки похожих функций в одном запросе повышают шанс, что модель выберет не ту, и раздувают ввод.