> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ruapi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Вызов функций (tool use)

> Как модель вызывает ваши функции через RuAPI: полный пример на Python с OpenAI SDK

Вызов функций (он же *tool use*) — это когда модель не отвечает текстом, а возвращает **структурированный запрос** на вызов вашей функции: её имя и аргументы. Сам код функции выполняете **вы**, на своей стороне, а результат отдаёте модели обратно. Дальше она уже формулирует человеческий ответ.

Звучит просто, но именно на этом механизме держатся все агенты: модель не умеет ходить в базу, считать погоду или дёргать чужой API — зато умеет попросить вас это сделать и разобраться с тем, что вы вернули.

Схема всегда одна и та же:

1. Вы описываете доступные функции (схему) и шлёте обычный запрос с `tools=`.
2. Модель отвечает не текстом, а массивом `tool_calls` — что и с какими аргументами вызвать.
3. Вы выполняете функцию у себя и шлёте **второй** запрос, дописав в историю ответ модели и результат вызова.
4. Модель возвращает финальный текст.

## Полный пример на Python

Возьмём классику — функцию `get_weather(city)`. В реальности она бы дёргала какой-нибудь сервис погоды; здесь для наглядности возвращаем заглушку.

```python theme={null}
import json
from openai import OpenAI

client = OpenAI(
    api_key="sk-ВАШ_КЛЮЧ",
    base_url="https://www.ruapi.ai/v1",
)

# Это ваш реальный код. Модель его НЕ выполняет — только просит вызвать.
def get_weather(city: str) -> str:
    # тут был бы запрос к погодному API
    return f"В городе {city} сейчас +18°C, переменная облачность"

# Описание функции для модели: что она умеет и какие аргументы принимает
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Возвращает текущую погоду в указанном городе",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "Название города, например «Москва»",
                    }
                },
                "required": ["city"],
            },
        },
    }
]

messages = [{"role": "user", "content": "Какая сейчас погода в Москве?"}]

# Шаг 1. Первый запрос — отдаём модели вопрос и список инструментов
response = client.chat.completions.create(
    model="claude-opus-4-8",  # точные имена — на www.ruapi.ai
    messages=messages,
    tools=tools,
)

message = response.choices[0].message

# Шаг 2. Проверяем, захотела ли модель вызвать функцию
if message.tool_calls:
    # ВАЖНО: сначала дописываем в историю сам ответ модели (с tool_calls)
    messages.append(message)

    # Шаг 3. Обрабатываем каждый вызов (их может быть несколько)
    for tool_call in message.tool_calls:
        # Аргументы приходят СТРОКОЙ с JSON — её нужно распарсить
        args = json.loads(tool_call.function.arguments)

        if tool_call.function.name == "get_weather":
            result = get_weather(args["city"])
        else:
            result = "Неизвестная функция"

        # Результат вызова отдаём отдельным сообщением role="tool"
        messages.append(
            {
                "role": "tool",
                "tool_call_id": tool_call.id,  # привязка к конкретному вызову
                "content": result,
            }
        )

    # Шаг 4. Второй запрос — теперь модель видит результат и даёт финальный ответ
    final = client.chat.completions.create(
        model="claude-opus-4-8",
        messages=messages,
        tools=tools,
    )
    print(final.choices[0].message.content)
else:
    # Модель решила ответить сразу, без вызова функции
    print(message.content)
```

<Note>
  Tool use поддерживают не все модели — уверенно с ним работают семейства [Claude](/ru/claude-api) и [GPT](/ru/gpt-api).
  Старые и облегчённые модели могут игнорировать `tools=`. Какие модели что умеют —
  смотрите по бейджам возможностей на странице **«Цены»** на [www.ruapi.ai](https://www.ruapi.ai).
</Note>

## Подводные камни

<AccordionGroup>
  <Accordion title="Модель может попросить несколько вызовов сразу">
    В одном ответе `message.tool_calls` бывает **массивом** из нескольких вызовов —
    например, погода сразу в двух городах. Обходите его циклом и на **каждый**
    `tool_call` добавляйте отдельное сообщение `role="tool"` со своим `tool_call_id`.
    Если хоть один вызов оставить без ответа — следующий запрос упадёт с ошибкой.
  </Accordion>

  <Accordion title="Сначала ответ модели, потом результат функции">
    Порядок в `messages` строгий: сперва дописываете **сам ответ ассистента** (объект
    `message` с `tool_calls`), и только **после** него — сообщения `role="tool"` с
    результатами. Если отправить результат без предшествующего ответа ассистента,
    API вернёт ошибку о несогласованной истории.
  </Accordion>

  <Accordion title="Аргументы приходят строкой, а не объектом">
    `tool_call.function.arguments` — это **строка с JSON**, а не готовый словарь.
    Перед использованием её обязательно нужно распарсить: `json.loads(...)`.
    Прямое обращение по ключу к строке выдаст ошибку.
  </Accordion>
</AccordionGroup>

## Что дальше

* [Быстрый старт](/ru/quickstart) — регистрация, ключ и первый запрос
* [Ошибки и коды ответов](/ru/errors) — что делать, если запрос упал
