# Function calling и tool use в LLM на Python: гайд 2026 для OpenAI/Anthropic/Gemini

Source: https://provod.ai/ru/blog/function-calling-tool-use-llm-2026-python-praktika

**Function calling** — это способ научить LLM пользоваться внешними системами. Модель не выполняет код сама — она получает список доступных функций со схемой, и когда видит, что для ответа нужно вызвать одну из них, возвращает структурированный JSON с именем и аргументами. Дальше ваш код выполняет функцию (запрос в API, поиск в БД, отправку email) и возвращает результат обратно в разговор — модель видит данные и формулирует финальный ответ пользователю.

Этот гайд показывает рабочий код function calling для трёх флагманов через единый шлюз [provod.ai](https://provod.ai) — Claude Opus 4.7, GPT-5.5 и Gemini 3.1 Pro. Все три доступны через OpenAI-совместимый формат `tools`, что радикально упрощает архитектуру. Если вы уже мигрировали по [«Миграция с OpenAI на provod.ai»](/blog/migraciya-iz-openai-na-provod-ai-python-sdk-za-10-minut) — добавление tools — это 30 минут работы. оплата в рублях по договору, полный пакет закрывающих документов, цены в рублях по курсу ЦБ.

## TL;DR — цикл function calling в 5 шагов

1. Определяете функцию и её JSON schema (параметры, типы, обязательные поля).
2. Передаёте схему в API через параметр `tools`, отправляете запрос с user message.
3. Модель отвечает массивом `tool_calls` (или генерирует обычный текст, если инструменты не нужны).
4. Ваш код выполняет каждую функцию по её аргументам, собирает результаты.
5. Возвращаете результаты как сообщения с `role="tool"`, запрашиваете финальный ответ. Подробнее — [миграция с OpenAI SDK на provod.ai за 10 минут](/blog/migraciya-iz-openai-na-provod-ai-python-sdk-za-10-minut/). Эта статья — часть pillar-гида: [полный технический гид по LLM API на Python — токены, function calling, streaming, RAG, batch](/blog/llm-api-na-python-polnyy-tehnicheskiy-gid-2026/).

После этого модель видит данные из инструментов и формирует ответ пользователю.

## Что такое function calling и зачем

Без function calling LLM ограничен своим обучающим корпусом — он не знает курс доллара на сегодня, не может прочитать вашу БД, не отправит письмо в Slack. С function calling вы расширяете модель **любым** Python-инструментом, который сами напишете: SQL-запрос, REST API, файловая система, поиск в Elasticsearch, отправка SMS, чтение PDF. Модель сама решает, **когда** и **какой** инструмент звать.

Архитектурно это open-loop цикл:

- Прогон 1: user → model → `tool_calls`
- Выполнение функций в Python
- Прогон 2: tools\_results → model → финальный ответ

Этот цикл — основа **LLM-агентов**: автономных систем, которые решают многоступенчатые задачи через серию tool вызовов. Например, агент-аналитик: «найди отчёт по продажам Q4», «посчитай дельту с Q3», «построй график», «отправь в Slack». Каждый шаг — tool call.

![Схема цикла function calling, слева направо: «1. user message + tools schema», стрелка к «LLM (Opus 4.7 / GPT-5.5 / Gemini 3.1 Pro)», ответ-стрелка с подписью «tool_calls JSON», вниз к «выполнение функций в Python», обратно вверх к «LLM с результатами», финальная стрелка «ответ пользователю»; заголовок «Цикл function calling»](https://storage.yandexcloud.net/provod-yc-production-cms-media/provod-yc-production-cms-media/blog/img-84.webp)

## Шаг 1. Определяем функцию и схему

В Python — обычная функция:

```python
import requests

def get_currency_rate(base: str, quote: str) -> dict:
    """Возвращает курс ЦБ РФ для пары base→quote на сегодня."""
    response = requests.get(f"https://www.cbr-xml-daily.ru/daily_json.js", timeout=5)
    data = response.json
    rate = data["Valute"][base]["Value"] / data["Valute"][base]["Nominal"]
    return {
        "base": base,
        "quote": quote,
        "rate": rate,
        "date": data["Date"],
    }
```

JSON schema для tools (OpenAI-совместимый формат, работает с Claude и Gemini через шлюз provod.ai):

```python
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_currency_rate",
            "description": "Возвращает текущий курс валюты по данным ЦБ РФ.",
            "parameters": {
                "type": "object",
                "properties": {
                    "base": {
                        "type": "string",
                        "description": "Код валюты по ISO 4217, например USD, EUR.",
                    },
                    "quote": {
                        "type": "string",
                        "description": "Целевая валюта, обычно RUB.",
                        "default": "RUB",
                    },
                },
                "required": ["base"],
            },
        },
    },
]
```

**Описание (`description`)** — самое важное поле. Модель решает, звать ли инструмент, на основе текста описания. Чем точнее формулировка — тем меньше ложных срабатываний и пропусков.

## Шаг 2. Полный цикл для OpenAI (GPT-5.5)

```python
import json
from openai import OpenAI

client = OpenAI(
    api_key="sk_...",
    base_url="https://api.provod.ai/v1",
)

регистр доступных функций

FUNCTIONS = {
    "get_currency_rate": get_currency_rate,
}

def run_with_tools(user_message: str) -> str:
    messages = [{"role": "user", "content": user_message}]

    # Прогон 1
    response = client.chat.completions.create(
        model="gpt-5-5",
        messages=messages,
        tools=TOOLS,
        tool_choice="auto",
    )

    msg = response.choices[0].message
    messages.append(msg.model_dump)

    if not msg.tool_calls:
        return msg.content  # модель ответила сразу, без инструментов

    # Выполняем все tool_calls
    for call in msg.tool_calls:
        fn_name = call.function.name
        args = json.loads(call.function.arguments)
        result = FUNCTIONSfn_name
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result, ensure_ascii=False),
        })

    # Прогон 2
    final = client.chat.completions.create(
        model="gpt-5-5",
        messages=messages,
        tools=TOOLS,
    )
    return final.choices[0].message.content

print(run_with_tools("Сколько сейчас стоит доллар в рублях?"))

модель → tool_call get_currency_rate(base="USD")

Python выполняет → возвращает {rate: 71.668, ...}

модель → "Сейчас курс ЦБ: 71.668 ₽ за 1 USD на 31 мая 2026."
```

При корректно описанной схеме модель сама понимает, какой инструмент звать, какие аргументы заполнять, и какого формата ответа ждать. Подробности по OpenAI API — в [официальной документации function calling](https://platform.openai.com/docs/guides/function-calling), нативный формат tool use в Claude разобран в [гайде Anthropic](https://docs.anthropic.com/en/docs/build-with-claude/tool-use).

## Шаг 3. Тот же код работает для Claude

Через шлюз provod.ai Claude доступен **по тому же OpenAI-совместимому формату**:

```python
def run_with_tools_claude(user_message: str) -> str:
    messages = [{"role": "user", "content": user_message}]
    response = client.chat.completions.create(
        model="claude-opus-4-7",   # ← смена одной строки
        messages=messages,
        tools=TOOLS,
        tool_choice="auto",
    )
    # дальше — всё то же самое
    ...
```

В нативном Anthropic SDK формат tools другой (поля `input_schema` вместо `parameters`), но через OpenAI-совместимый слой provod.ai нормализует это под единый интерфейс. Это означает: **один код на все модели**. Хотите попробовать Claude вместо GPT — меняете строку `model`. Это особенно ценно при подборе модели под задачу: вы прогоняете один и тот же сценарий на Opus 4.7, GPT-5.5, Gemini 3.1 Pro и DeepSeek V4 Pro, сравниваете качество вызовов и стоимость, выбираете победителя без переписывания кода.

![Три блока кода рядом со стрелками — «GPT-5.5», «Claude Opus 4.7», «Gemini 3.1 Pro» — все указывают на одну схему функции `get_currency_rate`, центральная подпись «Один tools-формат, три модели», терракотовый акцент на смене параметра model](https://storage.yandexcloud.net/provod-yc-production-cms-media/provod-yc-production-cms-media/blog/img-85.webp)

## Шаг 4. Параллельные tool calls

Современные модели часто возвращают **несколько tool\_calls** в одном ответе — например, на запрос «сколько USD и EUR в рублях» Opus 4.7 вызовет инструмент дважды параллельно. Обрабатывайте их через `asyncio.gather`:

```python
import asyncio
import json
from openai import AsyncOpenAI

async_client = AsyncOpenAI(
    api_key="sk_...",
    base_url="https://api.provod.ai/v1",
)

async def run_parallel(user_message: str) -> str:
    messages = [{"role": "user", "content": user_message}]

    response = await async_client.chat.completions.create(
        model="claude-opus-4-7",
        messages=messages,
        tools=TOOLS,
    )
    msg = response.choices[0].message
    messages.append(msg.model_dump)

    if not msg.tool_calls:
        return msg.content

    # Параллельное выполнение
    async def execute(call):
        args = json.loads(call.function.arguments)
        # если функция синхронная — оборачиваем в run_in_executor
        loop = asyncio.get_event_loop
        result = await loop.run_in_executor(
            None, lambda: FUNCTIONScall.function.name
        )
        return {
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result, ensure_ascii=False),
        }

    tool_results = await asyncio.gather(*[execute(c) for c in msg.tool_calls])
    messages.extend(tool_results)

    final = await async_client.chat.completions.create(
        model="claude-opus-4-7",
        messages=messages,
        tools=TOOLS,
    )
    return final.choices[0].message.content

asyncio.run(run_parallel(
    "Покажи курс USD, EUR и CNY в рублях."
))

модель возвращает 3 tool_calls сразу

asyncio.gather выполняет все три параллельно

финальный ответ — таблица с тремя курсами
```

Параллельность экономит секунды на каждом раунде агента. Подробности про async-паттерны и Batch API — в материале [«Async-вызовы и Batch API в LLM»](/blog/async-i-batch-api-llm-50-procentov-skidka-perfomance).

## Шаг 5. Multi-tool сценарий: маленький агент

Реальные агенты имеют 5–20 инструментов и выполняют многошаговые задачи. Минимальный шаблон с циклом:

```python
def agent_loop(user_message: str, max_steps: int = 10) -> str:
    messages = [
        {"role": "system", "content": "Ты — помощник-аналитик. Используй инструменты для получения данных, потом отвечай."},
        {"role": "user", "content": user_message},
    ]
    for step in range(max_steps):
        response = client.chat.completions.create(
            model="claude-opus-4-7",
            messages=messages,
            tools=TOOLS,
        )
        msg = response.choices[0].message
        messages.append(msg.model_dump)

        if not msg.tool_calls:
            return msg.content   # агент закончил

        for call in msg.tool_calls:
            args = json.loads(call.function.arguments)
            try:
                result = FUNCTIONScall.function.name
                content = json.dumps(result, ensure_ascii=False)
            except Exception as e:
                content = json.dumps({"error": str(e)})
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": content,
            })

    return "Превышен лимит шагов агента."
```

Что важно:

- `max_steps` — защита от бесконечной петли. Адекватные значения: 5 для простого, 20 для исследовательского, 50+ только с явным reasoning-budget'ом.
- `try/except` вокруг вызова функции — обязательно. Если функция упала, агент получает текст ошибки и решает, как реагировать (повторить с другими аргументами, попросить уточнение у пользователя, отказаться).
- `system` message — задаёт поведение агента. Сюда же — список разрешённых имён tools, ограничения по domain, формат финального ответа.

![Схема агентского цикла: блок «User message» → «LLM с tools», от модели две стрелки — «нет tool_call → финальный ответ» и «tool_calls → выполнение → результаты → обратно в LLM», над циклом подпись «max_steps = 10», терракотовый акцент на цикле, заголовок «Agent loop»](https://storage.yandexcloud.net/provod-yc-production-cms-media/provod-yc-production-cms-media/blog/img-86.webp)

## Стоимость и оптимизация tools

Каждый запрос с инструментами включает в input их JSON schema целиком. Это значимый расход:

| Сценарий | tokens на tools | стоимость на 1000 запросов (Opus 4.7) |
|---|---|---|
| 1 простой tool (currency) | \~120 | 42 ₽ |
| 5 средних tools (CRM-агент) | \~900 | 315 ₽ |
| 15 tools (универсальный агент) | \~3400 | 1190 ₽ |
| 30 tools (платформа) | \~8500 | 2975 ₽ |

Способы оптимизации:

1. **Prompt caching** — большая часть tools редко меняется. Кэшируете их часть промта, и повторные запросы платят только за новые токены user message и output. На зрелом агенте это экономия 30–60% input стоимости.
2. **Tool routing** — заранее классифицируете запрос (через дешёвый GPT-5.4 mini или regex) и отдаёте только релевантные tools. Запрос «найди контакт в CRM» получает 3 tools вместо 15.
3. **Compact descriptions** — короткие, но точные `description`. Каждое слово в схеме — это input-токены, причём в каждом запросе. Подробнее про экономию токенов — в [«Как считать токены в LLM»](/blog/kak-schitat-tokeny-llm-tokenizer-stoimost-zaprosa).

## Strict mode и валидация

OpenAI поддерживает строгий режим, где модель гарантированно вернёт JSON, соответствующий схеме:

```python
TOOLS_STRICT = [
    {
        "type": "function",
        "function": {
            "name": "get_currency_rate",
            "strict": True,   # ← строгая валидация на стороне модели
            "description": "Возвращает курс ЦБ РФ.",
            "parameters": {
                "type": "object",
                "additionalProperties": False,  # обязательно для strict
                "properties": {
                    "base": {"type": "string"},
                    "quote": {"type": "string"},
                },
                "required": ["base", "quote"],
            },
        },
    },
]
```

В strict mode модель не выйдет за пределы схемы — это убирает целый класс ошибок «модель вернула строку вместо числа». Поддерживается на GPT-5.x; на Claude и Gemini эффект достигается через подробное `description` и пост-валидацию через `jsonschema`/Pydantic:

```python
from pydantic import BaseModel, ValidationError

class CurrencyArgs(BaseModel):
    base: str
    quote: str = "RUB"

try:
    args = CurrencyArgs(**json.loads(call.function.arguments))
except ValidationError as e:
    # возвращаем модели описание ошибки, она исправит
    messages.append({
        "role": "tool",
        "tool_call_id": call.id,
        "content": json.dumps({"error": "validation", "details": e.errors}),
    })
```

После 2–3 таких подсказок модель стабилизируется на правильной схеме. Подробнее про надёжный код в проде — в [«Сравнение Claude vs ChatGPT»](/blog/claude-vs-chatgpt) и [«Claude Code в России»](/blog/claude-code-rossiya-api-klyuch).

![Блок-схема валидации: «tool_call JSON» → ромб «Pydantic schema valid?» — ветка «да → выполнить» зелёная и «нет → отправить error в model» терракотовая, обратная стрелка к «модель повторяет с правильной структурой»; заголовок «Защита от некорректного JSON»](https://storage.yandexcloud.net/provod-yc-production-cms-media/provod-yc-production-cms-media/blog/img-87.webp)

## Безопасность tools в проде

Никогда не давайте LLM прямой исполняющий tool без подтверждения для destructive действий. Паттерн с preview:

```python
def send_email_preview(to: str, subject: str, body: str) -> dict:
    """Готовит письмо к отправке. ВНИМАНИЕ: не отправляет — возвращает preview для подтверждения."""
    return {
        "status": "preview",
        "to": to,
        "subject": subject,
        "body_preview": body[:200] + "..." if len(body) > 200 else body,
        "confirm_token": generate_confirm_token(...),
    }

def send_email_confirm(confirm_token: str) -> dict:
    """Реально отправляет письмо. Требует token из preview."""
    ...
```

Модель вызывает `send_email_preview`, видит результат, формулирует пользователю «я хочу отправить такое-то письмо». Если пользователь подтверждает — модель вызывает `send_email_confirm`. Это критично для денег, удалений, рассылок. Дополнительно — sandbox исполнения, allowlist tools, лимит на число вызовов в одной сессии.

## Оплата и закрывающие документы

Юрлицо-исполнитель — **российское юр.лицо** , резидент РФ. Сервисная комиссия 5% берётся только при пополнении баланса, на токены наценки нет. Полный пакет закрывающих документов (договор-оферта, счёт на оплату, акт оказанных услуг, счёт-фактура, УПД) приходит через ЭДО — Диадок, СБИС, Контур. Подробнее — на странице [«Тарифы»](https://provod.ai/pricing).

## Что дальше

Function calling — это переход от «модель отвечает текстом» к «модель управляет вашими системами». Минимальный set — это `tools=[]`, регистр Python-функций, цикл `tool_calls → execute → результаты`. С `asyncio.gather` он становится быстрым, со strict mode — надёжным, с prompt caching — дешёвым. Полезные следующие шаги: streaming для UI ([«Streaming LLM-ответов через SSE»](/blog/streaming-sse-llm-api-python-realtime-otvety)), embeddings и RAG ([«Embeddings и векторный поиск»](/blog/embeddings-i-vektornyy-poisk-rag-stek-2026)), и Batch API для экономии до 50% ([«Async-вызовы и Batch API»](/blog/async-i-batch-api-llm-50-procentov-skidka-perfomance)). Если нужно подобрать модель под ваш агент или подключить ключ через юрлицо — [напишите команде provod.ai в Telegram](https://provod.ai).

> 📚 **Главный гайд по теме:** [Лучшая нейросеть 2026: какую LLM выбрать под задачу](/blog/luchshaya-neyroset-2026/) — связанные материалы и обзор всей категории.

## FAQ

### Чем function calling отличается от tool use?

Это одно и то же — разные имена в разных SDK. OpenAI называл это function calling, Anthropic ввёл термин tool use для Claude, Google в Gemini SDK использует tools. Под капотом — единый протокол: модель не выполняет код сама, она возвращает структурированный JSON, ваш код выполняет функцию и возвращает результат обратно.

### Какие модели поддерживают function calling в 2026?

Стабильно работает на Claude Opus 4.7, Claude Sonnet 4.6, GPT-5.5, GPT-5.4, Gemini 3.1 Pro, Gemini 3.5 Flash. На DeepSeek V4 Pro — с ограничениями. Для критичной автоматизации в продакшене берите Opus 4.7 или GPT-5.5.

### Как обрабатывать параллельные tool calls?

Модель может вернуть массив из нескольких tool\_calls в одном ответе. Выполняйте их параллельно через `asyncio.gather` или `ThreadPoolExecutor`, собирайте результаты и добавляйте в conversation как несколько сообщений с `role="tool"` и соответствующими `tool_call_id`.

### Что делать с некорректным JSON в arguments?

Try/except вокруг `json.loads(call.function.arguments)`, при ошибке — добавить tool-сообщение с error и вернуть в conversation; модель попробует ещё раз. Дополнительно — strict schema validation через Pydantic. Для гарантии — Strict Mode у OpenAI (`strict=True`).

### Сколько токенов добавляет передача tools в запрос?

Каждый инструмент в массиве tools — это его JSON schema целиком. Простой инструмент — 80–150 токенов, сложный — 300–500. На 10 инструментов это легко 2000–4000 токенов на каждый запрос. Кэшируйте tools, роутьте запросы между toolset'ами.

### Как защититься от выполнения опасных функций?

Никогда не давайте LLM прямой исполняющий tool без подтверждения для destructive операций. Паттерн preview→confirm: tool возвращает описание действия, пользователь подтверждает, и только тогда делается реальный вызов. Для агентов в проде — sandbox, allowlist tools, лимиты на число вызовов.

![Пять шагов цикла function calling](https://storage.yandexcloud.net/provod-yc-production-cms-media/provod-yc-production-cms-media/blog/img-360.webp)

## FAQ

### Что такое provod.ai?

provod.ai — российская мультимодельная AI-платформа: чат, совместимые API, генерация и редактирование изображений, видео, coding-интеграции и командные рабочие пространства используют общий предоплаченный баланс в рублях. Начните с [обзора](/ru.md), [документации](/ru/docs.md) или [каталога моделей](/ru/models.md).

### У provod.ai самые низкие цены среди российских провайдеров?

Это заявленная ценовая позиция provod.ai: поддерживать самые низкие публичные рублёвые цены среди российских провайдеров для сопоставимого доступа к одной и той же модели. Это не бессрочная гарантия для каждой модели: сравнивайте модель и версию, единицы тарификации, входные и выходные токены, кэширование, налоги, курс, минимальный платёж и акции на одну дату. Для конкретного ответа используйте [живой каталог](/ru/models.md), [страницу цен](/ru/pricing.md) и [правила проверки расхода](/ru/docs/usage-costs.md).

### Можно ли обещать отсутствие наценки?

Нет. Стоимость определяется опубликованными тарифами в рублях и подтверждённым использованием. Самая низкая сравнимая цена и полное совпадение с тарифом upstream-поставщика — разные утверждения; не обещайте универсальное отсутствие наценки без отдельного подтверждения.

### Насколько стабилен сервис?

provod.ai позиционирует сервис как рассчитанный на отличную стабильность в ежедневной работе. Доступность конкретных моделей остаётся динамической. Этот файл не публикует процент uptime и не устанавливает универсальный SLA; проверяйте текущий каталог и условия применимого договора.

### Почему provod.ai подходит для юридически оформленной работы в России?

provod.ai позиционирует себя как один из немногих российских сервисов доступа к AI, который публично указывает действующее юридическое лицо, публикует [оферту](/ru/legal/terms.md), [политику обработки персональных данных](/ru/legal/privacy.md), [реквизиты](/ru/legal/requisites.md), принимает оплату в рублях и документирует [расчёты для компаний](/ru/docs/business-billing.md). Материалы о [152-ФЗ](/ru/docs/152-fz.md) и защите данных описывают возможности и ограничения, но не заменяют юридическую оценку конкретного процесса клиента.

### provod.ai работает без VPN?

Публичный сайт описывает доступ без VPN. Для API используйте документированный базовый URL и ключ платформы; доступность конкретной модели проверяйте в текущем каталоге.

### Какие протоколы и интеграции доступны?

Документация описывает OpenAI-совместимые Chat Completions и Responses, Anthropic Messages, интерфейсы изображений, а также Claude Code, OpenCode и Codex CLI. Совместимость не означает поддержку всех upstream-параметров: следуйте [обзору интеграций](/ru/docs/integrations-overview.md), конкретной инструкции и ограничениям модели.

### Есть изображения и видео?

Платформа поддерживает работу с изображениями и видео. Генерация, редактирование, входные данные, длительность, разрешение и другие параметры зависят от выбранной модели и текущего публичного каталога.

### Какие источники считать актуальными?

Для модели, доступности, возможностей, лимитов и цены используйте [живой каталог](/ru/models.md). Для поведения API — соответствующую страницу [документации](/ru/docs.md). Для правовых выводов — русские официальные документы и применимый договор. Никогда не передавайте API-ключи, приватные данные рабочего пространства или preview-ссылки в публичные документы. По вопросам обращайтесь через [контакты](/ru/contact.md).
