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

Рішення · Транспорт та логістика

Коли клієнт запитує, де посилка, агент не вгадує: дзвонить кур’єру, запитує його і повертається з відповіддю.

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

Вже збудованоMecanismul de dispecerizare e scris și migrat în platforma noastră vocală: tabela `courier_calls` cu șapte stări posibile, trei încercări implicite, pauză de 120.000 ms între ele și rezultatul apelului păstrat structurat, plus cinci funcții de server care o folosesc — inițierea apelului către curier, verificarea rezultatului, mătura pentru apelurile rămase agățate, identificarea celui care sună înapoi și webhookul de după apel. A doua implementare e filtrul de ieșire scris pentru un client din transport, care elimină recursiv câmpurile de contact ale șoferului din rezultatul uneltei, păstrând numerele dispeceratului. Rezerva, spusă înainte să întrebi: partea de telefon trece printr-o gazdă SIP, iar gazda prin care merg liniile noastre de test nu răspunde la data scrierii — nu-ți dăm un număr de demonstrație pe care nu l-am putea ridica în fața ta.

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

Ми побудували саме цей ланцюг, як механізм зі станами, а не як маркетингову функцію. Агент розмовляє з клієнтом. Коли йому потрібна відповідь, якої він не має, він викликає функцію, що запускає другий дзвінок — до кур’єра — пов’язаний із розмовою, з якої він вийшов. Той дзвінок має власний рядок у таблиці, зі статусом, що переходить через `calling`, `in_progress`, `retrying`, `completed`, `failed`, `no_answer`, `timeout`, із номером спроби, максимум трьома спробами та паузою у дві хвилини між ними. Результат записується структуровано: підсумок того, що сказав кур’єр, орієнтовний час, місцезнаходження.

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

А найважливіше обмеження — інфраструктурне, а не інтелектуальне: голосовий агент по телефону залежить від SIP-хоста між вашим оператором і платформою. Коли цей хост падає, дзвінки не йдуть, а ознака збою впізнавана — запит прострочений, ідентифікатор дзвінка порожній, тривалість нульова. Хост, через який проходять наші тестові лінії, не відповідає на дату написання цієї сторінки. У вебі голосовий маршрут перевіряється сьогодні; по телефону перший етап будь-якої роботи — встановити, на якому хості входять ваші номери.

Що входить

Що конкретно змінюється в транспорті та логістиці

Агент, який розмовляє з клієнтом, може запустити другий дзвінок

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

Дзвінок до кур’єра має стани, а не лише «подзвонили»

Сім станів, записаних як обмеження в базі даних: `calling`, `in_progress`, `retrying`, `completed`, `failed`, `no_answer`, `timeout`. Плюс номер поточної спроби. Різниця між «не відповів» і «система впала» видна в даних, а не здогадується з журналів — і це важливо, коли хтось завтра питає, чому клієнт не отримав відповідь.

Повторні спроби налаштовані, а не імпровізовані

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

Результат дзвінка повертається структуровано, а не як оповідь

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

Номер водія не можна продиктувати, бо він не доходить до моделі

Фільтр виходу рекурсивно проходить результат інструмента і вилучає поля контакту водія, зберігаючи номери диспетчерської. Це написано в коді, застосовано точково до інтеграції, де виникає проблема. Причина проста: інтерфейс постачальника повертав номери водіїв у відповіді, а політика компанії забороняла агенту передавати їх клієнтам. Інструкцію в prompt можна було б обійти добре сформульованим запитанням; вилучене поле обійти неможливо.

Того, хто передзвонює, розпізнають за номером

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

Передача до диспетчера, під рукою того, хто розмовляє

Сліпа передача через `##` і передача з допомогою через `*2`, з контекстом приземлення в плані дзвінків, який розрізняє внутрішні чотирицифрові розширення та зовнішні номери. Коли клієнт наполегливо просить людину — а в транспорті просить, бо його вантаж запізнюється — передача не повинна проходити через меню.

Агента можна перервати, і це регулюється цифрами

На шляху OpenAI Realtime: семантичне виявлення мовлення з порогом 0,5, подушкою 300 ms, 500 ms тиші, переривання дозволено за замовчуванням. На шляху ElevenLabs, подія переривання передається центральній системі, щоб зупинити відтворення. `turn_timeout` встановлюється для кожного агента окремо. У транспортному дзвінку, де співрозмовник часто перебуває в шумній кабіні, ці пороги — половина сприйманої якості.

Чого не робить голосовий агент у транспорті

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

Traseul

Як запит проходить через систему.

01

Спершу визначаємо, на який SIP host заходять ваші номери

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

02

Ми пишемо сценарій диспетчеризації, з його винятками

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

03

Підключаємо джерело замовлень і ставимо фільтри на виході

Контекст замовлення приходить із вашої системи, а перед тим як результат інструмента потрапить до моделі, ми проходимося по списку полів, які не мають права виходити назовні. Ми надаємо інтеграцію, письмовий список відфільтрованих полів і доказ, що фільтр застосовується до результату, а не до інструкцій.

04

Запускаємо на малому обсязі, з транскриптами, прочитаними людиною

Починаємо на частині дзвінків, із широкою конфігурацією переведення на диспетчера, і читаємо транскрипти. Звідти налаштовуються пороги переривання, `turn_timeout`, формулювання запитань і поріг, після якого агент здається. Ми надаємо звіт про ці дзвінки з рішеннями щодо налаштування, а не лише запущеного агента.

1Clientul întreabă2agentul cheamăfuncția de apel3apel către curier, custare proprie4până la treiîncercări, la douăminute5rezultat structurat(rezumat, timpestimat, poziție)6răspuns la client, cunumărul șoferuluifiltrat din drum
Traseul, în 6 pași

Дані

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

Правила різняться від однієї галузі до іншої. Ось ті, що застосовуються в транспорті та логістиці.

Номер водія — це персональні дані людини, а не технічне поле
Кур’єр або водій-підрядник — це фізична особа. Його номер, його позиція і запис його голосу — це його дані. Тому вихідний фільтр — не забаганка безпеки, а мінімізація, застосована до джерела: поле не потрапляє до моделі, отже не може потрапити в розмову і не може потрапити в транскрипт.
Що зберігається з дзвінка
Номер того, хто телефонує, і номер того, кому телефонують, момент, тривалість, результат, аудіозапис, транскрипт і аналіз після дзвінка. Для неприйнятих дзвінків є номер, момент і причина відсутності відповіді — аудіо немає, бо воно не було створене. Усе це пов’язано з робочим простором вашої компанії; спільного сховища між клієнтами немає.
Повідомлення на початку дзвінка — це ваше рішення, а не налаштування
Що співрозмовник говорить із автоматизованою системою, що дзвінок записується і на якій підставі — визначається разом із вами і входить до сценарію в обидва боки: і до клієнта, і до кур’єра. Кур’єр, якому телефонує агент, має знати, з чим говорить, так само, як і клієнт.
Видалення на запит є; автоматичного видалення за строком ще ні
Ми кажемо це так, як є, бо це різниця між обіцянкою і функцією. Видалення на запит реалізовано: окрема функція видаляє об’єкти зі сховища файлів, викликає процедуру видалення з бази і інвалідовує сесії. Налаштовуване збереження для кожного робочого простору не реалізовано — воно є в документі проєктування, але не в жодній міграції. Поки цього не буде, видалення за строком виконується через процедуру.
Де зберігаються дані
PostgreSQL через Supabase на власній інфраструктурі, з версіонованими міграціями, частина з яких активує ізоляцію на рядок. Файли — записи, документи знань, зразки голосу — зберігаються в окремих сховищах, із шляхом, що починається з ідентифікатора робочого простору.

Випадок

Дзвінок, що має стан, а не лише результат

Ситуація

У потоці доставки запитання «де моє замовлення» не має відповіді в жодній системі: відповідь — у кур’єра. Проста версія — агент пише кур’єру повідомлення і сподівається — провалюється на першому кур’єрі, який кермує і не дивиться в телефон.

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

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

Що вийшло

Можна відповісти, будь-коли й із даних, на запитання, які диспетчерська отримує щодня: чи дзвонили, скільки разів, що відповіли, чому не дзвонили. Пропущений дзвінок більше не залишається заблокованим у проміжному стані, бо є кому його прибрати.

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

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

Питання

Що запитує хтось із транспорту та логістики

Агент сам дзвонить кур’єру чи лише надсилає йому повідомлення?

Дзвонить. Є серверна функція, яка запускає дзвінок кур’єру, пов’язаний із розмовою, з якої він пішов, а контекст замовлення передається як структура. Дзвінок має власний рядок у таблиці, зі станом, номером спроби та результатом — тож будь-коли можна відповісти на запитання «чи дзвонили, і що він сказав».

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

Спробу повторюють. За замовчуванням тричі, з інтервалом у дві хвилини, значення записані в таблиці та в конфігурації процесу. Якщо і втретє немає відповіді, дзвінок залишається у стані `no_answer` — окремому від `failed`, що означає: щось упало у нас. Саме через цю відмінність варто використовувати таблицю, а не журнал.

Чи може агент дати клієнту номер водія?

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

Чи може сказати, де зараз посилка?

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

Можу я зараз зателефонувати на демонстраційний номер?

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

Він говорить російською з клієнтом і румунською з диспетчером?

Конфігурація агента підтримує 32 мови, а румунська є мовою за замовчуванням для нового агента. Мова задається для кожного агента окремо, тож агент, який говорить із клієнтом, і той, що дзвонить кур’єру, можуть бути налаштовані по-різному. Що слід знати: для синтезу голосу іншими мовами, крім англійської, типовою є швидка модель, а варіант із кращою якістю помітно повільніший — компроміс обирають свідомо.

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

Він зупиняється. Семантичне виявлення мовлення має поріг 0,5, буфер 300 мс і 500 мс тиші, а перебивання дозволене за замовчуванням; на іншому маршруті подія перебивання передається центральній системі, щоб припинити відтворення. Якщо агента не можна перебити, це не проблема тону, а конфігурації — і занадто довгих відповідей.

Як довго зберігаються записи дзвінків із водіями?

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

Замінює диспетчера?

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

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

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

Поговорімо