Как проверить, что LLM корректно вызывает инструменты

Практический тест для вызовов функций: проверьте выбор инструмента, аргументы, отказ от ненужного действия и обработку ошибок на собственных сценариях.

Схема вызова инструмента LLM: модель выбирает функцию и передаёт ей аргументы
Схема вызова инструмента LLM: модель выбирает функцию и передаёт ей аргументы
Visit of Ursula von der Leyen, President of the European Commission, to India (P-065755-00-36).jpg | by Europäische Kommission — Audiovisueller Dienst, CE — Service audiovisuel, EC — Audiovisual Service, Dati Bendo | wikimedia_commons | CC BY 4.0

Вызовы инструментов LLM — не просто вопрос о том, назвала ли модель нужную функцию. Важно проверить весь переход: выбрала ли она подходящий инструмент, передала ли допустимые аргументы и остановилась ли, когда действие не требуется. Иначе корректный на вид ответ может запустить неверный запрос к API.

Надёжный тест не должен зависеть от того, насколько убедительно модель объясняет своё решение. Проверяйте структурированный результат вызова и отдельно — поведение приложения после него. Ниже — практический способ собрать такой набор проверок для API с функциями, поиска или внутренних операций.

Что считать корректным вызовом инструмента LLM

Разделите результат на несколько проверяемых решений:

Выбор: нужная функция выбрана или модель правильно ответила без неё.

Аргументы: значения соответствуют запросу и схеме — например, дата не подменена, а идентификатор не выдуман.
3. Количество действий: модель не вызывает функцию повторно без причины и не запускает несколько несовместимых операций.
4. Обработка результата: после ответа инструмента модель использует полученные данные, а не выдаёт их за собственные предположения.
5. Границы: приложение проверяет разрешения и корректность параметров до выполнения действия.

Эти пункты нельзя свести к одной метрике. Например, правильный выбор функции при неверном аргументе всё равно может привести к ошибке. А валидный JSON ещё не означает, что действие уместно.

В терминологии API OpenAI это обычно называется function calling или tool calling; Anthropic описывает tool use, а Gemini API — function calling. Конкретные форматы отличаются, поэтому тесты должны опираться на ответ выбранного провайдера и на контракт вашего приложения, а не на универсальное представление о «вызове».

Составьте набор сценариев до запуска модели

Начните не с большого корпуса, а с небольшого набора реальных задач. Для каждого сценария запишите вход, ожидаемое решение, допустимые аргументы и запрещённые побочные эффекты.

Например, у приложения есть инструменты `search_orders` и `cancel_order`:

Запрос Ожидаемое поведение Что проверять
«Найди заказ 4182» Вызвать `search_orders` Передан именно номер 4182
«Отмени заказ 4182» Вызвать `cancel_order` Передан верный ID; отмена разрешена приложением
«Какие способы доставки доступны?» Ответить без этих инструментов Никаких вызовов поиска или отмены
«Отмени мой последний заказ», когда заказов несколько Запросить уточнение либо выполнить поиск по правилам продукта Не выбирать случайный заказ

Сценарии должны включать нормальные формулировки, неполные запросы, неоднозначность, лишние данные и случаи, когда инструмент не нужен. Добавьте и отрицательные примеры: запросить действие без обязательного параметра, передать неверный формат ID, попросить отменить чужой заказ.

Не записывайте в ожидаемый результат дословное объяснение модели. Фиксируйте поведение: имя инструмента, аргументы, отсутствие вызова или необходимость уточнения. Это позволит менять формулировки, не превращая тест в сравнение строк.

Проверьте схему и смысл аргументов отдельно

Схема помогает отсеять неверный тип или пропущенное обязательное поле. В документации OpenAI параметр `strict` для function calling включает режим строгого соответствия заданной JSON Schema с оговорёнными ограничениями формата. У Anthropic и Google также есть описания структурированных параметров инструментов. Возможности и точные правила различаются между API, поэтому сверяйтесь с документацией используемой модели.

Но схема не проверяет смысл. Значение `order_id: 4182` может быть допустимым числом и при этом относиться не к тому заказу. Нужны два слоя проверок:

  • Структурный: JSON разбирается, обязательные поля присутствуют, типы и допустимые значения соблюдены.
  • Семантический: параметры соответствуют запросу, контексту и бизнес-правилам.

Для практического теста сравнивайте значения с ожидаемым результатом, а не с порядком ключей в JSON. Для идентификаторов проверяйте точное совпадение; для дат — нормализованный формат; для свободного текста — заранее определённые ограничения или ручную проверку выборки.

Особенно опасны параметры, которые меняют состояние: отправка письма, удаление файла, отмена заказа, перевод средств. Их нельзя считать безопасными только потому, что модель сформировала валидные аргументы. Серверная часть должна независимо проверять права пользователя, объект операции и необходимость подтверждения.

Измеряйте ошибки по типам, а не одной долей успеха

Для каждого тестового сценария отметьте как минимум четыре результата:

  • правильный ли выбран инструмент;
  • корректны ли его аргументы;
  • пропущен ли вызов там, где он не нужен;
  • не произошло ли запрещённого действия.

Посчитайте точность выбора инструмента среди сценариев, где он необходим, и долю лишних вызовов среди сценариев, где действие не требуется. Ошибки аргументов учитывайте отдельно: объединённый «процент успешных вызовов» скрывает причину сбоя.

Для чувствительных операций полезно вести отдельную метрику опасных ошибок — например, неверный объект или вызов без проверки полномочий. Средняя точность не должна компенсировать хотя бы один такой провал: политика приложения может требовать нулевого числа опасных действий в тестовом наборе.

Добавьте повторные запуски. Ответ модели может зависеть от версии, настроек генерации, текста описания функции и состава доступных инструментов. Проведите один и тот же тест несколько раз с зафиксированными параметрами; сравнивайте распределение результатов, а не только лучший запуск. Сохраняйте версию модели, параметры запроса, определения инструментов и дату теста — иначе регрессии будет трудно воспроизвести.

Запускайте модель без реальных побочных эффектов

На этапе оценки не подключайте тесты к рабочим системам. Подмените инструменты заглушками, которые записывают имя функции и аргументы, а затем возвращают заранее заданный ответ. Так можно проверить решение модели, не отправляя письма и не изменяя реальные записи.

Упрощённая логика проверки может выглядеть так:

python
def run_case(client, case, tools):
response = client.responses.create(
model=case.model,
input=case.input,
tools=tools,
)

calls = extract_tool_calls(response)

assert len(calls) == case.expected_call_count

if case.expected_tool is None:
assert calls == []
return

call = calls[0]
assert call.name == case.expected_tool

args = parse_arguments(call.arguments)
validate_schema(args, case.schema)
validate_semantics(args, case.expected_args)

`extract_tool_calls`, `parse_arguments` и `validate_schema` здесь зависят от SDK и формата ответа конкретного провайдера. Это не готовый универсальный адаптер: реализацию нужно сверить с актуальной документацией API.

Проверяйте также полный цикл. Заглушка должна вернуть результат, после чего модель получает его и формирует ответ пользователю. В этом шаге обнаруживаются другие ошибки: модель игнорирует результат инструмента, неверно пересказывает его или повторяет действие бесконечно.

Проверьте отказ, уточнение и ошибки инструмента

Хороший тест охватывает не только успешный вызов. Подготовьте ответы инструмента с пустым результатом, тайм-аутом, ошибкой доступа и некорректными данными. Ожидаемое поведение задаёт продукт: сообщить, что данных нет; запросить уточнение; безопасно остановиться; предложить повторить попытку.

Не просите модель самостоятельно решать, можно ли игнорировать отказ в доступе. Это правило должно обеспечиваться приложением. Аналогично, после ошибки нельзя автоматически повторять операцию с побочным эффектом, если повтор способен создать дубликат.

Для неоднозначного запроса задайте проверяемый критерий: модель должна запросить недостающий параметр или вызвать безопасный инструмент для получения контекста. Не оценивайте такой случай по «вежливости» ответа — проверяйте, что она не придумала значение и не совершила действие.

Сравнивайте версии модели и описания функций

Когда меняете модель, системные инструкции или описание инструмента, повторно запускайте один и тот же набор тестов. Отдельно сравнивайте варианты описания: краткое название функции, ясное назначение, обязательные параметры и условия, при которых её нельзя вызывать.

Меняйте по одному фактору за раз. Если одновременно заменить модель, схему и инструкции, улучшение или регрессию нельзя будет связать с конкретным изменением. Храните тестовый набор и результаты рядом с кодом, чтобы запускать его при обновлении интеграции. Для Python-проектов это можно встроить в обычный набор тестов с фикстурами и проверками; сам pytest не оценивает качество модели, но помогает воспроизводимо выполнять проверки.

Набор всё равно не доказывает, что модель безопасна во всех формулировках. Он показывает поведение на выбранных сценариях. После изменений в API, схеме или бизнес-логике добавляйте примеры из новых сбоев, а критические действия дополнительно защищайте ограничениями на стороне сервера.

Что проверить перед интеграцией

Перед подключением инструмента к реальным данным убедитесь, что тестовый набор включает успешный вызов, отказ от ненужного действия, неоднозначный запрос и ошибку инструмента. Проверьте аргументы не только по схеме, но и по смыслу; прогоните повторные запуски с зафиксированными настройками; оставьте реальные побочные эффекты за заглушками.

Затем отдельно проверьте серверную авторизацию и возможность безопасного повтора. Результаты тестов относятся к конкретной версии модели, описаниям функций и настройкам API — после любого из этих изменений оценки нужно обновить.

Источники