Это просто на память - схема работы сервиса с аналитикой звуоквых файлов телефонных переговоров в компании через нейросети. Кому интересно подробности обращайтесь. Эта схема показывает работу сервиса по сбору, распознавание, LLM-анализ и веб-панель для работы менеджеров и операторов компании для анализа большого количества звонков от клиентов. Полный пайплайн за 15–45 минут.
⚡ Итоговая схема потока данных
📞 Звонок в Mango
→
🐍 fetch_mango_calls.py
→
📦 Карточка в БД (pending)
→
🎤 recognize_remote.py
→
📝 Транскрипт (done)
→
🧠 pipeline.py
→
✅ Анализ (done/skipped)
→
🖥️ Веб-панель
Задержка: от звонка до готовой карточки — 15–45 минут (три фазы cron по 15 мин).
1. Сбор звонков — fetch_mango_calls.py pull, каждые 15 мин
Это НЕ вебхук. Скрипт сам ходит в Mango API и тянет историю звонков.
🔹 Как работает (по шагам)
- Определяет период — берёт дату последнего звонка в БД (
MAX(start_time)) и тянет всё с начала того дня до текущего момента. Если БД пуста — с сегодняшнего дня.
- Запрос истории (двухшаговый):
stats/request → получает key
stats/result → получает CSV со всеми звонками за период
- Поля:
recording_id, start, finish, answer, from_number, to_number, line_number, entry_id и др.
- Группирует по entry_id — один звонок может быть несколькими строками (при переводе/переадресации).
- Определяет направление по
from_number первой (самой ранней) строки:
sip:... → исходящий (звонили мы)
- внешний номер → входящий (звонил клиент)
- Отсеивает внутренние перезвоны (филиал → филиал).
- Скачивает аудио — по
recording_id получает временную ссылку (302 → Location), качает MP3. При переводе — несколько сегментов, склеивает через ffmpeg.
- Сохраняет карточку в таблицу
calls (INSERT или UPDATE по entry_id).
📌 Статусы на этом этапе
- no_answer — недозвон (answer=0, клиент не взял трубку), аудио не качается
- no_audio — нет записи разговора
- pending — аудио скачано, ждёт распознавания
🧩 Ключевые функции и нюансы fetch_mango_calls.py
sign(json_str) — считает подпись: sha256(API_KEY + json_str + API_SALT)
fetch_history(date_from, date_to) — шаги 1+2 (с retry на 429)
parse_history(text) — парсит CSV в список строк
group_by_entry(rows) — группирует по entry_id
get_recording_url(recording_id) — запрос 2 (302 → Location)
download_audio(recording_id, dest) — запрос 3 (GET MP3)
build_card(entry_id, rows) — собирает карточку из строк
⚠️ Тонкие моменты
- Подпись считается от сырой json-строки — без пробелов и переносов, поэтому json строится программно (
json.dumps), а не вручную.
stats/result возвращает CSV, а не JSON — парсится по ;.
- Ссылка на аудио одноразовая — качаем сразу, не храним Location.
- Rate limit — при 429 скрипт ждёт и повторяет (backoff 10с × попытка).
entry_id и recording_id — base64 — декодируются в числа, по ним связываются карточка и аудио.
def sign(json_str: str) -> str:
return hashlib.sha256((API_KEY + json_str + API_SALT).encode()).hexdigest()
def fetch_history(date_from, date_to):
payload = {"from": date_from, "to": date_to}
json_str = json.dumps(payload, separators=(',', ':'))
signature = sign(json_str)
2. Распознавание — recognize_remote.py каждые 15 мин, фаза :05
- Берёт звонки со статусом pending + есть аудио-файл.
- Отправляет MP3 на whisper-сервер (
122.111.133.170:5000).
- Получает текст (транскрипт), сохраняет в
calls.transcript.
- 5 попыток с паузами, при неудаче → failed.
Статусы: done (распознано) / failed (ошибка).
3. Анализ — pipeline.py каждые 15 мин, фаза :10, лимит 10
Берёт звонки с транскриптом и без analysis_status. Полный цикл:
- Диаризация — LLM делит диалог на «Оператор» и «Клиент»
- Транслитерация — LLM чистит ошибки распознавания → чистый диалог + % додуманности
- Классификация — LLM определяет:
call_type (первичный/вторичный/внутренний/несостоявшийся)
direction_type (медицина/коррекционка/детсад/другое)
outgoing_type (подтверждение/оплата/перенос/обзвон — для исходящих)
dropped (слился ли клиент)
- Выжимка — LLM: запрос клиента, цитаты, договорённости, почему слился
- Сравнение со скриптом — LLM сверяет диалог с эталонным скриптом → оценка 1-10 + список ошибок (для исходящих — с категориями)
- Email — только для входящих первичных (на
schimarin@logoakademia.ru)
🔻 Каскад отсечения (экономия токенов, только входящие)
- Номер в списке сотрудников → «внутренний» (0 токенов)
- Транскрипт < 200 симв → «несостоявшийся» (0 токенов)
- Грубая классификация → если не «первичный» → skip
- Полный цикл только для первичных
Исходящие каскад НЕ проходят — всегда полный цикл.
Статусы: done (проанализирован) / skipped (отсечён) / error (сбой).
4. Отображение — веб-панель
Карточка в веб-панели (index.php → вкладка «Звонки») читает всё из таблицы calls:
- список звонков (пагинация, фильтры)
- модальное окно (
ajax_call.php) — полная карточка: тип, направление, подтип, оценка, ошибки, резюме, цитаты, договорённости, аудио, чистый диалог
📊 Отдельно — отчёты (report_outgoing.py)
Раз в неделю (пн 09:00) и раз в месяц (1-е число 09:00) собирает косяки исходящих звонков и шлёт план тренинга управляющему.