Эвалуация ИИ-агентов в продакшене: от трейсов к тестовым наборам

Автоматический перевод Эта статья была автоматически переведена с оригинальной английской версии.

Обновление статьи

Изначально опубликовано 10 июня 2026 года. Проверено и обновлено 6 сентября 2026 года. Обновление охватывает новые бенчмарки агентов, изменения в работе грейдеров и данные о влиянии инфраструктуры на оценки.

В финальном ответе может быть сказано, что возврат средств выполнен, хотя из трейса видно, что verify_identity так и не запустился, issue_refund повторился 17 раз или агент объявил об успехе до изменения данных в базе. Оценка только по ответу скрывает такие сбои.

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

Краткое сравнение инструментов см. в разделе Лучшие инструменты для оценки ИИ-агентов.


Чем эвалы агентов отличаются

Традиционные эвалы LLM обычно оценивают одну пару «вход–выход»: релевантность, faithfulness, корректность, безопасность, иногда стиль. Агенты добавляют планирование, вызовы инструментов, ретраи и проверки завершения — и каждый шаг становится новой точкой отказа.

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

lookup_order -> issue_refund -> final_answer

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

Есть и вторая проблема: ошибки накапливаются. Если в рабочем процессе есть 20 обязательных шагов, каждый из них выполняется независимо, а надёжность каждого шага составляет 95%, то сквозная вероятность успеха будет около 36%:

0.95200.360.95^{20} \approx 0.36

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

Строка или дерево: где скрываются сбои агентовСтрока или дерево: где скрываются сбои агентов

Две исследовательские команды выразили это в числах.

tau-bench предоставляет агенту задачи авиакомпаний и розничного обслуживания клиентов. Агент взаимодействует с симулированным пользователем, вызывает API и должен соблюдать отраслевую политику. После разговора грейдер проверяет, перешла ли база данных в размеченное целевое состояние. Правдоподобный транскрипт с неправильными записями всё равно не проходит проверку.

При такой системе оценки GPT-4o решала только 35,2% задач авиакомпаний и чуть больше 60% задач в розничной торговле. В статье также была введена метрика pass^k — вероятность того, что все k независимых испытаний пройдут успешно, усреднённая по задачам.

В ритейле, где разбиение было проще, показатель pass^8 оказался ниже 25%. Для случайно выбранной задачи из ритейла и восьми независимых запусков вероятность того, что пройдут все восемь, была ниже 25%. Эвал одного запуска не может измерить такую стабильность.

MAST исследует причины сбоев агентов. Авторы построили таксономию из 14 режимов на основе 150 размеченных вручную трейсов, а затем применили её к более чем 1 600 трейсам из 7 популярных мультиагентных фреймворков. Таксономия включает расплывчатые определения ролей (системный дизайн), игнорирование одним агентом сообщения другого (межагентный мисалайнмент) и объявление об успехе без проверки результата (отсутствие верификации). Эти сбои указывают на проблемы в промптах, логике оркестрации и недостающих проверках в харнессе. Более сильная базовая модель не сможет выполнить шаг верификации, которого изначально не было, поэтому целью эвала должен быть харнесс вокруг модели.


Разрыв в использовании

Опрос LangChain State of Agent Engineering (1 340 респондентов, проведённый в конце 2025 года) показывает, что у многих команд уже есть исходные данные для более качественных эвалов. Согласно опросу, 89% используют хотя бы какую-то observability, 52,4% проводят офлайн-эвалы, а 37,3% — онлайн-эвалы.

Опрос также показывает, что 57,3% респондентов уже используют агентов в продакшене. На вопрос о препятствиях для выхода в продакшен 32% назвали качество, а 20% — латентность. Это вендорский опрос среди его респондентов, а не перепись всех команд, работающих с агентами, но он выявляет полезный разрыв между сбором трейсов и систематической оценкой.

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

Каждый диагностированный сбой в продакшене должен оставлять после себя трейс, метку, строку в датасете и скорер. Повторяющийся сбой должен попадать в регрессионный набор.


Выбирайте метрики по типу сбоя

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

  1. Эвалы результата отвечают на вопрос, выполнена ли задача.
  2. Эвалы траектории отвечают на вопрос, был ли путь корректным, эффективным и соответствующим политикам.
  3. Эвалы компонентов отвечают на вопрос, какой инструмент, retrieval, субагент или шаг принятия решения дал сбой.

Три уровня оценки агентов и их метрикиТри уровня оценки агентов и их метрики

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

ВопросСемейство метрикОфлайн-/онлайн-контрактДетерминированная метрика или джадж?На что обратить внимание
Агент вызвал правильные инструменты?Корректность вызовов инструментов: точное совпадение, совпадение по порядку или в любом порядкеЭталонные последовательности офлайн; инварианты обязательных инструментов и аномалии онлайнДетерминированнаяТочное совпадение наказывает допустимые альтернативные пути
Он вызвал их с правильными аргументами?Корректность аргументов, валидация схемы, совпадение параметровОжидаемые аргументы офлайн; проверки схемы, диапазонов и политик онлайнОбаПравильный инструмент с неправильными аргументами всё равно означает сбой
Он не сделал лишних шагов?Эффективность по шагам, число ретраев, обнаружение циклов, стоимость и латентностьБюджеты шагов и циклов офлайн; дрейф стоимости и латентности онлайнВ основном детерминированнаяВысокий процент выполнения задач может скрывать дорогое блуждание
Задача действительно выполнена?Выполнение задачи, оценка результата, diff финального состоянияСимулятор или эталонное состояние офлайн; финальное состояние, сигнал пользователя или асинхронный джадж онлайнДжадж или проверка состоянияПо возможности оценивайте состояние окружения
Сохранился ли контекст между ходами?Фиделити в мультиходовом взаимодействии, соблюдение роли, полнота диалогаСкриптовые долгие сценарии офлайн; выборка долгих сессий онлайнДжаджОдноходовые тесты ничего не говорят о 14-м ходу
Он остановился в нужный момент?Корректность завершения, преждевременный успех, бесконечная работаСценарные тесты офлайн; мониторы циклов, таймаутов и ложного успеха онлайнОба«Готово» может быть галлюцинированным состоянием
Он правильно интерпретировал результаты инструментов?Понимание результатов инструментов, проверки downstream-состоянияAdversarial-результаты инструментов офлайн; проверки downstream-состояния и выборочный ревью онлайнОбаОценивайте downstream-состояние, а не exit code инструмента

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

Корректность вызовов инструментов

Корректность инструментов сравнивает вызванные инструменты с ожидаемыми. Заранее определите нужную строгость:

  • Точное совпадение: последовательность должна совпадать полностью. Используйте этот режим, когда порядок задан политикой, например lookup_order -> verify_identity -> issue_refund.
  • Совпадение по порядку: обязательные инструменты должны появиться в правильном относительном порядке, но дополнительные безвредные вызовы разрешены.
  • Совпадение в любом порядке: обязательные инструменты должны быть вызваны, но их порядок может отличаться.

Для начала достаточно небольшого локального скорера:

from collections import Counter

def tool_correctness(called: list[str], expected: list[str], mode: str = "in_order") -> float:
    if mode not in {"exact", "in_order", "any_order"}:
        raise ValueError(f"unknown matching mode: {mode}")
    if mode == "exact":
        return float(called == expected)
    if not expected:
        return 1.0
    if mode == "any_order":
        matched = sum((Counter(called) & Counter(expected)).values())
        return matched / len(expected)

    rows = [[0] * (len(expected) + 1) for _ in range(len(called) + 1)]
    for i, tool in enumerate(called):
        for j, wanted in enumerate(expected):
            if tool == wanted:
                rows[i + 1][j + 1] = rows[i][j] + 1
            else:
                rows[i + 1][j + 1] = max(rows[i][j + 1], rows[i + 1][j])
    return rows[-1][-1] / len(expected)

called = ["lookup_order", "check_refund_policy", "issue_refund"]
expected = ["lookup_order", "verify_identity", "issue_refund"]

print(round(tool_correctness(called, expected, "exact"), 3))     # 0.0
print(round(tool_correctness(called, expected, "in_order"), 3))  # 0.667
assert tool_correctness(["issue_refund"], [], "exact") == 0.0
assert tool_correctness([], [], "exact") == 1.0
assert tool_correctness(["a", "b"], ["a", "a", "b"], "any_order") == 2 / 3
try:
    tool_correctness([], [], "typo")
except ValueError:
    pass
else:
    raise AssertionError("unknown modes must fail")

Метрика in_order — это полнота по longest-common-subsequence: какая доля требуемой последовательности сохранилась в правильном порядке. Обратите внимание, чего она не учитывает. Лишние вызовы не снижают результат, поэтому ИИ-агент может получить здесь 1.0, сделав вдвое больше нужных вызовов. Если дополнительные вызовы стоят денег или изменяют состояние, отслеживайте вместе с ней precision (совпавшие требуемые вызовы / общее число вызовов) и интерпретируйте эти две метрики совместно. Recall выявляет пропущенный шаг, а precision — блуждание. Ни recall 1.0, ни высокая precision не дают права на дополнительные мутации. Проверяйте каждый вызов, изменяющий состояние, по его разрешениям, ресурсу, аргументам и обязательной предварительной верификации. При пустом ожидаемом списке только exact mode означает «вызовы запрещены»; у остальных режимов нет положительных требований.

Метрика Tool Correctness в DeepEval предоставляет те же настройки через should_consider_ordering и should_exact_match.

Корректность аргументов

Вызов правильного инструмента с неправильными аргументами часто хуже вызова неправильного инструмента, потому что трейс выглядит нормально.

Для простых случаев проверяйте JSON-схему и точные значения. Для семантических случаев сохраняйте ожидаемые аргументы и оценивайте различия:

{
    "trace_id": "tr_2417",
    "input": "Reschedule order A-100 for June 19, 2026.",
    "expected_tools": ["lookup_order", "reschedule_delivery"],
    "expected_arguments": {
        "reschedule_delivery": {
            "order_id": "A-100",
            "date": "2026-06-19"
        }
    }
}

Метрика по имени инструмента не обнаружит 2026-06-17, когда политика требует 2026-06-19. Датасет должен хранить и аргументы.

В этой иллюстрации с одним вызовом на инструмент parameter-match — это доля ожидаемых троек (tool, key, value), которые ИИ-агент сопоставил правильно. Приведённые ниже словари корректны только тогда, когда каждый релевантный инструмент вызывается не более одного раза. Не создавайте их перезаписью предыдущих вызовов с тем же именем: так можно скрыть неправильный возврат средств, за которым последовал правильный. Для повторных вызовов сохраняйте ID вызовов и их порядок, сопоставляйте целевой вызов и отдельно проверяйте каждую мутацию.

def argument_correctness(called_args: dict, expected_args: dict) -> float:
    total = matched = 0
    for tool, params in expected_args.items():
        for key, want in params.items():
            total += 1
            if key in called_args.get(tool, {}) and called_args[tool][key] == want:
                matched += 1
    return matched / total if total else 1.0

assert argument_correctness({}, {"reschedule_delivery": {"date": None}}) == 0.0
assert argument_correctness({"reschedule_delivery": {}},
                            {"reschedule_delivery": {"date": None}}) == 0.0
assert argument_correctness({"reschedule_delivery": {"date": None}},
                            {"reschedule_delivery": {"date": None}}) == 1.0

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

Эффективность, циклы и тупиковые пути

ИИ-агент, который выполняет задачу после пяти избыточных вызовов инструментов, всё равно указывает на проблему с планированием и обходится дороже.

Начните с простых сигналов:

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

По возможности запускайте эти проверки до джаджа. Детектор циклов можно реализовать несколькими строками поверх трейса. Модель ему не нужна.

Выполнение задачи и оценка результата

При оценке результата вопрос звучит так: «Получил ли пользователь то, что просил?»

Лучше всего работают два паттерна:

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

Второй вариант лучше, если его удаётся реализовать. Финальное состояние — это контракт. Транскрипт — лишь свидетельство.

Чтобы оценка оставалась объективной, учитывайте два нюанса. В аудите агентных бенчмарков 2025 года обнаружилось, что tau-bench оценивает некоторые задачи исключительно по состоянию базы данных. В некоторых задачах размеченный результат не требует ни изменения состояния, ни конкретного текста. Поэтому ИИ-агент, который ничего не делает, может получить проходной балл: 38% в разбиении для авиакомпаний и 6,0% в ритейле при любом k. Anthropic сообщила о запуске Opus 4.5, который «провалил» задачу бронирования в tau2-bench — бенчмарке-преемнике. ИИ-агент нашёл лазейку в политике, которая на деле приводила к лучшему результату для пользователя. Грейдинг состояния лучше сопоставления транскриптов, но целевое состояние всё равно задаётся аннотацией, а в аннотациях бывают ошибки. Проверяйте случаи, которые слишком легко получают проходной балл, а не только провальные.

Версии бенчмарков и окружения меняют результат

Приведённые выше исходные показатели tau-bench объясняют надёжность при повторных запусках; это не текущий лидерборд моделей. В поддерживаемом репозитории tau-bench теперь представлен tau3-bench, в который добавлены retrieval знаний и полнодуплексный голосовой режим. Исправление грейдинга в версии v1.0.1 от июля 2026 года меняет оценки banking_knowledge: результаты предыдущих версий для этого домена несопоставимы. Фиксируйте ревизии задач и грейдера наряду с моделью и повторно оценивайте сохранённые траектории после исправления аннотации.

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

Сэндбокс тоже является частью теста. Исследование инфраструктуры Anthropic за февраль 2026 года выявило разницу в шесть процентных пунктов на Terminal-Bench 2.0 между строгой конфигурацией ресурсов и конфигурацией без ограничений при одной и той же модели, харнессе и задачах. Дополнительный запас ресурсов одновременно сократил число инфраструктурных сбоев и позволил использовать другие стратегии решения. Фиксируйте гарантии и лимиты CPU и RAM, таймауты, конкурентность, доступ к сети и обработку инфраструктурных ошибок. Отчитывайтесь о таких сбоях отдельно и не исключайте их незаметно из знаменателя ожидаемых задач.

Эвалы компонентов

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

Большую часть случаев покрывают три проверки:

  • Скоринг по каждому спану: запускайте метрику, подходящую для типа спана. Для спанов retrieval проверяйте recall и precision относительно размеченного чанка, для спанов субагентов — валидацию схемы и отдельный скор корректности инструментов, для спанов инструментов — error rate и латентность.
  • Интерпретация результата инструмента: подайте агенту корректный, но неудобный вывод инструмента — пустой список, частичное совпадение или устаревший timestamp — и проверьте, что он сделает дальше. Инструмент может вернуть правильный результат, а агент — неверно его интерпретировать; такая ошибка проявится через два шага.
  • Атрибуция ошибки: видимая ошибка обычно возникает не там, где была допущена исходная. Относите её к самому раннему спану, вывод которого уже был неверным, а не к шагу, на котором возникла ошибка.

Здесь также окупается математика накопления ошибок из начала главы. Даже если каждый из 20 шагов по отдельности выглядит корректным, весь прогон всё равно может завершаться ошибкой в большинстве случаев. Pass rate по отдельным спанам показывает, какой шаг выполняется с вероятностью 95%, а какой — с вероятностью 70%.


Цикл trace-to-eval

Изучайте ошибки в продакшене, прежде чем придумывать новые кейсы для эвалов.

Цикл trace-to-evalЦикл trace-to-eval

Цикл выглядит так:

  1. Соберите достаточно данных трейсинга, чтобы восстановить ошибку, контролируя чувствительный контент.
  2. Разметьте, что именно сломалось.
  3. Сгруппируйте похожие ошибки.
  4. Оставьте репрезентативные goldens, включая варианты, для которых нужны разные результаты.
  5. Версионируйте датасет.
  6. Запускайте его в CI.
  7. Продолжайте онлайн-скоринг выбранных трейсов из продакшена.

В сопутствующем репозитории trace2evals реализован полный цикл для неисправного саппорт-агента. Он собирает GenAI-спаны OpenTelemetry, обнаруживает ошибки с помощью детерминированных правил, дедуплицирует кейсы в версионируемый golden-датасет и повторно запускает каждый golden в CI. Бэкенд по умолчанию заменяет модель детерминированными правилами, которые воспроизводят решения багнутого агента, поэтому make demo полностью воспроизводит цикл офлайн и не требует API-ключа. Запустите uv sync --extra live и задайте API-ключ — те же команды начнут использовать настоящую модель.

Это обучающий пайплайн. В ревизии, проверенной 6 сентября 2026 года, всё ещё есть крайние случаи в скорере, проверки авторизации по имени и общее состояние тестовых запусков. Его адаптер трейсинга ожидает собственные атрибуты спанов и форматы сообщений. Приведённые здесь исправленные примеры не обновляют этот репозиторий и не обеспечивают авторизацию в продакшене; прежде чем полагаться на вердикты CI, проверяйте успешную авторизацию каждой мутации с ограничением по ресурсам и изолируйте тестовые запуски.

Изучайте ошибки с помощью error analysis

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

  1. Изучите трейсы и сделайте заметки в свободной форме о том, что пошло не так.
  2. Сгруппируйте повторяющиеся ошибки в именованные категории.
  3. Разметьте трейсы в соответствии с этой таксономией.
  4. Создайте конкретные тесты для самых крупных кластеров, по которым можно принять меры.

Не начинайте с таких меток, как reasoning_issue или tool_problem. Они слишком расплывчаты и непригодны для тестирования. Используйте метки вроде missing_identity_verification, date_argument_mismatch, retried_same_tool_after_429 или stopped_before_database_update. Настолько конкретная метка сразу показывает, что именно должен проверять регрессионный тест.

Дедуплицируйте перед продвижением

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

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

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

Версионируйте датасет

Версионируйте датасеты так же, как промпты и код. При любом существенном изменении — модели, промпта, схемы инструмента, промпта джаджа или поведения приложения — нужно прогонять одну и ту же версию датасета до и после изменения.

CI-проверка должна фиксировать:

  • версию датасета
  • версию приложения
  • версию промпта
  • модель джаджа
  • промпт джаджа
  • версию кода эвалуатора
  • схемы инструментов, policy, харнесс и конфигурацию управления контекстом
  • ревизию модели, настройки ризонинга и сэмплирования
  • начальный fixture окружения и разрешённые внешние эффекты

Если меняется что-либо из этого списка, сравнение до и после становится неоднозначным. Файл goldens-v3.json в git подходит для небольшого масштаба. Когда датасет становится коллективным, помогают нативные для инструментов снапшоты в Langfuse, Phoenix, Braintrust или LangSmith.

Храните отдельно регрессии разработки, примеры для калибровки джаджа, отложенную валидацию и образцы мониторинга. Перед разбиением группируйте связанные сессии, пользователей и задачи, чтобы почти дубликаты не просочились между наборами. Как только кейс начинает влиять на промпт или рубрику, считайте его данными разработки. Набор найденных ошибок тестирует известные регрессии; его среднее значение не оценивает production success rate.

Сбрасывайте изменяемое состояние перед каждым прогоном: файлы, строки в базе данных, кэши и fixture инструментов. Изолируйте credentials и внешние эффекты, а бюджеты кандидата и baseline держите одинаковыми. Отчитывайтесь о количестве задач отдельно от количества испытаний, включая все запущенные попытки, таймауты, крэши и результаты, которые невозможно оценить. Сравнивайте парные результаты на одних и тех же задачах. Эти решения следуют подходу с чистыми испытаниями и оценкой результатов, описанному в руководстве Anthropic по эвалуациям.

Запускайте эвалы в CI

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

После восстановления fixture кейса в изолированном испытании тест должен заново запустить текущего агента на golden-вводе. Он не должен просто воспроизводить старый неудачный трейс (эскиз; рабочая версия находится в companion-репозитории):

@pytest.mark.parametrize("golden", GOLDENS, ids=[item["id"] for item in GOLDENS])
def test_agent_regression(golden: dict) -> None:
    answer, fresh_trace = run_agent_and_capture_trace(golden["input"])

    refired = set(flag_failures(fresh_trace)) & set(golden["failure_modes"])
    assert not refired, f"failure mode regressed: {sorted(refired)}"

    assert tool_correctness(
        called=[call["name"] for call in fresh_trace["tool_calls"]],
        expected=golden["expected_tools"],
        mode=golden.get("tool_match", "in_order"),
    ) >= golden.get("tool_threshold", 1.0)

Эту разницу легко упустить. Задача датасета — поймать следующую версию агента, если она повторит старую ошибку, а не архивировать саму ошибку.


Откалибруйте джаджа, прежде чем ему доверять

LLM-as-judge помогает. Но с ним также легко обмануть себя.

G-Eval оценивает три бенчмарка мета-оценки: SummEval, созданный на основе новостных саммари CNN/DailyMail; Topical-Chat — бенчмарк диалогов с опорой на знания; и QAGS, проверяющий фактическую согласованность саммари из CNN/DailyMail и XSum. Используя GPT-4 в качестве базовой модели, G-Eval-4 достиг коэффициента корреляции Спирмена 0,514 с человеческими оценками на SummEval. Его функция скоринга взвешивает уровни оценок по вероятности токенов (score=ip(si)si\text{score} = \sum_i p(s_i)\,s_i).

В статье вероятности токенов GPT-4 оценивались по 20 сэмплам, поскольку в эксперименте модель их не предоставляла. Хостинговая модель может не возвращать пригодные logprobs, поэтому сохраняйте рубрику, но не создавайте впечатление, будто вы воспроизвели взвешивание вероятностей из статьи. Эти результаты сопоставляют протокол статьи с её NLG-бейзлайнами на данных бенчмарков. Они подтверждают необходимость тестировать джаджа с явной рубрикой, но не служат основанием для замены автоматических метрик в целом или бенчмарка траекторий production-агентов.

MT-Bench показал, что GPT-4 соглашался с человеческими предпочтениями примерно так же часто, как люди соглашались друг с другом. Этот результат помог сделать оценивание с помощью LLM мейнстримом. Последующие исследования выявили смещения по позиции, длине и self-preference. Оценки джаджа также могут меняться при изменении промпта или версии модели.

JudgeBench сформировал пары ответов, в которых один вариант был объективно неверным в задачах на проверяемые знания, ризонинг, математику и код. С обычным промптом для джаджа GPT-4o набрал 50,9% — едва выше случайного угадывания; более сильный промпт Arena-Hard из статьи поднял результат той же модели лишь до 56,6%. При таком сильном промпте важнее смена самой модели: Claude 3.5 Sonnet, лучший универсальный джадж в тесте, достиг 64,3%, а o3-mini при высоком уровне усилий на ризонинг — 80,9%. Уверенные, но неверные ответы остаются сложными для джаджа, который не выполняет ризонинг перед оцениванием.

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

Цикл калибровки джаджаЦикл калибровки джаджа

Если джадж необходим, делайте вердикт структурированным. Schema-Guided Reasoning (SGR) задаёт для вердикта схему, которая определяет форму вывода и упрощает его инспекцию. Structured Outputs или constrained decoding могут принудительно обеспечить форму объекта, обязательные поля и ограничения значений для таких полей, как evidence, passed_criteria, failed_criteria, failure_mode и score.

Размещайте поля с доказательствами перед оценкой, если так запись проще инспектировать. Порядок полей — это способ представления, а не гарантия корректного ризонинга. Вердикт, соответствующий схеме, всё ещё может содержать неподтверждённые доказательства или ненадёжную оценку. Используйте калибровку по человеческим меткам, детерминированные валидаторы и ревью транскриптов, чтобы проверять надёжность джаджа. CI может сравнивать стабильный JSON-объект, но это проверяет инспектируемость и форму, а не доказывает, что этапы рубрики были соблюдены.

Структурированный вердикт также может изменить кривую затрат. Рассматривайте более дешёвую модель как кандидата, а не как автоматическую замену. Прогоните её на том же датасете для калибровки с человеческими метками. Сравните её согласие, долю ложных пропусков и долю ложных срабатываний с более крупным джаджем. Используйте её только для штатных случаев и только если она проходит пороги, заданные вашим приложением. Более крупный джадж оставьте для разногласий, случаев высокого риска и калибровочных прогонов.

Чеклист гигиены джаджа по умолчанию:

  1. По возможности используйте бинарные вердикты pass/fail. Пятибалльные шкалы создают иллюзию точности.
  2. Перед финализацией рубрики разметьте траектории, охватывающие реальные режимы отказа. Выбирайте размер выборки с учётом покрытия и неопределённости, которую может выдержать решение, а затем выделите отдельные случаи для валидации.
  3. Измеряйте согласие джаджа с людьми с помощью каппы Коэна, confusion matrix и полноты по положительному и отрицательному классам. Каппа измеряет согласие с поправкой на согласие, ожидаемое случайно; чем она выше, тем лучше. Джадж, который всегда отвечает «pass», не обладает полезной различающей способностью, поэтому каппа может быть равна нулю или не определена. До использования этой метрики для допуска релиза заранее решите, что делать в случае неопределённого значения.
  4. Декомпозируйте слишком общие критерии. «Проверил ли агент личность пользователя перед вызовом refund tool?» лучше, чем «Была ли траектория хорошей?».
  5. Выдавайте вердикт через SGR-схему с доказательствами, невыполненными критериями, режимом отказа и оценкой.
  6. Сравнивайте джаджей из одной и разных семейств с отложенными человеческими метками; одно лишь разделение по семействам не подтверждает надёжность.
  7. Измеряйте чувствительность к порядку в парных сравнениях. Сохраняйте рандомизацию или агрегацию по переставленному порядку только в том случае, если это улучшает решения на отложенной выборке; в контролируемом исследовании 2026 года обнаружилось, что перестановка порядка может ухудшать результаты на adversarial-кейсах.
  8. Не начисляйте баллы за дополнительный текст, если он не добавляет корректный, релевантный и подтверждённый контент. Более длинный ответ не становится от этого лучше.
  9. Фиксируйте модель джаджа, промпт, датасет, схему и версию приложения.
  10. Повторно калибруйте систему после изменений модели, промпта, инструментов, политики или схемы.

Панель — ещё один кандидат для тестирования. В PoLL сообщалось о лучшем согласии с человеческими суждениями, снижении внутримодельного смещения и меньшей стоимости по сравнению с базовым вариантом на одном GPT-4 для шести датасетов. Эти результаты относятся к использованным моделям, задачам и историческим ценам. Они не доказывают, что панель безопаснее на вашей задаче. Сравните её доли ложных пропусков и ложных срабатываний, стоимость и объём работы по разбору разногласий с одним калиброванным джаджем на отложенных метках.

Универсального порога каппы, который делает джадж пригодным для CI, не существует. Отчитывайтесь о confusion matrix, количестве меток, доле ложных пропусков среди человеческих отказов и доле ложных срабатываний среди человеческих одобрений — с указанием неопределённости. Выбирайте лимиты для релиза исходя из последствий этих ошибок. Используйте очереди на ревью, когда доказательств недостаточно для автоматического принятия, и сохраняйте авторизацию человека для значимых действий, если этого требует рабочий процесс.


Гардрейлы работают inline, а online evals наблюдают постфактум

!!! byte «Byte говорит»

Асинхронная проверка на промпт-инъекцию может обнаружить нарушение только после выполнения tool call.

Их часто путают, потому что оба подхода выдают оценки. Разница — в месте применения: inline — на пути запроса, до выдачи результата, а online evals — после ответа.

Гардрейлы и online evalsГардрейлы и online evals

Гардрейлы работают inline. Они быстрые и видимы пользователю. Гардрейл может заблокировать вызов инструмента, отредактировать PII, отклонить промпт-инъекцию или принудительно запустить повторную попытку до того, как ответ покинет вашу систему. Ложное срабатывание — это баг в продакшене. Ложный пропуск менее заметен и хуже, поскольку в цепочке обработки запроса ничего о нём не сообщает. Проверки схемы, диапазонов и политик детерминированы. Детекция инъекций и PII выполняется классификаторами, поэтому считайте пропуски ожидаемыми и держите асинхронный эвал, отслеживающий то, что они пропускают.

Офлайн-эвалы запускаются до релиза. Они воспроизводимы. Они проверяют промпты, модели, инструменты, ретриверы и политики на фиксированном датасете.

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

Неправильно выберите место запуска — и пострадает что-то одно из двух:

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

В security-тестах различайте детекцию атаки, попытку выполнить запрещённое действие и фактический успешный вредоносный эффект. Отчитывайтесь об успешных вредоносных эффектах на каждую попытку атаки, указывая threat model и бюджет попыток, а также успешность легитимной задачи и число ложных блокировок на каждую добросовестную попытку. Один лишь скор детектора не доказывает, что данные остались приватными или что запись была предотвращена. Используйте изолированные цели; отчёт Anthropic об инциденте с эвалуацией кибербезопасности документирует, почему эффекты эвалуации необходимо локализовать.

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


Выбор инструментов

Ни один инструмент не владеет всем циклом целиком. Сравнивайте отдельно хранилище трейсов/датасетов и CI/эвал-раннер; один продукт может закрывать обе задачи, но покупать оба инструмента у одного вендора не обязательно.

Это авторский срез, проверенный 2026-09-06. Каждая ссылка ведёт на актуальную документацию, которую я использовал для подтверждения заявленной возможности. По-прежнему действуют ограничения планов и лицензий, требования к API-ключам и доступу к провайдерам, а также требования к инфраструктуре.

ИнструментВыбирайте его, если…Проверяемая возможность и условие
DeepEvalВы запускаете проверки в Python и pytest.deepeval test run выполняет файлы с эвалами, а метрики, не прошедшие проверку, завершают сборку с ошибкой. Для пометки официального baseline от Confident AI требуется CONFIDENT_API_KEY.
Inspect AIВам нужны задачи по безопасности, frontier-моделям или задачи агентов в сэндбоксе.inspect eval и Python API запускают задачи; лимиты, агенты, сэндбоксы и доступ к провайдерам моделей настраиваются отдельно. Это раннер для эвалов, а не production-хранилище трейсов.
PhoenixВам нужны self-hosted трейсинг и эвалы с хранением данных в вашей инфраструктуре.Phoenix документирует бесплатный self-hosting без ограничений по функциям, а также детерминированные эвалуации и эвалуации с LLM. Деплой вы обслуживаете самостоятельно.
LangfuseВам нужен open-source workflow для трейсов, датасетов и экспериментов.Ядро можно развернуть в своей инфраструктуре; Docker Compose для небольших нагрузок не обеспечивает high availability, масштабирование и бэкапы, а для некоторых аддонов нужна лицензия. Его CI action для экспериментов может зафиксировать версию датасета и завершить пайплайн с ошибкой при регрессии.
LangSmithВы уже используете LangChain/LangGraph и принимаете ограничения его платформы.Платформа поддерживает Cloud, Bring Your Own Cloud (BYOC) и self-hosted-варианты; для BYOC и self-hosted требуется Enterprise. Гибридный деплой относится к Agent Servers и не связан с хостингом платформы для трейсинга и эвалуации.
BraintrustУправляемый фидбэк по PR и сопоставимые снапшоты экспериментов важнее самостоятельного хостинга.В документации по CI/CD показан GitHub Action, который публикует результаты в pull request; CI нужны BRAINTRUST_API_KEY и управляемый сервис.
PromptfooРегрессии промптов или red-team должны запускаться до деплоя.В документации по CI описаны варианты через CLI и GitHub Action; action нужны конфигурация, GitHub-токен и секреты провайдера, если они требуются выбранному провайдеру. Это не хранилище трейсов.

Примечания о компромиссах описывают источник затрат, а не их размер. Страницы с ценами меняются, а вендоры считают разные сущности: трейсы, observations, спаны, scores, пользователей, retention или обработанные данные. Перед принятием решения перепроверьте актуальные цены.

Рекомендации с учётом ограничений:

  • Выбирайте Phoenix, если self-hosting, приватность и совместимый с OTel трейсинг — жёсткие требования, а ваша команда может поддерживать деплой.
  • Выбирайте Langfuse, если вам также нужны версионирование датасетов и эксперименты, а вы можете самостоятельно поддерживать его storage-стек или приобрести необходимые add-ons.
  • Выбирайте DeepEval, если основной контракт — pass/fail в Python/pytest CI.
  • Выбирайте Inspect AI, если основная задача — эвалуация безопасности или frontier-агентов в настраиваемых сэндбоксах.
  • Выбирайте LangSmith, если интеграция с LangChain/LangGraph вписывается в ваш workflow; используйте Cloud или учитывайте Enterprise-требование к BYOC либо self-hosted размещению платформы.
  • Выбирайте Braintrust, если управляемая обратная связь по pull request и сравнение экспериментов оправдывают сервис с авторизацией по API-ключу.
  • Выбирайте Promptfoo, если промпт-проверки или red-team-проверки — основная поверхность регрессий, а хранилище трейсов вам не требуется.

Выбор инструмента вторичен. Если production-сбои не превращаются в тест-кейсы, вы в основном платите за хранение трейсов.


Практический чеклист rollout

Сначала постройте пайплайн для сбора evidence, а уже потом расширяйте стек метрик. Начните с решения, откуда будут поступать примеры.

  1. Сначала соберите исторические запуски. Если ИИ-агент уже существует, соберите трейсы, тикеты поддержки, баг-репорты, сессии с thumbs-down, транскрипты ручного QA и заметки dogfooding до изменения реализации. Если ИИ-агент ещё не существует, логируйте каждый прототип и каждый ручной тестовый запуск с первого дня.

  2. Инструментируйте структуру трейса. Собирайте сообщения, вызовы инструментов, аргументы, результаты работы инструментов, ошибки, количество токенов, латентность, стоимость, пользовательский фидбэк, версию приложения, версию промпта, версию модели, версию схемы инструментов и финальное состояние окружения. Используйте соглашения OpenTelemetry GenAI или спаны в стиле OpenInference, если нужна переносимость, и фиксируйте версию соглашения и адаптера. Собирайте содержимое выборочно: редактируйте секреты и персональные данные, ограничивайте доступ и настройте retention до публикации трейсов в датасетах. Используйте Langfuse, LangSmith, Phoenix или Braintrust, если вам сразу нужны UI для трейсов и workflow для работы с датасетами.

  3. Превращайте реальные сбои в seed-кейсы. Прочитайте трейсы до того, как суммировать их с помощью модели. Для каждого полезного сбоя сохраняйте входные данные, ID исходного трейса, ожидаемое состояние, ожидаемые инварианты инструментов, тип сбоя, severity и заметку ревьюера. Langfuse может связывать элементы датасета с production-трейсами; LangSmith умеет создавать датасеты из протрассированных запусков. Сохраняйте ссылку на источник, чтобы кейс оставался аудируемым.

  4. Если истории нет, сгенерируйте cold-start-кейсы. Попросите LLM составить задачи на основе продуктовых требований, политик, схем инструментов, машин состояний и support-макросов. Покройте happy path и сбои, включая неверные права, отсутствие проверки identity, устаревшие результаты инструментов, неоднозначные даты, ретраи после rate limit и противоречивые результаты инструментов.

  5. Не доверяйте синтетическим кейсам, пока их не проверит человек. Синтетические примеры полезны для покрытия, но не являются источником истины. Помечайте их с помощью source: synthetic и требуйте от ревьюера подтвердить ожидаемый результат. По возможности запускайте эталонный путь с заранее известным корректным результатом и независимо валидируйте сгенерированные ожидания; использование другой линейки моделей не заменяет такую проверку.

  6. Соберите небольшой сбалансированный датасет. Включите успешные и неуспешные кейсы, отказы, пограничные случаи, длинные сценарии, кейсы, чувствительные с точки зрения политик, и допустимые альтернативные пути. Не делайте golden «точной старой транскрипцией». Храните всё, что обычно хранится в golden, а также тип сбоя, из-за которого кейс попал в набор.

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

  8. Добавьте одного джаджа в форме SGR. Используйте его только для частей, требующих интерпретации. Откалибруйте его по человеческим меткам и протестируйте выбранную рубрику на отложенных валидационных кейсах. Если он не может разделить хорошие и плохие примеры на калибровочном наборе, исправьте рубрику до подключения джаджа к CI.

  9. Свяжите всё в цикл. Запускайте небольшой офлайн-набор в CI, прогоняйте расширенный набор перед релизом, оценивайте выборочный продакшен-трафик онлайн и возвращайте регулярно возникающие кластеры онлайн-сбоев в офлайн-датасет.

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


Ссылки