Вызов инструментов (tool calling)
Подключение функций к моделям — модель просит вызвать вашу функцию, вы исполняете её локально, отдаёте результат и получаете финальный ответ.
Tool calling позволяет модели запросить у вас выполнение функции (получить погоду, прочитать БД, отправить письмо), получить результат и продолжить диалог с учётом этого результата. Сама модель не исполняет код — она только описывает, что и с какими аргументами надо вызвать. Исполняет — ваш клиент.
Поддерживается большинством современных моделей: Claude (Sonnet, Opus, Haiku), GPT-4o и новее, Gemini Pro/Flash, Llama 3.1+, Mistral Large. Конкретная карточка модели в каталоге показывает поддержку.
Как это работает
Жизненный цикл одного «раунда» вызова инструмента:
- Вы шлёте запрос с
messagesиtools(описанием доступных функций). - Модель решает: ответить текстом или попросить вызвать функцию. В случае второго — возвращает
finish_reason: "tool_calls"и массивtool_calls[]в assistant-сообщении. - Вы локально исполняете эти функции, формируете ответы.
- Шлёте новый запрос: добавляете assistant-сообщение с
tool_callsи одно или несколькоrole: "tool"сообщений с результатами. - Модель формирует финальный ответ для пользователя.
При параллельных вызовах модель может попросить вызвать несколько функций за один шаг.
Минимальный пример
Шаг 1 — запрос с описанием функции:
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [
{"role": "user", "content": "Какая погода в Москве?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Возвращает текущую погоду в указанном городе.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "Название города"},
"units": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}
]
}'Ответ модели — она просит вызвать get_weather:
{
"id": "chatcmpl-...",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Москва\",\"units\":\"celsius\"}"
}
}
]
},
"finish_reason": "tool_calls"
}]
}Шаг 2 — исполняете функцию у себя и шлёте результат. Важно: сохраните id вызова из tool_calls[].id и проставьте его в tool_call_id.
curl -s https://api.hubris.pw/v1/chat/completions \
-H "Authorization: Bearer sk-gw-..." \
-d '{
"model": "anthropic/claude-haiku-4.5",
"messages": [
{"role": "user", "content": "Какая погода в Москве?"},
{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"Москва\",\"units\":\"celsius\"}"
}
}]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temperature\": -3, \"conditions\": \"снег\"}"
}
],
"tools": [ /* те же tools, что и в первом запросе */ ]
}'Модель ответит текстом с учётом результата:
{
"choices": [{
"message": {
"role": "assistant",
"content": "В Москве сейчас −3 °C и идёт снег. Одевайтесь теплее."
},
"finish_reason": "stop"
}]
}tool_choice — как заставить или запретить вызовы
По умолчанию (auto) модель сама решает, нужен ли инструмент. Можно переопределить:
| Значение | Эффект |
|---|---|
"auto" (по умолчанию) | Модель сама решает: текст или вызов |
"none" | Запретить вызовы, модель ответит только текстом |
"required" | Заставить вызвать какой-нибудь инструмент |
{ "type": "function", "function": { "name": "X" } } | Заставить вызвать именно функцию X |
"tool_choice": { "type": "function", "function": { "name": "get_weather" } }Параллельные вызовы
Если в одном шаге модель решит вызвать несколько функций сразу (например, погоду в трёх городах) — в tool_calls[] придёт несколько элементов. Вы исполняете их параллельно, шлёте обратно столько же role: "tool" сообщений с соответствующими tool_call_id.
Отключить параллельные вызовы (модель будет звать функции по одной):
"parallel_tool_calls": falseСтриминг
При stream: true поля tool_calls приходят в delta.tool_calls[] по частям — имя функции и аргументы могут разбиться на несколько чанков. Собирайте по index:
data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_abc","type":"function","function":{"name":"get_weather"}}]}}]}
data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"city\":"}}]}}]}
data: {"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"Москва\"}"}}]}}]}
data: {"choices":[{"index":0,"finish_reason":"tool_calls"}]}Финальный чанк с finish_reason: "tool_calls" означает, что модель закончила формировать вызов и ждёт результата.
OpenAI SDK
Официальные SDK OpenAI поддерживают tools «как есть».
Python:
from openai import OpenAI
client = OpenAI(
base_url="https://api.hubris.pw/v1",
api_key="sk-gw-...",
)
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Возвращает текущую погоду в указанном городе.",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"units": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["city"],
},
},
}]
resp = client.chat.completions.create(
model="anthropic/claude-haiku-4.5",
messages=[{"role": "user", "content": "Какая погода в Москве?"}],
tools=tools,
)
msg = resp.choices[0].message
if msg.tool_calls:
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = get_weather(**args) # ваша локальная функция
# ... добавьте assistant + tool сообщения и сделайте второй вызовTypeScript:
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'https://api.hubris.pw/v1',
apiKey: 'sk-gw-...',
});
const resp = await client.chat.completions.create({
model: 'anthropic/claude-haiku-4.5',
messages: [{ role: 'user', content: 'Какая погода в Москве?' }],
tools: [
{
type: 'function',
function: {
name: 'get_weather',
description: 'Возвращает текущую погоду в указанном городе.',
parameters: {
type: 'object',
properties: {
city: { type: 'string' },
units: { type: 'string', enum: ['celsius', 'fahrenheit'] },
},
required: ['city'],
},
},
},
],
});
const msg = resp.choices[0].message;
if (msg.tool_calls?.length) {
for (const call of msg.tool_calls) {
const args = JSON.parse(call.function.arguments);
const result = await getWeather(args);
// ... добавьте assistant + tool сообщения и сделайте второй вызов
}
}Биллинг
Tool-calling не имеет отдельного тарифа. Списываются обычные prompt- и completion-токены за каждый шаг диалога:
- Шаг 1 (модель просит вызвать функцию) —
prompt_tokensза описание инструментов и сообщения,completion_tokensза сгенерированныйtool_calls[]. - Шаг 2 (вы шлёте результат) —
prompt_tokensза весь диалог (включаяrole: "tool"сообщения),completion_tokensза финальный ответ.
Описания инструментов считаются как часть промпта на каждом шаге, где они переданы. Если у вас 10 функций по 200 токенов каждая — это +2000 prompt-токенов за вызов.
Что дальше
- POST /v1/chat/completions — полная схема параметров и ответа.
- Структурированный вывод — если вам нужен не вызов функции, а строго типизированный JSON-ответ.
- Поиск в интернете — встроенный server-side инструмент, который мы исполняем за вас.
- Каталог моделей — какие модели поддерживают tool-calling.
Обновлено: