модель работает только с текстом: она не сходит в базу данных, не узнает курс валют и не оформит заказ. зато она умеет сказать, какую функцию для этого нужно вызвать и с какими аргументами. выполняет функцию приложение, результат возвращается модели, и та формулирует окончательный ответ.
как устроен цикл
- в запрос вместе с сообщениями передаётся описание доступных функций.
- модель отвечает не текстом, а списком вызовов: имя функции и аргументы.
- приложение выполняет вызов своими силами и получает результат.
- результат отправляется следующим запросом, и модель отвечает уже с учётом данных.
модель ничего не вызывает сама и не имеет доступа к коду. она лишь сообщает, что хотела бы вызвать — решение остаётся за приложением.
описание функций
функции перечисляются в поле tools. у каждой есть имя, описание и схема аргументов в формате json schema. по описанию модель понимает, когда функция уместна, поэтому формулировать его стоит внятно.
curl "https://api.airakurs.ru/v1/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <КЛЮЧ>" \
-d '{
"model": "google/gemini-3.5-flash",
"messages": [
{"role": "user", "content": "Какая погода в Иркутске?"}
],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Текущая погода в городе",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "Название города"}
},
"required": ["city"]
}
}
}]
}'
ответ с вызовом
если модель решила воспользоваться функцией, поле content приходит пустым, а finish_reason равен tool_calls. сами вызовы лежат в массиве tool_calls:
{
"choices": [{
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_a1b2c3",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"Иркутск\"}"
}
}]
},
"finish_reason": "tool_calls"
}]
}
аргументы приходят строкой, а не объектом: поле arguments нужно разобрать как json. и проверить — модель может передать неожиданное значение или пропустить обязательное поле.
возврат результата
результат отправляется следующим запросом. в messages добавляются два сообщения: ответ модели с вызовом — как есть, и результат в роли tool с тем же идентификатором tool_call_id.
{
"model": "google/gemini-3.5-flash",
"messages": [
{"role": "user", "content": "Какая погода в Иркутске?"},
{
"role": "assistant",
"tool_calls": [{
"id": "call_a1b2c3",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"city\": \"Иркутск\"}"}
}]
},
{
"role": "tool",
"tool_call_id": "call_a1b2c3",
"content": "{\"temperature\": -18, \"condition\": \"ясно\"}"
}
]
}
описание tools передаётся и во втором запросе тоже: без него модель не поймёт, откуда взялось сообщение в роли tool. цикл может повториться — ничто не мешает модели запросить ещё один вызов после первого результата.
управление вызовами
| параметр | что делает |
|---|---|
tool_choice: "auto" | модель решает сама, вызывать функцию или ответить текстом. поведение по умолчанию |
tool_choice: "none" | функции описаны, но вызывать их запрещено |
tool_choice: "required" | ответить текстом нельзя, нужно выбрать хотя бы одну функцию |
tool_choice с именем функции | обязать вызвать конкретную функцию |
parallel_tool_calls: false | запретить несколько вызовов в одном ответе |
поддержка этих параметров зависит от модели: не каждая умеет вызывать несколько функций сразу или подчиняться жёсткому tool_choice.
что дальше
- структурированный вывод — как получить json по схеме, если внешний вызов не нужен.
- генерация текста — устройство запроса и ответа.
частые вопросы
модель сама выполняет функцию?
нет. она только сообщает имя функции и аргументы. выполнить вызов и вернуть результат должно приложение — доступа к коду у модели нет.
почему аргументы приходят строкой?
так устроен формат: поле arguments содержит json в виде текста, который нужно разобрать самостоятельно. значения стоит проверять — модель может ошибиться в типе или пропустить обязательное поле.
работает ли вызов функций с моделями не от openai?
да, формат описания функций одинаковый для всех моделей каталога, которые умеют вызывать инструменты. различаются только аккуратность выбора и поддержка нескольких вызовов за раз.
сколько функций можно описать в одном запросе?
ограничения на число нет, но каждое описание занимает место в контексте и оплачивается как входные токены. чем длиннее список, тем чаще модель выбирает неподходящую функцию.