Пронин
Назад к блогу

Как я на связке двух чужих API собрал сервис, который слушает звонки менеджеров и ставит им оценки

Не ML-инженер, а маркетолог: как склейка Yandex SpeechKit и OpenRouter выросла в сервис аудита звонков для языковой школы — а потом для целой франшизной сети.

19 июн. 2026 г.
TL;DR

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

Что это на самом деле: склейка двух API, а не обучение модели

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

И ещё честнее. Большую часть кода я вайб-кодил с агентом. Садился, объяснял задачу, смотрел, что он принёс, правил, гонял дальше. В 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. Ничего экзотического.

Распознавание — чужое, оценочная модель — чужая. Моя работа — склеить их так, чтобы на входе был кривой 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 урока, без регистраций и «премиум-разблокировок» — заходишь и проходишь. А если по работе нужно собрать что-то похожее под свою задачу — пиши в личку, обсудим.

© 2026 Александр Пронин · цены не офертаконфиденциальностьсоглашение
ЛентаFreeБизнесуКурс