> ## Documentation Index
> Fetch the complete documentation index at: https://docs.befailproof.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Evaluation Suite

> Документация AgentEye Evaluation Suite.

AgentEye оценивает завершённые сеансы агента, отправляя полную стенограмму событий с помощью POST-запроса на **единственный сервис оценки, принадлежащий клиенту**. Оценщик возвращает оценки либо сразу, либо передав `job_id` для опроса AgentEye. Результаты сохраняются и отображаются на панели управления.

Это руководство охватывает:

1. Как определяется завершение сеанса.
2. HTTP-контракт, который должен реализовать оценщик.
3. Конфигурирование сервера AgentEye.
4. Просмотр результатов.
5. Устранение неполадок.

Для вспомогательного пакета Python, который реализует контракт за вас, см.
[пакет `agenteye-evaluator` на PyPI](https://pypi.org/project/agenteye-evaluator/).

***

## Как это работает

```text theme={null}
ingest /events  ──▶  AgentEye  ──── POST /evaluate ────▶  Evaluator service
   (agent_end)        server   ◀──── done | pending ────
                          │
                          │     GET /evaluate/{job_id} ─────▶
                          │     ◀──── done ──────────────
                          ▼
                     evaluations  (terminal results)
```

Когда SDK AgentEye излучает событие `agent_end` для сеанса, сервер планирует оценку. Затем он отправляет POST-запрос с полной стенограммой событий на ваш сервис оценки, который может:

* **Вернуть результат сразу** с `{"status":"done", "scores":{...}, "reasoning":{...}, "summary":"..."}`. Результат добавляется на временную шкалу оценки сеанса. Поля `reasoning` и `summary` являются необязательными.
* **Отложить** с `{"status":"pending", "job_id":"abc-123"}`. AgentEye затем вызывает `GET {EVALUATOR_ENDPOINT}/evaluate/abc-123` до тех пор, пока ваш оценщик не вернёт `{"status":"done", ...}` или `{"status":"error", "error":"..."}`.

  Частота опроса зависит от задачи: ответ `pending` может включать `next_poll_secs` для переопределения; в противном случае AgentEye использует значение `default_poll_interval_secs` из `GET /config`; в противном случае сервер возвращается к `EVALUATOR_POLLING_INTERVAL_SECS` (по умолчанию 10s). Все значения ограничены диапазоном \[1s, 1h].

Сеансы, которые никогда не излучают `agent_end` (например, процесс агента, который упал), также могут быть обработаны: `GET /config` оценщика может вернуть `{"inactivity_timeout_secs": 1800}`, и AgentEye будет оценивать любой сеанс, простаивающий так долго. Установите это поле в `null` или опустите его, чтобы отключить этот резервный вариант.

Конвейер полностью неоперативен, когда `EVALUATOR_ENDPOINT` не установлен.

Сеанс может накапливать **несколько терминальных оценок со временем**: каждое событие `agent_end` (и каждый ручной переоценка с панели управления) добавляет новую строку оценки. Это рекомендуемый способ оценить возобновленный диалог: пользователь завершает агента, возвращается позже, отправляет больше событий, снова завершает агента, и выполняется вторая оценка для полной обновлённой стенограммы. На панели управления показывается последняя оценка как основной результат, а предыдущие оценки — как сворачиваемая временная шкала. Пока одна оценка выполняется для сеанса, дополнительные события `agent_end` для этого сеанса игнорируются; следующее после завершения текущей оценки поставит в очередь новую оценку как обычно.

Резервный вариант неактивности повторно включается и на возобновлённых сеансах: если новые события поступают после предыдущей терминальной оценки и сеанс затем переходит в режим ожидания свыше `inactivity_timeout_secs`, новая оценка ставится в очередь.

Временные сбои (5xx, 429, таймауты, ошибки сети) повторяются с экспоненциальной задержкой до `EVALUATOR_MAX_ATTEMPTS`; ответы 4xx являются терминальными. AgentEye можно безопасно запускать с несколькими горизонтально масштабируемыми экземплярами серверов; работа распределена так, чтобы один и тот же сеанс никогда не отправлялся дважды одновременно.

***

## HTTP-контракт

Все аутентифицированные маршруты используют **аутентификацию с маркером-носителем**. Одно и то же значение должно быть настроено на обеих сторонах:

* Сервер AgentEye: переменная окружения `EVALUATOR_TOKEN`
* Сервис оценки: настроено так же (SDK `agenteye-evaluator` по соглашению читает `EVALUATOR_TOKEN`)

Если `EVALUATOR_TOKEN` не установлен, сервер не отправляет заголовок `Authorization`; оценщик может затем принимать анонимные запросы, что приемлемо для внутренней сети, но не рекомендуется в открытом интернете.

### Маршруты, которые должен обслуживать оценщик

| Маршрут              | Тело / параметры   | Ответ                                                                                        |
| -------------------- | ------------------ | -------------------------------------------------------------------------------------------- |
| `GET /health`        | нет                | `{"status":"ok"}` (открыто, без аутентификации)                                              |
| `GET /config`        | нет                | `{"inactivity_timeout_secs": <int> \| null, "default_poll_interval_secs": <int> \| опущено}` |
| `POST /evaluate`     | JSON `EvalRequest` | `{"status":"done", ...}` или `{"status":"pending", "job_id":"..."}`                          |
| `GET /evaluate/{id}` | нет                | такая же форма ответа как `/evaluate`                                                        |

### Тело запроса `EvalRequest`, отправляемое сервером

```json theme={null}
{
  "schema_version": "1",
  "session_id":     "session-abc123",
  "agent_id":       "planner",
  "environment":    "production",
  "started_at":     "2026-05-10T12:00:00Z",
  "ended_at":       "2026-05-10T12:05:00Z",
  "events": [
    { "id": 1234, "ts": "...", "event_type": "agent_start", "payload": { ... } },
    ...
  ]
}
```

### Формы ответа

**Синхронно (выполнено):**

```json theme={null}
{
  "status": "done",
  "scores": { "helpfulness": 0.85, "tool_efficiency": 0.6 },
  "reasoning": {
    "helpfulness": "answered the question directly with citations",
    "tool_efficiency": "called list_files three times when one would have done"
  },
  "summary": "strong answer quality, weak tool selection"
}
```

`reasoning` (карта обоснования для каждой оценки) и `summary` (общее однопараграфное описание) являются необязательными. Ключи в `reasoning` должны соответствовать ключам в `scores`; панель управления отображает каждую запись в строке под полосой оценки. Более старые оценщики, которые возвращают только `scores`, продолжают работать без изменений; `reasoning` и `summary` просто отображаются как null, и соответствующие элементы интерфейса опускаются.

**Асинхронно (отложено):**

```json theme={null}
{ "status": "pending", "job_id": "abc-123", "next_poll_secs": 30 }
```

`next_poll_secs` является необязательным; если опущено, сервер возвращается к `default_poll_interval_secs` оценщика из `/config`, затем к своей переменной окружения `EVALUATOR_POLLING_INTERVAL_SECS`.

**Терминальная ошибка на стороне оценщика:**

```json theme={null}
{ "status": "error", "error": "model service unavailable" }
```

Сервер обрабатывает любое другое тело 2xx как ошибку протокола и записывает терминальную `error` для сеанса.

***

## Написание оценщика с помощью SDK

Пакет `agenteye-evaluator` Python предоставляет типизированную обёртку FastAPI, которая реализует HTTP-контракт выше. Установите его из PyPI:

```bash theme={null}
pip install agenteye-evaluator
```

Минимальный жизнеспособный оценщик:

```python theme={null}
import os
from agenteye_evaluator import Evaluator, EvalRequest, EvalResponse

app = Evaluator(token=os.environ["EVALUATOR_TOKEN"])

@app.evaluator
def run(req: EvalRequest) -> EvalResponse:
    # Inspect req.events (the full session transcript) and return scores.
    tool_calls = sum(1 for e in req.events if e.event_type == "tool_use")
    return EvalResponse(
        scores={"tool_calls": float(tool_calls)},
        reasoning={"tool_calls": f"{tool_calls} tool invocations in the transcript"},
        summary="tight tool loop" if tool_calls < 5 else "agent looped on tools",
    )
```

Экземпляр `app` вызывается ASGI, поэтому `uvicorn module:app` запускает его.

Для оценщиков, которым нужно отложить дорогостоящую работу, возвращайте `JobPending` и зарегистрируйте обработчик `@app.job_lookup`; сервер AgentEye опрашивает `GET /evaluate/{job_id}` до тех пор, пока вы не вернёте терминальный статус или не истечёт лимит `EVALUATOR_MAX_POLL_DURATION_SECS` (по умолчанию 1 ч).

Полная ссылка API, асинхронный шаблон и схема событий: README `agenteye-evaluator` поставляется внутри каждого архива выпуска на [странице выпусков agenteye-enterprise](https://github.com/agenteye-enterprise/releases), или вы можете прочитать его на странице пакета в PyPI.

***

## Запуск оценщика на Kubernetes

Оценщик — это **ваш сервис**: AgentEye не поставляет контейнер оценщика по умолчанию. Выпуск включает ссылочные манифесты Kubernetes в `deploy/examples/evaluator/`, которые вы можете применить как есть после замены вашего образа и общего маркера-носителя.

### 1. Контейнеризируйте ваш оценщик

Минимальный Dockerfile для вашего оценщика:

```dockerfile theme={null}
FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir agenteye-evaluator uvicorn
COPY my_evaluator.py .
RUN useradd --uid 10001 --create-home --shell /usr/sbin/nologin evaluator \
    && chown -R evaluator:evaluator /app
USER evaluator
EXPOSE 9000
CMD ["uvicorn", "my_evaluator:app", "--host", "0.0.0.0", "--port", "9000"]
```

`runAsNonRoot` (UID 10001) сохраняет совместимость контейнера с профилями ограниченной безопасности Pod.

### 2. Создайте общий маркер-носитель

```bash theme={null}
kubectl -n agenteye create secret generic evaluator-token \
  --from-literal=token="$(openssl rand -hex 32)"
```

Используйте то же значение как `EVALUATOR_TOKEN` на сервере AgentEye. Сервер отправляет `Authorization: Bearer <token>` при каждом запросе; SDK использует `hmac.compare_digest` для проверки в постоянное время и отклоняет несоответствия с HTTP 401.

### 3. Примените примеры манифестов

```bash theme={null}
# Edit deploy/examples/evaluator/deployment.yaml first to point
# `image:` at your registry, then:
kubectl apply -k deploy/examples/evaluator/
```

Пример включает:

* Развёртывание с 2 репликами с `runAsNonRoot`, файловой системой только для чтения, всеми возможностями отключены, проверки живости и готовности на `/health`
* ClusterIP сервис на порту 9000
* Шаблон `secret.example.yaml` (намеренно исключён из Kustomization; создайте реальный секрет вне пределов, чтобы ни один маркер не попал в git)

### 4. Подключите AgentEye к нему

На сервере AgentEye установите:

```bash theme={null}
EVALUATOR_ENDPOINT=http://evaluator:9000
EVALUATOR_TOKEN=<the value generated above>
```

Сервер распределяет `EVALUATOR_WORKERS × EVALUATOR_CLAIM_BATCH` одновременных запросов по всем подам оценщика (по умолчанию: `2 × 4 = 8`). Масштабируйте `replicas` и лимиты ресурсов на поде в соответствии с этими серверными переключателями.

### Проверка

```bash theme={null}
kubectl -n agenteye port-forward svc/evaluator 9000:9000
curl -s http://localhost:9000/health   # → {"status":"ok"}
```

После полного запуска агента `GET /evaluations` на сервере AgentEye должен вернуть строку со статусом `"done"` и оценками, которые производит ваш оценщик.

***

## Конфигурирование сервера AgentEye

Установите в процессе сервера:

| Переменная окружения               | Значение                                                                                                                                                                                                                         |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `EVALUATOR_ENDPOINT`               | Базовый URL вашего оценщика (`http://evaluator:9000`). Не установлено = конвейер отключён.                                                                                                                                       |
| `EVALUATOR_TOKEN`                  | Маркер-носитель. Должен соответствовать значению, которое настроено на сервисе оценщика.                                                                                                                                         |
| `EVALUATOR_WORKERS`                | Рабочие задачи на экземпляр сервера (по умолчанию 2).                                                                                                                                                                            |
| `EVALUATOR_CLAIM_BATCH`            | Строки, запрашиваемые за тик работника (по умолчанию 4). Пакеты обрабатываются **параллельно**; фактическое параллелизм на вашей конечной точке оценщика — это `EVALUATOR_WORKERS × EVALUATOR_CLAIM_BATCH`.                      |
| `EVALUATOR_POLL_IDLE_SECS`         | Как долго работник спит между попытками отправки, когда оценка не назначена (по умолчанию 2s).                                                                                                                                   |
| `EVALUATOR_POLLING_INTERVAL_SECS`  | Окончательный резервный вариант для частоты `GET /evaluate/{id}` когда ни per-response `next_poll_secs`, ни `default_poll_interval_secs` оценщика не установлены (по умолчанию 10s).                                             |
| `EVALUATOR_REQUEST_TIMEOUT_MS`     | Таймаут для каждого запроса (по умолчанию 30000).                                                                                                                                                                                |
| `EVALUATOR_MAX_ATTEMPTS`           | После этого количества временных сбоев результат записывается как терминальная `error` (по умолчанию 5).                                                                                                                         |
| `EVALUATOR_CONFIG_REFRESH_SECS`    | Частота `GET /config` (по умолчанию 300).                                                                                                                                                                                        |
| `EVALUATOR_MAX_POLL_DURATION_SECS` | Максимальное реальное время, в течение которого сеанс может оставаться в очереди опроса, прежде чем он будет завершён как `timeout` (по умолчанию 3600s). Защищает от оценщика, который продолжает возвращать `pending` навечно. |

Чтобы включить автоматическое оценивание для всего экземпляра, подготовьте секрет `agenteye-evaluator` с обоими установленными ключами. В поставляемых манифестах Kubernetes сервер читает `EVALUATOR_ENDPOINT` и `EVALUATOR_TOKEN` из этого необязательного секрета. Создайте его через стандартный процесс управления секретами вашей организации, затем перезагрузите развёртывание сервера, чтобы подхватить изменение.

Переключатели настройки выше по умолчанию не подключены; выставьте соответствующие переменные окружения на контейнере сервера в манифесте развёртывания, если вам нужно переопределить значения по умолчанию.

См. [deployment.md](/ru/agenteye/deployment) для полной таблицы переменных окружения.

***

## Ссылка API

| Метод  | Путь                                | Требуемое разрешение  | Назначение                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ------ | ----------------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/evaluations`                      | `evaluations:read`    | Запросить терминальные результаты. Поддерживает `session_id`, `agent_id`, `environment`, `status` (`done`/`error`/`timeout`), `ts_from`, `ts_to`, `cursor`, `limit`, `score_filters`, `latest_per_session`. `limit` по умолчанию 50 и ограничен 200 (обратите внимание, это отличается от `/events`, который ограничен 1000). `environment` принимает список, разделённый запятыми (например, `environment=prod,staging`); отдельные значения по-прежнему работают. С `latest_per_session=true` ответ содержит не более одной строки на `session_id` (самой свежей по `completed_at`), используемой на странице списка сеансов для свёртывания временной шкалы оценки сеанса в его текущий заголовок. По умолчанию false (возвращает полную историю). |
| `GET`  | `/evaluations/aggregate`            | `evaluations:read`    | Свёрнутое здоровье оценок для отфильтрованного среза: общее количество, разбивка done/error/timeout, статистика по ключам оценки (count/avg/min/max/p50 по произвольным ключам `scores`), и временная шкала по периодам. Принимает **те же параметры фильтра как `/evaluations`** плюс `featured_keys` (CSV ключей оценки для тренда) и `latest_per_session`. Питает функцию Dashboards; метрики точны над целым совпадающим набором, не выбираются.                                                                                                                                                                                                                                                                                                  |
| `GET`  | `/evaluations/environments`         | `evaluations:read`    | Отличные значения окружения из таблицы `evaluations`. Используется для заполнения выпадающих меню фильтра, ограниченных данными, доступными для чтения оценок.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `GET`  | `/evaluation-jobs`                  | `evaluations:read`    | Видимость в полёте оценок. Фильтр по `status` (`pending`/`polling`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `GET`  | `/events`                           | `events:read`         | Поток необработанных событий сеанса. Поддерживает `session_id`, `agent_id`, `event_type` (CSV), `environment` (CSV), `ts_from`, `ts_to`, `cursor`, `limit` и `order`. `order` — это `desc` (новое в первую очередь, по умолчанию) или `asc` (старое в первую очередь); неправильное значение возвращается к `desc`. Постраничное переключение курсора через `next_cursor` ответа (id события): передайте его как `cursor` для получения следующей страницы; с `asc` следующая страница — события после этого id, с `desc` события до него. `limit` по умолчанию 50 и ограничен 1000.                                                                                                                                                                  |
| `GET`  | `/sessions/:session_id/export`      | `events:read`         | Возвращает точное тело JSON, которое оценщик получит для этого сеанса, поданное как загружаемое вложение с именем `session-<id>.json`. Полезно для повторного воспроизведения сеансов производства через `agenteye-evaluator` для автономного тестирования. Байты идентичны тому, что отправляет конвейер оценщика.                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `POST` | `/sessions/:session_id/re-evaluate` | `evaluations:trigger` | Поставить в очередь свежую оценку для сеанса; работает независимо от того, существует ли предыдущая оценка. Новый результат **добавляется** на временную шкалу оценки сеанса вместо перезаписи предыдущей, поэтому предыдущие оценки остаются видимыми как история. Возвращает `202` при постановке в очередь, `404` для неизвестного сеанса, `409` если оценка уже выполняется. Используйте это после развёртывания нового оценщика или для сеансов, которые никогда не излучали `agent_end`.                                                                                                                                                                                                                                                        |

### Фильтрация по диапазону оценок: `score_filters`

`GET /evaluations` принимает необязательный параметр `score_filters`, который сужает результаты по числовым значениям внутри объекта `scores`. Параметр — это список, разделённый запятыми, записей `key:min..max`; либо граница может быть опущена. Несколько записей объединяются с логическим И. Строки, где названный ключ отсутствует или не является числовым, исключаются. Запрос может содержать не более 20 записей фильтра; превышение возвращает HTTP 400.

Примеры:

```text theme={null}
# helpfulness в [0.5, 0.8]
GET /evaluations?score_filters=helpfulness:0.5..0.8

# tool_efficiency не более 0.3 (без нижней границы)
GET /evaluations?score_filters=tool_efficiency:..0.3

# helpfulness >= 0.5 AND factuality >= 0.9
GET /evaluations?score_filters=helpfulness:0.5..,factuality:0.9..
```

Каждый объект ответа `/evaluations` имеет эти поля:

| Поле            | Тип                   | Примечания                                                                                                                                                                                      |
| --------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `evaluation_id` | string (UUID)         | Канонический идентификатор для этой терминальной оценки. Каждая терминальная оценка получает новый UUID; один сеанс может содержать несколько.                                                  |
| `id`            | string (UUID)         | Обратная совместимость псевдоним, несущий то же значение как `evaluation_id`.                                                                                                                   |
| `session_id`    | string                | Сеанс, против которого выполнялась оценка. Сеанс может иметь несколько оценок на временной шкале.                                                                                               |
| `agent_id`      | string                | Идентифицирует агента, который произвёл сеанс.                                                                                                                                                  |
| `environment`   | string                | Метка окружения, скопированная из сеанса.                                                                                                                                                       |
| `status`        | enum                  | Один из `"done"`, `"error"`, `"timeout"`.                                                                                                                                                       |
| `scores`        | object \| null        | Оценки, возвращённые вашим оценщиком.                                                                                                                                                           |
| `reasoning`     | object \| null        | Необязательная карта обоснования для каждой оценки, возвращённая вашим оценщиком. Ключи обычно отражают те, что в `scores`. На панели управления каждая запись отображается под полосой оценки. |
| `summary`       | string \| null        | Необязательное однопараграфное общее описание, возвращённое вашим оценщиком. На панели управления это отображается выше разбивки per-score как заголовок оценки.                                |
| `error`         | string \| null        | Заполнено только на `"error"` / `"timeout"`.                                                                                                                                                    |
| `attempt_count` | integer               | Количество попыток отправки (≥ 1).                                                                                                                                                              |
| `duration_ms`   | integer \| null       | Длительность последней попытки.                                                                                                                                                                 |
| `completed_at`  | string (ISO 8601 UTC) | Когда был записан терминальный результат. Результаты упорядочены по `completed_at` (новое в первую очередь).                                                                                    |
| `created_at`    | string (ISO 8601 UTC) | Несёт тот же временной штамп как `completed_at` (семантика write-once).                                                                                                                         |

***

## Разрешения

| Разрешение            | Предоставляет                                                                                                                             |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `evaluations:read`    | Список результатов оценок, просмотр оценок на панели управления и загрузка метрик здоровья панели управления.                             |
| `evaluations:trigger` | Ручная постановка оценки для сеанса в очередь через `POST /sessions/:session_id/re-evaluate` или кнопку re-evaluate на панели управления. |
| `dashboards:read`     | Просмотр сохранённых панелей управления (также требует `evaluations:read` для загрузки их метрик).                                        |
| `dashboards:write`    | Создание и редактирование панелей управления.                                                                                             |
| `dashboards:delete`   | Удаление панелей управления.                                                                                                              |

Администратор начальной загрузки (`ADMIN_KEY`, `ADMIN_EMAIL`) автоматически получает эти.

***

## Просмотр результатов

* **`/sessions/<id>`**: временная шкала событий + правая колонка, показывающая оценки сеанса и любую ошибку из попытки отправки. Если ваш ключ имеет `evaluations:trigger`, рядом с кнопкой экспорта появляется кнопка **re-evaluate**, полезная для сеансов, которые никогда не излучали `agent_end`, или для обновления оценок после развёртывания нового оценщика. Панель управления опрашивает новый результат и обновляет правую колонку, когда он прибывает.
* **`/sessions`**: фильтруемая сетка сеансов; столбец оценки показывает статус оценки каждого сеанса и оценки с одного взгляда.
* **`/dashboards`**: сохранённые представления здоровья оценок (см. [Dashboards](#dashboards) ниже).

<img src="https://mintcdn.com/exosphere/RgxYS1UZshqb4m7m/agenteye/images/sessions-list.png?fit=max&auto=format&n=RgxYS1UZshqb4m7m&q=85&s=10de161665eef64465d3d100e80b643f" alt="Сетка Sessions с таблетками статуса оценки per-session и раскрашенными в цвет значками оценки (helpfulness, factuality, tool_efficiency, safety, coherence)" width="2880" height="1800" data-path="agenteye/images/sessions-list.png" />

*Сетка сеансов показывает статус оценки каждого запуска и оценки с одного взгляда; красные/жёлтые/зелёные значки выделяют низкие оценки.*

<img src="https://mintcdn.com/exosphere/RgxYS1UZshqb4m7m/agenteye/images/session-detail.png?fit=max&auto=format&n=RgxYS1UZshqb4m7m&q=85&s=63cc2480e8124a05ea6a0a92d5f20690" alt="Представление деталей сеанса с оценками оценки и состоянием отправки в правой колонке" width="2880" height="1800" data-path="agenteye/images/session-detail.png" />

*Открытие сеанса показывает его полную временную шкалу рядом с оценками оценки и любой ошибкой диспетчера в правой колонке.*

***

## Панели управления

Страница **Dashboards** (`/dashboards`) позволяет вам сохранить комбинацию фильтров оценок как названное, переиспользуемое представление и видеть, как этот срез оценок делает дела с одного взгляда. Панели управления **совместно используются всей вашей организацией**; все, у кого есть `dashboards:read`, видят один и тот же набор.

Каждая панель управления закрепляет:

* **Фильтры**: те же элементы управления как на странице сеансов: окружение, статус, агент, скользящее временное окно и фильтры диапазона оценок (`key:min..max`).
* **Конфигурацию отображения**: какие ключи оценок выделять, пороги здоровья зелёный/жёлтый/красный, какие панели показывать и свёртывать ли в последнюю оценку на сеанс.

Каждая карточка показывает количество совпадающих сеансов, разбивку done/error/timeout, среднее для каждой выделенной оценки и маленький трендовый спарклайн. Открытие панели управления показывает полномасштабные панели; **open in sessions** переносит вас на страницу сеансов предварительно отфильтрованную на точно этот срез. Метрики вычисляются серверной стороной по целому совпадающему набору (через `GET /evaluations/aggregate`), поэтому числа точны, а не выбираются.

<img src="https://mintcdn.com/exosphere/RgxYS1UZshqb4m7m/agenteye/images/dashboard-quality.png?fit=max&auto=format&n=RgxYS1UZshqb4m7m&q=85&s=0cbe5452995b2ff187f72b8978716bb3" alt="Панель управления оценкой-здоровьем с полосами средней оценки на измерение оценщика, разбивкой инструмента ok-vs-error, лучшими инструментами и трендом events-per-hour" width="2880" height="1800" data-path="agenteye/images/dashboard-quality.png" />

**Разрешения:** просмотр требует обоих `dashboards:read` и `evaluations:read`; создание и редактирование требует `dashboards:write`; удаление требует `dashboards:delete`. Администратор начальной загрузки автоматически получает все эти.

***

## Устранение неполадок

**Сеансы существуют, но оценки не создаются.** Подтвердите, что `EVALUATOR_ENDPOINT` установлен в процессе сервера, что сервер и оценщик совместно используют то же значение `EVALUATOR_TOKEN`, и что конечная точка `/health` оценщика достижима с сервера. Когда `EVALUATOR_ENDPOINT` не установлен, конвейер неоперативен.

**Оценки в полёте накапливаются.** Запросите `GET /evaluation-jobs`, чтобы увидеть очередь в полёте. Проверьте `attempt_count`, `next_attempt_at` и `last_error` для каждой строки. Частые причины: сервис оценщика недостижим или возвращает 5xx (повторяется с задержкой), неправильный `EVALUATOR_TOKEN` (401 является терминальным), или асинхронный оценщик, который возвращает `pending` бесконечно (см. ниже).

**Сеансы завершены, но нет терминальной оценки.** Запросите `GET /evaluation-jobs?status=polling`; результат всё ещё может быть в полёте. Если задача зависает в `pending`, сервер имеет проблемы с достижением оценщика; проверьте, что оценщик работает и что `EVALUATOR_TOKEN` совпадает.

**`HTTP 401 from evaluator: invalid bearer token`.** `EVALUATOR_TOKEN` на сервере не совпадает со значением, которое настроено на сервисе оценщика. Они должны быть идентичны.

**Асинхронный оценщик возвращает `pending` навечно.** Сервер опрашивает `GET /evaluate/{job_id}` до тех пор, пока оценщик не вернёт `done` или `error`, или пока не истечёт `EVALUATOR_MAX_POLL_DURATION_SECS` (по умолчанию 1 ч). После лимита оценка записывается как `timeout` и удаляется из очереди в полёте. Поднимите `EVALUATOR_MAX_POLL_DURATION_SECS`, если ваш оценщик законно нуждается дольше, чем по умолчанию.
