Аналитика разговоров
Загрузка записей, разбор по характеристикам и отчёты в Voxagent
Модуль аналитики разбирает записи живых продаж так же, как Voxagent разбирает разговоры AI-агентов: запись превращается в диалог по ролям, диалог оценивается по набору характеристик, оценки складываются в отчёты по отделу.
Разговоры агентов и записи людей лежат в одной сущности и меряются одними метриками — сравнивать бота с живым менеджером можно напрямую, без сведения двух отчётов вручную.
Как это устроено
Загрузка записи → Транскрипция с диаризацией → Разбор по характеристикам → Отчёты| Шаг | Что происходит |
|---|---|
| Загрузка | Аудио уезжает в хранилище напрямую, минуя бэкенд; в системе появляется разговор |
| Транскрипция | Внешний сервис разбирает запись на реплики с ролями и тайм-кодами |
| Разбор | Считаемые метрики вычисляет бэкенд, оценочные — языковая модель |
| Отчёты | Значения агрегируются по сотруднику, дате, компании |
Готовый транскрипт
Если расшифровка уже есть, шаг транскрипции пропускается. Это единственный способ разобрать записи из системы, которая отдаёт текст, но не отдаёт аудио.
Загрузка записей
Раздел Analytics → Звонки, кнопка Загрузить записи. Поддерживаются три варианта:
- только аудио — обычный случай, транскрипцию сделает сервис разбора;
- аудио и транскрипт — расшифровка ваша, звук нужен для прослушивания и тайм-кодов;
- только транскрипт — без записи, разбор по тексту.
Аудио и транскрипт связываются по имени файла без расширения: call-12.mp3 и call-12.json — это один разговор.
В окне загрузки выбираются сотрудник и клиент — оба из справочника пространства, и оба заводятся прямо здесь кнопкой рядом с выбором. Галочка сразу отправить в разбор ставит записи в очередь немедленно; без неё они лягут в список со статусом «в очереди» и будут ждать массового прогона.
Там же — название загрузки и дата записей. Название нужно, чтобы потом найти в фильтре именно свою выгрузку; оставите пустым — загрузка назовётся сама и возьмёт в имя момент: «Загрузка 21.09.2026 18:20, файлов: 3». Дата применяется только к тем файлам, которые нигде не несут свою собственную: то, что известно о самом файле, она не перебивает.
Формат транскрипта
{
"language": "ru",
"duration_ms": 356000,
"segments": [
{ "start_ms": 2000, "end_ms": 4100, "role": "Operator", "text": "Добрый день, меня зовут…" },
{ "start_ms": 4300, "end_ms": 7200, "role": "Client", "text": "Да, слушаю вас." }
]
}Роли обязательны: без них непонятно, чью долю речи считать и чью работу оценивать. Тайм-коды необязательны — без них разговор разберётся, но метрики, завязанные на время (доля речи, перебивания, темп), посчитаны не будут и в карточке останутся прочерком, а не нулём.
Архивом
ZIP с записями удобен, когда выгрузка приходит из телефонии. Метаданные берутся из имени файла:
<id>_<in|out>_<телефон>_<ГГГГ_ММ_ДД-ЧЧ_ММ_СС>_<хвост>.mp3Например 883140777409555_out_79251154445_2026_09_10-14_42_50_akww.mp3 — исходящий звонок на номер 79251154445 от 10 сентября 2026, 14:42:50.
Сначала проверка
Импорт архива по умолчанию идёт в режиме проверки: система покажет, сколько записей приняла, сколько сочла дублями и что отклонила с указанием причины, но ничего не загрузит. Снимите галочку, когда результат проверки устроит.
Повторная загрузка того же файла не создаёт дубль: записи сверяются по содержимому. Служебные файлы macOS — папка __MACOSX, теневые ._имя и .DS_Store — пропускаются: это не записи, а ресурс-форки, и разговорами они не становятся.
Загрузка как единица
Каждая загрузка запоминается целиком: название, вид (файлы или архив), кто и когда её сделал, сколько записей принято и сколько отклонено. По ней потом фильтруют звонки — «покажи то, что я залил во вторник», — и строят отчёты: загрузка доступна и как фильтр, и как разрез, так что две выгрузки можно сравнить между собой. Проверочный прогон архива ничего не создаёт, поэтому и записи о нём не остаётся.
Дата разговора
Дата в списке и в отчётах — это когда звонок состоялся, а не когда его загрузили. Берётся она из первого источника, который её знает:
- дата, указанная при загрузке, или колонка
started_atв манифесте архива; - имя файла, если оно совпадает с шаблоном телефонии выше;
- таймстемп записи внутри архива — то, что zip сохранил о самом файле;
- ничего не нашлось — даты нет.
В последнем случае в списке стоит время загрузки с пометкой, что дата неизвестна. Молча подставить сегодняшний день было бы хуже всего: годовой архив превратился бы в один занятой день, и отличить выдумку от факта стало бы нечем.
Время самой загрузки живёт отдельно и всегда честное — это момент, когда файл приехал в систему.
Кто оператор, а кто клиент
Диаризация отличает голоса, а не людей. Она слышит, что говорили двое, и подписывает их ролями по косвенным признакам — а ошибается молча: доля речи «оператора» оказывается долей клиента, этапы скрипта оцениваются не тому человеку, и карточка при этом выглядит совершенно нормально.
Поэтому у каждой загруженной записи разбор сначала отдельно спрашивает модель: не перепутаны ли стороны. Модель отвечает «да/нет» и одной фразой объясняет, по чему решила, — обе части хранятся у разговора. Разговоры агентов этот шаг пропускают: там известно, кто говорил.
Дальше запись считается дважды — в исходной разметке и в перевёрнутой. Арифметику для второй платформа считает сама: перевернуть роли — это посмотреть на те же реплики с другой стороны. А вот оценки приходится спрашивать у модели ещё раз: «оператор не отработал возражение», сказанное про другого человека, — это другой вердикт, а не тот же самый с переставленными подписями.
В карточке разговора у заголовка транскрипта стоит Поменять роли местами. Поскольку обе разметки посчитаны заранее, это переключение, а не новый разбор: транскрипт и все оценки меняются сразу. Рядом видно, откуда нынешняя разметка — причина модели или пометка, что роли выбрал человек.
Решение человека не перетирается
Если роли переключили руками, следующий разбор об этом не спорит: он пересчитает оценки, но оставит ту сторону, которую выбрали вы. Модель ошиблась один раз — незачем давать ей ошибиться во второй.
Отчёты, список звонков и значения фильтров всегда берут ту разметку, в которой разговор сейчас показывается. Второй набор лежит рядом и ждёт переключения — сложить их в одно среднее означало бы получить число, которое ничего не измеряет.
Сами реплики при этом не переписываются: транскрипт остаётся тем, что выдал диаризатор, а роли переворачиваются на чтении.
Что измеряется
Дефолтный набор приезжает в каждое пространство и делится на две группы.
Считаемые метрики
Вычисляет бэкенд по тайм-кодам, модель не участвует.
| Метрика | Норма | Почему так |
|---|---|---|
| Доля речи оператора | до 43% | Продажа — это вопросы, а не монолог |
| Доля речи клиента | от 57% | Обратная сторона той же нормы |
| Перебивания | — | Сколько раз оператор начал говорить поверх клиента |
| Доля пересечений | — | Какая часть разговора прошла в одновременной речи |
| Темп речи оператора | — | Слов в минуту |
| Доля тишины | — | Паузы между репликами |
| Длительность разговора, мин | — | От начала до конца сессии, у загруженной записи — по длине файла |
Оценочные характеристики
Их ставит языковая модель. Каждая оценка приходит с обоснованием и ссылкой на реплику, из которой она следует — в карточке ссылка кликается и перематывает запись на это место.
Сюда входят этапы скрипта (открытие, квалификация, выявление потребности, презентация, работа с возражениями, бюджет, ЛПР, закрытие), тон разговора, вежливость, сленг и мат, тип звонка и фиксация следующего шага.
Соблюдение скрипта — взвешенная сумма этапов, приведённая к 0–100. Общий балл считается так же, но берёт всё, чему назначен вес, а не только этапы. Веса видны и меняются в каталоге характеристик; пока вес стоит только у этапов, оба числа совпадают.
«Не измерено» — не ноль
Если характеристику посчитать не удалось, в карточке стоит прочерк и причина. Ноль в доле речи читается как «оператор молчал» и тянет вниз средние по отделу — поэтому неизмеренное в агрегаты не попадает вовсе.
Свой набор характеристик
Раздел Analytics → Характеристики. Слева папки, справа метрики.
Создайте папку под свой набор — например «Возражения» или «Онбординг». Папка Default есть всегда и удалить её нельзя.
Добавьте характеристику. Ключ — латиницей, по нему метрику находят отчёты; после создания он не меняется. Напишите инструкцию модели: что именно оценивать и по какому признаку. Просите обоснование и цитату — без них оценка нечитаема.
Задайте норму. Она красит отклонения в карточке и отчётах, а если характеристика участвует в общем балле — служит и мерой выполнения.
Считаемые метрики руками не заводятся: их значение вычисляет код, и «добавить такую» означало бы написать формулу, которой нет. Их же нельзя удалить — только выключить: пайплайн продолжает считать по ключу. У встроенных оценочных характеристик правится всё, включая инструкцию модели: это просто текст, уходящий в промпт, и уточнить формулировку под свой продукт — нормальное желание.
Удаление характеристики убирает её из каталога и конструктора отчётов совсем. Уже посчитанные значения остаются при разговорах и видны в карточке с пометкой «удалена»: их посчитали, пока характеристика была. В отчётах такая характеристика не участвует, сохранённое представление просто теряет колонку.
Что будет с историей
Правка метрики не переписывает уже посчитанные значения: у каждого сохранена версия метрики на момент расчёта. Отчёт за прошлый месяц не изменится задним числом от того, что вы поменяли вес. Чтобы новые правила применились к старым звонкам, разговоры нужно разобрать заново.
Норма, шкала и вес
Диалог характеристики предлагает только то, что для выбранного типа значения что-то значит:
| Тип значения | Что настраивается |
|---|---|
| Балл | максимум шкалы, нормы, направление, вес |
| Проценты | нормы, направление, вес |
| Число, Длительность | нормы, направление, вес; нормы длительности задаются в миллисекундах |
| Да / нет | «хорошо, когда да» или «когда нет», вес |
| Категория | ничего из этого |
Максимум шкалы есть только у балла: это и потолок, выше которого модель не ответит, и знаменатель при подсчёте общего балла.
Вес — это участие в общем балле, а не приоритет в списке. Общий балл — взвешенная доля выполнения: сумма (доля × вес), делённая на сумму весов. Долю каждый тип даёт по-своему: балл делится на максимум шкалы; «да/нет» даёт единицу или ноль, и какой ответ хороший, говорит направление; число, длительность и проценты делятся на свою норму — «больше — лучше» делит на нижнюю границу, «меньше — лучше» даёт полный вес внутри верхней и падает по мере превышения. Поэтому у числа без нормы вес ни на что не влияет: делить не на что, и характеристика в итог просто не войдёт.
Соблюдение скрипта и Общий балл — разные числа: первое считает только этапы скрипта, второе — всё, чему назначен вес. Пока вес стоит только у этапов, они совпадают.
Язык названий
Языков здесь два
Язык интерфейса — ваш личный: на нём подписаны характеристики, папки и колонки отчётов. Язык разбора — общий для пространства: на нём модель пишет обоснования оценок. Первый переключается в шапке и меняет картинку сразу; второй живёт в настройках аналитики и применяется к следующему разбору.
Названия и описания встроенных характеристик, папок и представлений хранятся в базе по-английски, а переводы — отдельными строками. Интерфейс сообщает выбранный язык заголовком x-lang — тем, что стоит в переключателе, а не тем, что настроено в браузере, — и в ответ приезжает текст на нём. Для прямых обращений к API запасным остаётся Accept-Language; для языка, которого нет в переводах, остаётся английский — это честнее пустого названия.
Переводятся только тексты, которые поставляет продукт. Названия, которые ввели вы — своя характеристика, своя папка, своё представление, — остаются ровно такими, как их набрали: переводить их означало бы угадывать, что вы имели в виду на языке, на котором не писали.
Если переименовать встроенную характеристику, её переводы удаляются и всюду остаётся ваш текст — иначе переименование было бы видно только на одном языке.
Инструкция для модели не переводится. Это промпт, а не надпись в интерфейсе: две языковые версии означали бы два разных разбора одного разговора.
Отчёты и представления
Раздел Analytics → Представления. Конструктор сверху, готовые представления слева.
Отчёт складывается из четырёх ответов: за какой период, в разрезе чего, какие метрики и как показать (таблица, столбцы, линия, круговая, плитки).
Измерений может быть несколько, и добавляются они строками, как меры: строка отчёта тогда это сочетание значений — «сотрудник × неделя × тип звонка». Разрезы: сотрудник, дата (с выбором дня, недели или месяца), компания, клиент, источник, загрузка, причина завершения звонка и значение любой характеристики. Одно и то же поле дважды не добавить — кроме даты и характеристики, где повтор осмыслен: месяцы внутри дней, разные характеристики подряд.
Числовую характеристику режут диапазонами: у разреза появляются строки «от–до» с необязательной подписью. Без них каждое значение стало бы отдельной строкой — получился бы список, а не отчёт. Границы полуоткрытые: значение попадает ровно в один диапазон, а не угодившее ни в один собирается в отдельную строку.
Кроме характеристик мерой можно взять количество разговоров — это просто число строк в группе.
Фильтры
Разрез отвечает «в каком виде показать», фильтр — «что вообще брать». Фильтры добавляются строками там же, в конструкторе, и сужают выборку по тем же полям, что и разрезы: сотрудник, клиент, компания, источник, загрузка, тип звонка (агент или загруженная запись), причина завершения и любая характеристика.
Исполнитель в фильтре — и человек, и голосовой агент: разговоры тех и других лежат в одной таблице и меряются одними метриками, поэтому список исполнителей один, но разделён на группы «Сотрудники» и «Голосовые агенты». Тот же фильтр — над списком звонков: там он тоже принимает нескольких исполнителей сразу.
Условие зависит от того, что фильтруют: у полей и текстовых характеристик это «одно из» и «ни одно из» с выбором из значений, которые в данных пространства действительно встречались; у числовой характеристики — «не меньше», «больше», «не больше», «меньше» и число; у любой характеристики — ещё «измерено» и «не измерено». Список значений берётся из самих разговоров, а не из справочника: у загруженной записи клиент и компания могут остаться текстом с импорта, и такую строку отчёта иначе нечем было бы отфильтровать.
Недозаполненная строка фильтра ничего не сужает: фильтр собирают по одному контролу, и «выбрал поле — отчёт опустел» читалось бы как поломка. Фильтр по удалённой характеристике перестаёт применяться вместе с ней — так же, как теряется её колонка.
Фильтры сохраняются вместе с представлением. Период — нет: одно и то же представление смотрят и за неделю, и за месяц, и переключение периода не меняет сохранённый отчёт.
Сохранение
Собранный отчёт сохраняется как представление — для всего пространства или только для себя. Сохранить пишет изменения в открытое представление, Сохранить как заводит новое. Встроенные представления правятся только через копию: они одинаковые у всех, и правка «под свой отдел» сломала бы их остальным, — у них кнопка сохранения выключена.
Звёздочка рядом с представлением выносит его на дашборд. Закрепление личное: у каждого свой дашборд, состав представлений — общий.
Дашборд
Analytics → Дашборд показывает закреплённые представления плитками. Каждая грузится независимо: дашборд из шести отчётов не ждёт самый медленный, чтобы показать хоть что-то.
Встроенных представлений четыре: «Обзор отдела» (ключевые числа), «Сравнение менеджеров» (строка на сотрудника), «Звонки» (журнал) и «Динамика по неделям».
Кнопка Редактировать включает режим правки: плитки перетаскиваются мышью, у каждой появляется крестик, а Сохранить запоминает порядок. Вне этого режима дашборд только читается — случайным кликом плитку не снести.
Массовый разбор
Когда записей много, разбор запускается пачкой: Analytics → Звонки, отметить разговоры и создать прогон. Прогон показывает, сколько разобрано, сколько упало и на чём, его можно остановить и продолжить с того же места. Повторный запуск только для упавших не трогает уже разобранное.
Модель для разбора выбирается в Analytics → Настройки и переопределяется для конкретного прогона — дешёвой моделью можно прогнать весь архив, дорогой перепроверить спорные звонки. Пока модель не выбрана, оценочные характеристики не считаются: разбор дойдёт до метрик по тайм-кодам и остановится. Рядом с выбором модели настраиваются её параметры — температура, top-p, потолок ответа: разбор должен быть воспроизводимым, и температура для него уместна другая, чем для разговора агента.
Язык разбора
Там же выбирается язык разбора: на нём модель читает характеристики и пишет обоснования оценок.
Это настройка пространства, а не читателя. Обоснование пишется один раз и остаётся при разговоре, а читает его вся команда — если бы язык брался из заголовка запроса, один и тот же звонок объяснялся бы по-разному в зависимости от того, кто открыл карточку, и переспросить «почему здесь 2 из 5» было бы не у кого.
Язык, на котором шёл сам разговор, тут ни при чём: русский звонок можно разбирать по-английски, и наоборот. Модель читает транскрипт как есть — переводить его никто не просит.
У каждого разговора сохраняется язык, на котором его разобрали. Через месяц это единственный способ ответить, почему обоснования написаны именно так: настройку с тех пор могли переключить. Переключение применяется к следующему разбору — уже посчитанные оценки не переписываются, для этого разговоры нужно разобрать заново.
Инструкции характеристик при этом не переводятся: это промпт, а не надпись, и две его версии означали бы два разных разбора одного разговора.
Справочник участников
Там же живёт справочник участников — две стороны разговора в одном списке.
Сотрудники — те, чью работу оценивают. Идентификаторы из телефонии, указанные в карточке, позволяют загруженной записи находить своего менеджера самой: без них у звонка не будет исполнителя, и сравнивать в отчётах будет некого.
Клиенты — вторая сторона. У клиента есть компания, и именно она делает разрез «по компании» осмысленным: пять звонков разным людям одной организации складываются в одну строку отчёта. Заводить клиента можно и здесь, и прямо там, где его назначают разговору.
В карточке звонка выбираются оба участника и номер конкретного звонка; правки уезжают на сервер по кнопке Сохранить. Рядом — Обновить (разбор идёт фоном, результат приезжает не сразу) и Разобрать заново, который переспрашивает: он заменяет прежние оценки.
API
Все действия модуля доступны через REST. Полное описание с примерами — в справочнике API.
| Что | Запрос |
|---|---|
| Зарегистрировать разговор | POST /api/analytics/conversations |
| Загрузить пачкой | POST /api/analytics/conversations/batch |
| Импортировать архив | POST /api/analytics/conversations/archive |
| Список загрузок | GET /api/analytics/uploads |
| Список разговоров | POST /api/analytics/conversations/search |
| Карточка разговора | GET /api/analytics/conversations/{id} |
| Поправить разговор | PUT /api/analytics/conversations/{id} |
| Запустить разбор | POST /api/analytics/conversations/{id}/analyze |
| Построить отчёт | POST /api/analytics/reports/run |
| Каталог характеристик | GET /api/analytics/metrics |
| Значения для фильтров | GET /api/analytics/reports/filter-options |
| Справочник участников | GET /api/analytics/sales-reps?kind=Manager|Client |
| Порядок плиток дашборда | PUT /api/analytics/views/pins/order |
| Расход на разбор | GET /api/analytics/usage |
Почему список — это POST
У списка разговоров те же фильтры, что у отчётов, вместе с периодом и набором значений. Складывать это в query-строку — значит упереться в её длину и в разное экранирование у клиентов.