Вызов инструментов
Модель не выполняет функции сама. Она получает их описания и возвращает намерение: имя функции и аргументы в JSON. Код выполняете вы, результат отправляете следующим запросом. Дальше модель либо просит ещё один вызов, либо отвечает текстом.
Инструменты поддерживает почти весь каталог. Проверить конкретную модель можно по GET /v1/models: в capabilities.features должно быть tools. См. Модели.
Цикл из трёх шагов
Заголовок раздела «Цикл из трёх шагов»- Запрос с полем
tools. - Ответ с
finish_reason: "tool_calls"и массивомtool_calls. Вы выполняете функции у себя. - Запрос с той же историей плюс сообщение ассистента и результаты в сообщениях
role: "tool".
История не хранится на стороне роутера: каждый запрос отправляется целиком, включая предыдущие вызовы и их результаты.
1. Запрос с инструментами
Заголовок раздела «1. Запрос с инструментами»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"] } } }] }'2. Ответ с вызовом
Заголовок раздела «2. Ответ с вызовом»{ "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вызова обязателен на третьем шаге. По нему модель сопоставляет результат с вызовом.
3. Результат обратно в модель
Заголовок раздела «3. Результат обратно в модель»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.
Управление выбором: tool_choice
Заголовок раздела «Управление выбором: tool_choice»| Значение | Поведение |
|---|---|
"auto" |
Модель сама решает, вызывать инструмент или ответить текстом. Поведение по умолчанию |
"none" |
Инструменты видны модели, но вызывать их нельзя |
"required" |
Обязательно вызвать хотя бы один инструмент |
{"type": "function", "function": {"name": "get_weather"}} |
Вызвать именно эту функцию |
Значения проходят к модели как есть, поэтому набор поддерживаемых вариантов зависит от семейства модели. Универсально работают "auto" и "none".
Формат Anthropic Messages
Заголовок раздела «Формат Anthropic Messages»На 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, иначе в ответе обычный текст. - Держите инструментов немного. Десятки похожих функций в одном запросе повышают шанс, что модель выберет не ту, и раздувают ввод.