Почему мало финального ответа
Оценка только результата (task completion из урока про offline-метрики) отвечает «дошёл ли агент до цели». Но у агента путь к цели — часть качества, и по одному ответу невидимы целые классы проблем:
- Правильный ответ неправильным путём. Агент угадал итог, но вызвал не тот инструмент, проигнорировал данные, «повезло». В проде на другом входе везение кончится.
- Неэффективность. Тот же результат за 10 шагов вместо 2 — это латентность и деньги, которых в финальном ответе не видно.
- Зацикливание и блуждание. Агент ходит по кругу, повторяет вызовы, «размышляет» вхолостую — и всё же выдаёт ответ.
- Опасные действия. Дошёл до правильного ответа, но по дороге дёрнул инструмент удаления/оплаты, который не следовало.
У агента два независимых измерения: outcome (правильный ли результат) и trajectory (правильным ли путём он получен). Хороший агент силён по обеим. Оценка только outcome пропускает «правильно по случайности» и «дорого/опасно по дороге»; оценка только trajectory — рискует придраться к нестандартному, но верному пути. Нужны обе: outcome отвечает «решил ли задачу», trajectory — «решил ли её хорошо».
Что такое траектория
Траектория (trajectory) — это записанная последовательность шагов агента от запроса до ответа: какие инструменты вызваны, с какими аргументами, что вернули, какие узлы графа посещены, сколько итераций. По сути это тот самый трейс из раздела observability, но рассматриваемый как объект оценки, а не отладки.
Оба агента на диаграмме дали верный ответ — outcome-оценка поставила бы обоим «pass». Но траектории кардинально разные: первый эффективен, второй блуждал, дёргал не те инструменты и «угадал». Trajectory evaluation делает эту разницу измеримой.
Сравнение с эталонной траекторией
Базовый подход: задать эталонную траекторию (какие инструменты в каком порядке ожидаются) и сравнить с фактической. Но «сравнить» — не всегда «точно совпасть»: у задачи может быть несколько правильных путей. Отсюда несколько режимов строгости:
| Режим | Что проверяет | Когда |
|---|---|---|
| Exact match | Точно те инструменты, в точном порядке, без лишних | Строгий процесс, единственно верный путь |
| In-order | Ожидаемые инструменты в нужном порядке, лишние допустимы | Порядок важен, но шаги можно добавлять |
| Any-order | Нужные инструменты вызваны, порядок не важен | Порядок свободный (например, параллельный сбор) |
| Superset / поднабор | Все нужные вызваны (плюс-минус лишние) | Главное — не пропустить обязательные шаги |
# из трейса достаём фактическую последовательность вызванных инструментов
def tool_sequence(trace) -> list[str]:
return [step.tool for step in trace.steps if step.type == "tool"]
expected = ["search", "summarize"] # эталонная траектория
actual = tool_sequence(run_trace) # напр. ["search", "calc", "summarize"]
def exact_match(exp, act): return exp == act
def in_order(exp, act): # exp — подпоследовательность act
it = iter(act)
return all(tool in it for tool in exp)
def any_order(exp, act): return set(exp) <= set(act)
assert in_order(expected, actual) # порядок соблюдён, calc — лишний
# для регрессии по траектории: эталон в golden dataset, режим — под задачу
Соблазн — жёсткий exact match: «ожидаю ровно эти шаги». Но у многих задач несколько правильных траекторий (искать в вебе или в БД — обе верны). Слишком строгое сравнение будет штрафовать корректные, но нестандартные пути — тест начнёт флапать и мешать легитимным улучшениям. Выбирай режим по природе задачи: exact — только для процессов с единственно верным порядком; чаще нужен in-order/any-order или оценка судьёй.
Что оценивать в траектории
Сравнение с эталоном — лишь один аспект. Полная оценка траектории смотрит на несколько измерений:
- Tool selection. Выбраны ли правильные инструменты под задачу? Не вызван ли ненужный (или, хуже, опасный)?
- Аргументы вызовов. Переданы ли инструментам корректные параметры (правильный запрос в поиск, верные поля)?
- Эффективность. Число шагов/итераций, токены, латентность. Достигнут ли результат минимальным разумным путём?
- Отсутствие блужданий. Нет повторов, зацикливаний, «пустых» размышлений, лишних вызовов.
- Безопасность пути. Не дёрнуты действия, которых не следовало (удаление, оплата, запись) — даже если ответ верный.
Особенно ценное измерение для прода: два агента с верным ответом, но один за 2 шага, другой за 10 — это 5× разница в стоимости и времени (вспомни cost/latency-метрики из урока про дашборды). Trajectory evaluation ловит «правильно, но дорого» ещё до прода — на датасете видно, что новая версия промпта стала решать те же задачи в 2 раза длиннее. Outcome-оценка этого не покажет: ответ-то верный.
LLM-судья по траектории
Многое в траектории — детерминированно (какие tools, сколько шагов) и проверяется кодом. Но некоторые вопросы требуют суждения: «разумна ли была эта последовательность?», «оправдан ли лишний вызов?», «логичен ли ход рассуждений?». Здесь — тот же LLM-as-judge, но судящий путь, а не ответ:
TRAJ_JUDGE = """Оцени РАЗУМНОСТЬ последовательности действий агента (не ответ).
Задача: {task}
Траектория (шаги агента):
{trajectory}
Критерий: были ли выбраны уместные инструменты, без лишних/повторных
вызовов, разумным ли путём достигнута цель? Шкала 1–5.
Сначала обоснуй, потом верни JSON {{"reasoning": "...", "score": <1-5>}}"""
def judge_trajectory(task: str, trace) -> dict:
steps = format_steps(trace) # человекочитаемый список шагов из трейса
raw = llm(TRAJ_JUDGE.format(task=task, trajectory=steps), temperature=0)
return json.loads(raw)
# всё из урока про judge применимо: калибровать по людям, следить за искажениями,
# детерминированное (число шагов, набор tools) — считать КОДОМ, а не судьёй
Оценка пути судьёй — частный случай LLM-as-judge, со всеми оговорками: судья недетерминирован, подвержен искажениям, его надо калибровать по человеческой разметке траекторий. И тот же принцип «дешёвое — кодом»: число шагов, набор вызванных инструментов, наличие опасного вызова — детерминированные проверки; судью зови только для «разумности» пути, которую кодом не выразить.
Типичные ошибки
«Ответ верный → всё хорошо» пропускает правильно-по-случайности, неэффективность, опасные действия по дороге. Для агента оценивай и outcome, и trajectory.
Если у задачи несколько верных траекторий, exact match штрафует корректные нестандартные пути и флапает. Выбирай режим (in-order/any-order/judge) по природе задачи.
Верный ответ за 10 шагов вместо 2 — это скрытые деньги и латентность. Меряй число шагов/токенов траектории, иначе пропустишь дорогую деградацию.
Число шагов, набор инструментов, наличие опасного вызова — детерминированны, LLM-судья тут дорог и менее надёжен. Судью — только для «разумности» пути.
Шпаргалка
АГЕНТ = вопрос → ЦЕПОЧКА решений → ответ. Оценивай ПУТЬ, не только пункт назначения.
2 оси: OUTCOME (верный результат?) + TRAJECTORY (верным ли путём?)
только outcome пропускает: верно-по-случайности · неэффективность · блуждание · опасные действия
ТРАЕКТОРИЯ = записанная последовательность шагов (= трейс как ОБЪЕКТ ОЦЕНКИ):
какие tools + аргументы · порядок/число шагов · результаты · узлы графа · стоимость пути
СРАВНЕНИЕ С ЭТАЛОНОМ (режимы строгости):
exact match — ровно те tools, тот порядок (единственно верный путь)
in-order — ожидаемые по порядку, лишние ок · any-order — набор нужных, порядок не важен
superset — все обязательные вызваны
⚠️ НЕ exact там, где путей несколько → штрафует верные нестандартные, флапает
ЧТО ОЦЕНИВАТЬ: tool selection (те ли tools?) · аргументы · ЭФФЕКТИВНОСТЬ (шаги/токены/latency)
· отсутствие блужданий/повторов · БЕЗОПАСНОСТЬ пути (не дёрнут опасный вызов)
эффективность = деньги+латентность, ловит «верно, но дорого» ДО прода
LLM-СУДЬЯ ПО ТРАЕКТОРИИ (для «разумности» пути):
частный случай LLM-as-judge → калибровать по людям, следить за искажениями, temp=0
детерминированное (# шагов, набор tools, опасный вызов) — КОДОМ, не судьёй
ГРАБЛИ: только финальный ответ · exact при неск. путях · игнор эффективности · судья вместо кода
Практическое задание
Задание: оцени траектории агента
- Для агента из модулей 03–05 возьми один запрос и запиши его траекторию из трейса (LangSmith/Langfuse): список вызванных инструментов с аргументами и число шагов.
- Задай эталонную траекторию для этой задачи. Один ли верный путь или несколько? Какой режим сравнения (exact/in-order/any-order) уместен и почему?
- Напиши функцию, которая достаёт последовательность tools из трейса и сравнивает с эталоном в выбранном режиме. Проверь на «хорошей» и «блуждающей» траекториях.
- Измерь эффективность: число шагов и токены на двух версиях промпта для одной задачи. Стала ли новая версия длиннее при том же ответе?
- Найди в траектории потенциально опасный вызов (запись/удаление/оплата). Как детерминированно проверить, что агент его не делает без нужды?
- Напиши judge-промпт, оценивающий «разумность» траектории. Что ты доверишь судье, а что посчитаешь кодом? Как откалибруешь судью?
- Со звёздочкой: добавь trajectory-проверку в regression-набор из прошлого раздела: пусть тест падает не только при неверном ответе, но и при деградации пути (лишние шаги / не тот инструмент).
Что дальше
Раздел «Тестирование агентов» закрыт: юниты (компоненты), интеграционные (стыки), датасеты (тест-кейсы) и trajectory (путь) вместе с оценкой качества дают полную защиту агента как программы и как LLM-системы. Но даже идеально протестированный агент в проде встретит то, что не зависит от него: провайдер упадёт, API затаймаутит, придёт лимит. Следующий раздел — «Надёжность и обработка ошибок»: retry, fallback, rate limiting и graceful degradation.