Auto-invocation: Автоматический выбор и запуск инструментов
Введение: От ручного управления к автопилоту
Добро пожаловать в третий урок модуля «Агентность и вызов функций». В предыдущих занятиях мы научились описывать инструменты для модели и обрабатывать её запросы вручную. Вы, вероятно, заметили, что паттерн всегда один и тот же: отправить запрос -> получить FunctionCall -> выполнить код -> отправить FunctionResponse -> получить финальный ответ.
Это классический цикл «Человек в контуре» (Human-in-the-loop) на уровне кода. Он надежен, но требует написания большого количества шаблонного кода (boilerplate), особенно если для решения задачи модели требуется вызвать не одну, а пять функций подряд. Например, чтобы «запланировать ужин», агенту может понадобиться: найти ресторан, проверить наличие мест, забронировать стол и отправить приглашение другу.
В этом уроке мы разберем Auto-invocation (автоматический вызов) в SDK Gemini. Мы превратим нашу архитектуру из пошагового контроля в автономную систему, где модель сама «крутит педали» цикла выполнения до тех пор, пока задача не будет решена или не потребуется вмешательство пользователя.
Концепция автоматического цикла выполнения
Суть Auto-invocation заключается в делегировании управления циклом «мысль — действие — наблюдение» (Thought-Act-Observe) на сторону клиентской библиотеки (SDK). Вместо того чтобы ваша программа останавливалась каждый раз, когда модель хочет вызвать функцию, SDK перехватывает это желание, исполняет функцию и молча возвращает результат модели.
Как это работает «под капотом»:- Инициация: Вы отправляете промпт (например, «Какая погода в Париже и Лондоне?»).
- Первый проход модели: Gemini анализирует запрос и видит, что у неё есть инструмент
get_weather. Она решает вызвать его для Парижа. Вместо возврата текста пользователю, она генерирует структуру вызова функции. - Перехват SDK: Клиентская библиотека обнаруживает, что модель вернула не текст, а запрос на вызов. Если включен авто-вызов, библиотека находит соответствующую Python-функцию в вашем коде и запускает её.
- Инъекция результата: Результат (например, «Париж: +15, Облачно») автоматически форматируется в объект
FunctionResponseи отправляется обратно в модель. При этом история чата обновляется. - Рекурсия: Модель получает данные о Париже, «вспоминает», что нужно ещё узнать про Лондон, и снова генерирует вызов функции. SDK повторяет процесс.
- Финализация: Когда модель получила все данные, она генерирует финальный текстовый ответ для пользователя. Только в этот момент управление возвращается в ваш основной код.
import google.generativeai as genai
import os
# Настройка API ключа
genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
# 1. Определяем наши функции (Инструменты)
def get_exchange_rate(currency_from: str, currency_to: str) -> dict:
"""Возвращает текущий курс обмена валют."""
# Имитация запроса к API биржи
rates = {
"USD-EUR": 0.92,
"EUR-USD": 1.09,
"USD-RUB": 92.5,
"BTC-USD": 65000.0
}
key = f"{currency_from}-{currency_to}"
return {"rate": rates.get(key, "Unknown pair")}
def calculate_total(amount: float, rate: float) -> float:
"""Умножает сумму на курс."""
return amount * rate
# 2. Создаем словарь инструментов для автоматического исполнения
# Это ключевой момент: SDK должен знать, какую функцию Python вызывать по имени
tools_map = {
'get_exchange_rate': get_exchange_rate,
'calculate_total': calculate_total
}
# 3. Инициализируем модель, передавая сами функции
# Gemini SDK (Python) умеет автоматически извлекать схему из типизированных функций
model = genai.GenerativeModel(
model_name='gemini-1.5-pro-latest',
tools=[get_exchange_rate, calculate_total]
)
# 4. Запускаем чат с автоматическим вызовом функций
chat = model.start_chat(enable_automatic_function_calling=True)
# 5. Отправляем сложный запрос, требующий цепочки вызовов
response = chat.send_message(
"У меня есть 500 долларов. Сколько это будет в евро?"
)
# Модель сама вызовет get_exchange_rate, получит 0.92,
# затем вызовет calculate_total(500, 0.92) и вернет ответ.
print(response.text)
Архитектурные нюансы и управление состоянием
При использовании enable_automatic_function_calling=True (или аналогичных настроек в зависимости от версии SDK), вы фактически создаете агента. Однако, как архитектор ИИ-решений, вы должны понимать риски и ограничения такого подхода.
1. Проблема бесконечных циклов
Иногда модель может «зациклиться», вызывая одну и ту же функцию с ошибочными аргументами снова и снова. В современных SDK часто встроены лимиты на количество итераций (например, макс. 5 вызовов функций на один ход пользователя). Если вы реализуете этот цикл вручную, вам обязательно нужно добавить счетчик итераций (max_iterations), чтобы предотвратить зависание программы и перерасход токенов.
2. Контекстное окно и история чата
Каждый автоматический вызов функции добавляет в историю чата две записи: запрос модели (Call) и ответ системы (Response). В сложных цепочках контекстное окно может заполниться быстрее, чем вы ожидаете. Важно следить за размером истории, особенно если данные, возвращаемые функциями, объемны (например, полный JSON из API погоды вместо конкретного значения температуры).
3. Обработка ошибок внутри цикла
Что произойдет, если функция get_exchange_rate вернет ошибку или исключение? В автоматическом режиме SDK обычно перехватывает исключение и передает его строковое представление обратно модели. Модель, видя ошибку, часто пытается исправить свои параметры и повторить вызов. Это мощное свойство самовосстановления (self-healing), но оно требует, чтобы сообщения об ошибках в вашем коде были информативными для LLM.
# Пример функции с информативной обработкой ошибок для модели
def search_product_in_database(query: str) -> str:
"""Ищет товар в базе данных по названию."""
try:
# Симуляция подключения к БД
if len(query) < 3:
raise ValueError("Запрос слишком короткий. Используйте минимум 3 символа.")
results = fake_db_search(query)
if not results:
return "Товары не найдены. Попробуйте изменить поисковый запрос или категорию."
return str(results)
except Exception as e:
# Мы возвращаем ошибку как строку, чтобы модель могла её прочитать и исправиться
return f"System Error: {str(e)}"
# Если модель отправит "TV", она получит ответ про 3 символа
# и следующим ходом автоматически отправит "Televisor".
Паттерн: Зависимые цепочки инструментов (Tool Chaining)
Наибольшая сила авто-вызова раскрывается, когда выходные данные одного инструмента становятся входными данными для другого. Это позволяет решать задачи, которые модель не может решить за один шаг.
Рассмотрим сценарий Ассистента технической поддержки. У него есть инструменты:
get_customer_id(email)-> возвращает ID клиента.get_latest_order(customer_id)-> возвращает статус последнего заказа.check_delivery_status(tracking_number)-> возвращает местоположение посылки.
При запросе: «Где моя посылка? Мой email: alex@example.com», модель с авто-вызовом выполнит следующую хореографию:
- Вызовет
get_customer_id('alex@example.com')-> получитID: 12345. - Поймет, что для статуса заказа нужен ID. Использует полученный
12345для вызоваget_latest_order(12345)-> получит{'order': 'Laptop', 'tracking': 'TRACK-999'}. - Поймет, что пользователю нужно местоположение. Возьмет
TRACK-999из предыдущего шага и вызоветcheck_delivery_status('TRACK-999'). - Сформирует финальный ответ.
Без авто-вызова вам пришлось бы писать сложный конечный автомат (State Machine) на стороне приложения, чтобы управлять этими передачами данных.
Создайте агента «Умный календарь». Реализуйте две фиктивные функции: 1) `get_current_date()` возвращает текущую дату (строкой), 2) `get_day_of_week(date_str)` возвращает день недели для даты. Настройте модель Gemini с автоматическим вызовом функций так, чтобы на вопрос «Какой день недели будет через 5 дней от сегодня?» она самостоятельно вызывала обе функции в правильном порядке (сначала узнавала текущую дату, прибавляла 5 дней в уме, затем запрашивала день недели).
Безопасность и контроль: Когда НЕ использовать Auto-invocation
Несмотря на удобство, автоматический вызов функций несет риски. Вы фактически даете ИИ ключи от вашей системы. В каких случаях лучше вернуться к ручному управлению (Manual Orchestration)?
- Деструктивные действия: Функции типа
delete_user(),transfer_money(),shutdown_server(). Для таких действий обязательно требуется подтверждение человека. Модель может «галлюцинировать» и решить удалить базу данных просто потому, что не нашла в ней запись. - Дорогие API: Если каждый вызов функции стоит денег (например, платные API сторонних сервисов), авто-вызов может быстро истощить бюджет из-за цикла повторных попыток при ошибках.
- Сложная валидация: Если перед выполнением функции требуется сложная логическая проверка, которую трудно описать типами данных (например, «можно переводить деньги только если сумма меньше 5000 и сегодня не воскресенье»), безопаснее проверить это в коде приложения перед реальным вызовом.
Компромиссное решение: Вы можете использовать смешанный режим. Разрешите авто-вызов для безопасных функций чтения (GET), но настройте систему так, чтобы функции записи/удаления (POST/DELETE) требовали подтверждения пользователя.
Что произойдет, если в режиме автоматического вызова функций (enable_automatic_function_calling=True) функция вернет исключение (ошибку) во время выполнения?
Лучшие практики для Архитектора ИИ
Завершая тему автоматического выбора и запуска инструментов, сформулируем чек-лист для создания надежных систем:
- Идемпотентность: Старайтесь делать функции идемпотентными. Если модель случайно вызовет функцию дважды, это не должно ломать систему (например, повторное нажатие «включить свет» ничего не меняет, если он уже включен).
- Чистота Docstrings: При авто-вызове у вас нет возможности вмешаться и пояснить модели, что она делает не так. Поэтому описания функций должны быть кристально четкими. Указывайте форматы дат, единицы измерения (метры или футы?) и возможные возвращаемые значения.
- Атомарность: Создавайте маленькие, специализированные инструменты. Модели проще собрать решение из кубиков «получить_координаты» и «рассчитать_расстояние», чем бороться с одним мега-инструментом «сделать_логистику», у которого 20 параметров.
В следующем модуле мы перейдем к продвинутым паттернам проектирования агентов, где Auto-invocation станет фундаментом для построения систем класса ReAct (Reasoning + Acting).