Что именно нужно проверять
Tool calling превращает ответ языковой модели в структурированное действие: модель выбирает инструмент и передаёт ему аргументы. Для прикладной системы этого недостаточно. Важно понять, когда вызов уместен, соответствует ли выбранная функция пользовательскому запросу и можно ли безопасно передать её параметры во внешний сервис.
Удобнее рассматривать один ответ модели как набор независимых проверок:
был ли вызов нужен;
выбран ли правильный инструмент;
3. соблюдена ли структура аргументов;
4. соответствуют ли значения бизнес-ограничениям;
5. не повторился ли уже выполненный вызов;
6. укладывается ли операция в допустимые задержку и бюджет.
Такой разбор помогает не смешивать разные ошибки. Например, неверный инструмент и правильный инструмент с неправильным идентификатором клиента требуют разных исправлений: в первом случае стоит пересмотреть описания функций или маршрутизацию, во втором — схему данных и валидацию.
Модель не должна получать прямой доступ к критичным операциям. Перед выполнением вызова приложение должно проверить аргументы, права пользователя, идемпотентность операции и допустимый диапазон значений.
Метрики для рабочего контура
Одной общей «точности агента» недостаточно. Минимальный набор метрик выглядит так:
| Метрика | Что измеряет | Как считать | Зачем нужна |
|---|---|---|---|
| Точность выбора | Долю правильных инструментов среди вызовов | Правильные вызовы / все вызовы | Показывает ошибки маршрутизации |
| Полнота обязательных вызовов | Долю сценариев, где нужный инструмент действительно вызван | Обязательные вызовы / все сценарии с обязательным вызовом | Выявляет пропуски |
| Точность аргументов | Долю вызовов без ошибок в параметрах | Валидные вызовы / все вызовы | Оценивает качество структурированного ответа |
| Доля лишних вызовов | Вызовы там, где требовался обычный текстовый ответ | Лишние вызовы / сценарии без вызова | Контролирует ненужные расходы и побочные эффекты |
| P95 задержки | Время до получения пригодного вызова | 95-й перцентиль длительности | Показывает редкие медленные случаи |
Для задач выбора инструмента полезно отдельно хранить false positive и false negative:
- false positive — модель вызвала функцию, хотя запрос этого не требовал;
- false negative — модель не вызвала функцию, хотя без неё корректный ответ невозможен.
F1 можно использовать как сводную метрику для обязательных вызовов, однако она скрывает характер ошибки. В рабочем отчёте лучше показывать её рядом с полнотой и долей лишних вызовов.
Аргументы стоит проверять на двух уровнях. Сначала применяется JSON Schema: типы, обязательные поля, перечисления и формат. Затем запускаются прикладные проверки: существует ли объект, принадлежит ли он текущему пользователю, разрешено ли действие и не истёк ли срок операции.
Стоимость также нужно считать на уровне сценария, а не только одного ответа. В неё входят входные токены с описанием инструментов, выходные токены, повторные попытки, промежуточные сообщения и фактический вызов внешнего API. Цены зависят от модели и тарифа, поэтому их следует брать из актуальной документации поставщика, а не зашивать в статью или тестовый код.
Эталонный набор сценариев
Тестовый набор должен описывать ожидаемое поведение, а не пересказывать внутреннюю реализацию агента. Для каждой записи достаточно хранить:
- пользовательский запрос;
- список доступных инструментов;
- ожидаемое действие;
- допустимые значения аргументов;
- признак обязательности вызова;
- разрешённые варианты ответа;
- уровень критичности ошибки.
Начните с коротких детерминированных сценариев.
Один инструмент
Запрос однозначно требует одной функции. Проверяются имя инструмента, обязательные поля и типы значений. Такой тест быстро показывает проблемы в схеме или в описании функции.
Выбор из нескольких вариантов
Несколько функций имеют похожие названия, однако подходит только одна. В эталоне нужно зафиксировать причину выбора: тип операции, объект, канал или требуемый результат.
Ответ без вызова
Пользователь просит объяснение, преобразование текста или другую операцию, для которой внешний инструмент не нужен. Тест защищает от лишних вызовов и позволяет измерять false positive.
Последовательность действий
Один запрос требует получить данные, проверить условие, затем выполнить вторую операцию. Проверяйте порядок шагов, передачу результата между ними и поведение при ошибке первого шага.
Неполные данные
В запросе отсутствует обязательный параметр. Ожидаемый результат — уточняющий вопрос или безопасный отказ, а не вызов с выдуманным значением.
Конфликтующие ограничения
Пользователь задаёт несовместимые условия. Такой сценарий проверяет, что приложение не передаёт во внешний сервис заведомо некорректную комбинацию параметров.
Повтор запроса
Один и тот же ответ модели или сетевой повтор может привести к повторному действию. Для операций с побочным эффектом тестируйте ключ идемпотентности и поведение при уже выполненном вызове.
Как сравнивать аргументы
Сравнение JSON-объектов «строка в строку» даёт много ложных срабатываний. Сначала нормализуйте структуру, затем применяйте правила для конкретного поля.
Например, порядок ключей обычно не важен, а регистр идентификатора или формат даты может иметь значение. Для массивов заранее решите, является ли порядок частью контракта. Для чисел задайте допустимую погрешность, если значение вычисляется приблизительно.
Простейший валидатор на Python:
python
from jsonschema import ValidationError, validate
def validate_arguments(arguments: dict, schema: dict) -> tuple[bool, str | None]:
try:
validate(instance=arguments, schema=schema)
return True, None
except ValidationError as error:
return False, error.message
Схема должна быть строгой настолько, насколько это безопасно для приложения. Если неизвестные поля не используются, их лучше запрещать через `additionalProperties: false`. Это не заменяет прикладную проверку: JSON Schema не знает, имеет ли пользователь право выполнить действие.
Для каждого теста сохраняйте фактический вызов целиком:
json
{
«scenario_id»: «account_lookup_missing_id»,
«tool_name»: null,
«arguments»: null,
«expected»: {
«action»: «ask_clarifying_question»
},
«latency_ms»: 842,
«input_tokens»: 912,
«output_tokens»: 74
}
Лог должен содержать обезличенные данные. Секреты, токены доступа и полные пользовательские сообщения с персональной информацией нельзя отправлять в систему отчётности без отдельного контроля доступа.
Пороговые проверки в CI
Проверка в CI должна отвечать на один вопрос: ухудшилась ли новая версия относительно принятого уровня? Не обязательно требовать идеальный результат от каждого сценария. Для редких или вероятностных ошибок полезнее задавать минимальные пороги и отдельные блокирующие тесты для критичных действий.
Пример конфигурации порогов:
yaml
metrics:
tool_selection_accuracy:
minimum: 0.97
required_call_recall:
minimum: 0.98
argument_validity:
minimum: 0.99
unwanted_call_rate:
maximum: 0.02
latency_p95_ms:
maximum: 2500
Тестовый запуск должен фиксировать версию модели, параметры генерации, набор инструментов и версию датасета. Иначе сравнение двух прогонов будет ненадёжным: изменение описания функции может повлиять на результат сильнее, чем изменение бизнес-кода.
Пример шага GitHub Actions:
yaml
name: Tool calling evaluation
on:
pull_request:
push:
branches: [main]
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
— uses: actions/checkout@v4
— uses: actions/setup-python@v5
with:
python-version: «3.11»
— run: pip install -r requirements.txt
— run: python evaluate_tool_calling.py —dataset tests/tools.jsonl —output metrics.json
— run: python check_thresholds.py metrics.json thresholds.yaml
Внешние вызовы в CI лучше отделять от тестов с реальными побочными эффектами. Для платёжных, административных и изменяющих данные операций используйте заглушки или песочницу. Отдельный nightly-прогон может проверять интеграцию с реальным сервисом, однако он не должен случайно отправлять письма, создавать заказы или менять пользовательские данные.
Где брать тестовые данные
Синтетические примеры удобны для покрытия схемы, крайних значений и редких комбинаций. Реальные обезличенные запросы нужны, чтобы обнаружить неоднозначные формулировки, сокращения и неполный контекст.
Хорошая практика — разделить набор на три части:
- smoke-набор из коротких критичных сценариев для каждого pull request;
- регрессионный набор с историческими ошибками;
- расширенный набор для ночных запусков и смены модели.
Добавляйте в регрессионный набор каждый подтверждённый инцидент. Запись должна содержать исходный класс ошибки, ожидаемое поведение и минимальный тест, который воспроизводит проблему. Это превращает разовые исправления в постоянную защиту.
Для проверки инструментов и схем полезны следующие материалы:
- OpenAI: Function calling — описание вызовов функций и структурированных аргументов;
- OpenAI Cookbook: How to call functions with chat models — пример интеграции;
- Anthropic Cookbook: Tool use evaluation guide — подходы к оценке использования инструментов;
- LangGraph: Tool calling — пример построения графа с вызовами;
- arXiv: Tool-use evaluation — исследовательский контекст для оценки инструментального поведения моделей.
Документация поставщиков меняется, поэтому перед настройкой порогов проверяйте актуальные форматы ответов, ограничения API и правила тарификации по официальным страницам.
План внедрения на неделю
Если тестового контура пока нет, не начинайте с большого бенчмарка. Рабочая последовательность может быть такой:
Соберите 20–30 запросов: обязательный вызов, отсутствие вызова, неправильные и неполные аргументы.
2. Зафиксируйте ожидаемый инструмент и допустимые значения параметров.
3. Добавьте JSON Schema и отдельную прикладную валидацию.
4. Сохраните имя модели, версию набора и параметры запуска вместе с результатом.
5. Посчитайте выбор инструмента, полноту, валидность аргументов, лишние вызовы и P95 задержки.
6. Подключите smoke-набор к pull request.
7. Переносите каждый найденный сбой в регрессионный набор.
8. Для опасных операций добавьте идемпотентность, авторизацию и ручное подтверждение.
После этого улучшайте описание функций по данным тестов, а не по единичным удачным ответам. Если модель часто выбирает соседний инструмент, сначала проверьте пересечение формулировок и границы ответственности функций. Если выбор правильный, а аргументы неверны, пересмотрите схему, примеры значений и серверную валидацию. Такой разбор связывает измеримую ошибку с конкретным изменением в коде и делает качество tool calling контролируемым параметром, а не впечатлением от нескольких демонстрационных запросов.






