Function calling (он же tool calling или tool use) — способ дать модели доступ к вашему коду. Вы описываете функции: «узнать статус заказа», «посчитать доставку», «найти клиента в CRM». Модель сама решает, какую вызвать и с какими аргументами, а ваш код её выполняет и возвращает результат. На этом механизме построены все агенты — от чат-бота магазина до Claude Code.
В PlusVibe function calling работает через стандартный /v1/chat/completions в формате OpenAI и через /v1/messages в формате Anthropic. Ниже — полный рабочий цикл, который мы прогнали на трёх моделях разных производителей.
Как это устроено
- Вы отправляете запрос с сообщениями и списком
tools— у каждого инструмента есть имя, описание и JSON Schema параметров. - Модель отвечает не текстом, а списком
tool_calls: какую функцию вызвать и с какими аргументами.finish_reasonв этом случае равенtool_calls. - Ваш код выполняет функции и добавляет в историю сообщения с ролью
tool— по одному на каждый вызов, с тем жеtool_call_id. - Вы отправляете историю снова. Модель либо просит ещё вызовов, либо отвечает пользователю текстом.
Важно: модель сама ничего не выполняет. Она только просит вызвать функцию. Что именно запускать и с какими правами, решает ваш код.
Полный пример: помощник интернет-магазина
Две функции — статус заказа и стоимость доставки. Пользователь спрашивает сразу о двух вещах, и модель вызывает обе функции в одном ответе.
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://plusvibeapi.ru/v1",
api_key=os.environ["PLUSVIBE_API_KEY"],
)
MODEL = "gpt-5.6-luna"
def get_order_status(order_id: str) -> dict:
# здесь ваш запрос в базу или CRM
return {"order_id": order_id, "status": "передан в доставку", "eta": "2026-10-02"}
def get_delivery_price(city: str) -> dict:
return {"city": city, "price_rub": 390}
TOOLS = [
{
"type": "function",
"function": {
"name": "get_order_status",
"description": "Статус заказа по его номеру",
"parameters": {
"type": "object",
"properties": {"order_id": {"type": "string", "description": "Номер заказа, например A-1042"}},
"required": ["order_id"],
},
},
},
{
"type": "function",
"function": {
"name": "get_delivery_price",
"description": "Стоимость курьерской доставки в город",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
},
]
FUNCTIONS = {"get_order_status": get_order_status, "get_delivery_price": get_delivery_price}
messages = [
{"role": "system", "content": "Ты помощник интернет-магазина. Отвечай кратко."},
{"role": "user", "content": "Где мой заказ A-1042? И сколько стоит доставка в Тверь?"},
]
for _ in range(5): # ограничиваем число кругов
resp = client.chat.completions.create(model=MODEL, messages=messages, tools=TOOLS)
msg = resp.choices[0].message
if not msg.tool_calls:
print(msg.content)
break
messages.append(msg.model_dump(exclude_none=True))
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = FUNCTIONS[call.function.name](**args)
print("вызов:", call.function.name, args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})
Мы запустили этот код без изменений на трёх моделях, меняя только MODEL: gpt-5.6-luna, deepseek-v4.1-flash и claude-haiku-4.5. Все три за первый круг вызвали обе функции — get_order_status с A-1042 и get_delivery_price с «Тверь», — а на втором круге ответили текстом со статусом заказа, датой доставки и её стоимостью.
Что в этом коде важно
- Ответ модели с
tool_callsкладётся в историю целиком. Без него сообщениеtoolокажется ответом на вызов, которого модель не видит. В нашей проверкеgpt-5.6-lunaв такой ситуации просто вызвала ту же функцию ещё раз — и цикл пошёл по кругу. - На каждый вызов — своё сообщение
toolс тем жеtool_call_id. Модель может попросить несколько вызовов сразу, как здесь. - Аргументы приходят строкой.
arguments— это JSON в строке, его нужно разобрать черезjson.loadsи проверить: модель может ошибиться в значении. - Ограничение на число кругов. Без него ошибка в функции может превратить цикл в бесконечный — и в счёт за токены.
tool_choice: когда вызов обязателен
По умолчанию модель сама решает, звать инструмент или ответить текстом ("auto"). Параметр tool_choice меняет это поведение:
"none"— не вызывать инструменты;"required"— обязательно вызвать какой-нибудь;{"type": "function", "function": {"name": "..."}}— вызвать конкретную функцию.
Последний вариант удобен для извлечения данных. Схема параметров функции становится схемой ответа, и это работает даже на моделях без строгого JSON-режима:
curl https://plusvibeapi.ru/v1/chat/completions \
-H "Authorization: Bearer $PLUSVIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4.1-flash",
"messages": [{"role": "user", "content": "Иван Петров, ivan@example.ru, хочет демо в четверг"}],
"tools": [{
"type": "function",
"function": {
"name": "save_lead",
"description": "Сохранить заявку в CRM",
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"request": {"type": "string"}
},
"required": ["name", "email", "request"]
}
}
}],
"tool_choice": {"type": "function", "function": {"name": "save_lead"}}
}'
В ответе пришёл вызов save_lead с аргументами name — «Иван Петров», email — ivan@example.ru и request — «Хочет демо в четверг». Другие способы получить JSON по схеме — в статье про Structured Outputs.
Формат Anthropic: /v1/messages
Если вы работаете через Anthropic SDK, инструменты описываются чуть иначе: схема параметров лежит в поле input_schema, а вызов приходит блоком tool_use:
curl https://plusvibeapi.ru/v1/messages \
-H "x-api-key: $PLUSVIBE_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-haiku-4.5",
"max_tokens": 512,
"tools": [{
"name": "get_order_status",
"description": "Статус заказа по его номеру",
"input_schema": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"]
}
}],
"messages": [{"role": "user", "content": "Где мой заказ A-1042?"}]
}'
Ответ пришёл со stop_reason, равным tool_use, и блоком tool_use с {"order_id": "A-1042"}. Результат возвращается сообщением пользователя с блоком tool_result. В Responses API своя форма — function_call и function_call_output, её мы показали в статье Responses API и Chat Completions.
Как писать описания инструментов
- Название — глагол и объект:
get_order_status,create_invoice. Модель выбирает инструмент в первую очередь по имени и описанию. - В описании — когда вызывать. «Статус заказа по его номеру» лучше, чем «Работа с заказами».
- У параметров — формат и пример: «Номер заказа, например A-1042». Это заметно снижает число вызовов с неверными аргументами.
- Меньше инструментов — точнее выбор. Описания всех инструментов уходят в каждый запрос и оплачиваются как входные токены. Если функций десятки, передавайте только те, что нужны на текущем шаге.
Безопасность
Аргументы функции пишет модель, а на неё влияет всё, что попало в контекст: сообщение пользователя, текст веб-страницы, содержимое письма. Поэтому проверяйте аргументы так же, как любой пользовательский ввод. Функции, которые что-то меняют — списывают деньги, удаляют данные, отправляют письма, — стоит подтверждать отдельно, а не выполнять по первому запросу модели.
Какую модель выбрать
Поддержку инструментов у конкретной модели можно проверить через GET /v1/model-capabilities с вашим ключом — поле capabilities.toolCalling. Цены за 1 млн токенов у моделей из наших примеров:
| Модель | Вход | Выход |
|---|---|---|
| gpt-5.6-luna | 3,78 ₽/1M | 22,63 ₽/1M |
| deepseek-v4.1-flash | 1,14 ₽/1M | 4,53 ₽/1M |
| claude-haiku-4.5 | 30 ₽/1M | 144 ₽/1M |
Цены подставляются из каталога в момент открытия страницы. Полный список — в каталоге моделей.
Итог
Function calling — это обмен сообщениями: модель просит вызов, ваш код выполняет его и возвращает результат. Один и тот же код цикла работает с моделями разных производителей через один ключ PlusVibe. Параметры описаны в документации. Если вы собираете агента на фреймворке, пригодятся статьи про LangChain, CrewAI и Hermes Agent.
Инструменты для ваших агентов
Function calling на GPT, Claude, DeepSeek и других моделях через один ключ. Оплата в рублях.
Получить ключ →


