Вызов функций
Модель не выполняет ваш код — она сообщает, какую функцию и с какими аргументами хотела бы вызвать. Выполняете вы, а результат возвращаете следующим сообщением. На этом цикле держатся все агенты в редакторах.
Описать инструмент#
{
"model": "anthropic/claude-sonnet-4.6",
"messages": [{ "role": "user", "content": "Какая погода в Москве?" }],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Текущая погода в городе",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "Название города" },
"units": { "type": "string", "enum": ["c", "f"] }
},
"required": ["city"]
}
}
}
]
}description — не украшение: по нему модель решает, вызывать функцию или ответить сама. «Погода» работает хуже, чем «Текущая погода в городе; возвращает температуру и осадки». То же про описания полей.Цикл вызова#
- 1Отправляете запрос с
tools. - 2Модель отвечает
finish_reason: "tool_calls". Текста для пользователя в таком ответе нет — толькоtool_calls. - 3Выполняете функцию у себя.
- 4Отправляете новый запрос: всю прежнюю историю, сообщение
assistantсtool_callsи сообщениеtoolс результатом. - 5Модель отвечает текстом — или снова просит инструмент. Шагов может быть несколько подряд.
Ответ модели на втором шаге:
{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"Москва\"}" }
}
]
},
"finish_reason": "tool_calls"
}
]
}Что вы досылаете на четвёртом:
"messages": [
{ "role": "user", "content": "Какая погода в Москве?" },
{
"role": "assistant",
"content": null,
"tool_calls": [
{ "id": "call_abc123", "type": "function",
"function": { "name": "get_weather", "arguments": "{\"city\":\"Москва\"}" } }
]
},
{ "role": "tool", "tool_call_id": "call_abc123", "content": "-3 °C, снег" }
]tool_call_id должен совпадать буквально, а сообщение assistant с вызовом — остаться в истории. Без него модель не понимает, к чему относится результат, и переспрашивает то же самое по кругу.Аргументы — строка#
function.arguments приходит строкой с JSON внутри, а не объектом. Это формат OpenAI, и он сохранён ради совместимости. Разбирать нужно вам:
import json
call = message.tool_calls[0]
args = json.loads(call.function.arguments) # {"city": "Москва"}Модель может прислать невалидный JSON или лишнее поле — оберните разбор в try и на ошибке верните её текст в tool-сообщении. Модель обычно исправляется со второй попытки.
tool_choice#
"auto"— по умолчанию: модель решает сама."none"— запретить вызовы, только текст."required"— обязать вызвать хоть что-то.{"type":"function","function":{"name":"get_weather"}}— обязать вызвать именно эту функцию.
В протоколе Anthropic те же значения записываются иначе (auto, any, none, tool) — шлюз переводит их между форматами, см. Messages.
Несколько вызовов сразу#
Модель может попросить два инструмента одним ответом — тогда в tool_calls два элемента. Выполните оба и дошлите два tool-сообщения, каждое со своим tool_call_id. Порядок между ними не важен, важна полнота: если ответить только на один, модель будет ждать второй.
В стриме#
Аргументы приходят по частям и склеиваются по index — целиком они появляются только к finish_reason. Разбор показан в стриминге.
Устаревшая форма#
Старые клиенты присылают functions и function_call вместо tools и tool_choice. Шлюз принимает и их, переводя в новую форму. В новом коде используйте tools: functions не умеет несколько вызовов за ход.