中文翻译暂不可用,显示俄语原文。
Как вы переключаете агента между инструментами (function calling) с разными сигнатурами?
Краткий тезис
Переключение агента между инструментами с разными сигнатурами — это задача динамического выбора и вызова внешних функций на основе семантики запроса пользователя. Агент использует описание инструментов в формате JSON Schema, выбирает подходящий инструмент (часто через LLM-рассуждение), генерирует аргументы, строго соответствующие схеме, валидирует их (например, с помощью Pydantic), и обрабатывает ошибки. Ключевой элемент — test-time compute: модель тратит дополнительные токены на размышление о том, какой инструмент вызвать и как сформировать аргументы, что повышает надёжность переключения.
1. Термин: Function Calling (вызов функций)
Function calling — это механизм, позволяющий LLM (Large Language Model) не только генерировать текст, но и возвращать структурированные запросы на вызов внешних инструментов (API, функций, баз данных). Агент получает список доступных инструментов с их сигнатурами, и на основе запроса решает, какой инструмент вызвать и с какими аргументами.
Зачем нужно переключение между инструментами
В реальных сценариях агент должен уметь:
- получать погоду (инструмент
get_weather) - искать документы (инструмент
retrieve_docs) - выполнять вычисления (инструмент
calculate) - отправлять email (инструмент
send_email)
Каждый инструмент имеет свою сигнатуру (набор параметров, типы, обязательность). Агент должен корректно переключаться между ними, не путая аргументы.
2. Описание инструментов через JSON Schema
Для того чтобы LLM могла вызывать инструменты, их сигнатуры описываются в формате JSON Schema — стандарте описания структуры JSON-данных. Каждый инструмент представляется как объект с полями:
name— уникальное имя инструментаdescription— текстовое описание, когда и как его использовать (важно для семантического выбора)- parameters — JSON Schema объекта, описывающая ожидаемые аргументы (типы, обязательные поля, ограничения)
Пример описания двух инструментов с разными сигнатурами:
[
{
"name": "get_weather",
"description": "Получить текущую погоду для города",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "Название города"
},
"units": {
"type": "string",
"enum": ["metric", "imperial"],
"default": "metric"
}
},
"required": ["city"]
}
},
{
"name": "calculate",
"description": "Выполнить математическое вычисление",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Математическое выражение, например '2 + 2'"
}
},
"required": ["expression"]
}
}
]
Зачем JSON Schema
- LLM обучается на таких схемах и умеет генерировать корректные JSON-вызовы.
- Схема позволяет валидировать аргументы на стороне агента до реального вызова.
- Разные сигнатуры кодируются в разных схемах, что даёт агенту возможность переключаться.
3. Процесс выбора инструмента
Агент не просто вызывает первый попавшийся инструмент — он должен выбрать правильный на основе запроса. Есть несколько стратегий:
3.1 Прямое указание LLM (наиболее распространённое)
LLM получает список инструментов (как часть системного промпта или через специальный API) и генерирует ответ, который может содержать вызов функции. Модель сама решает, какой инструмент подходит, анализируя семантику запроса и описание инструментов.
Пример:
Запрос: «Какая погода в Москве?»
Модель выбирает get_weather, а не calculate.
3.2 Классификация запроса (rule-based или ML)
Можно предварительно классифицировать намерение пользователя (intent) и на основе этого выбрать набор инструментов. Например, если запрос содержит слово «погода» → активируется инструмент погоды.
3.3 Многошаговое рассуждение (ReAct, Chain-of-Thought)
Агент сначала генерирует «мысли» (reasoning), а потом решает, какой инструмент вызвать. Это часть test-time compute: модель тратит дополнительные токены на обдумывание, что повышает точность выбора.
4. Генерация аргументов
После выбора инструмента LLM должна сгенерировать аргументы, строго соответствующие его JSON Schema. Обычно это делается в формате JSON-объекта.
Проблема разные сигнатуры требуют разных полей. Например, get_weather требует city (строка), а calculate требует expression (строка). Модель должна не перепутать.
Как это решается
- В промпте явно указывается: «Для вызова функции используй JSON с полями, описанными в схеме».
- Современные LLM (GPT-4, Claude 3, Llama 3) обучены генерировать корректные JSON по схеме.
- Используется constrained decoding — техника, при которой генерация ограничивается грамматикой JSON Schema (например, библиотека
outlinesилиguidance).
5. Валидация аргументов (Pydantic)
Даже если LLM сгенерировала JSON, его нужно проверить перед реальным вызовом. Для этого удобно использовать Pydantic — библиотеку Python для валидации данных на основе моделей.
Пример:
from pydantic import BaseModel, Field
from typing import Optional
class GetWeatherArgs(BaseModel):
city: str = Field(..., description="Название города")
units: Optional[str] = "metric"
class CalculateArgs(BaseModel):
expression: str = Field(..., description="Математическое выражение")
# Валидация
try:
args = GetWeatherArgs(**json.loads(llm_output))
# вызов get_weather(args.city, args.units)
except ValidationError as e:
# обработка ошибки: повторная генерация или fallback
Преимущества
- Автоматическая проверка типов, обязательных полей, значений по умолчанию.
- Чёткие сообщения об ошибках, которые можно передать обратно LLM для исправления.
- Легко расширять: добавление нового инструмента = новая Pydantic модель.
6. Обработка ошибок при вызове
Даже после валидации возможны ошибки выполнения (инструмент недоступен, неверные данные). Агент должен уметь:
- Повторная генерация если аргументы не прошли валидацию, отправить LLM сообщение об ошибке и попросить сгенерировать заново.
- Fallback: если инструмент не сработал, переключиться на другой (например, если не удалось получить погоду через API, использовать парсинг HTML).
- Логирование: записывать все вызовы и ошибки для отладки.
Пример цикла
1. Пользователь: "Сколько будет 2+2?"
2. LLM решает вызвать calculate с аргументом {"expression": "2+2"}
3. Валидация: CalculateArgs(expression="2+2") — успешно
4. Вызов calculate("2+2") → возвращает 4
5. LLM формирует ответ: "Результат: 4"
Если на шаге 3 ошибка (например, поле expression отсутствует), агент отправляет LLM: «Ошибка: не хватает поля expression. Попробуй ещё раз».
7. Переключение между инструментами в multi-step сценариях
В сложных задачах агент может вызывать несколько инструментов последовательно или параллельно. Например:
- Запрос: «Какая погода в столице Франции и сколько будет 5!?»
- Агент сначала вызывает
get_weatherдля города «Париж», затемcalculateдля «5!». - Результаты объединяются в финальный ответ.
Управление состоянием агент должен помнить, какие инструменты уже вызваны и какие результаты получены. Для этого используется память (conversation history) и контекст (переменные окружения).
Параллельные вызовы если инструменты независимы, можно вызвать их одновременно (например, через asyncio), что сокращает время ответа.
8. Test-time compute и рассуждение перед вызовом
Test-time compute — это парадигма, при которой модель тратит дополнительные вычислительные ресурсы во время инференса на «размышление» перед генерацией ответа. В контексте function calling это означает:
- Chain-of-Thought (CoT): модель сначала пишет рассуждение: «Пользователь спрашивает о погоде, значит нужно вызвать get_weather. Город — Москва, единицы — метрические.»
- ReAct (Reasoning + Acting): чередование мыслей и действий (вызовов инструментов).
- Self-ask модель задаёт себе уточняющие вопросы, чтобы понять, какой инструмент нужен.
Это повышает точность переключения, особенно когда сигнатуры похожи (например, два инструмента с параметром city, но один для погоды, другой для времени).
9. Пример реализации на Python (OpenAI API)
import json
from openai import OpenAI
from pydantic import BaseModel, ValidationError
client = OpenAI()
# Определение инструментов
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"units": {"type": "string", "enum": ["metric", "imperial"]}
},
"required": ["city"]
}
}
},
{
"type": "function",
"function": {
"name": "calculate",
"description": "Evaluate a math expression",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string"}
},
"required": ["expression"]
}
}
}
]
# Pydantic модели для валидации
class GetWeatherArgs(BaseModel):
city: str
units: str = "metric"
class CalculateArgs(BaseModel):
expression: str
# Функции-исполнители
def get_weather(city, units="metric"):
return f"Погода в {city}: 20°C"
def calculate(expression):
return eval(expression) # упрощённо, в реальности безопасный eval
# Основной цикл агента
def agent_loop(user_input):
messages = [{"role": "user", "content": user_input}]
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
tools=tools,
tool_choice="auto"
)
msg = response.choices[0].message
if msg.tool_calls:
for tool_call in msg.tool_calls:
func_name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
# Валидация
try:
if func_name == "get_weather":
validated = GetWeatherArgs(**args)
result = get_weather(validated.city, validated.units)
elif func_name == "calculate":
validated = CalculateArgs(**args)
result = calculate(validated.expression)
else:
result = "Unknown tool"
except ValidationError as e:
result = f"Validation error: {e}"
# Добавляем результат в историю
messages.append(msg)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
# Второй вызов LLM для финального ответа
final_response = client.chat.completions.create(
model="gpt-4",
messages=messages
)
return final_response.choices[0].message.content
else:
return msg.content
print(agent_loop("Какая погода в Лондоне?"))
print(agent_loop("Сколько будет 2+2?"))
Этот код демонстрирует переключение между инструментами с разными сигнатурами: LLM сама выбирает, какой вызвать, а Pydantic проверяет аргументы.
10. Сравнение подходов к переключению
| Подход | Описание | Плюсы | Минусы |
|---|---|---|---|
| Прямое указание LLM | Модель сама выбирает инструмент на основе описания | Гибкость, простота | Зависит от качества модели, возможны галлюцинации |
| Rule-based классификация | Предопределённые правила (ключевые слова) | Быстро, детерминированно | Не масштабируется, плохо для сложных запросов |
| Многошаговое рассуждение (CoT/ReAct) | Модель «думает» перед вызовом | Высокая точность, интерпретируемость | Дороже по токенам, медленнее |
| Иерархический выбор | Сначала выбирается категория, потом конкретный инструмент | Хорошо для большого числа инструментов | Сложнее в реализации |
Пет-проект для закрепления
Задача Создать агента, который умеет переключаться между тремя инструментами с разными сигнатурами:
get_stock_price(ticker: str)— цена акцииget_news(topic: str, limit: int = 5)— новости по темеtranslate(text: str, target_lang: str)— перевод текста
Инструменты Python, OpenAI API (или локальная LLM через Ollama), Pydantic, FastAPI (для веб-интерфейса).
Шаги:
- Определите JSON Schema для каждого инструмента.
- Реализуйте Pydantic модели для валидации.
- Напишите функцию-агента, которая принимает запрос, вызывает LLM с инструментами, обрабатывает ошибки.
- Добавьте возможность последовательных вызовов (например, «Найди новости про Apple и переведи заголовки на русский»).
- Протестируйте на разных запросах, включая неоднозначные (например, «Что с акциями?» — нужно уточнить тикер).
Ожидаемый результат Рабочий агент, который корректно выбирает инструмент, генерирует аргументы, валидирует их и возвращает ответ. Вы научитесь обрабатывать ошибки валидации и повторные вызовы.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 145 | Как агент планирует последовательность действий (multi-step reasoning)? |
| 147 | Что такое ReAct и как он используется в агентах? |
| 148 | Как обрабатывать ошибки при вызове инструментов? |
| 149 | Как агент управляет состоянием (памятью) в длинных сессиях? |
| 151 | Как тестировать агента с инструментами (unit-тесты, симуляция)? |
| 155 | Как объединять результаты нескольких инструментов в один ответ? |
Навигация
- Предыдущий: 149
- Следующий: 151
- Индекс: 00. Индекс разборов