Перейти до основного вмісту
megapromotingПоговорімо

Експертиза · Телефонія та контакт-центр

Шар телефонії між вашим оператором і тим, хто відповідає — людиною чи агентом.

Ми будуємо центральний вузол: SIP-транки, правила маршрутизації за графіком, IVR-меню, зчитувані з бази, черги очікування, переадресацію на людину, запис і аналіз дзвінків. Дзвінок стає записом із транскриптом і резюме, а не спогадом.

Вже збудованоTrei implementări proprii, citate mai jos. (1) Stratul Asterisk din platforma Kallina: 5.522 de linii de configurație de dialplan, scripturi AGI și punți audio în `asterisk/`, plus șabloane de trunk generate automat per număr. (2) `asterisk-manager` — un serviciu separat care administrează cozile, trunkurile și fluxul RTP prin interfața de management a centralei. (3) Middleware-ul pentru centrala virtuală a unui operator, care a rulat în producție: commitul de reconciliere din 14 iulie 2026 aduce în depozit cod care rulase pe server și era cu circa 40 de zile înaintea git-ului. Rezerva, spusă înainte să întrebi: gazda SIP prin care trec liniile noastre de test nu răspunde — verificat azi, 06.09.2026, fără răspuns la ping și fără răspuns pe HTTP. Nu prezentăm un număr de demonstrație pe care să suni acum, pentru că nu l-am putea ridica în fața ta.

Між вашим телефонним оператором і тим, хто фактично відповідає — людиною чи голосовим агентом — має існувати шар, який ухвалює рішення. Хто приймає дзвінок о 23:40. Що відбувається, якщо ніхто не відповідає. Куди йде дзвінок, коли клієнт просить «людину». Що лишається від розмови після її завершення. Цей шар — це АТС, і ми будуємо його так, щоб він був власністю бізнесу, а не голосового постачальника: якщо завтра ви зміните движок агента, правила маршрутизації, черги й історія залишаться у вас.

Конкретно, що ми побудували: дванадцять файлів dialplan — продакшен, IVR, черга, переадресація, voicemail, перенаправлення, вихідні дзвінки з агентом — чотири скрипти AGI в Python і два аудіомости, разом 5.522 рядки. Двигун IVR не має меню, записаних у файлі: він читає їх із бази через скрипт AGI і повертає одне з шести рішень — до агента, до іншого меню, до черги, переадресацію на зовнішній номер, фінальне повідомлення або завершення. Черги називаються за своїм ідентифікатором і не мають членів, записаних у конфігурації: їх додають і вилучають ззовні, через інтерфейс керування, що означає, що оператор може увійти або вийти з черги без перезапуску АТС.

Маршрутизація має справжні правила, а не одне «дзвонити сюди»: години роботи й часовий пояс, нічний графік на кшталт 22:00 → 06:00, і зіставлення за пріоритетом — SIP-заголовок `Diversion`, який містить оригінальний номер, з якого переадресовано дзвінок, потім власний номер, потім резервне правило. Trunk-и генеруються для кожного номера, з назвою, побудованою з постачальника й номера, функцією конфігурації — не записуються вручну для кожного нового рядка.

Те, що виходить з дзвінка, так само важливе, як і сам дзвінок. Для лінії, пов’язаної з віртуальною АТС оператора, ми побудували middleware на Python, який опитує список записів щохвилини, з широкою звіркою кожні шість годин, щоб мережевий збій нічого не втратив, і перевіркою кожні три хвилини пропущених дзвінків — у них немає запису, інакше вони були б повністю невидимі. Кожен запис завантажується, транскрибується, аналізується і віддзеркалюється в аналітиці дзвінків, а пропущений дзвінок стає сповіщенням. Ідемпотентність тримається в наборі Redis, щоб один і той самий запис не оброблявся двічі.

Що входить

Робота, за складовими

Номер потрапляє на згенерований trunk, а не записаний вручну

Шаблони trunk-ів параметризовані й заповнюються функцією конфігурації, з назвою, сформованою з постачальника й номера. У шаблоні записані транспорт, дозволені кодеки, обробка NAT, режим DTMF та автентифікація. Практичний наслідок: десятий номер підключається так само, як перший, а відмінності між операторами зосереджені в одному місці.

АТС вирішує, куди йде дзвінок, за записаними правилами

Години роботи й часовий пояс, нічний графік на кшталт 22:00 → 06:00, зіставлення за пріоритетом: SIP-заголовок `Diversion` (номер, з якого було переадресовано), потім власний номер, потім резервне правило. Пошук правила виконується через AGI-скрипт, який запитує платформу під час дзвінка, із власним тайм-аутом — тож правило, змінене в інтерфейсі, застосовується до наступного дзвінка, без перезапуску.

Дзвінок потрапляє до агента, у чергу або до людини

До голосового агента аудіо проходить через міст, який конвертує кодек в обидва боки, `g711_ulaw` ↔ `PCM16`. До людей дзвінок потрапляє в чергу, якою керують зовні. А переадресація доступна тому, хто говорить: сліпий трансфер через `##` і assisted transfer через `*2`, причому дзвінок потрапляє в контекст, написаний саме для цього, який розрізняє внутрішні розширення з чотирьох цифр і зовнішні номери.

Меню IVR читається з бази, а не з файла

Двигун IVR отримує ідентифікатор меню й читає його з бази через AGI-скрипт. Результат — одне з шести рішень: до агента, до іншого меню (рекурсивно), до черги, зовнішній трансфер, фінальне повідомлення, завершення. Повідомлення можуть бути синтезованими або файлами аудіо, записаними заздалегідь. Змінити меню означає змінити один рядок у базі, а не редагувати файл конфігурації на сервері.

Черги з динамічними членами

Кожна черга має назву, похідну від свого ідентифікатора. Члени не зберігаються в конфігурації: їх додають і прибирають під час роботи через інтерфейс керування АТС, а одночасне розподілення на кілька вільних місць увімкнене. Практично, оператор входить у чергу або виходить із неї без перезапуску АТС і без впливу на дзвінки в очікуванні.

Диспетчеризація до людей на місцях

Агент може сам подзвонити комусь зовні й повернутися з відповіддю. Механізм реалізований із власною таблицею та явними станами — дзвінок, у процесі, повторити спробу, завершено, невдало, без відповіді, прострочено — з максимумом трьох спроб і паузою дві хвилини між ними, а результат (включно з очікуваним часом прибуття) повертається структуровано в розмову, яка його запросила. Це не концепт: є п’ять окремих серверних функцій плюс прибирання дзвінків, що залишилися висіти.

Дзвінки стають даними, включно з тими, на які ніхто не відповів

Запланований процес читає список записів щохвилини, з широкою звіркою кожні шість годин, щоб збій нічого не втратив. Запис завантажується у WAV, транскрибується, аналізується і віддзеркалюється в аналітиці дзвінків разом з аудіо. Окремо, кожні три хвилини читаються неприйняті дзвінки — у них немає запису, інакше їх не було б ніде, — і вони стають сповіщенням. Пропущений дзвінок — це втрачений клієнт; зробити його видимим — це найдешевше покращення в кол-центрі.

Гальма щодо API оператора

Клієнт, який спілкується з АТС оператора, має власний обмежувач швидкості, з bucket of tokens, і явну обробку помилок. Це не теоретична пересторога: middleware, який опитує щохвилини й звіряє кожні шість годин, без гальма може вдаритися в ліміт оператора і бути заблокованим саме тоді, коли він потрібен.

Як це виглядає

Шлях, крок за кроком.

01

Інвентар ліній перед будь-якою конфігурацією

Які номери існують, в якого оператора, хто сьогодні відповідає за кожен, за яким графіком, і що відбувається зараз, коли ніхто не відповідає. Це здається бюрократією; саме це запобігає сюрпризам. З нашого досвіду, у парку номерів майже завжди є лінії, щодо яких база каже одне, а централька — інше, і вони виявляються не на запуску, а зараз.

02

Trunk, потім тест в обидва боки, окремо

Номер заходить на trunk, а вхідні та вихідні дзвінки тестуються як дві окремі речі, бо ламаються вони окремо. Ми задокументували випадок, коли вхідні дзвінки працювали бездоганно днями, тоді як усі вихідні дзвінки відхилялися централькою оператора з `403 Forbidden`, без жодної зміни з нашого боку. Один тест «подзвонило і спрацювало» цього не покриває.

03

Dialplan: маршрутизація, IVR, черга, переадресація, voicemail

Правила графіка та пріоритетів, меню, черги й шляхи переадресації записуються як dialplan і як рядки в базі, а не як усна домовленість. Наприкінці ви точно знаєте, що відбувається з дзвінком о 23:40, у суботу, коли агент не розуміє запит.

04

Дзвінки потрапляють у ваші системи

Результат дзвінка — transcript, резюме, sentiment, адреса запису, тривалість — передається далі через підписаний webhook, а організація ідентифікується за номером телефону. Там, де є CRM, дзвінок прив’язується до картки і може автоматично змінювати стан; про цю частину ми пишемо на сторінці CRM та автоматизації продажів.

05

Безперервність обговорюють на початку, а не після першого збою

Централька на одному хості — це одна точка відмови, і ми пережили це дослівно: коли хост не відповідає, усі лінії, що проходять через нього, падають разом із ним, незалежно від того, наскільки добре налаштований агент. Ознака збою чітка — дзвінок повертає „request timed out” з порожнім ідентифікатором SIP-дзвінка, тобто SIP-дзвінок ніколи не встановився. Саме тому в реальному проєкті питання «що відбувається, коли падає хост» ставлять і бюджетують на початку: другий хост, моніторинг, який дзвонить людині, і резервний шлях на звичайні номери.

Ce vede utilizatorulAplicațiaRețea și securitateGăzduire și dateFIECARE STRAT, ALES ȘI EXPLICAT
Straturile unei linii telefonice: trunkul operatorului, centrala cu regulile de rutare, puntea audio către agent, coada și transferul către om, apoi înregistrarea, transcrierea și analiza.

Дані

Чого торкаємося, де воно лежить і скільки лишається

Запитання, які ставить кожен, у кого є відповідальний за захист даних, — поставлені тут раніше, ніж їх поставить він.

Куди потрапляє облік дзвінків
Облік дзвінків центральки сьогодні записується локально, у форматі CSV, на хості центральки; запис у базу PostgreSQL підготовлений у конфігурації, але залишається вимкненим. Ми це кажемо, бо це має прямий наслідок: якщо вам потрібні звіти по дзвінках поза централькою, увімкнення обліку в базі — це робота, яку треба зробити, а не прапорець, який треба поставити. Transcript-и та аналізи зберігаються окремо, на платформі, прив’язані до вашого робочого простору.
Що зберігається з дзвінка
Номер абонента і номер, на який дзвонили, момент, тривалість, результат, аудіозапис, transcript і аналіз. Для неприйнятих дзвінків є номер, момент і причина відсутності відповіді — аудіо немає, бо його не було. Запис завантажується у WAV на 8 kHz, mono, і конвертується в стиснений формат для передачі людям.
Ідемпотентність, щоб дані не дублювалися
Кожен оброблений запис позначається в наборі Redis. Широка звірка може перечитувати ту саму часову вікно без повторної відправки чогось. Це деталь, яка відрізняє систему, яку можна спокійно перезапустити, від тієї, що при кожному перезапуску знову надсилає всі вчорашні сповіщення.
Хто може прослухати запис
Дані викликів пов’язані з робочим простором бізнесу, а доступ проходить через автентифікацію. Немає спільного сховища між клієнтами і немає доступу «з платформи» без ідентичності. Хто саме з вашої команди має право слухати, визначається під час впровадження, не за замовчуванням.
Строк зберігання та повідомлення про запис — це ваші рішення
Скільки зберігаються аудіо, транскрипт і облік викликів, і що говориться на початку розмови про те, що вона записується, — це рішення оператора даних, тобто ваше. Видалення на вимогу реалізовано в платформі; автоматичне видалення після строку сьогодні робиться через процедуру, а не через годинник, тож якщо вам потрібне автоматичне, це входить як робота в проєкт.

Випадок

Довгі виклики зникали. Короткі — ні.

Ситуація

У потоці аналізу розмов частина викликів потрапляла в аналіз, а частина — ні. Шаблон і підказав причину: бракувало саме довгих викликів. Дефект, який звітують як «іноді не працює» і помилково шукають у транскрипції.

Що ми збудували

Було два шляхи транскрипції, і обидва падали з різних причин. Мультимодальний шлях надсилає весь аудіофайл, закодований у тілі запиту; понад певний розмір ліміт тіла проміжного сервера відповідав 413. Резервний шлях вимагав модель транскрипції, яку ключ доступу вже не дозволяв, тож відповідав 403. Обидва завершувалися помилкою, процес повідомляв, що всі моделі транскрипції зазнали невдачі, і виклик відхилявся. Короткі виклики залишалися нижче ліміту тіла, тож проходили — звідси й шаблон. Виправлення було таким: мультимодальний шлях пропускається для WAV-файлів понад 12 MB (поріг налаштовується з середовища), повторно використовується вже обчислений транскрипт замість повторної транскрипції, а транскрипція як останній засіб була перенесена на дозволену модель.

Що вийшло

Перевірено на записі, що залишився заблокованим: розмова тривалістю 21 хвилину, WAV-файл 41 MB — мультимодальний шлях обійдено, транскрипт взято, аналіз згенеровано, рядок і аудіо потрапили в аналіз викликів, нуль збоїв. Згодом звірка підтвердила, що інших заблокованих записів більше немає.

Чого цей випадок не каже

Поріг 12 MB — це властивість проміжного сервера, а не виклику. Коли змінюється шлюз, ключ або модель, поріг потрібно перевіряти ще раз — тому ми зробили його налаштовуваним із середовища, а не записаним у коді.

Питання

Про що нас питають, перш ніж зателефонувати

У вас є номер, на який я можу зараз подзвонити, щоб почути агента?

Не сьогодні, і ми воліємо сказати це, ніж дати номер, який дзвонить у порожнечу. Частину агента можна прослухати на сторінці, одразу. Телефонна частина залежить від SIP-хоста, а хост, через який проходять наші тестові лінії, на дату написання не відповідає — перевірено сьогодні, без відповіді на ping і без відповіді по HTTP. Для вашого проєкту телефонія піднімається на виділеному для проєкту хості, а не на тестовому.

Чому власна централь і не напряму постачальник голосового агента?

Тому що правила маршрутизації, черги, переадресація, voicemail і історія викликів належать бізнесу, а не постачальнику голосу. З власною централью ви можете змінити мотор агента, не переписуючи, як поводиться ваш телефон, можете спрямувати один і той самий виклик то до агента, то до людини, і можете тримати облік викликів у себе. Без неї ви прив’язані до того, що вирішить показати постачальник.

Що відбувається, якщо сервер центральі падає?

Падають усі лінії, що проходять через нього. Це не гіпотеза: це те, що пережили ми, і сигнатура легко впізнавана — виклик повертає «request timed out» з порожнім ідентифікатором SIP-виклику, тож це не вина агента, номера чи prompt. Висновок, який ми зробили і який тепер закладаємо в кожен проєкт: безперервність — це не функція центральі, а рішення архітектури й бюджету, ухвалене на початку. Це вирішується другою хост-машиною та резервним маршрутом на звичайні номери, а не налаштуванням.

Чи вихідні виклики працюють надійно, якщо вхідні працюють?

Ні. Це дві різні речі, і оператор може обробляти їх по-різному. У нас задокументовано випадок, коли з нашою незмінною конфігурацією та з працюючими вхідними викликами централь оператора почала відхиляти всі вихідні виклики з `403 Forbidden` — доказом, що проблема була в оператора, стало те, що другий обліковий запис, зі структурно ідентичною конфігурацією, продовжував дзвонити назовні. Тому ми не обіцяємо кампанію вихідних викликів до успішного тесту виходу на ваш номер.

Чи можете ви працювати з віртуальною централью, яку дає мені оператор?

Так, і ми це робили: ми написали middleware на Python поверх API віртуальної центральі оператора, з власним клієнтом, обмежувачем частоти, завантаженням записів, транскрипцією, аналізом і сповіщенням, запущений у продакшені. Що треба знати заздалегідь: доступ до розширеного API часто є окремою послугою, замовленою окремо, а креденціали можуть не спрацювати з першої спроби — у нас цикл уточнення специфікації та скидання пароля з оператором тривав місяці, при тому що сервіс уже був виставлений у рахунку. Тому в пропозиції, що залежить від API оператора, ми явно ставимо умову: робота починається після того, як автентифікацію було продемонстровано, а не після того, як її було обіцяно.

Чи записуються дзвінки і хто може їх слухати?

Їх можна записувати, транскрибувати й аналізувати. Вони залишаються прив’язаними до робочого простору вашого бізнесу, а доступ потребує автентифікації — спільного сховища між клієнтами немає. Хто з вашої команди має право на прослуховування, визначається під час впровадження. Повідомлення співрозмовника і підстава запису — це ваші рішення як оператора даних; ми записуємо це в сценарії, не припускаємо.

Що відбувається з дзвінками, на які ніхто не відповідає?

Це найгірше оброблюваний випадок у більшості компаній, бо не лишає сліду: немає запису, немає транскрипту, є лише в обліку дзвінків. У нас процес читає облік кожні три хвилини, визначає неприйняті дзвінки й перетворює їх на сповіщення та видимий рядок. Щоб було зрозуміло, від чого це залежить: ми бачимо їх лише настільки, наскільки оператор показує їх у своєму обліку.

Чи можете робити меню типу «натисніть 1 для...»?

Так, і меню зберігаються в базі, а не у файлах на сервері. Двигун читає конфігурацію меню під час дзвінка і визначає один із шести шляхів: до агента, до іншого меню, до черги, переадресація на зовнішній номер, фінальне повідомлення або завершення. Повідомлення можуть бути синтезовані або записані заздалегідь. Практично, зміна меню не потребує втручання в телефонну станцію.

Як дізнатися, чи дзвінок загубився дорогою, між телефонною станцією та аналізом?

Через звірку, і це частина, яку ми будуємо явно. Швидкий запит має вікно в кілька десятків годин, а поверх нього кожні шість годин запускається звірка на вікні в один тиждень, яка перечитує кілька сторінок результатів. Кожен оброблений запис позначається, тож повторне читання нічого не дублює. Без звірки збій на годину означає постійну прогалину в даних — і ніхто цього не помічає.

На чому ґрунтуються твердження вище (22 джерел)

22 з них — це код і файли з наших репозиторіїв. Ми не публікуємо їхні назви чи номери рядків: разом, на одній сторінці, це надто точно описало б, як побудовані системи, які належать не лише нам. Ми проходимо їх разом із вами, у репозиторії, за запитом — перевірка залишається можливою, просто вона відбувається в розмові.

Що вам хотілося б налагодити?

Розкажіть про свій процес. Разом визначимо, що варто збудувати, що можна підключити і як перевіримо результат.

Поговорімо