Function Calling: полное руководство по GPT и Claude API для вызова инструментов (2026)
(updated )

Function Calling: полное руководство по GPT и Claude API для вызова инструментов (2026)

Кратко

  • Function Calling (вызов инструментов, Tool Use) позволяет LLM в процессе ответа вызывать заданные вами функции — получать данные в реальном времени, работать с внешними системами, выполнять действия
  • OpenAI и Claude поддерживают вызов инструментов — форматы API немного различаются, но логика одинаковая
  • Через OpenAI-совместимый шлюз (например, Ofox.ai) один и тот же код работает с GPT, Claude, DeepSeek и 50+ другими моделями
  • В продакшене ключевые вещи: качество описания инструментов, обработка ошибок, параллельный вызов, управление context window

Содержание

  1. Что такое Function Calling?
  2. Формат OpenAI Function Calling API
  3. Формат Claude Tool Use API
  4. Полная реализация: агент запроса погоды
  5. Параллельный вызов нескольких инструментов
  6. Лучшие практики для продакшена
  7. Бенчмарки и реальные замеры
  8. FAQ
  9. Итоги и план действий

Что такое Function Calling?

Function Calling (также Tool Use, вызов инструментов) — механизм взаимодействия LLM с внешними системами. Суть проста:

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

Ограничения обычной LLM: модель работает только со знаниями из обучающей выборки. Актуальные данные — погода, курсы, записи в базе — ей недоступны.

Что решает Function Calling:

  • Получение данных в реальном времени (погода, курсы валют, котировки)
  • Операции с внешними системами (SQL-запросы, вызовы API, отправка email)
  • Извлечение структурированных данных из текста
  • Многоэтапные рабочие процессы AI-агентов (модель сама планирует и выполняет цепочку действий)

Function Calling vs RAG: RAG подходит для поиска по статическим базам знаний (PDF, документация), Function Calling — для динамических данных и выполнения действий. В продакшене их часто комбинируют.


Формат OpenAI Function Calling API

В OpenAI инструменты передаются через параметр tools — каждый описывается JSON Schema:

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Получает текущую погоду в указанном городе. Вызывать при вопросах о погоде, температуре, осадках.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "Название города, например: Москва, Санкт-Петербург, London"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "Единица температуры, по умолчанию celsius"
                    }
                },
                "required": ["city"]
            },
            "strict": True  # Structured Outputs — модель обязана вернуть валидный JSON
        }
    }
]

Описание ключевых полей:

ПолеОписание
type: "function"Пока поддерживается только тип function
nameИмя функции, допустимы [a-zA-Z0-9_-], до 64 символов
descriptionСамое важное поле — модель решает, вызывать ли инструмент, именно по описанию
parametersСтандартная JSON Schema, описывает структуру параметров
strict: trueВключает Structured Outputs — устраняет ошибки формата параметров

Формат ответа модели при вызове инструмента:

# Когда finish_reason == "tool_calls" — модель хочет вызвать инструмент
response = {
    "choices": [{
        "finish_reason": "tool_calls",
        "message": {
            "role": "assistant",
            "content": None,
            "tool_calls": [{
                "id": "call_abc123",
                "type": "function",
                "function": {
                    "name": "get_weather",
                    "arguments": '{"city": "Москва", "unit": "celsius"}'
                    # Внимание: arguments — это JSON-строка, нужен json.loads()
                }
            }]
        }
    }]
}

Формат Claude Tool Use API

Формат инструментов в Claude похож на OpenAI, но с отличиями в именах полей:

tools = [
    {
        "name": "get_weather",  # Без обёртки type
        "description": "Получает текущую погоду в указанном городе",
        "input_schema": {       # Внимание: input_schema, а не parameters
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "Название города"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
        }
    }
]

Сравнение ключевых отличий OpenAI и Claude:

АспектOpenAIClaude
Структура инструмента{"type":"function","function":{...}}Напрямую {"name":..., "input_schema":...}
Поле схемыparametersinput_schema
Причина остановкиfinish_reason: "tool_calls"stop_reason: "tool_use"
ID результатаtool_call_idtool_use_id
Серверные инструментыНетЕсть (например, web_search_20250305)

Если вы используете OpenAI-совместимый шлюз (например, Ofox.ai), то даже при работе с Claude можно использовать формат tools от OpenAI — шлюз автоматически конвертирует запрос. Один код на все модели.


Полная реализация: агент запроса погоды

Ниже — полный, готовый к продакшену пример на Python. Используем API-шлюз Ofox.ai для вызова любой модели:

import json
import os
from openai import OpenAI

# Единый шлюз Ofox.ai — один ключ для GPT/Claude/DeepSeek и 50+ моделей
client = OpenAI(
    api_key=os.environ.get("OFOX_API_KEY"),
    base_url="https://api.ofox.ai/v1"
)

# Определение инструментов
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Получает текущую погоду в городе. Используется при вопросах о погоде, температуре, вероятности осадков. Не подходит для исторических данных или прогноза на неделю.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "Название города на русском или английском: Москва, Saint Petersburg"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "Единица температуры, по умолчанию celsius"
                    }
                },
                "required": ["city"]
            },
            "strict": True
        }
    }
]

# Имитация инструмента (в реальном проекте — вызов настоящего API)
def get_weather(city: str, unit: str = "celsius") -> dict:
    """Вызов API погоды для получения актуальных данных"""
    # В продакшене: requests.get(f"https://api.weather.com/v1/current?city={city}")
    return {
        "city": city,
        "temperature": 22 if unit == "celsius" else 72,
        "unit": unit,
        "condition": "Ясно",
        "humidity": "45%",
        "wind": "Юго-восточный, 3 м/с"
    }

def run_weather_agent(user_query: str, model: str = "gpt-4o-mini") -> str:
    """
    Запуск агента погоды

    Args:
        user_query: вопрос пользователя на естественном языке
        model: модель — подойдёт любая из каталога Ofox
    Returns:
        Финальный ответ модели
    """
    messages = [
        {"role": "system", "content": "Ты — помощник по погоде. Получай данные через инструменты, потом отвечай пользователю."},
        {"role": "user", "content": user_query}
    ]

    # Раунд 1: модель решает, нужен ли инструмент
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=TOOLS,
        tool_choice="auto"  # Модель решает сама
    )

    message = response.choices[0].message

    # Если инструмент не нужен — возвращаем ответ сразу
    if response.choices[0].finish_reason != "tool_calls":
        return message.content

    # Обработка вызова инструментов
    messages.append(message)  # Добавляем намерение модели в историю

    for tool_call in message.tool_calls:
        # Парсим параметры (arguments — JSON-строка)
        args = json.loads(tool_call.function.arguments)

        # Выполняем соответствующий инструмент
        if tool_call.function.name == "get_weather":
            result = get_weather(**args)
        else:
            result = {"error": f"Неизвестный инструмент: {tool_call.function.name}"}

        # Результат — обратно в историю диалога
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps(result, ensure_ascii=False)
        })

    # Раунд 2: модель генерирует финальный ответ на основе данных
    final_response = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=TOOLS
    )

    return final_response.choices[0].message.content


# Тест
if __name__ == "__main__":
    answer = run_weather_agent("Какая погода в Москве? Нужно ли брать зонт?")
    print(answer)
    # Пример вывода: В Москве сейчас ясно, +22°C, влажность 45%, юго-восточный ветер 3 м/с. Зонт не нужен — отличная погода для прогулки!

Ключевые моменты:

  1. Два вызова API: первый — модель «думает», какой инструмент вызвать; второй — получает результат и формирует ответ
  2. tool_call_id должен совпадать, иначе модель запутается
  3. arguments — это JSON-строка, не объект; нужен json.loads()

Параллельный вызов нескольких инструментов

Современные LLM (GPT-4o, Claude Sonnet) умеют возвращать несколько вызовов инструментов за один ответ — это существенно снижает задержку:

# Регистрируем несколько инструментов
MULTI_TOOLS = [
    {"type": "function", "function": {"name": "get_weather", ...}},
    {"type": "function", "function": {"name": "search_news", ...}},
    {"type": "function", "function": {"name": "get_stock_price", ...}}
]

# Обработка параллельных вызовов (модель может вернуть несколько tool_calls)
if message.tool_calls:
    import concurrent.futures

    def execute_tool(tool_call):
        args = json.loads(tool_call.function.arguments)
        if tool_call.function.name == "get_weather":
            return tool_call.id, get_weather(**args)
        elif tool_call.function.name == "search_news":
            return tool_call.id, search_news(**args)
        # ...

    # Параллельное выполнение всех инструментов — минимальная задержка
    with concurrent.futures.ThreadPoolExecutor() as executor:
        results = list(executor.map(execute_tool, message.tool_calls))

    for tool_call_id, result in results:
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call_id,
            "content": json.dumps(result, ensure_ascii=False)
        })

Выигрыш по времени: если три инструмента работают по 500 мс каждый, последовательный вызов — 1500 мс, параллельный — ~500 мс.


Лучшие практики для продакшена

1. Описание инструмента — самая важная инженерная задача

Точность вызова инструментов зависит не от модели, а от качества описания:

# ❌ Плохое описание
"description": "Погода"

# ✅ Хорошее описание: когда вызывать, когда НЕ вызывать, что возвращает
"description": """Получает актуальную погоду в городе (температура, влажность, ветер, состояние).
Когда использовать: текущая погода, нужен ли зонт, одеваться ли тепло.
Когда НЕ использовать: историческая погода, прогноз на 7 дней (для этого — get_weather_forecast).
Возвращает: температуру (celsius), влажность (%), ветер (направление и скорость), описание."""

2. Ограничение глубины рекурсии

Защита от бесконечных циклов вызова инструментов:

MAX_TOOL_ROUNDS = 3  # Максимум 3 раунда

for round_num in range(MAX_TOOL_ROUNDS):
    response = client.chat.completions.create(...)
    if response.choices[0].finish_reason != "tool_calls":
        break  # Модель завершила работу
    # Обработка вызовов...
else:
    # Достигнут лимит — просим модель подвести итог
    messages.append({"role": "user", "content": "Дай финальный ответ на основе имеющихся данных."})

3. Обработка ошибок: даём модели шанс восстановиться

try:
    result = execute_tool(tool_call)
except Exception as e:
    # Ошибку передаём модели — она решит, что делать
    result = {
        "error": str(e),
        "suggestion": "Попробуйте вызвать с другими параметрами"
    }

messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "content": json.dumps(result, ensure_ascii=False)
})

4. Управление Context Window

Инструменты потребляют много токенов. 58 инструментов — это ~55K токенов, что почти полностью забивает контекст маленьких моделей.

Решения:

  • Динамическая загрузка: передавайте только релевантные инструменты (3-5 штук) исходя из намерения пользователя
  • Обрезка результатов: лимитируйте размер ответа инструмента (например, 2000 символов)
  • Сжатие истории: после N раундов — компрессия ранних сообщений

Больше приёмов работы с API — в документации Ofox.ai для разработчиков.


Бенчмарки и реальные замеры

Точность вызова инструментов (Berkeley BFCL V4)

МодельОдин инструментНесколько инструментовПараллельный вызов
GPT-4o92%87%Да
Claude Sonnet 4.594%89%Да
GPT-4o-mini85%79%Да
DeepSeek-V383%76%Да

Источник: Berkeley Function Calling Leaderboard V4, результаты 2025-2026

Замеры задержки (через Ofox.ai)

СценарийПоследовательноПараллельноУскорение
3 инструмента по 300 мс~1100 мс~450 мс59%
5 инструментов по 200 мс~1300 мс~380 мс71%

Параллельный вызов даёт критическое преимущество при работе с несколькими инструментами.


FAQ

В: Function Calling или RAG — что выбрать?

Задачи разные. RAG (Retrieval-Augmented Generation) — для поиска по статическим базам знаний (документация, FAQ, регламенты). Function Calling — для динамических данных (актуальный курс, свежие записи в БД) и операций (отправка писем, запись в БД). Типичная production-система использует оба подхода: RAG для базы знаний, Function Calling для real-time данных и действий.

В: Инструмент вернул пустой результат или ошибку — что делать?

Передайте ошибку модели в виде структурированного JSON (см. пример выше). Модель решит: повторить вызов с другими параметрами, переключиться на другой инструмент или сообщить пользователю. Нельзя просто проглатывать ошибку — модель начнёт галлюцинировать.

В: Обязательно ли strict: true?

В продакшене — да. strict: true включает Structured Outputs от OpenAI: модель обязана вернуть параметры, соответствующие JSON Schema. Это убирает ~95% ошибок формата. Цена — дополнительные ~100 мс на компиляцию схемы при первом запросе, дальше работает из кеша.

В: Формат инструментов у Claude и OpenAI разный — как писать один код?

Через OpenAI-совместимый шлюз, например Ofox.ai. Пишете в формате OpenAI tools — шлюз конвертирует в input_schema для Claude автоматически. Переключение модели — одна строка model, остальной код без изменений.

В: Сколько инструментов можно передавать без деградации?

Выше 10 — точность падает, расход context window растёт нелинейно. Рекомендации для продакшена:

  • Активных инструментов — не больше 10
  • Динамически выбирайте релевантное подмножество (3-5) на основе запроса пользователя
  • В сложных сценариях используйте технику Tool Search от Anthropic — снижает потребление токенов инструментами на 85%

В: Function Calling тарифицируется отдельно?

Нет. Описания инструментов, запросы на вызов и результаты — всё считается как обычные токены, без дополнительной платы. Но учитывайте: каждый инструмент занимает 100-500 токенов, при большом количестве это заметная статья расходов.

В: Как использовать Function Calling из России?

Напрямую API OpenAI и Anthropic из России недоступны из-за региональных ограничений. Решение — API-агрегатор: например, Ofox.ai поддерживает оплату криптовалютой и в рублях, доступ из России работает без VPN. Регистрация и получение ключа — 5 минут.


Итоги и план действий

Function Calling — ключевая технология для создания полезных AI-приложений. С ним LLM перестаёт быть просто «чат-ботом» и становится агентом, который работает с реальными системами и актуальными данными.

Что можно сделать прямо сейчас:

  1. Начните с простого: выберите один из ваших API (погода, CRM, БД), опишите его как инструмент и протестируйте по шаблону из этой статьи
  2. Отшлифуйте описания: итеративно улучшайте description — описывайте, когда вызывать и когда НЕ вызывать. Это даёт максимальный прирост точности при минимальных затратах
  3. Используйте единый шлюз: через Ofox.ai один код работает с GPT, Claude, DeepSeek — удобно для A/B-тестов и автоматического fallback
  4. Включите strict: true: убирает ошибки формата параметров — must have для продакшена
  5. Задайте лимит рекурсии: защита от бесконечных циклов вызовов

Больше примеров интеграции и документация SDK — на странице для разработчиков Ofox.ai.


Ссылки