Аналитика (ClickHouse)
Платформа автоматически собирает аналитические события по всем сервисам — выполнения воркфлоу, вызовы LLM, голосовые сессии, биллинг, API-запросы. Данные хранятся в ClickHouse и доступны для анализа через любой BI-инструмент.
Какие данные собираются
Платформа фиксирует следующие типы событий:
Выполнение рабочих процессов
| Тип события | Описание | Заполняемые поля |
|---|---|---|
execution.started | Рабочий процесс запущен на выполнение. Фиксируется в момент старта Celery-задачи или SSE-стрима. | workflow_id, execution_id, project_id, user_id, thread_id |
execution.completed | Рабочий процесс успешно завершён. | workflow_id, execution_id, project_id, user_id, thread_id, duration_ms (общая длительность), value_int (количество шагов) |
execution.failed | Рабочий процесс завершился с ошибкой. | workflow_id, execution_id, project_id, user_id, thread_id, duration_ms, dim1 (тип ошибки) |
execution.interrupted | Выполнение прервано пользователем или по таймауту. | workflow_id, execution_id, project_id, user_id, thread_id, duration_ms |
execution.node.completed | Отдельная нода рабочего процесса успешно выполнена. | workflow_id, execution_id, node_id, project_id, user_id, thread_id, duration_ms (время выполнения ноды), dim1 (тип ноды: agent, http, function и т.д.) |
execution.node.failed | Нода завершилась с ошибкой. | workflow_id, execution_id, node_id, project_id, user_id, thread_id, duration_ms, dim1 (тип ноды), dim2 (сообщение об ошибке) |
Вызовы LLM
| Тип события | Описание | Заполняемые поля |
|---|---|---|
llm.call | Каждый вызов языковой модели (OpenAI, Anthropic, Groq и др.). Фиксируется при биллинг-трекинге после получения ответа от LLM. | workflow_id, execution_id, project_id, user_id, dim1 (название модели, например gpt-4o), value_int (общее количество токенов — input + output), value_float (стоимость в кредитах) |
Голосовые сессии
| Тип события | Описание | Заполняемые поля |
|---|---|---|
voice.session.ended | Голосовая сессия завершена. Записывается при сохранении метрик сессии. | agent_id, session_id, project_id, user_id, thread_id, dim1 (режим: realtime или pipeline), dim2 (канал: web, sip и т.д.), value_int (количество реплик / turn count), value_float (длительность в секундах), duration_ms |
Биллинг
| Тип события | Описание | Заполняемые поля |
|---|---|---|
billing.credits.consumed | Списание кредитов за использование ресурсов платформы. | workflow_id, execution_id, user_id, value_int (количество кредитов), dim1 (модель, если применимо), dim2 (тип операции: credits_consumed, seats и т.д.), properties (JSON с дополнительными метаданными) |
API-запросы
| Тип события | Описание | Заполняемые поля |
|---|---|---|
user.api_call | Входящий HTTP-запрос к Published API, Execution API или Webhook-эндпоинтам. Не фиксирует внутренние CRUD-запросы, health-check и документацию. | user_id, dim1 (путь запроса, например /api/v1/published/run/...), dim2 (HTTP-метод: POST, GET), value_int (HTTP-статус ответа: 200, 400, 500), value_float (время ответа в мс), duration_ms |
Все события автоматически содержат organization_id, project_id, timestamp (UTC), service (имя сервиса-источника) и environment. Это обеспечивает полную изоляцию данных между организациями и проектами.
Агрегированные представления
Помимо сырых событий, доступны предагрегированные таблицы для быстрых запросов:
execution_stats_hourly— почасовая статистика выполнений (количество, средняя длительность, шаги)llm_usage_hourly— почасовое потребление LLM (количество вызовов, токены, кредиты)
Эти таблицы обновляются автоматически через ClickHouse Materialized Views и оптимальны для дашбордов с большими временными диапазонами.
Создание ключа аналитики
Для подключения BI-инструмента необходимо создать ключ аналитики. Ключ даёт read-only доступ к данным вашей организации.
Шаги
- Перейдите в Интеграции → вкладка Аналитика.
- Нажмите Создать ключ аналитики.
- В карточке ключа отобразятся реквизиты подключения:
- Хост — адрес сервера ClickHouse
- Порт — порт для подключения (обычно
8124) - Пользователь — сгенерированное имя пользователя
- Пароль — отображается один раз при создании
- База данных —
analytics
Пароль показывается только в момент создания ключа. Скопируйте его сразу. Если пароль утерян — используйте Сменить пароль для генерации нового.
Безопасность и ограничения
Каждый ключ создаёт отдельного пользователя в ClickHouse со следующими ограничениями:
| Ограничение | Значение | Описание |
|---|---|---|
| Режим доступа | readonly = 1 | Только SELECT — запись, изменение и удаление данных невозможны |
| Максимум памяти на запрос | 2 ГБ | Запросы, превышающие лимит, автоматически прерываются |
| Таймаут запроса | 30 секунд | Долгие запросы завершаются по таймауту |
| Изоляция данных | Row-level security | Пользователь видит только события своей организации |
| Привязка к проекту | Опционально | Ключ можно ограничить данными одного проекта |
| Доступные таблицы | 3 таблицы | events, execution_stats_hourly, llm_usage_hourly |
Создание и управление ключами аналитики доступно только администраторам и владельцам организации.
Подключение BI-инструментов
Grafana
- Установите плагин grafana-clickhouse-datasource:
grafana-cli plugins install grafana-clickhouse-datasource - В Grafana перейдите в Configuration → Data Sources → Add data source.
- Выберите ClickHouse.
- Заполните реквизиты из карточки ключа:
- Server address — хост
- Server port — порт
- Username — пользователь
- Password — пароль
- Database —
analytics
- Нажмите Save & Test.
Metabase
- В Metabase перейдите в Admin → Databases → Add database.
- Выберите тип ClickHouse (встроенный драйвер).
- Заполните реквизиты из карточки ключа.
- Нажмите Save.
SQL-клиент
Подключение через clickhouse-client:
clickhouse-client --host <хост> \
--port <порт> \
--user <пользователь> \
--password <пароль> \
--database analytics
Управление ключами
Ротация пароля
Нажмите Сменить пароль на карточке ключа. Старый пароль немедленно перестаёт работать, новый отображается один раз.
Отзыв ключа
Нажмите Отозвать на карточке ключа. Пользователь ClickHouse и все политики доступа будут удалены. BI-инструменты, использующие этот ключ, потеряют доступ.
Вы можете создать несколько ключей — например, отдельный для Grafana и отдельный для Metabase. Это позволяет отозвать доступ одного инструмента, не затрагивая другой.
Схема данных
Таблица events
Основная таблица со всеми аналитическими событиями.
| Колонка | Тип | Описание |
|---|---|---|
event_id | UUID | Уникальный идентификатор события (генерируется автоматически) |
event_type | String | Тип события (например, execution.completed, llm.call) |
event_category | String | Категория, вычисляемая из типа: execution, llm, voice, billing, user |
organization_id | String | ID организации |
project_id | String | ID проекта (может быть пустым) |
user_id | String | ID пользователя, инициировавшего действие |
workflow_id | String | ID рабочего процесса |
execution_id | String | ID конкретного выполнения |
agent_id | String | ID агента (для голосовых сессий и агентных нод) |
session_id | String | ID голосовой сессии |
node_id | String | ID ноды рабочего процесса |
thread_id | String | ID потока диалога |
timestamp | DateTime64 | Время события в UTC |
duration_ms | Int64 | Длительность операции в миллисекундах |
value_int | Int64 | Целочисленная метрика (токены, кредиты, HTTP-статус, количество реплик) |
value_float | Float64 | Дробная метрика (стоимость в кредитах, длительность в секундах, latency в мс) |
dim1 | String | Измерение 1 — зависит от типа события (модель, тип ноды, путь запроса, режим) |
dim2 | String | Измерение 2 — зависит от типа события (HTTP-метод, канал, тип операции) |
dim3 | String | Измерение 3 — резервное поле для дополнительных данных |
properties | String | Произвольные свойства в формате JSON |
service | String | Имя сервиса-источника (backend-api, execution-service) |
environment | String | Окружение (production, development) |
Таблица execution_stats_hourly
Предагрегированная почасовая статистика выполнений.
| Колонка | Описание |
|---|---|
hour | Начало часа |
organization_id | ID организации |
project_id | ID проекта |
workflow_id | ID рабочего процесса |
executions | Количество выполнений |
avg_duration_ms | Средняя длительность |
total_steps | Общее количество шагов |
Таблица llm_usage_hourly
Предагрегированная почасовая статистика использования LLM.
| Колонка | Описание |
|---|---|
hour | Начало часа |
organization_id | ID организации |
project_id | ID проекта |
model | Название модели (берётся из dim1) |
calls | Количество вызовов |
total_tokens | Суммарное количество токенов |
total_credits | Суммарная стоимость в кредитах |
Примеры SQL-запросов
Статистика выполнений за последние 7 дней
SELECT
toDate(timestamp) AS day,
countIf(event_type = 'execution.completed') AS success,
countIf(event_type = 'execution.failed') AS failed,
avg(duration_ms) / 1000 AS avg_duration_sec
FROM events
WHERE event_category = 'execution'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY day
ORDER BY day
Потребление токенов по моделям
SELECT
dim1 AS model,
count() AS calls,
sum(value_int) AS total_tokens,
round(sum(value_float), 2) AS total_credits
FROM events
WHERE event_type = 'llm.call'
AND timestamp >= now() - INTERVAL 30 DAY
GROUP BY model
ORDER BY total_credits DESC
Активность по Published API
SELECT
toStartOfHour(timestamp) AS hour,
dim1 AS endpoint,
count() AS requests,
avg(duration_ms) AS avg_latency_ms
FROM events
WHERE event_type = 'user.api_call'
AND timestamp >= now() - INTERVAL 24 HOUR
GROUP BY hour, endpoint
ORDER BY hour, requests DESC
Средняя длительность нод по типам
SELECT
dim1 AS node_type,
count() AS executions,
round(avg(duration_ms)) AS avg_ms,
round(quantile(0.95)(duration_ms)) AS p95_ms
FROM events
WHERE event_type = 'execution.node.completed'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY node_type
ORDER BY avg_ms DESC
Голосовые сессии: длительность и реплики
SELECT
toDate(timestamp) AS day,
dim1 AS mode,
count() AS sessions,
round(avg(value_float)) AS avg_duration_sec,
round(avg(value_int)) AS avg_turns
FROM events
WHERE event_type = 'voice.session.ended'
AND timestamp >= now() - INTERVAL 30 DAY
GROUP BY day, mode
ORDER BY day
Использование агрегированных таблиц
Для дашбордов с большими временными диапазонами используйте предагрегированные таблицы — они работают значительно быстрее:
-- Выполнения по воркфлоу за месяц (из агрегированной таблицы)
SELECT
workflow_id,
sum(executions) AS total,
avg(avg_duration_ms) / 1000 AS avg_sec
FROM execution_stats_hourly
WHERE hour >= now() - INTERVAL 30 DAY
GROUP BY workflow_id
ORDER BY total DESC
LIMIT 10
-- Расход токенов по моделям за квартал (из агрегированной таблицы)
SELECT
model,
sum(calls) AS total_calls,
sum(total_tokens) AS tokens,
round(sum(total_credits), 2) AS credits
FROM llm_usage_hourly
WHERE hour >= now() - INTERVAL 90 DAY
GROUP BY model
ORDER BY credits DESC