API · Инструменты

Вызов функций

Модель не выполняет ваш код — она сообщает, какую функцию и с какими аргументами хотела бы вызвать. Выполняете вы, а результат возвращаете следующим сообщением. На этом цикле держатся все агенты в редакторах.

Описать инструмент#

json
{
  "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. 1Отправляете запрос с tools.
  2. 2Модель отвечает finish_reason: "tool_calls". Текста для пользователя в таком ответе нет — только tool_calls.
  3. 3Выполняете функцию у себя.
  4. 4Отправляете новый запрос: всю прежнюю историю, сообщение assistant с tool_calls и сообщение tool с результатом.
  5. 5Модель отвечает текстом — или снова просит инструмент. Шагов может быть несколько подряд.

Ответ модели на втором шаге:

json
{
  "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"
    }
  ]
}

Что вы досылаете на четвёртом:

json
"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, и он сохранён ради совместимости. Разбирать нужно вам:

python
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 не умеет несколько вызовов за ход.