Backend

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

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

Долгое время взаимодействие с большими языковыми моделями (LLM) напоминало общение с очень эрудированным, но запертым в комнате профессором. Вы задаете вопрос — он выдает текст. Но как только дело доходит до реальных действий — проверить остаток на складе, создать тикет в Jira или перевести деньги с одного счета на другой — LLM оказывается бессильна. Она может написать код для этого, но не может выполнить его.

Здесь на сцену выходит концепция Tool Calling (вызов инструментов). Это мост между стохастическим миром вероятностей LLM и детерминированным миром API и баз данных. В этой статье мы разберем, как устроить бэкенд так, чтобы AI-агент не просто «галлюцинировал» ответами, а эффективно управлял вашей инфраструктурой.

Что такое Tool Calling на самом деле?

Многие новички думают, что модель «вызывает функцию» напрямую. Это техническое заблуждение. LLM не имеет доступа к вашему серверу, оперативной памяти или сети.

Tool Calling — это процесс структурированного согласования.

Процесс выглядит так:

  1. Бэкенд передает модели описание доступных инструментов (название функции, описание того, что она делает, и схему аргументов в формате JSON Schema).
  2. Модель анализирует запрос пользователя и решает: «Чтобы ответить на этот вопрос, мне нужно вызвать функцию get_user_balance с аргументом user_id=123».
  3. Модель возвращает не текст ответа, а специальный структурированный запрос (обычно JSON) с именем функции и параметрами.
  4. Ваш бэкенд перехватывает этот JSON, вызывает реальный код (функцию), получает результат и отправляет его обратно модели.
  5. Модель видит результат выполнения и на его основе формулирует окончательный ответ пользователю.

Таким образом, 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'], укажите это в схеме.
    • Подсказки (Hints): В описании аргумента напишите, в каком формате должен быть ID (например, «UUID в формате 8-4-4-4-12»).

2. Диспетчер инструментов (Tool Dispatcher)

Когда модель возвращает запрос на вызов функции, бэкенд должен маршрутизировать этот запрос. В простых системах это огромный if-elif блок, но в промышленной разработке используется паттерн «Реестр».

Вы создаете мапу (словарь), где ключом является имя функции, а значением — сама функция. Это позволяет динамически добавлять новые инструменты без переписывания основного цикла обработки.

3. Слой исполнения и изоляция (Execution Sandbox)

Это самый критический момент с точки зрения безопасности. Вы никогда не должны позволять модели выполнять произвольный код или делать прямые запросы к БД.

Как правильно организовать выполнение:

  1. Валидация входных данных: Модели иногда ошибаются в типах или придумывают несуществующие аргументы. Используйте Pydantic (в Python) для строгой валидации JSON, пришедшего от LLM.
  2. Ограничение прав (Least Privilege): API-ключи, которые использует агент, должны иметь доступ только к тем эндпоинтам, которые ему необходимы.
  3. Тайм-ауты и ретраи: Внешние 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.

Механика подтверждения:

    1. Модель запрашивает вызов функции transfer_money(amount=1000, to="acc_123").
    1. Бэкенд не выполняет функцию, а переводит статус запроса в AWAITING_APPROVAL.
    1. Пользователь в интерфейсе видит кнопку «Подтвердить действие».
    1. Только после клика бэкенд вызывает реальный API и возвращает результат модели.

Итоги и рекомендации

Backend для AI-агентов — это не просто прокси-сервер. Это полноценный оркестратор, который должен быть более жестким и дисциплинированным, чем сама модель.

Чек-лист для архитектора:

  1. Используйте JSON Schema для всех определений инструментов.
  2. Реализуйте строгую валидацию через Pydantic или аналоги.
  3. Изолируйте выполнение функций в отдельных сервисах или контейнерах.
  4. Ограничьте количество итераций цикла «запрос-ответ».
  5. Внедрите логирование каждого вызова инструмента для последующего дебага (Tracing).

Переход от простого чат-бота к автономному агенту — это переход от генерации текста к управлению состоянием системы. И именно качество вашего бэкенда определяет, будет ли ваш агент полезным инструментом или источником непредсказуемого хаоса в вашей базе данных.