Перейти к основному содержимому
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 можно было бы обойти хорошо сформулированным вопросом; удаленное поле обойти нельзя.

Кто перезванивает, распознается по номеру

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

Передача диспетчеру, под рукой у того, кто разговаривает

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

Агент можно прерывать, и это настраивается цифрами

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

Чего не делает голосовой агент в перевозках

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

Traseul

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

01

Сначала определяем, на какой SIP-хост входят ваши номера

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

Вопросы

Что спрашивает человек из transport & logistică

Агент сам звонит курьеру или только отправляет ему сообщение?

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

Что происходит, если водитель не отвечает?

Попытка повторяется. По умолчанию три раза, с интервалом в две минуты, значения записаны в таблицу и в конфигурацию процесса. Если и в третий раз ответа нет, вызов остаётся в состоянии `no_answer` — отдельном от `failed`, которое означает, что у нас что-то сломалось. Именно из-за такого различия стоит использовать таблицу, а не журнал.

Может ли агент дать клиенту номер водителя?

Нет, и не потому, что мы ему это запретили. Контактные поля водителя рекурсивно удаляются из результата инструмента до того, как этот результат попадёт в модель. Номера диспетчерской остаются. То, что не попадает в модель, не может быть продиктовано, как бы искусно ни был сформулирован вопрос.

Может ли он сказать, где сейчас посылка?

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

Могу ли я сейчас позвонить на демонстрационный номер?

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

Говорит с клиентом по-русски и с диспетчером по-румынски?

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

Что происходит, когда клиент его перебивает?

Он останавливается. Семантическое обнаружение речи имеет порог 0,5, буфер 300 мс и 500 мс тишины, а прерывание по умолчанию разрешено; на другом маршруте событие прерывания передаётся в центр, чтобы остановить воспроизведение. Если агента нельзя перебить, это не проблема тона, а проблема конфигурации — и слишком длинных ответов.

Как долго хранятся записи звонков с водителями?

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

Заменяет диспетчера?

Нет. Оно берет на себя повторяющуюся часть — тот же вопрос, тот же звонок, тот же ответ — и оставляет ему исключения. Перевод на него по-прежнему выполняется кодом с клавиатуры, а в транспорте исключения часты: отказанный груз, неверный адрес, клиент, который не отвечает. Там нужен человек, и лучше, чтобы это был человек, который до этого не отвечал на сотню «где посылка».

Что вы хотели бы наладить?

Расскажите о своём процессе. Вместе определим, что стоит построить, что можно подключить и как проверим результат.

Давайте обсудим