Backend для AI-агентов: tool calling

Долгое время взаимодействие с большими языковыми моделями (LLM) напоминало общение с очень эрудированным, но запертым в комнате профессором. Вы задаете вопрос — он выдает текст. Но как только дело доходит до реальных действий — проверить остаток на складе, создать тикет в Jira или перевести деньги с одного счета на другой — LLM оказывается бессильна. Она может написать код для этого, но не может выполнить его.
Здесь на сцену выходит концепция Tool Calling (вызов инструментов). Это мост между стохастическим миром вероятностей LLM и детерминированным миром API и баз данных. В этой статье мы разберем, как устроить бэкенд так, чтобы AI-агент не просто «галлюцинировал» ответами, а эффективно управлял вашей инфраструктурой.
Что такое Tool Calling на самом деле?
Многие новички думают, что модель «вызывает функцию» напрямую. Это техническое заблуждение. LLM не имеет доступа к вашему серверу, оперативной памяти или сети.
Tool Calling — это процесс структурированного согласования.
Процесс выглядит так:
- Бэкенд передает модели описание доступных инструментов (название функции, описание того, что она делает, и схему аргументов в формате JSON Schema).
- Модель анализирует запрос пользователя и решает: «Чтобы ответить на этот вопрос, мне нужно вызвать функцию
get_user_balanceс аргументомuser_id=123». - Модель возвращает не текст ответа, а специальный структурированный запрос (обычно JSON) с именем функции и параметрами.
- Ваш бэкенд перехватывает этот JSON, вызывает реальный код (функцию), получает результат и отправляет его обратно модели.
- Модель видит результат выполнения и на его основе формулирует окончательный ответ пользователю.
Таким образом, Tool Calling — это не «магия» нейросети, а итеративный цикл «Запрос $\rightarrow$ Решение $\rightarrow$ Выполнение $\rightarrow$ Анализ».
Архитектура бэкенда: от простого скрипта к агентской системе
Если вы строите серьезного агента, вам недостаточно просто обернуть API OpenAI или Anthropic в FastAPI. Вам нужна инфраструктура, которая обеспечит безопасность, надежность и предсказуемость.
1. Слой описания инструментов (Tool Definition Layer)
Первая проблема — как описывать инструменты. Если написать «функция для получения погоды», модель может ошибиться. Описания должны быть максимально эксплицитными.
Правила хорошего тона при описании инструментов:
-
- Семантическая точность: Вместо
get_dataиспользуйтеfetch_customer_order_history.
- Семантическая точность: Вместо
-
- Типизация: Четко указывайте типы данных (integer, string, enum). Если параметр принимает только значения
['active', 'pending', 'closed'], укажите это в схеме.
- Типизация: Четко указывайте типы данных (integer, string, enum). Если параметр принимает только значения
-
- Подсказки (Hints): В описании аргумента напишите, в каком формате должен быть ID (например, «UUID в формате 8-4-4-4-12»).
2. Диспетчер инструментов (Tool Dispatcher)
Когда модель возвращает запрос на вызов функции, бэкенд должен маршрутизировать этот запрос. В простых системах это огромный if-elif блок, но в промышленной разработке используется паттерн «Реестр».
Вы создаете мапу (словарь), где ключом является имя функции, а значением — сама функция. Это позволяет динамически добавлять новые инструменты без переписывания основного цикла обработки.
3. Слой исполнения и изоляция (Execution Sandbox)
Это самый критический момент с точки зрения безопасности. Вы никогда не должны позволять модели выполнять произвольный код или делать прямые запросы к БД.
Как правильно организовать выполнение:
- Валидация входных данных: Модели иногда ошибаются в типах или придумывают несуществующие аргументы. Используйте Pydantic (в Python) для строгой валидации JSON, пришедшего от LLM.
- Ограничение прав (Least Privilege): API-ключи, которые использует агент, должны иметь доступ только к тем эндпоинтам, которые ему необходимы.
- Тайм-ауты и ретраи: Внешние API могут тормозить. Бэкенд должен уметь обрывать выполнение функции, чтобы пользователь не ждал ответа 30 секунд.
Проблемы и «грабли» при реализации
Реальный продакшн показывает, что Tool Calling — это не только успех, но и ряд проблем, которые нужно решать на стороне бэкенда.
Галлюцинации в аргументах
Модель может вызвать функцию send_email, но вместо реального адреса почты подставить email="user@example.com", потому что она «догадалась» о формате.
-
- Решение: Внедрение слоя предварительной проверки. Если аргумент не прошел валидацию, бэкенд должен вернуть модели ошибку: «Ошибка: переданный email некорректен. Пожалуйста, запросите у пользователя правильный адрес». Модель прочитает эту ошибку и исправит свой запрос.
Бесконечные циклы (Agentic Loops)
Бывает, что модель вызывает функцию A $\rightarrow$ получает ошибку $\rightarrow$ снова вызывает функцию A $\rightarrow$ снова ошибка.
-
- Решение: Введение счетчика итераций (Max Iterations). Если агент сделал более 5–10 вызовов инструментов без финального ответа, выполнение принудительно прерывается с ошибкой.
Проблема контекстного окна
Каждый вызов инструмента и каждый ответ от функции добавляют токены в историю переписки. В длинных сессиях контекстное окно забивается «техническим мусором» (JSON-ответами API).
-
- Решение: Суммаризация истории или удаление промежуточных технических ответов, оставляя только итоговые результаты.
Безопасность: Human-in-the-loop (HITL)
Самый страшный кошмар разработчика — когда агент по ошибке вызывает функцию delete_all_users(). В системах с высоким уровнем ответственности внедряется механизм Human-in-the-loop.
Механика подтверждения:
-
- Модель запрашивает вызов функции
transfer_money(amount=1000, to="acc_123").
- Модель запрашивает вызов функции
-
- Бэкенд не выполняет функцию, а переводит статус запроса в
AWAITING_APPROVAL.
- Бэкенд не выполняет функцию, а переводит статус запроса в
-
- Пользователь в интерфейсе видит кнопку «Подтвердить действие».
-
- Только после клика бэкенд вызывает реальный API и возвращает результат модели.
Итоги и рекомендации
Backend для AI-агентов — это не просто прокси-сервер. Это полноценный оркестратор, который должен быть более жестким и дисциплинированным, чем сама модель.
Чек-лист для архитектора:
- Используйте JSON Schema для всех определений инструментов.
- Реализуйте строгую валидацию через Pydantic или аналоги.
- Изолируйте выполнение функций в отдельных сервисах или контейнерах.
- Ограничьте количество итераций цикла «запрос-ответ».
- Внедрите логирование каждого вызова инструмента для последующего дебага (Tracing).
Переход от простого чат-бота к автономному агенту — это переход от генерации текста к управлению состоянием системы. И именно качество вашего бэкенда определяет, будет ли ваш агент полезным инструментом или источником непредсказуемого хаоса в вашей базе данных.

