ЧТО ТАКОЕ FUNCTION CALLING И ЗАЧЕМ ОН
Голая LLM умеет одно — генерировать текст. Она не знает погоду прямо сейчас, не видит вашу БД, не считает надёжно большие числа. Function calling (он же tool use) закрывает эту дыру: вы описываете набор инструментов, модель сама решает, когда и с какими аргументами их вызвать, а исполнение остаётся за вашим кодом.
Ключевой момент, который путают новички: модель не выполняет функцию. Она лишь возвращает JSON вида «хочу вызвать get_weather с аргументом city=Berlin». Реальный HTTP-запрос, поход в базу, запуск скрипта — всё это делаете вы. Модель — диспетчер, а не исполнитель. Это разграничение — фундамент безопасности всей схемы.
Почему это взлетело именно как отдельная фича, а не «попросим модель вернуть JSON в тексте»? Потому что современные модели дообучены на tool use: они выделяют вызовы в отдельный канал ответа, соблюдают схему аргументов и понимают, что делать с результатом. Парсить JSON из свободного текста регуляркой в 2026 — антипаттерн.
КАК ВЫГЛЯДИТ ЦИКЛ ВЫЗОВА
Полный оборот tool use — это всегда минимум два обращения к API. Модель не отвечает пользователю сразу, если ей нужен инструмент:
- Запрос. Вы шлёте сообщение пользователя плюс список описаний инструментов.
- Решение модели. Модель возвращает не текст, а запрос на вызов: имя функции + аргументы, уже провалидированные под вашу JSON schema.
- Исполнение. Ваш код выполняет функцию — идёт в API, БД, файловую систему.
- Возврат результата. Вы дописываете результат в историю диалога и снова зовёте модель.
- Финальный ответ. Модель формулирует человекочитаемый ответ, опираясь на данные инструмента. Либо решает вызвать ещё один инструмент — тогда цикл повторяется.
Именно этот петлевой характер («вызов → результат → вызов → …») превращает обычный чат в агента. Агент — это function calling в цикле с условием остановки.
JSON SCHEMA — СЕРДЦЕ ОПИСАНИЯ ИНСТРУМЕНТА
Модель понимает инструмент ровно настолько, насколько хорошо вы его описали. Описание — это JSON schema: имя, человекочитаемое описание, типы и обязательность параметров. От качества этого текста напрямую зависит, дёрнет ли модель нужную функцию с правильными аргументами.
Практический минимум, который реально влияет на попадание:
- description у самой функции — 3-4 предложения о том, когда её звать, а не только что она делает. Пишите как для джуна.
- description у каждого параметра — формат, единицы, примеры. «Город на английском» лучше, чем «город».
- enum, где значения фиксированы. Это режет галлюцинации аргументов почти в ноль.
- required — явно перечислите. Иначе модель начнёт домысливать необязательные поля.
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "Возвращает текущую погоду в городе. "
"Вызывай, когда пользователь спрашивает про погоду, "
"температуру или осадки в конкретном месте.",
"input_schema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Название города на английском, напр. 'Berlin'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Единица измерения температуры"
}
},
"required": ["city"]
}
}
]
resp = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Какая погода в Берлине?"}]
)
# resp.stop_reason == "tool_use" — модель просит вызвать функцию
for block in resp.content:
if block.type == "tool_use":
print(block.name, block.input) # get_weather {'city': 'Berlin'}
Дальше вы исполняете get_weather('Berlin'), а результат возвращаете новым сообщением с ролью user и блоком tool_result, ссылаясь на tool_use_id. Модель во втором запросе выдаст текст «В Берлине сейчас +12°C, облачно».
ФОРМАТЫ: ANTHROPIC VS OPENAI VS OPEN-SOURCE
Концепция одна, обёртки разные. Если вы строите мультипровайдерного агента, знать различия обязательно — иначе ловите ошибки схемы при переключении бэкенда.
| Аспект | Anthropic (Claude) | OpenAI | Open-source (через vLLM/Ollama) |
|---|---|---|---|
| Ключ схемы | input_schema | parameters | Обычно OpenAI-совместимый |
| Обёртка инструмента | плоский объект | {"type":"function","function":{…}} | зависит от шаблона модели |
| Флаг завершения | stop_reason: tool_use | finish_reason: tool_calls | варьируется |
| Параллельные вызовы | да, из коробки | да | частично, зависит от модели |
| Строгое соответствие схеме | высокое | Structured Outputs (strict) | ниже, нужен валидатор |
Тренд 2026: большинство inference-серверов (vLLM, llama.cpp, Ollama) отдают OpenAI-совместимый эндпоинт с tool calling, поэтому де-факто стандарт описания — формат OpenAI. Но качество попадания на open-source моделях 7-8B заметно ниже: без внешней валидации аргументов на прод не выходите. Свежие релизы фреймворков-оркестраторов и локальных движков удобно отслеживать в каталоге REDDYX.
ПАРАЛЛЕЛЬНЫЕ ВЫЗОВЫ И ЦЕПОЧКИ
Две модели поведения, которые важно разделять:
- Параллельные вызовы. На запрос «сравни погоду в Берлине и Токио» модель за один ход возвращает два независимых
tool_use-блока. Вы исполняете их конкурентно (asyncio, thread pool) и возвращаете оба результата разом. Экономит и время, и токены. - Последовательные цепочки. «Найди самый дешёвый рейс и забронируй» — сначала
search_flights, потом на основе результатаbook_flight. Модель не может звать второе, пока не увидела ответ первого. Это уже полноценный агентский цикл.
По наблюдениям сообщества, около половины реальных агентских задач содержат хотя бы одну цепочку из двух и более шагов. Поэтому проектируйте оркестратор как цикл `while stop_reason == tool_use`, а не как единичный обмен.
НАДЁЖНОСТЬ: КАК ПОДНЯТЬ ПРОЦЕНТ ПОПАДАНИЯ
Топовые модели 2026 попадают в правильный инструмент с корректными аргументами примерно в 90-97% случаев на несложных наборах — но с ростом числа инструментов точность падает. Что реально помогает:
- Держите набор инструментов узким. 5-8 функций — комфорт. 30+ — модель начинает путаться. Если инструментов много, вводите роутер: сначала выбор категории, потом узкий поднабор.
- Валидируйте аргументы своим кодом. Никогда не доверяйте, что
cityдействительно город. Модель может галлюцинировать ID, которых нет в базе. - Возвращайте осмысленные ошибки. Если функция упала, верните в
tool_resultтекст ошибки сis_error: true. Модель отлично умеет ретраить с исправленными аргументами. - Не прячьте состояние в description. Описания инструментов кэшируются и должны быть стабильны. Динамику передавайте в системном промпте или аргументах.
- Тестируйте на edge-кейсах. Пустой ввод, неоднозначный запрос, запрос, где инструмент не нужен вовсе. Хорошая схема заставляет модель ответить текстом, а не звать функцию впустую.
MCP И БУДУЩЕЕ ИНСТРУМЕНТОВ
Большой сдвиг последних полутора лет — Model Context Protocol (MCP). Вместо того чтобы вручную описывать каждый инструмент в коде под каждую модель, вы поднимаете MCP-сервер, который отдаёт список инструментов стандартизированно. Клиент (ваш агент, IDE, десктоп-приложение) подключается и получает готовый набор.
Что это меняет на практике:
- Инструменты становятся переиспользуемыми между проектами и провайдерами — один MCP-сервер для Postgres обслуживает любого клиента.
- Появляется экосистема готовых серверов: файлы, БД, поиск, Git, браузер. Многие открытые серверы и коннекторы удобно мониторить через каталог REDDYX.
- Function calling из «фичи API» превращается в транспортный слой между моделью и внешним миром.
Под капотом MCP всё равно опирается на те же JSON schema и тот же цикл вызов-результат. Разберётесь с базовым tool use — MCP освоите за вечер.
Частые вопросы
Чем function calling отличается от structured output?
Structured output заставляет модель вернуть ответ строго по заданной JSON schema — это про формат финального ответа. Function calling — про решение модели вызвать внешнюю функцию и получить её результат. Часто их совмещают: strict-режим гарантирует, что аргументы вызова точно соответствуют схеме.
Модель сама выполняет мою функцию?
Нет. LLM только возвращает имя функции и аргументы в структурированном виде. Реальное исполнение — HTTP-запрос, поход в БД, запуск кода — делает ваш бэкенд. Это ключевая граница безопасности: модель никогда не получает прямой доступ к вашим системам.
Сколько инструментов можно подключить к одной модели?
Технически десятки, но точность выбора падает с ростом числа. Практический комфорт — 5-8 функций. При большем количестве стройте двухуровневый роутер: модель сначала выбирает категорию, потом работает с узким поднабором инструментов.
Что делать, если модель вызывает не тот инструмент?
Улучшите description — добавьте в него явные условия «когда вызывать» и негативные примеры. Используйте enum для фиксированных значений и валидируйте аргументы своим кодом. При ошибке возвращайте её текст в tool_result — модель умеет самокорректироваться на следующем ходу.
Function calling — это тот навык, который отделяет чат-бота от настоящего агента: научитесь описывать инструменты чисто, гонять цикл вызов-результат и валидировать аргументы — и дальше всё масштабируется от одной функции до целого MCP-парка. Хотите видеть свежие инструменты, фреймворки-оркестраторы и MCP-серверы первыми — Telegram-канал REDDYX AI — новые репозитории каждые 30-60 минут.