Основы Function Calling: Определение инструментов и сигнатур
Введение: Когда слов недостаточно
Добро пожаловать в третий модуль. До этого момента мы взаимодействовали с Gemini в основном как с генератором текста. Мы спрашивали — модель отвечала. Это прекрасно работает для эссе, анализа данных или написания кода. Но что, если мы хотим, чтобы ИИ сделал что-то в реальном мире?
Представьте, что у вас есть гениальный аналитик, запертый в пустой комнате. У него нет ни телефона, ни компьютера, ни доступа к текущим котировкам акций или прогнозу погоды. Всё, что он знает — это то, чему его учили до момента «заточения» (cut-off date). Function Calling (вызов функций) — это способ провести в эту комнату защищенный кабель, через который аналитик может передавать записки с просьбой выполнить конкретные действия.
В этом уроке мы не будем писать сложную логику оркестрации (это будет позже). Сегодня мы сосредоточимся на фундаменте: как правильно объяснить модели, какие инструменты у неё есть. Качество этого объяснения напрямую определяет, насколько умным будет ваш AI-агент.
Концепция: Модель не исполняет код
Это самое важное заблуждение, которое нужно разрушить прямо сейчас. Gemini не запускает функции, которые вы ей даете. У модели нет интерпретатора Python внутри её нейронных весов (по крайней мере, в контексте стандартного Function Calling API).
Процесс выглядит так:
- Определение: Вы отправляете модели текст + список доступных инструментов (описания функций).
- Решение: Модель анализирует запрос пользователя. Если она понимает, что для ответа ей нужны внешние данные (например, «Какая погода в Москве?»), она не генерирует ответ-текст. Вместо этого она генерирует структурированный запрос на вызов функции.
- Исполнение (Ваша часть): Ваше приложение получает этот запрос, видит, что модель хочет вызвать функцию
get_weather(city='Moscow'). Ваше приложение реально вызывает эту функцию, обращается к API погоды и получает результат. - Возврат: Вы отправляете результат выполнения функции обратно модели.
- Ответ: Модель читает результат и формирует финальный ответ пользователю на естественном языке.
Сегодня мы фокусируемся на пункте №1: Определение инструментов.
import google.generativeai as genai
from google.generativeai.types import FunctionDeclaration, Tool
# Пример простой Python-функции
def get_exchange_rate(currency_from: str, currency_to: str, date: str = 'latest'):
"""
Получает текущий курс обмена валют.
Args:
currency_from: Код исходной валюты (например, 'USD').
currency_to: Код целевой валюты (например, 'EUR').
date: Дата в формате YYYY-MM-DD или 'latest'.
"""
# Здесь была бы реальная логика запроса к API банка
return {"rate": 0.92, "from": currency_from, "to": currency_to}
# В Gemini SDK вы можете передать функцию напрямую,
# и библиотека сама попытается создать описание (tool definition).
tools_list = [get_exchange_rate]
# Инициализация модели с инструментами
model = genai.GenerativeModel(
model_name='gemini-1.5-pro-latest',
tools=tools_list
)
Анатомия инструмента: Сигнатуры и Типы
Хотя SDK позволяет просто передать функцию (как в примере выше), на уровне архитектора решений вы должны понимать, что происходит «под капотом». Модель не читает ваш код на Python. Она читает JSON Schema.
Когда вы объявляете инструмент, качество его работы зависит от трех параметров:
- Name (Имя): Должно быть понятным и уникальным. Модель использует его как идентификатор.
- Description (Описание): Это, по сути, промпт для конкретной функции. Здесь вы должны сказать модели когда и зачем использовать этот инструмент.
- Parameters (Параметры): Типизация аргументов.
Gemini 3 очень чувствительна к типам данных. Если вы укажете, что параметр — это integer, модель будет стараться вернуть именно число, а не строку "пять".
Почему Docstrings критически важны?
Взгляните на код выше еще раз. Видите строку документации (docstring)? Для обычного кода это просто хорошая практика. Для Function Calling — это инструкция для нейросети. SDK парсит этот текст и передает его модели. Если вы напишете функцию без описания или с плохим описанием, модель будет галлюцинировать или игнорировать инструмент.
Продвинутый уровень: Использование Pydantic
Передача обычных функций работает для простых скриптов. Но когда мы строим сложные системы, нам нужна жесткая валидация и чистота кода. Здесь на сцену выходит библиотека Pydantic. Это стандарт де-факто в современной Python-разработке, и Gemini SDK отлично с ней интегрируется.
Использование Pydantic позволяет:
- Описывать сложные вложенные структуры данных.
- Использовать Enums (перечисления) для ограничения выбора модели (например, статус заказа может быть только 'new', 'processing', 'shipped').
- Автоматически валидировать то, что модель прислала вам в ответ, еще до выполнения бизнес-логики.
import enum
from pydantic import BaseModel, Field, PositiveInt
from typing import List, Optional
# 1. Определение перечислений (Enums) помогает модели выбирать из фиксированного списка
class MaterialType(str, enum.Enum):
WOOD = "wood"
METAL = "metal"
PLASTIC = "plastic"
# 2. Определение структуры аргументов через Pydantic
class FurnitureOrder(BaseModel):
item_name: str = Field(..., description="Название предмета мебели, например 'Стул' или 'Стол'")
material: MaterialType = Field(..., description="Материал изделия")
quantity: PositiveInt = Field(default=1, description="Количество предметов, должно быть больше 0")
dimensions: Optional[List[float]] = Field(
None,
description="Размеры в см [высота, ширина, глубина]. Если не указаны, используются стандартные."
)
def place_order(order_details: FurnitureOrder):
"""Размещает заказ на производство мебели."""
print(f"Заказ принят: {order_details.item_name} из {order_details.material.value}")
return {"status": "confirmed", "id": 12345}
# Передаем функцию, типизированную Pydantic-моделью
model = genai.GenerativeModel(
model_name='gemini-1.5-pro-latest',
tools=[place_order]
)
Лучшие практики нейминга и описаний
Как архитектор ИИ-решений, вы должны писать описания функций не для программистов, а для модели. Вот несколько правил «Промпт-инжиниринга для функций»:
- Будьте конкретны: Вместо
get_data()используйтеget_customer_purchase_history_by_id(). - Объясняйте нюансы: В описании параметра укажите формат. Например: «Дата начала периода в формате ISO 8601».
- Обрабатывайте неоднозначность: Если функция тяжелая (долго выполняется), напишите в описании: «Использовать только если пользователь явно запрашивает детальный отчет, так как операция занимает время».
- Избегайте пересечений: Не создавайте два инструмента с похожими целями (например,
find_userиsearch_user). Это запутает модель. Если нужно, объедините их в один инструмент с параметромsearch_strategy.
Создайте определение инструмента для системы «Умный Дом». <br><br>Задача: Написать функцию (используя Pydantic или обычные типы), которая управляет кондиционером.<br><br>Требования к инструменту:<br>1. Имя функции: `set_ac_state`.<br>2. Параметры:<br> - `mode` (Режим): Может быть только 'cooling', 'heating', 'dry', 'fan'. (Подсказка: используйте Enum).<br> - `temperature` (Температура): Целое число. Обязательно только для режимов 'cooling' и 'heating'.<br> - `fan_speed` (Скорость вентилятора): От 1 до 5.<br>3. Добавьте подробные описания (docstrings/Field descriptions), чтобы модель знала, когда включать какой режим.
Вы определили инструмент `delete_file(filename: str)` и передали его в Gemini. Пользователь пишет: «Удали файл report.txt». Что произойдет технически на следующем шаге?