AI-агенты и вызов функций: создание инструментов для LLM
Вы создаёте AI-агента, которому нужно получать данные в реальном времени, выполнять вычисления или взаимодействовать с внешними API. Но как связать генерацию текста LLM с фактическим выполнением кода? Ответ — вызов функций (также известный как использование инструментов). Он позволяет LLM запрашивать конкретные действия, а ваш код их выполняет. Это руководство поможет вам спроектировать, реализовать и отладить вызов функций для AI-агентов.
Что такое вызов функций?
Вызов функций — это механизм, при котором LLM может выдать структурированный запрос на вызов определённой вами функции. Вместо генерации свободного текста модель возвращает JSON-объект с именем функции и аргументами. Затем ваше приложение выполняет функцию и передаёт результат обратно модели. Это позволяет агентам выполнять действия за пределами генерации текста, например запрашивать базы данных, отправлять электронные письма или вызывать API.
Крупные поставщики LLM, такие как OpenAI, Anthropic и Google, поддерживают вызов функций. Основная идея едина: вы описываете доступные инструменты, модель решает, когда их использовать, а вы обрабатываете выполнение.
Проектирование инструментов для LLM
Хорошо спроектированные инструменты критически важны для надёжного поведения агента. Следуйте этим принципам:
- Понятные имена и описания: Используйте описательные имена функций и подробные описания. LLM полагается на них при выборе нужного инструмента.
- Простые параметры: Минимизируйте параметры и используйте стандартные типы (string, number, boolean, array, object). По возможности избегайте сложных вложенных структур.
- Идемпотентность: Где возможно, проектируйте инструменты идемпотентными (безопасными для повторного вызова), чтобы корректно обрабатывать сбои.
- Обработка ошибок: Возвращайте информативные сообщения об ошибках, чтобы LLM мог скорректировать свой подход.
Пример: инструмент погоды
Вот простое определение инструмента в формате JSON Schema, обычно используемом с API OpenAI:
{
"name": "get_weather",
"description": "Get the current weather for a given city",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "The city name, e.g., San Francisco"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit"
}
},
"required": ["city"]
}
}
Реализация вызова функций: пошагово
Давайте создадим минимальный цикл агента с использованием API OpenAI (шаблон применим и к другим провайдерам).
1. Определите инструменты
Создайте список схем инструментов и сопоставление имён функций с реальными функциями Python.
import json
import openai
# Схемы инструментов
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}
]
# Реальные функции
def get_weather(city: str, unit: str = "celsius") -> dict:
# В реальности вызовите API погоды
return {"city": city, "temperature": 22, "unit": unit, "condition": "sunny"}
# Сопоставление имён с функциями
function_map = {
"get_weather": get_weather
}
2. Создайте цикл агента
Цикл агента отправляет сообщения в LLM, проверяет наличие вызовов инструментов, выполняет их и повторяет, пока модель не вернёт окончательный ответ.
def run_agent(user_message: str):
messages = [{"role": "user", "content": user_message}]
while True:
response = openai.ChatCompletion.create(
model="gpt-4",
messages=messages,
tools=tools,
tool_choice="auto"
)
message = response.choices[0].message
messages.append(message)
# Если нет вызовов инструментов, вернуть содержимое
if not message.get("tool_calls"):
return message["content"]
# Выполнить каждый вызов инструмента
for tool_call in message.tool_calls:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
if function_name in function_map:
result = function_map[function_name](**arguments)
else:
result = {"error": f"Unknown function: {function_name}"}
# Добавить результат инструмента в сообщения
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
Этот цикл продолжается, пока LLM не выдаст ответ без вызовов инструментов, что означает, что у него достаточно информации.
3. Обработка ошибок и крайних случаев
Реальные агенты должны обрабатывать:
- Некорректные аргументы: Перехватывайте исключения при разборе аргументов или вызове функций.
- Неизвестные инструменты: Возвращайте сообщение об ошибке в LLM, чтобы он мог исправиться.
- Тайм-ауты: Устанавливайте тайм-ауты для внешних вызовов API, чтобы избежать зависаний.
- Ограничения скорости: Реализуйте повторные попытки с экспоненциальной задержкой.
Лучшие практики для надёжных агентов
- Ограничьте количество инструментов: Слишком много инструментов сбивает модель с толку. Группируйте связанные функции или используйте маршрутизатор.
- Проверяйте входные данные: Санируйте и валидируйте все аргументы перед выполнением.
- Логируйте всё: Записывайте вызовы инструментов и результаты для отладки и аудита.
- Тестируйте с разными промптами: Убедитесь, что агент выбирает правильные инструменты в различных сценариях.
- Предусмотрите запасные варианты: Если инструмент не сработал, агент должен попробовать альтернативы или запросить уточнение.
Сравнение поддержки вызова функций
| Провайдер | Название функции | Формат |
|---|---|---|
| OpenAI | Function Calling | JSON Schema |
| Anthropic | Tool Use | JSON Schema |
| Function Calling | OpenAPI Schema |
Продвинутые паттерны
По мере роста вашего агента рассмотрите эти паттерны:
- Параллельные вызовы инструментов: Некоторые модели могут запрашивать несколько инструментов одновременно. Выполняйте их параллельно для скорости.
- Human-in-the-loop: Для чувствительных действий (например, отправки денег) требуется одобрение человека перед выполнением.
- Память: Храните историю разговора и результаты инструментов, чтобы обеспечить контекст в длинных сессиях.
- Маршрутизация инструментов: Используйте лёгкий классификатор для выбора релевантных инструментов перед вызовом основной LLM.
Отладка вызова функций
Когда что-то идёт не так, проверьте:
- Ясны ли и однозначны описания инструментов?
- Правильны ли имена и типы параметров?
- Достаточно ли у модели контекста для выбора правильного инструмента?
- Правильно ли вы обрабатываете результаты инструментов (например, сериализацию JSON)?
Используйте логирование для записи полной истории сообщений и вызовов инструментов. Часто проблема заключается в несоответствии ожидаемых и фактических аргументов.
FAQ
В чём разница между вызовом функций и использованием инструментов?
Они относятся к одному и тому же понятию. OpenAI называет это «function calling», а Anthropic использует «tool use». Оба позволяют LLM запрашивать выполнение внешних функций.
Можно ли использовать вызов функций с открытыми моделями?
Да, некоторые открытые модели, такие как Llama 3.1, поддерживают вызов функций, а фреймворки вроде LangChain предоставляют абстракции. Однако поддержка различается, и вам может потребоваться тонкая настройка или использование определённых форматов промптов.
Как предотвратить вызов опасных функций LLM?
Никогда не предоставляйте опасные функции напрямую. Используйте белые списки, проверяйте входные данные и реализуйте проверки разрешений. Для чувствительных операций требуйте подтверждения человека перед выполнением.
Готовы создать собственного AI-агента? Начните с определения простого инструмента и тестирования цикла агента. Для других инструментов разработчика ознакомьтесь с нашим JSON Formatter для отладки payload'ов вызовов инструментов.