Evaluation · Evaluation · урок 5 · 12 мин

Evals на практике: смотреть в traces

Evals in practice: reading traces

ТемыEvalsНаблюдаемость

Зачем

После урока ты сможешь описать рабочий метод практиков: сначала дешёвые assertions (проверки кодом), потом чтение traces и error analysis (разбор ошибок), и только потом judge и A/B-тесты. Вопрос «you've shipped an LLM feature and quality feels off — what do you do first?» проверяет именно это, и ответ «напишу judge» — слабый.

Три уровня и их порядок

Hamel Husain в статье «Your AI Product Needs Evals» описывает три уровня оценки на примере Lucy — AI-ассистента для риелторов компании Rechat, у которого улучшения prompt перестали давать результат:

Уровень Что это Когда гоняют
1. Unit tests (assertions) быстрые проверки кодом на каждое изменение
2. Human & model eval чтение traces людьми, затем judge регулярно, по расписанию
3. A/B testing сравнение версий на живых пользователях после крупных изменений продукта

Цена растёт снизу вверх: уровень 3 дороже уровня 2, уровень 2 дороже уровня 1. Поэтому порядок такой же: сначала дешёвое, что ловит очевидное.

Уровень 1: assertions идут первыми

Assertion — проверка кодом, как в pytest. У Rechat пример: regex проверяет, что во внешний ответ не попали внутренние UUID (идентификаторы записей). Для поиска объектов недвижимости — три входа: найден один, найдено несколько, не найдено ничего.

Как их писать: разбить продукт на функции и сценарии и попросить LLM сгенерировать тестовые входы, например «напиши 50 разных инструкций, которые риелтор мог бы дать ассистенту, чтобы создать контакт». У Rechat таких тестов сотни, и они пополняются с каждым новым видом провала. Процент прохождения — продуктовое решение: 100% не обязательно.

Почему первыми: - дёшево и быстро — секунды, без вызова judge, можно гонять в CI на каждый pull request; - детерминированно — нет вопроса «а можно ли верить оценщику»; - ловят худшее — утечку чужих данных, неверную сумму, сломанный формат; - освобождают людей — читать traces имеет смысл, когда очевидные провалы уже отсеяны.

Пример для банка — ответ не должен показывать внутренние идентификаторы:

import re
UUID = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", re.I)

def check_no_internal_ids(answer: str) -> bool:
    return UUID.search(answer) is None

Уровень 2: traces и как их читать

Trace — журнал одного запроса: вход пользователя, найденные chunks (куски документов) с их ID и версиями, что попало в context, какие tools вызваны с какими аргументами, что они вернули, ответ, число токенов, задержка, цена, версии prompt и модели. Без traces нельзя найти, почему ответ плохой.

Чтение — работа, а не формальность. Советы Husain: - Убери трение. Сделай простой просмотрщик под свою предметную область (на Gradio или Streamlit он собирается быстро), где весь контекст на одном экране. - Бинарные метки. Хорошо или плохо, а не шкала 1–5. - Смотри всегда. «You can never stop looking at data»: traces всех тестовых случаев и реальных пользователей; со временем выборку можно уменьшать, но не до нуля.

Error analysis: open coding → axial coding

В FAQ Husain и Shankar (18.09.2026) метод разбора ошибок выглядит так: 1. Набор. Около 100 разнообразных traces; если трафика нет, синтетические. 2. Open coding (свободные пометки). Эксперт читает traces и пишет короткую заметку о проблеме, как в дневнике. Помечать первый провал в trace: ошибка выше по цепочке вызывает ошибки ниже. Первые 30 разметь сам, прежде чем доверять подсказкам модели. 3. Axial coding (группировка). Заметки сводятся в taxonomy (классификацию) провалов, и провалы каждого вида считают. Здесь LLM может помочь. 4. До насыщения. Продолжать, пока новые traces не перестанут давать новые виды провалов.

Если реального трафика ещё нет, синтетику делают по осям, а не «придумай 100 вопросов»: выбирают измерения (тип вопроса, язык, настроение клиента), руками пишут несколько комбинаций, и только потом модель превращает каждую комбинацию в живой текст. Оговорка авторов: синтетика не скажет, как часто провал случается в production, и ненадёжна в узких областях, на редких языках и там, где ставки высоки.

Пример с числами. 100 traces банковского ассистента, 31 с проблемами:

Вид провала Число
процитирован старый тариф (retrieval взял прошлую версию документа) 12
не уточнил, о какой карте речь 8
ответил по-английски на вопрос по-казахски 6
обещал то, чего не может (отменить комиссию) 5

Решение: начать со старых тарифов — 12 из 31 и чинится кодом (фильтр по дате вступления в силу на retrieval). Проверка для suite — тоже код: сумма в ответе совпадает с действующим тарифом. «Обещал отменить комиссию» — размытое: сюда judge, но только с примерами из этих пяти traces.

Почему не отдать всё сразу judge

Сами авторы пишут, что тратят 60–80% времени разработки на error analysis и evals. Это не отдельная статья бюджета, а основная работа.

Из traces в regression set

Размеченные traces не выбрасывают: из них собирают regression set (набор, на котором новая версия prompt или модели не должна стать хуже прежней).

  1. Выборка. Из логов production берут разнообразные traces. Clustering (группировка по embeddings) помогает не набрать сто вариантов одного вопроса: Husain называет его способом выборки. Важность кластер не показывает: самый большой кластер — самый частый вопрос, а не самый дорогой провал. Что важно, решает error analysis.
  2. Эталон. LLM может набросать черновик ожидаемого ответа, но эталоном его делает человек, который прочитал и поправил. Где можно, эталон — проверка кодом (верный тариф, нет чужих данных), а не текст целиком.
  3. Прогон на каждую версию. Набор гоняют в CI; падение на известном виде провала блокирует выпуск.
  4. Приватность. В логах клиентов есть PII: перед набором trace проходит маскирование (урок a4-1), а сам набор хранят там же, где эти данные.

Уровень 3: A/B

A/B testing (сравнение двух версий на живых пользователях) показывает, меняет ли продукт поведение людей. Husain разрешает отложить его, пока продукт не созрел для реальных пользователей. В банке перед ним идут shadow (новая версия отвечает параллельно, клиенты видят старую) и canary (новая версия на малой доле трафика).

Схема

Термины · Key terms

assertion
проверка кодом на одно правило
trace
журнал одного запроса со всеми шагами и tools
error analysis
разбор ошибок по traces с подсчётом видов провала
open coding
свободные пометки о проблемах при чтении traces
axial coding
группировка пометок в виды провала
failure taxonomy
классификация видов провала с частотами
theoretical saturation
новые traces больше не дают новых видов провала
A/B testing
сравнение двух версий на живых пользователях
shadow / canary
параллельный прогон без показа / выкатка на малую долю трафика
regression set
набор случаев из прошлых провалов, на котором новая версия не должна стать хуже

Сделай сам

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

Обсуди с тьютором: покажи три проверки и спроси, какие входные данные (session, таблица тарифов) нужны каждой, чтобы она была детерминированной.

Проверь себя · ответ под вопросом

1Почему traces надо читать самому, а не сразу отдавать judge?
Критерии появляются из данных: пока не прочитал traces, не знаешь, какие провалы бывают, и judge закодирует догадки. Общие метрики вроде helpfulness дают ложную уверенность. К тому же judge надо проверять по человеческой разметке, а она и получается при чтении traces.
2Сколько traces смотреть на старте?
Около 100 разнообразных, первые 30 размечая самому, и продолжать, пока новые traces не перестанут давать новые виды провала. Смотреть все тестовые случаи и реальные traces пользователей; выборку со временем можно уменьшать, но не прекращать.
3Почему тесты уровня 1 идут первыми?
Они дешёвые, быстрые и детерминированные, их можно гонять в CI на каждое изменение. Они ловят самое опасное и очевидное — утечки, неверные суммы, сломанный формат. После них люди тратят время на тонкие провалы, а не на очевидные.

Interview questions · US / UK

1You've shipped an LLM assistant and users say quality "feels off". What do you do in the first week?
Strong answer: Pull ~100 diverse real traces and read them; open-code the first failure in each, then group into a taxonomy and count; fix the most frequent category (often retrieval or a missing check); write assertions for crisp failures and a validated judge only for fuzzy ones; avoid generic off-the-shelf metrics.
2What belongs in a trace for a RAG agent, and why?
Strong answer: User input, retrieved chunk IDs and versions, final context, tool calls with arguments and results, model output, prompt and model versions, tokens, latency and cost; lets you locate the failing step and reproduce it; redact PII before storage; standard format such as OpenTelemetry GenAI.
3Follow-up: why not just set up an LLM judge on helpfulness and track that number?
Strong answer: Generic scores don't map to your actual failure modes and give false confidence; you can't write good judge criteria before seeing data; the judge itself needs validation against human labels; binary, failure-specific judges built from error analysis are actionable.

Ключевые фразы · Say it aloud

Материалы