172 lines
14 KiB
Markdown
172 lines
14 KiB
Markdown
# Глава 11. Оценка качества агента
|
|
|
|
«Стал ли агент лучше после твоих правок?» — самый неудобный вопрос, который задают себе люди, собирающие агентов. Неудобный, потому что интуитивно кажется: «ну, я же потыкал, посмотрел, вроде работает». А объективно сказать, что вчерашний агент хуже сегодняшнего (или наоборот, ваш новый замечательный промпт всё сломал на половине задач), — тяжело. Агенты — недетерминированные системы с разнородными задачами, и наивная метрика «точность» к ним плохо применима.
|
|
|
|
Эта глава — про то, какой практический минимум нужен, чтобы отвечать на этот вопрос, и почему его нельзя откладывать до «когда-нибудь потом».
|
|
|
|
## Две плоскости оценки
|
|
|
|
Полезно сразу различать.
|
|
|
|
**Оценка итога.** Задача решена или нет. Это то, что волнует пользователя: починили ли тест, нашли ли нужный факт, правильный ли конспект.
|
|
|
|
**Оценка траектории.** Как агент к итогу пришёл. Сколько шагов занял, какие инструменты звал, не было ли лишних вызовов, не ходил ли по кругу.
|
|
|
|
Обе важны, и измеряются они по-разному.
|
|
|
|
Результативный, но дорогой агент (правильный ответ за 50 шагов и $2) и результативный дешёвый агент (правильный ответ за 8 шагов и $0,15) с точки зрения первой метрики равны, с точки зрения второй — очень разные. Для продакшна это разница между «работает» и «окупается».
|
|
|
|
## Golden set: тестовый набор задач
|
|
|
|
Первое, что должно быть у любого агента старше двух недель жизни — фиксированный набор задач с ожидаемыми результатами. Не для одноразового теста, а как постоянный референс.
|
|
|
|
Для кодового агента такой набор — это репозиторий-фикстура с подготовленными задачами:
|
|
|
|
```
|
|
golden/
|
|
├── task_001_fix_failing_test/
|
|
│ ├── project/ # начальное состояние проекта
|
|
│ ├── task.md # формулировка задачи
|
|
│ └── check.sh # скрипт проверки, возвращает 0 при успехе
|
|
├── task_002_add_endpoint/
|
|
│ ├── project/
|
|
│ ├── task.md
|
|
│ └── check.sh
|
|
...
|
|
```
|
|
|
|
Прогон: агент запускается в копии `project/`, работает над задачей из `task.md`, потом `check.sh` говорит, получилось или нет. Результат — процент пройденных задач.
|
|
|
|
Для исследовательского агента набор более хитрый. «Правильный ответ» на «что я читал про MoE» — не один-в-один текст, а набор свойств:
|
|
|
|
- упомянута ли нужная заметка?
|
|
- процитированы ли ключевые идеи?
|
|
- не было ли галлюцинаций про факты, которых в заметках нет?
|
|
|
|
Формат:
|
|
|
|
```yaml
|
|
task: "Что я читал про MoE?"
|
|
expected:
|
|
must_cite: ["notes/llm/switch-transformer.md"]
|
|
must_mention: ["routing", "mixture of experts", "балансировка"]
|
|
must_not_hallucinate: ["Mixture-of-Recursions"] # это мы не читали
|
|
```
|
|
|
|
## Метрики для двух архетипов
|
|
|
|
### Кодовый агент
|
|
|
|
**Pass rate на golden set.** Сколько процентов задач прошло `check.sh`. Главная метрика.
|
|
|
|
**Среднее число шагов до решения.** Медиана по успешным задачам. Растущая — повод насторожиться: агент или разучился быть прямолинейным, или промпт стал многословным.
|
|
|
|
**Доля ошибок инструментов.** Сколько вызовов пришло с невалидными аргументами или попало в блоклист. Признак расхождения между тем, что модель «думает», и тем, что инструменты готовы принять.
|
|
|
|
**Стоимость задачи.** Среднее число токенов (входных + выходных) на успешную задачу. Полезно отдельно: стоимость падающих задач может превышать стоимость успешных, потому что агент долго бьётся.
|
|
|
|
### Исследовательский агент
|
|
|
|
**Citation accuracy.** Доля утверждений в ответе, которые действительно подтверждаются цитированными источниками. Вычисляется проверкой: для каждого утверждения ищем подтверждающий фрагмент в указанных заметках.
|
|
|
|
**Recall.** Нашёл ли агент релевантный материал, который у пользователя действительно есть в заметках. Частая поломка — пропустил нужную заметку, потому что порог релевантности был высоковат или формулировка запроса промахнулась.
|
|
|
|
**Hallucination rate.** Доля ответов, содержащих факты, которых нет в цитированных источниках. Главная метрика для анти-галлюцинации.
|
|
|
|
**Satisfaction.** Нефункциональная, но важная: устроил ли ответ пользователя в слепом тесте. Измеряется вручную.
|
|
|
|
## LLM-as-judge
|
|
|
|
Ручная оценка — дорогая и не масштабируется. Для большинства метрик выше можно использовать саму LLM как судью.
|
|
|
|
Принцип: для каждого ответа агента запускаем отдельный LLM-вызов с промптом вида:
|
|
|
|
```
|
|
Вот задача пользователя: <task>
|
|
Вот ответ агента: <answer>
|
|
Вот материалы, на которые агент опирался: <sources>
|
|
|
|
Оцени ответ по шкале 1-5 по следующим критериям:
|
|
- Релевантность задаче
|
|
- Фактическая корректность
|
|
- Наличие галлюцинаций
|
|
|
|
Верни JSON: {"relevance": int, "correctness": int, "hallucinations": int, "reasoning": str}
|
|
```
|
|
|
|
Важные нюансы.
|
|
|
|
**Судья должен быть отдельной моделью или отдельной сессией.** Если оценивает та же модель в той же сессии — она склонна одобрять свою же работу.
|
|
|
|
**Судья работает хуже исполнителя на сложных задачах.** Если задача на уровне самой модели — судья тоже ошибётся. Работает для слабого агента + сильного судьи; не работает наоборот.
|
|
|
|
**Оценки шумные.** Любой LLM-judge даёт разные оценки на идентичные входы. Прогоняйте каждую задачу несколько раз, берите среднее.
|
|
|
|
**LLM-judge нужна калибровка.** Сравните его оценки с ручной разметкой на небольшом наборе (50–100 задач). Если расходятся — перепишите промпт судьи.
|
|
|
|
Практически: LLM-judge — это не замена ручной оценки, а её масштабируемый спутник. Ручную держим на меньшем, но качественном срезе, LLM-judge прогоняем постоянно.
|
|
|
|
## Как не обмануться
|
|
|
|
Типичные способы сделать себе иллюзию прогресса.
|
|
|
|
**Тестировать на тех же задачах, на которых настраивали.** Если вы подкручивали промпт, глядя на 20 конкретных задач, — оценивать надо на других 20. Иначе вы измеряете не качество, а свою способность запомнить, что агенту нравится.
|
|
|
|
**Смотреть только успешные случаи.** Самая информативная часть — где агент провалился. Трасса неудачного прогона говорит больше, чем десять успешных.
|
|
|
|
**Мерить только средние.** Медиана и хвосты важнее среднего. Агент, который решает 80% задач за 5 шагов и 20% задач за 200 шагов, в среднем «решает за 44 шага» — и это число ничего не говорит.
|
|
|
|
**Тестировать только крупные правки.** Маленькие промптовые изменения часто незаметно ломают что-то. Постоянный прогон золотого набора после каждого значимого изменения ловит регрессии, пока они дешёвые.
|
|
|
|
## Регрессионное тестирование
|
|
|
|
Похоже на юнит-тесты, но для агентов:
|
|
|
|
- Есть базовая версия (main branch): `agent_baseline`.
|
|
- Есть версия с изменениями: `agent_candidate`.
|
|
- Прогоняем оба на golden set.
|
|
- Сравниваем: где candidate лучше baseline, где хуже, где равно.
|
|
- Смотрим, устраивает ли нас новая форма баланса.
|
|
|
|
Это особенно важно для изменений системного промпта. Промпт — это код. Правка промпта — это коммит. Без тестов на эту правку вы работаете с системой, которую нельзя сравнить саму с собой.
|
|
|
|
Минимальный автоматический формат:
|
|
|
|
```python
|
|
def run_suite(agent, golden_dir):
|
|
results = []
|
|
for task_dir in sorted(glob.glob(f"{golden_dir}/*/")):
|
|
task = open(f"{task_dir}/task.md").read()
|
|
outcome = run_in_sandbox(agent, task_dir, task)
|
|
check_result = subprocess.run([f"{task_dir}/check.sh"], capture_output=True)
|
|
results.append({
|
|
"task": os.path.basename(task_dir.rstrip("/")),
|
|
"pass": check_result.returncode == 0,
|
|
"steps": outcome.n_steps,
|
|
"tokens": outcome.tokens,
|
|
})
|
|
return results
|
|
|
|
baseline = run_suite(agent_baseline, "golden/")
|
|
candidate = run_suite(agent_candidate, "golden/")
|
|
compare(baseline, candidate)
|
|
```
|
|
|
|
Раз в неделю, раз в день, перед каждым коммитом — зависит от того, как активно вы меняете агента. Для каждого — сохраняйте результаты в отдельный файл, чтобы можно было проследить тренд.
|
|
|
|
## Онлайн-метрики
|
|
|
|
Всё выше — оффлайн. Отдельная тема — что мерить на живых пользователях, когда агент уже работает.
|
|
|
|
**Success signals.** Что-то, что явно говорит «задача решена». Для кодового — прошёл ли CI после merge-а агентского коммита. Для исследовательского — перешёл ли пользователь к следующему вопросу (условный признак, что предыдущим доволен) или снова спрашивает то же другими словами (не доволен).
|
|
|
|
**User feedback.** Кнопки «хорошо / плохо» рядом с ответом. Простая, но крайне ценная метрика при достаточном объёме.
|
|
|
|
**Drop-off.** На каких типах задач пользователи быстрее всего бросают сессию. Это либо слишком сложные для агента задачи, либо задачи, где агент проваливается неочевидно для себя.
|
|
|
|
Онлайн-метрики отражают реальность, но запаздывают: вы узнаёте о регрессии через дни. Оффлайн — ловят регрессии до релиза. Работает комбинация.
|
|
|
|
## Что дальше
|
|
|
|
Оценка отвечает на вопрос «правильно ли работает агент». Следующий практический вопрос — «сколько это стоит и можно ли быстрее». Глава 12 — про стоимость, латентность и кэширование промптов.
|