AI-инжиниринг

Tool Calling / Function Calling: LLM управляет внешними системами

Цели урока

  • Понять механизм tool calling: как LLM генерирует запросы на вызов функций вместо текста
  • Научиться описывать tools через JSON Schema с эффективными descriptions
  • Реализовать полный цикл tool calling: request → tool_call → execute → respond
  • Обрабатывать parallel tool calls через Promise.all для оптимальной производительности
  • Построить production-ready обработчик с валидацией, таймаутами и rate limiting

Предварительные знания

  • Structured Output и JSON Schema
  • OpenAI Chat Completions API
  • Structured Output
  • API Integration

Tool calling - момент когда LLM перестаёт быть текстовым генератором и становится агентом. Модель не выполняет код. Она говорит: "вызови вот это с вот этими аргументами". Разница принципиальная - и в ней вся безопасность. Июнь 2023: OpenAI запускает function calling. Schick et al. (Toolformer, 2023) незадолго до этого показали, что модели могут self-supervised учиться использовать инструменты - но без стандартного API это было лабораторным результатом. Function calling сделал это продуктом. За год - стандарт всей индустрии.

  • ChatGPT Plugins и GPT Actions работают через tool calling: от бронирования ресторанов до покупки авиабилетов - каждое действие это JSON-запрос от модели
  • GitHub Copilot Workspace вызывает инструменты для редактирования файлов, запуска тестов, создания PR - tool calling как основа agentic-интерфейса
  • Stripe AI Assistant вызывает 50+ internal API через function calling - обрабатывает возвраты, проверяет платежи, генерирует отчёты
  • Cursor IDE - весь доступ к codebase, файловой системе, терминалу через tool calling: модель не имеет прямого доступа, только через явные вызовы

От описаний к действиям

**Февраль 2023**: Schick et al. публикуют Toolformer - первая работа о self-supervised обучении LLM использованию инструментов. Модель учится сама решать когда и как вызывать API, калькулятор, поиск. Академически убедительно, но без стандартизированного интерфейса. **Июнь 2023**: OpenAI запускает function calling - первый массовый API для tool use. За одну ночь сотни команд получают возможность подключить LLM к реальным системам. **Конец 2023**: Anthropic выпускает tool use, Google - function calling. Параллельные tool calls появляются с GPT-4o. **2024**: tool_choice, structured tool outputs, MCP (Model Context Protocol) от Anthropic - стандартизация на уровне протокола.

Что такое Tool Calling: LLM как диспетчер

Tool calling - момент когда LLM перестаёт быть текстовым генератором и становится агентом. До июня 2023 GPT-4 умел только описывать действия: "я бы проверил статус заказа". Shopify подключил модель к CRM - бот обработал 12 000 запросов за неделю и не выполнил ни одного. Текстовые описания без исполнения - это именно то, чем LLM является без tool calling.

Модель не выполняет функцию сама. Она говорит: "вызови вот это с вот этими аргументами". Разница принципиальная - и в ней вся безопасность. Исполнение остаётся на стороне backend: проверить права, валидировать аргументы, залогировать, применить rate limit. LLM только генерирует JSON с именем функции и её параметрами - структурированный запрос вместо текста.

Без Tool CallingС Tool Calling
Модель генерирует текст "Я бы проверил..."Модель возвращает JSON: { name: 'check_order', arguments: {...} }
Backend не может распарсить намерениеBackend получает typed вызов функции
Пользователь сам выполняет действиеСистема автоматически выполняет действие
Галлюцинации в формате ответаСтрогая JSON Schema гарантирует формат

Июнь 2023 - OpenAI запускает function calling. До этого момента исследователи экспериментировали: Schick et al. 2023 (Toolformer) показали, что модели можно научить использовать инструменты через self-supervised обучение - но без стандартизированного API. OpenAI function calling стал первым массовым интерфейсом. Anthropic ответил tool use, Google - function calling. За год это превратилось в стандарт индустрии.

LLM вызывает функцию напрямую - как вызов метода в коде

Модель только генерирует JSON: имя функции + аргументы. Никакого исполнения. Backend решает - запускать ли вообще

Это не семантика - это архитектура. Модель не имеет доступа к runtime среде. Она не знает, есть ли функция `delete_account` в продакшне. Она только видит описание в schema и решает: "вот что нужно вызвать". Исполнение, валидация, rate limit, логирование - всё остаётся на стороне кода. Именно поэтому tool calling безопаснее, чем code execution.

Что возвращает LLM при tool calling вместо обычного текстового ответа?

Описание Tools: JSON Schema и описания

Описание tool - это prompt. Не техническая документация, не комментарий в коде. Prompt, по которому модель решает: вызывать ли эту функцию, с какими аргументами, в каком контексте. Исследование Microsoft (2024) показало: одно дополнительное предложение-пример в description повышает accuracy с 73% до 91%. Разрыв в 18 процентных пунктов - от одного предложения.

Правила написания эффективных описаний

  • **name** - глагол + существительное: `search_products`, `create_order`, `get_user_balance`. Не `do_thing` и не `handler`
  • **description функции** - 2-3 предложения: что делает, что возвращает, когда использовать. Описание - это prompt для модели
  • **description параметров** - примеры значений: 'например "красные кроссовки"'. Для enum - когда выбирать каждое значение
  • **required** - только действительно обязательные параметры. Опциональные параметры с описаниями "когда указывать" повышают точность
  • **enum вместо string** - если множество значений ограничено, используй enum. Это исключает галлюцинации

Не добавляй больше 20 tools в один запрос. Исследование показало: при 5 tools accuracy = 97%, при 20 tools = 88%, при 50 tools = 64%. Если нужно много инструментов - группируй их и используй двухэтапный подход: сначала модель выбирает категорию, потом - конкретный tool из категории.

Какой description для функции get_user_balance наиболее эффективен?

Цикл Tool Calling: request → tool_call → execute → respond

Самая частая ошибка при первом знакомстве с tool calling: разработчик получает tool_call от модели - и считает, что на этом всё. Пишет ответ пользователю. Но модель ещё не видела результат функции. Она сказала "вызови get_weather с Москвой" - но не знает, что вернул вызов. Tool calling - это цикл минимум из двух запросов к LLM.

Полная схема цикла

Обратите внимание на `tool_call_id` - это обязательное поле. Каждый tool result должен быть привязан к конкретному tool_call через его id. Без этого API вернёт ошибку. При параллельных tool calls (несколько вызовов за раз) каждый результат должен иметь свой уникальный tool_call_id.

Сколько МИНИМУМ запросов к LLM API требуется для полного цикла tool calling (от вопроса пользователя до финального ответа)?

Parallel Tool Calls: несколько функций за один запрос

"Сравни погоду в Москве и Лондоне" - логичный запрос. Наивная реализация: последовательно вызвать get_weather дважды, два round-trip к LLM. Но модель умнее: она возвращает **оба tool_call в одном ответе**. Parallel tool calls появились вместе с GPT-4o и сразу дали -35-40% к latency для multi-tool запросов.

Параметр `parallel_tool_calls` в API управляет поведением: `true` (по умолчанию) - модель может вернуть несколько tool_calls, `false` - строго один за раз. Отключение полезно, когда tools имеют зависимости: результат первого нужен для второго.

Сценарийparallel_tool_callsПочему
Погода в 3 городах одновременноtrueЗапросы независимы - можно выполнять параллельно
Найти пользователя → получить его заказыfalseВторой вызов зависит от результата первого
Поиск товаров + проверка балансаtrueНезависимые запросы к разным системам
Создать заказ → отправить уведомлениеfalseУведомление нужно только после успешного создания заказа

Пользователь спрашивает: "Какая погода в Москве и сколько стоит зонтик в каталоге?". Модель вернула 2 tool_calls: get_weather и search_products. Как backend должен их обработать?

Error Handling и валидация аргументов

Phantom function - реальный класс ошибок в production. GPT-4 вызвал `delete_user_account` вместо `get_user_account` - оба начинаются с `user_account`, model routing по описанию дал сбой. За 2 часа до обнаружения удалено 37 аккаунтов. Tool calling безопасен ровно настолько, насколько надёжен код вокруг него. Модель генерирует JSON - backend решает, выполнять ли.

Защита от опасных вызовов

ЗащитаЧто предотвращаетРеализация
Валидация имени функцииВызов несуществующей или опасной функцииWhitelist разрешённых имён
Zod-валидация аргументовSQL injection, невалидные данныеТипизированные схемы с ограничениями
Таймаут выполненияЗависание на медленном APIPromise.race с timeout
Rate limiting per toolDDoS через бота ("вызови поиск 1000 раз")Счётчик вызовов per tool per session
Confirmation для опасных toolsСлучайное удаление/изменение данныхПауза перед create_return, delete_account
Лимит итерацийБесконечный цикл tool callsMAX_TOOL_ITERATIONS = 5

При возврате ошибки в tool result - модель обычно пробует исправить вызов. Например, при невалидном order_id модель спросит пользователя уточнить номер. Это **желаемое поведение** - не бросайте исключения, возвращайте описание ошибки в content поля tool result.

Если описать tool в schema, LLM будет вызывать его правильно. Валидация и error handling - забота клиентского кода, а не модели.

LLM регулярно генерирует невалидные аргументы, путает похожие функции и пропускает обязательные поля. Production-системы обязаны валидировать каждый tool call перед исполнением и возвращать структурированные ошибки в loop, чтобы модель могла исправиться.

Schema воспринимается как контракт, который LLM «обязан» соблюдать - это перенос интуиции от строго типизированных API. На деле LLM генерирует JSON как текст, и phantom functions, missing required fields, type mismatch встречаются в 5-15% вызовов даже у GPT-4. Без error-loop эти ошибки превращаются в инциденты в проде.

LLM вызвал функцию create_return с невалидным order_id. Как должен поступить backend?

LLM вызывает функцию сам - как метод в объекте

Модель только генерирует JSON: имя функции + аргументы. Исполнение целиком на стороне backend

Это не просто семантика - это граница безопасности. Модель не имеет доступа к runtime. Она не знает, существует ли `delete_account` в продакшне, есть ли у пользователя права, прошёл ли rate limit. Она видит JSON Schema с описаниями и решает: "вот что нужно вызвать". Backend получает этот JSON, валидирует, проверяет права, логирует - и только потом выполняет. Именно поэтому tool calling безопаснее любого code execution: каждый вызов проходит через явный backend-код.

Итоги

  • Tool calling - граница между LLM-как-текст и LLM-как-агент: модель генерирует JSON-запрос, backend исполняет
  • Описание tool - это prompt: одно предложение-пример поднимает accuracy с 73% до 91%
  • Цикл минимум из 2 запросов: первый возвращает tool_call, второй (с результатом) - финальный ответ пользователю
  • Parallel tool calls через Promise.all: -35-40% к latency для независимых вызовов
  • Production-стек: Zod-валидация + whitelist + таймауты + rate limit + MAX_TOOL_ITERATIONS = 5

Вопросы для размышления

  • Почему именно backend должен решать - выполнять ли tool call? Что изменилось бы, если бы модель сама исполняла функции?
  • Описание tool - это prompt для модели. Как это меняет подход к написанию descriptions по сравнению с обычной документацией?
  • Parallel tool calls экономят latency. Но в каких ситуациях последовательность вызовов принципиальна - и как это повлияет на архитектуру?

Что дальше

Tool calling - один вызов функции. Но что если задача требует цепочки вызовов с рассуждениями между ними? Это уже AI агент - система, которая планирует, рассуждает и действует в цикле.

  • Agent Fundamentals — Tool calling в цикле reasoning - ReAct pattern, planning, memory для агентов
  • Agent Frameworks — LangGraph, CrewAI, AutoGen - фреймворки, абстрагирующие tool calling в агентные системы
  • Error Handling для LLM — Глубже в error handling: retries, fallbacks, graceful degradation для LLM-систем

Связанные уроки

  • aie-07-structured-output — Tool call - это structured output в JSON-схему
  • aie-17-agent-fundamentals — Tool calling - атомарный примитив агентов
  • aie-15-conversation-memory — Агент с памятью требует обоих - memory + tools
  • aie-19-multi-agent — Multi-agent: агенты вызывают друг друга как инструменты
  • bt-09-grpc — Tool schema - это IDL как в gRPC Protobuf
  • st-01-feedback-loops — ReAct цикл observe-think-act - классический feedback loop
  • net-64-api-gateway
Tool Calling / Function Calling: LLM управляет внешними системами

0

1

Войти