Система оценки качества сессий Navi через LLM-as-judge. Анализирует реальные разговоры (не тестовые сценарии) по 7 осям, с усреднением по 3 независимым экспертам.
Статус: реализовано (фазы 1–5). UI: /debug/eval/. CLI: python -m debug.eval.
Веб-интерфейс доступен по адресу:
http://<your-host>:8000/debug/eval
Требует PostgreSQL (DATABASE_URL в .env). Если база не настроена — система вернёт 503.
Судья (тот же LLM, которым пользуется Navi) читает полную расшифровку сессии — все сообщения пользователя, ответы ассистента, вызовы инструментов, результаты, thinking-блоки, логи планирования. Никакие сообщения не вырезаются и не заменяются сжатыми summary — судья видит реальный процесс работы.
👍 / 👎).| Ось | Что оценивается | Шкала |
|---|---|---|
task_complexity |
Сложность запроса пользователя, судится только по вопросу, не по ответу Navi | 10–100+ |
goal_completion |
Получил ли пользователь то, что хотел. Смотрим на финальный ответ и реакции | 10–100+ |
tool_usage_quality |
Правильно ли выбраны инструменты, нет ли лишних/повторных вызовов | 10–100+ |
efficiency |
Соотношение итераций к результату. Петли, тупики, переделки штрафуются | 10–100+ |
communication |
Ясность, честность, отсутствие галлюцинаций, лаконичность | 10–100+ |
subagent_orchestration |
Качество делегирования под-агентам (spawn_agent). Null, если под-агенты не использовались |
10–100+ или null |
self_extension |
Качество саморасширения (write_tool, reload_tools). Null, если инструменты не писались |
10–100+ или null |
rubric_v1)Шкала открытая — если судья видит что-то за пределами 100, он может поставить 120+. Это становится якорем для будущих версий рубрики.
Каждая сессия оценивается 3 параллельными вызовами с разными system prompt:
| Эксперт | Наклон |
|---|---|
strict_critic |
Ищет ошибки, штрафует за любой промах, консервативные оценки |
pragmatist |
«Пользователь получил результат?» — итог важнее пути |
tech_lead |
Архитектура, выбор инструментов, эффективность, технические решения |
Итоговый балл по каждой оси — среднее арифметическое трёх экспертов. Nullable-оси (subagent_orchestration, self_extension) усредняются только по ненулевым значениям.
Разброс между экспертами тоже сохраняется — большой разброс = спорная/шумная сессия.
Если эксперт вернул невалидный JSON — делается один retry с корректирующим сообщением. Если и retry не удался — сессия помечается failed в этом run.
Каждый run привязан к двум версиям:
judge_version — версия кода судьи (prompts, логика рендеринга, retry policy)rubric_version — версия рубрики (оси, якоря, описания)Когда вы меняете prompt эксперта или рубрику — бампаете версию в коде. Старые оценки остаются в базе, новые пишутся под новой версией.
| Статус | Значение |
|---|---|
evaluated |
Есть полный run по текущим judge_version + rubric_version (все 3 эксперта) |
pending |
Нет ни одной оценки |
stale |
Есть оценки, но по старым версиям judge или rubric. Нужен re-run |
Таблица всех сессий, новые сверху. Колонки:
evaluated / pending / stale)goal_completion, tool_usage_quality, communicationФильтры: профиль, статус, лимит строк.
Клик по строке → переход в Detail с подставленным ID.
avgКнопка "evaluate this session" — запускает run только для этой сессии.
Недельные средние по осям за выбранный период (7–90 дней).
Опция "split by complexity bucket" — разбивает по сложности задач (0-25, 26-50, 51-75, 76+). Позволяет отловить смещение выборки: если средний балл вырос, но только потому что стали приходить простые задачи.
Форма для триггера фонового run:
| Поле | Описание |
|---|---|
scope |
unevaluated — только неоценённые, all — всё (re-eval), session — одна сессия |
session id |
Только для scope=session |
since |
Только сессии, начатые после этой даты |
limit |
Макс. количество сессий |
model |
Модель судьи (default: gemma4:31b-cloud) |
backend |
Бэкенд LLM (default: ollama) |
После запуска появляется панель Active run с прогрессом: сколько сессий обработано, статус каждой (pending / running / ok / failed), средние баллы.
Poll каждые 2.5 секунды. По завершении run обновляется история внизу.
Альтернатива UI — запуск из терминала. Не требует запущенного сервера, но требует доступа к PostgreSQL.
# Оценить все неоценённые сессии python -m debug.eval run # Оценить одну сессию python -m debug.eval run --session <session_id> # Re-evaluate всего (после смены judge/rubric) python -m debug.eval run --re-evaluate-all # Только сессии после даты python -m debug.eval run --since 2026-04-01 # Сухой прогон — показать, что будет оценено, но не звать LLM python -m debug.eval run --dry-run # Сменить модель судьи python -m debug.eval run --model qwen3.6:27b # Показать оценки сессии python -m debug.eval show <session_id> # Статистика (Phase 4 — пока заглушка) python -m debug.eval stats --days 30 python -m debug.eval stats --days 30 --csv
В основном чате (не в eval UI) на каждом сообщении ассистента есть 👍 / 👎. Клик отправляет:
POST /eval/feedback
{ "session_id": "...", "message_index": N, "rating": 1 }
rating: 1 — лайкrating: -1 — дизлайкrating: 0 — очиститьИндекс — позиция сообщения в session.messages (display history, append-only, индексы стабильны).
Судья видит эти реакции в transcript: 👍 / 👎 рядом с соответствующим сообщением ассистента.
Все эндпоинты под префиксом /eval:
| Метод | Путь | Описание |
|---|---|---|
| POST | /eval/feedback |
Поставить/убрать лайк или дизлайк |
| GET | /eval/feedback/{session_id} |
Список feedback для сессии |
| GET | /eval/sessions |
Список сессий с оценками и статусами |
| GET | /eval/sessions/{session_id} |
Детали сессии + все evaluation runs |
| GET | /eval/stats |
Агрегированная статистика |
| POST | /eval/run |
Запустить фоновый run |
| GET | /eval/run/{run_id} |
Статус конкретного run |
| GET | /eval/runs |
История всех runs |
Таблицы создаются лениво при первом подключении (debug/eval/schema.sql):
-- Пользовательский feedback
CREATE TABLE message_feedback (
session_id TEXT NOT NULL,
message_index INTEGER NOT NULL,
rating SMALLINT NOT NULL CHECK (rating IN (-1, 1)),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (session_id, message_index)
);
-- Оценки судьи
CREATE TABLE evaluations (
id UUID PRIMARY KEY,
session_id TEXT NOT NULL,
eval_run_id UUID NOT NULL, -- группирует 3 экспертов одного run
eval_date TIMESTAMPTZ NOT NULL DEFAULT now(),
judge_model TEXT NOT NULL,
judge_version TEXT NOT NULL,
rubric_version TEXT NOT NULL,
expert_id TEXT NOT NULL, -- strict_critic | pragmatist | tech_lead
scores JSONB NOT NULL, -- {task_complexity: 65, ...}
comment TEXT NOT NULL DEFAULT ''
);
SQLite не поддерживается для eval-системы — только PostgreSQL.
3 эксперта × полный transcript на сессию. Для длинных сессий (50+ сообщений, 50k+ токенов контекста) — это 3 больших LLM-вызова. Рекомендуется запускать batch overnight, а не в реальном времени.
Run использует тот же FallbackOllamaBackend, что и основной агент — мульти-серверный fallback работает и для судьи.
debug/eval/
api.py # FastAPI router (GET/POST эндпоинты)
cli.py # CLI: run, show, stats
db.py # asyncpg: feedback + evaluations
judge.py # Рендеринг сессии, 3 эксперта, усреднение
runner.py # Фоновый runner (async tasks)
schema.py # Pydantic-модели
schema.sql # DDL для PostgreSQL
index.html # SPA UI (4 вкладки)
prompts/
rubric_v1.yaml # Рубрика с якорями
expert_strict_critic.txt # System prompt строгого критика
expert_pragmatist.txt # System prompt прагматика
expert_tech_lead.txt # System prompt техлида
Судья и все вспомогательные функции — в debug/eval/. Ни одна из этих модулей не импортируется в production runtime агента; eval-система полностью обособлена.