Назад к блогу

Я не ML-инженер. А собрал сервис аудита звонков из двух чужих API

Как маркетолог без ML-команды собрал ИИ-аудит звонков под ключ: диаризация, оценка A–F с цитатами, грабли с фейк-стерео и модели, ломающие JSON.

19 июн. 2026 г.
TL;DR

Слушай, короче. Руководитель языковой школы не мог физически прослушивать сотни звонков своих менеджеров. Я собрал ему сервис: кидаешь аудио → получаешь расшифровку с ролями → и отчёт A–F с разбором и цитатами прямо из разговора. Внутри два чужих API и аккуратный промпт. Ни одной модели я не учил. Зато словил полтора десятка граблей, которые хочу рассказать по-человечески.

Сразу честно: я не учу модели, я их применяю

Давай без понтов с самого начала. Я не ML-инженер. Вся «магия» этого аудита звонков — это склейка двух готовых API и правильно собранный промпт оценки. Если ждёшь рассказ про обучение модели на корпусе разговоров — закрывай вкладку, тут другое кино.

И ещё честнее. Большую часть кода я вайб-кодил с агентом. Садился, объяснял задачу, смотрел, что он принёс, правил, гонял дальше. В 2026-м это уже не стыдно — именно так сейчас выглядит «разработка под ключ» для тех, у кого нет в штате ML-команды. Маркетолог, который понимает код-базу и умеет внятно поставить задачу агенту, закрывает целый пласт работы, который ещё пару лет назад требовал отдельного разработчика. Я как раз такой маркетолог (я ж маркетолог, да).

Пишу я это не как образец крутой архитектуры. Пишу, потому что путь от «кинул mp3» до «получил адекватный отчёт» оказался цепочкой совершенно неочевидных граблей. И почти каждая — не про ИИ, а про скучные детали реальности: битрейт записи, числовые ключи в JavaScript, формат стерео-контейнера. Про них и поговорим, как с другом на кухне.

Держи в голове одну линию с самого начала: сервис я делал под одну студию — чтобы руководитель мог наконец контролировать качество звонков своих менеджеров. А закончилось тем, что им заинтересовалась целая франшизная сеть, и теперь по тому же движку оценивают уже администраторов в студиях-партнёрах.

Зачем это вообще

Представь руководителя в языковой школе. Команда менеджеров целый день звонит родителям: рассказывают про методику, закрывают возражения, записывают детей на пробные. От качества этих звонков напрямую зависит выручка.

А прослушать их физически некому. Их сотни в неделю. Один звонок — двадцать минут внимательного прослушивания с блокнотом. Перемножи на сотни — и получишь вторую полноценную ставку, которой ни в одной небольшой школе нет. Поэтому обучение менеджеров идёт «на ощущениях»: кажется, что Маша звонит хорошо, а Петя — так себе. Данных под этим ощущением нет.

Задача: чтобы каждый звонок проходил через автоматический контроль качества по корпоративному стандарту. Не выборочно, когда дойдут руки, — а по каждому. Чтобы у руководителя был балл, разбор по этапам и конкретная цитата: «вот тут менеджер не выявил потребность, послушай».

Что снаружи и как устроено внутри

Снаружи просто. Кидаешь аудиофайл (MP3, WAV, OGG, M4A, AAC, WebM — до 50 МБ) в веб-интерфейс. Сервис распознаёт речь, раскладывает её на «Менеджер» и «Клиент», отдаёт расшифровку языковой модели и возвращает отчёт: итоговый балл и буква (A–F), сильные стороны, зоны роста, следующие шаги и таблица по критериям — каждый с комментарием и цитатой. Шкала: A ≥ 90%, B — 75–89%, C — 60–74%, D — 40–59%, F < 40%.

Самое ценное — кликабельные цитаты. Модель не просто пишет «контакт установлен слабо», а вытаскивает конкретный фрагмент: вот эта реплика менеджера. Клик по реплике в расшифровке перематывает аудио-плеер ровно туда. Это снимает главное возражение к любой автоматической оценке — «откуда система это взяла». Система не выносит приговор, она показывает, на что смотреть.

[screenshot placeholder] Экран отчёта по звонку — итоговый балл с буквой A–F, блоки сильных сторон и зон роста, таблица критериев с комментариями
Каждый критерий — с комментарием и цитатой из реального разговора

Если убрать всю обвязку, схема влезает в одну строчку: аудио → распознавание речи → расшифровка → оценка моделью → отчёт A–F.

Распознавание речи — Yandex SpeechKit. Русская речь, плюс диаризация: размечает, кто из двух говорящих сейчас в эфире.

Оценка — через OpenRouter. Дешёвая модель работает основной, дорогая подключается запасной, когда дешёвая выдала что-то кривое.

Обвязка — Next.js, база данных, Docker, деплой по SSH. Ничего экзотического.

Ещё раз честно: я не строил ML-пайплайн. Распознавание — чужое, оценочная модель — чужая. Моя работа — склеить их так, чтобы на входе был кривой mp3 из CRM, а на выходе — отчёт, которому можно верить. Вся сложность спряталась в слове «склеить».

[screenshot placeholder] Расшифровка звонка — реплики Менеджер и Клиент, аудио-плеер слева для перемотки к цитате
Клик по реплике в расшифровке перематывает плеер ровно к тому моменту

Грабли, на которых я сидел вечерами

Дальше — не список фич, а хронология столкновений с реальностью. По собственной git-истории. Показательная история: почти ни одна грабля не была про «ИИ сложный». Все — про детали.

Кто тут менеджер, а кто клиент

Начинал с Whisper — его все хвалят. На русских записях из реального колл-центра он повёл себя хуже, чем на демках: плюс региональные блокировки на доступ к API. Переехал на Yandex SpeechKit. Российский провайдер, русская речь — родная задача.

Грабля раз: они поменялись местами. Расшифровка выглядит идеально, роли разнесены — но менеджер и клиент поменялись местами. Система уверенно пишет «Клиент: Здравствуйте, меня зовут [менеджер], я из школы…». Первая мысль — у Yandex плохая диаризация. Я почти смирился.

А потом полез в сырой ответ API и увидел причину. Yandex размечает говорящих метками `0` и `1`. А я по наивности ждал `1` и `2` — и завязал на это маппинг ролей. В итоге мой код брал говорящего под номером `1` — а это, в терминах Yandex, уже второй человек. Лечится тривиально — маппинг по порядку появления, а не по номеру тега. Но пока не упрёшься носом в сырой ответ — не догадаешься.

Грабля два: язык сам всё переставил. Думал, разобрался. Баг вернулся — реже, исподтишка. Я держал слоты говорящих в объекте с числовыми ключами. А JavaScript, когда у объекта ключи — числа, любит молча отсортировать их по-своему, по возрастанию. То есть порядок появления, который я честно сохранял, движок языка втихую переставлял за меня. Перевёл хранение на массив, где порядок гарантирован, — отпустило. Вечер потерял.

Грабля три, моя любимая: фейк-стерео. Эта — чемпион по издевательству. Часть записей приходила в стерео-контейнере, но по факту с одним реальным каналом: моно, упакованное как стерео. На глаз и на слух — обычный файл, метаданные гордо сообщают «2 канала». А диаризация по таким записям просто не работала — Yandex возвращал всё одним говорящим.

Первый диагноз снова увёл в сторону: я грешил на плохую запись, думал — голоса наложились. Слушаю — нормальная запись, два разных голоса прекрасно различимы. Почему модель видит одного? Дошло, когда начал смотреть не на расширение, а на реальное содержимое каналов. SpeechKit при диаризации опирается на то, что в стереозаписи левый и правый каналы разные. А тут оба канала были побитово одинаковые — то самое «фейк-стерео». Модель честно не могла развести говорящих по каналам, потому что разводить было нечего.

Теперь я прогоняю каждый файл через `ffprobe` — смотреть реальное число каналов, битрейт, кодек, а не верить расширению. Для фейк-стерео добавил детект и сведение в честное моно перед отправкой.

Из всех трёх граблей выросла одна привычка — retry-логика. Если с первого раза диаризация не удалась, сервис не падает с ошибкой в лицо пользователю, а повторяет распознавание в облегчённом режиме. Именно эта обвязка вокруг чужого API и отличает «демо, которое я показал заказчику» от «сервиса, в который заказчик каждый день заливает файлы».

[screenshot placeholder] Вывод ffprobe по файлу — стерео-контейнер с идентичными каналами, рядом лог retry распознавания
ffprobe показывает 2 канала, но они побитово одинаковые — диаризации не за что зацепиться

Модель, которая ломает JSON

С распознаванием более-менее разобрались. Дальше расшифровку надо отдать языковой модели — и тут я налетел на главный инсайт всего проекта.

Мне от модели нужна не «умная» свободная форма. Мне нужна строго заданная структура: по каждому критерию — балл, комментарий, цитата. Эта структура разбирается кодом и рисуется в отчёт. Если модель вернула красивый текст, но с поехавшей структурой или лишней болтовнёй, — весь конвейер встаёт.

Я взял три реальных звонка и прогнал через три разных модели одним промптом:

gpt-4.1-mini: 3 валидных из 3, структура с первого раза — 3 из 3, прошло гейт — 3 из 3.

deepseek-v4-pro: 0 из 3 / 0 из 3 / 0 из 3.

kimi-k2.6: 0 из 3 / 0 из 3 / 0 из 3.

Скромная по «уму» модель отработала идеально. А две модели, про которые пишут «монстры рассуждений», не вернули ни одного пригодного ответа из трёх попыток. Не потому что глупые. На задаче «отдай мне строго вот такую структуру» — они стабильно ломали формат: заворачивали в лишние пояснения, «улучшали» схему по своему вкусу, добавляли поля, которых я не просил.

«В продакшене стабильность валидного структурированного ответа важнее «ума» модели.»

из выводов проекта

Самая умная модель бесполезна, если один ответ из трёх роняет тебе разбор. Руководителю всё равно, насколько глубоко модель «поняла» звонок, если в трети строк вместо оценки — ошибка.

Отсюда родилась двухстадийная схема: дешёвая надёжная модель работает основной, дорогая подключается запасной — когда основная выдала что-то не прошедшее проверку. Экономика сходится: подавляющее большинство звонков закрывает дешёвая модель, фолбэк включается на считанных процентах, и средняя стоимость оценки одного разговора остаётся копеечной.

Как штука уехала из одной студии в целую сеть

Сервис я начинал под одну языковую студию. Классическая разработка под ключ: один клиент, одна задача, один корпоративный стандарт. Встал на прод, им начали пользоваться старшие менеджеры. Уже успех по исходному ТЗ.

А дальше случилось то, чего я не закладывал. У студии есть франшизная сеть — партнёры по тем же стандартам. Для сети контроль качества звонков — головная боль на порядок больше, чем для одной студии. Управляющая компания не может физически прослушивать звонки администраторов в десятках партнёрских точек. А качество этих звонков бьёт по бренду всей сети.

Оказалось, что инструмент «для себя» закрывает ровно эту боль — только этажом выше. Тот же движок, который оценивал менеджеров одной студии, оценивает администраторов в студиях-партнёрах по стандарту сети. Контроль качества из «руководитель студии слушает свою команду» вырос в «сеть видит уровень сервиса по всем точкам».

Для меня это был сигнал — и как для разработчика, и как для маркетолога. Когда штука, собранная под одного клиента, без переделки нужна на уровне сети — значит, попал не в частную хотелку, а в системную боль.

И вот тут выстрелили две вещи, заложенные «на всякий случай». Корпоративный стандарт я с самого начала вынес в настраиваемый шаблон, а не зашил в код. И сделал мультитенант: разные аккаунты — разные брендинги, разные шаблоны оценки, общий движок. На старте это выглядело как «избыточная гибкость для проекта с одним клиентом». На деле — ровно то, что позволило сервису переехать из студии в сеть без переписывания половины кода.

[screenshot placeholder] Экран настройки шаблона оценки — выбор отраслевого стандарта, список подключённых провайдеров без видимых ключей
Стандарт оценки — в шаблоне, а не в коде. Поэтому один движок работает для разных команд

Что я понял на этом проекте

Для огромного класса бизнес-задач сегодня не нужен ML-инженер. Нужен человек, который умеет три вещи: внятно сформулировать, что должна делать система; собрать промпт, который выжимает из готовой модели стабильный структурированный ответ; и аккуратно склеить чужие API так, чтобы они пережили реальные кривые данные. Это не профессия дата-сайентиста — скорее, инженерная насмотренность плюс дисциплина довести до прода. Я зашёл как маркетолог, который понимает код. И этого хватило.

Самым важным техническим решением оказался не выбор «самой мощной модели», а та маленькая сравнялка на трёх звонках. Полчаса замера — и всё встало на места. Будешь делать что-то подобное — меряй надёжность формата на своих данных раньше, чем споришь про IQ моделей.

По ощущениям: на «магию ИИ» ушло процентов двадцать времени, на «склеить и не уронить» — все восемьдесят. Промпт я собрал за день. А фейк-стерео, числовые ключи в JavaScript и модели, ломающие структуру, — на них и ушла настоящая работа. Если идёшь в подобный проект с ожиданием «ну там же просто промпт» — закладывай время на восемьдесят процентов, которые про водопровод.

И ещё одно. Я недооценил, как быстро частный кейс становится продуктом. Гибкость, заложенная «на всякий случай», — это то, что превратило разовый заказ в масштабируемый сервис. Если собираешь решение под одного клиента и видишь, что задача системная, — закладывай точки гибкости там, где её дешевле заложить сразу.

И ремарка, чтобы не подумали лишнего. Никаких автоматических санкций «балл F → штраф менеджеру» я делать не стал и не советую. Как только система из «помощника, который показывает, на что смотреть» превращается в «надзирателя, который наказывает» — доверие схлопывается, и менеджеры начинают играть против метрики. Это не техническое ограничение — продуктовое решение.

P.S. Мне правда интересно, кто что ловил на стыке распознавания речи и языковых моделей в проде. Диаризация, которая врёт; модели, которые ломают структуру; форматы аудио из ада — если у тебя были свои грабли, напиши в комментариях или в личку.

P.P.S. Если хочется самому пощупать ИИ и понять, что он умеет, — у меня на сайте лежит бесплатный курс Prompt.exe. 22 урока, без регистраций и «премиум-разблокировок» — заходишь и проходишь. А если по работе нужно собрать что-то похожее под свою задачу — пиши в личку, обсудим.

BUILD 2.0 · MSKPROMPT.EXE ↗