Голосовые вызовы с преобразованием текста в речь (TTS) с использованием Python на AWS Lambda и Vonage

Источник: Vonage API Developer•

Голосовые вызовы с преобразованием текста в речь (TTS) с использованием Python на AWS Lambda и Vonage

Используйте Python, AWS Lambda и Vonage Voice API для совершения вызовов с преобразованием текста в речь, запросами PIN-кода, логикой повторных попыток и обратным вызовом (webhook) на ваш сервер.

Некоторые сценарии Vonage Voice API требуют нескольких циклов обмена данными: вы совершаете вызов, Vonage обращается к вашему вебхуку ответа, вы ожидаете ввода с клавиатуры, воспроизводите последующее сообщение и собираете результат. Для сценария проверки PIN-кода это четыре или пять взаимодействий с вебхуками, прежде чем вы узнаете, ввел ли пользователь правильный код.

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

Вы развернете эту функцию здесь. К концу этого руководства у вас будет приложение на Python в AWS Lambda, которое:

  • Звонит на номер и воспроизводит настраиваемое приветственное сообщение.

Звонит на номер и воспроизводит настраиваемое приветственное сообщение.

  • Ожидает, пока получатель введет PIN-код.

Ожидает, пока получатель введет PIN-код.

  • Воспроизводит сообщение об ошибке и повторяет попытку при неверном вводе до трех раз.

Воспроизводит сообщение об ошибке и повторяет попытку при неверном вводе до трех раз.

  • Завершает вызов без уведомления после трех неудачных попыток.

Завершает вызов без уведомления после трех неудачных попыток.

  • Отправляет обратный вызов на ваш вебхук с идентификатором транзакции и результатом ввода PIN-кода после завершения вызова.

Отправляет обратный вызов на ваш вебхук с идентификатором транзакции и результатом ввода PIN-кода после завершения вызова.

Это заменяет устаревший TTS Prompt API на поддерживаемый бессерверный подход, который вы полностью контролируете.

Кратко: посмотрите готовый пример в репозитории Vonage Community на GitHub.

Кратко: посмотрите готовый пример в репозитории Vonage Community на GitHub.

Перед началом

Что такое AWS Lambda?

AWS Lambda — это бессерверный вычислительный сервис от Amazon Web Services. Вы пишете функцию, развертываете ее, и AWS запускает ее по требованию в ответ на HTTP-запрос или событие. Нет серверов, которые нужно подготавливать, обновлять или масштабировать. Lambda берет все это на себя, и вы платите только за фактически использованное время вычислений.

Это руководство затрагивает четыре сервиса AWS:

  • Lambda: функция, которая выполняет логику вашего вызова.

Lambda: функция, которая выполняет логику вашего вызова.

  • API Gateway: HTTP-уровень, который предоставляет вашу функцию Lambda как набор URL-адресов, доступных для Vonage.

API Gateway: HTTP-уровень, который предоставляет вашу функцию Lambda как набор URL-адресов, доступных для Vonage.

  • DynamoDB: хранилище «ключ-значение», которое сохраняет состояние вызова между вызовами Lambda.

DynamoDB: хранилище «ключ-значение», которое сохраняет состояние вызова между вызовами Lambda.

  • IAM: политики разрешений, которые позволяют Lambda читать и записывать данные в DynamoDB и записывать логи в CloudWatch.

IAM: политики разрешений, которые позволяют Lambda читать и записывать данные в DynamoDB и записывать логи в CloudWatch.

Chalice, используемый здесь фреймворк, создает и связывает все четыре сервиса автоматически при выполнении команды chalice deploy.

Как выглядит успешная реализация

Вот как выглядят два сценария в сравнении.

Без Lambda ваш сервер приложений должен участвовать в каждом шаге вызова:

  • Ваш сервер совершает вызов через Vonage API.

Ваш сервер совершает вызов через Vonage API.

  • Vonage обращается к вебхуку ответа вашего сервера. Ваш сервер возвращает NCCO.

Vonage обращается к вебхуку ответа вашего сервера. Ваш сервер возвращает NCCO.

  • Vonage обращается к вебхуку ввода вашего сервера с цифрами PIN-кода. Ваш сервер проверяет PIN-код.

Vonage обращается к вебхуку ввода вашего сервера с цифрами PIN-кода. Ваш сервер проверяет PIN-код.

  • Если PIN-код неверный, ваш сервер возвращает новый NCCO для повторной попытки и ждет следующего вебхука ввода.

Если PIN-код неверный, ваш сервер возвращает новый NCCO для повторной попытки и ждет следующего вебхука ввода.

  • После трех попыток ваш сервер записывает результат и переходит дальше.

После трех попыток ваш сервер записывает результат и переходит дальше.

Ваше основное приложение заблокировано на обработке трафика вебхуков в течение всего времени вызова, и вам необходимо управлять состоянием повторных попыток в памяти или базе данных на своей стороне.

С Lambda ваш сервер участвует только в начале и в конце:

  • Ваш сервер отправляет один POST-запрос на эндпоинт Lambda /call с номером, PIN-кодом и URL-адресом обратного вызова.

Ваш сервер отправляет один POST-запрос на эндпоинт Lambda /call с номером, PIN-кодом и URL-адресом обратного вызова.

  • Lambda совершает вызов и самостоятельно обрабатывает каждый вебхук Vonage: ответ, ввод, повторную попытку и завершение вызова.

Lambda совершает вызов и самостоятельно обрабатывает каждый вебхук Vonage: ответ, ввод, повторную попытку и завершение вызова.

  • Как только вызов достигает терминального состояния, Lambda отправляет один обратный вызов на ваш сервер с идентификатором транзакции и результатом (ok, failed или error).

Как только вызов достигает терминального состояния, Lambda отправляет один обратный вызов на ваш сервер с идентификатором транзакции и результатом (ok, failed или error).

Ваш сервер отправляет один запрос и ждет один обратный вызов. Lambda и DynamoDB обрабатывают все управление состоянием и повторные попытки между ними.

Предварительные требования

Перед началом убедитесь, что у вас есть следующее:

  • Учетная запись AWS. Это приложение работает в рамках бесплатного уровня AWS Lambda.

Учетная запись AWS. Это приложение работает в рамках бесплатного уровня AWS Lambda.

AWS CLI (руководство по установке), установленный и настроенный с вашими учетными данными.

  • Chalice (), установленный на вашем компьютере. Установите его с помощью:

Chalice (), установленный на вашем компьютере. Установите его с помощью:

  • Учетная запись Vonage API. Зарегистрируйтесь бесплатно, если у вас ее нет. Вам понадобятся ваш API key, API secret и приложение Vonage с закрытым ключом.

Учетная запись Vonage API. Зарегистрируйтесь бесплатно, если у вас ее нет. Вам понадобятся ваш API key, API secret и приложение Vonage с закрытым ключом.

  • Приложение Vonage с включенными функциями Voice, а закрытый ключ сохранен локально как private.key. Запишите идентификатор приложения (Application ID).

Приложение Vonage с включенными функциями Voice, а закрытый ключ сохранен локально как private.key. Запишите идентификатор приложения (Application ID).

  • Виртуальный номер Vonage, привязанный к вашему приложению. Вы можете купить его в разделе Numbers > Buy Numbers на панели управления Vonage.

Виртуальный номер Vonage, привязанный к вашему приложению. Вы можете купить его в разделе Numbers > Buy Numbers на панели управления Vonage.

Примечание: это руководство было протестировано с Python 3.11, Chalice 1.33, AWS CLI 2.x и версией 4.x Vonage Python SDK. Версия 4 SDK была полностью переписана и несовместима с кодом v3. Если вы работаете по более старому руководству, см. v3 to v4 migration guide.

Развертывание и настройка

Развертывание функции

Клонируйте репозиторий на свой локальный компьютер:

Перейдите в папку проекта:

Разверните функцию в своей учетной записи AWS:

Chalice предложит вам подтвердить политику выполнения IAM. Просмотрите перечисленные разрешения и введите y для продолжения:

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

Настройка DynamoDB

Функция использует AWS DynamoDB для хранения состояния вызова между взаимодействиями. Каждый вебхук, отправляемый Vonage, является отдельным вызовом Lambda без памяти о предыдущем, поэтому PIN-код, сообщения и счетчик попыток должны находиться где-то вне функции.

После первого развертывания создайте необходимую таблицу, выполнив HTTP GET-запрос к эндпоинту /setup:

Обратите внимание, что URL chalice уже заканчивается косой чертой, поэтому перед setup вторая не нужна. Вам нужно сделать это только один раз: эндпоинт идемпотентен, и повторный вызов просто вернет {"result": "already exists"}.

Инициирование вызова

Вызов функции

После развертывания функции инициируйте вызов, отправив HTTP POST-запрос на <your-base-url>call со следующими параметрами:

Параметр

Значение

Пример

Номер для вызова в формате E.164, без знака + в начале

14155550100

from

Номер Vonage в вашей учетной записи, используемый в качестве идентификатора вызывающего абонента

14155550101

text

Приветственное сообщение, которое проигрывается получателю

"Введите ваш PIN-код"

pin_code

PIN-код, который должен ввести получатель (только цифры, максимум 20)

1234

callback

URL, на который будет отправлен результат

https://example.com/callback

callback_method

HTTP-метод, используемый для обратного вызова

GET или POST

bye_text

Сообщение, которое проигрывается при успешном вводе PIN-кода

"Спасибо, до свидания"

failed_text

Сообщение, которое проигрывается при неверном вводе PIN-кода

"Неверно, попробуйте еще раз"

Аутентификация

Функция Lambda не хранит ваши учетные данные Vonage. Вы передаете их с каждым запросом, и они используются только для этого конкретного вызова.

У вас есть два варианта аутентификации: передать закрытый ключ напрямую в cURL-запросе или сгенерировать JWT и отправить его в качестве Bearer-токена. Эндпоинт /call принимает тела запросов как в формате form-encoded, так и в формате JSON, поэтому оба стиля работают с одним и тем же маршрутом. Рекомендуется использовать подход с JWT. Ваш закрытый ключ никогда не покидает ваш компьютер, а токен, который вы отправляете вместо него, является кратковременным и ограничен рамками одного приложения.

cURL (аутентификация через закрытый ключ)

Перед запуском замените URL на свой собственный базовый URL:

Важно: Никогда не передавайте и не добавляйте файл private.key в репозиторий. Держите его вне системы контроля версий и не раскрывайте в общей истории команд или скриптах.

Python (JWT)

Установите Vonage Python SDK и библиотеку requests:

SDK использует структуру монорепозитория, поэтому установка пакета верхнего уровня vonage также подтягивает vonage-jwt, который вы будете использовать для подписи токена. Нет необходимости устанавливать его отдельно.

Сгенерируйте JWT с вашим идентификатором приложения и закрытым ключом, затем отправьте его в качестве Bearer-токена:

Деталь, на которой стоит остановиться: generate_application_jwt() возвращает байты, а не строку. Уберите .decode(), и f-строка с радостью интерполирует repr, отправив Authorization: Bearer b'eyJhbGci...' и заработав 401, что совсем не похоже на проблему с кодировкой.

Метод также принимает необязательный словарь утверждений (claims), если вам нужно переопределить значения по умолчанию. Одно значение по умолчанию, о котором стоит знать: срок действия токенов истекает через 15 минут. Вызов с запросом PIN-кода включает в себя несколько циклов вебхуков и ввод данных человеком с клавиатуры, поэтому, если вы ожидаете, что получатели будут тратить время, или если вы ставите вызовы в очередь, а не совершаете их немедленно, подумайте об увеличении срока действия:

Максимально допустимое время жизни JWT составляет 24 часа. Делайте его настолько коротким, насколько позволяет ваш процесс.

Для обоих методов ответом является JSON-объект, содержащий идентификатор транзакции:

Сохраните значение tid. Это ссылка на вызов, которая появится в обратном вызове после завершения взаимодействия.

Как работает процесс вызова

Как только вызов совершен, Vonage управляет процессом через серию вебхуков. Вот что происходит на каждом этапе.

Сбор PIN-кода

NCCO, который возвращает ваш вебхук ответа, — это место, где происходит основная работа. Он озвучивает ваше сообщение, а затем собирает цифры:

Действие ввода (input) сбивает разработчиков с толку в двух местах.

Первое: типы ввода должны быть явно объявлены в массиве типов, при этом параметры DTMF (двухтональный многочастотный сигнал) должны быть вложены в ключ dtmf. Более старые примеры устанавливали плоский параметр maxDigits непосредственно в действии; эта форма больше не принимается.

Второе: форма того, что возвращается. Vonage отправляет результат на ваш eventUrl следующим образом (полная справка по событию ввода):

dtmf — это объект, а не строка. Сравнение его напрямую с ожидаемым PIN-кодом будет молча приводить к ошибке каждый раз: код выглядит правильно, исключение не выбрасывается, и каждому вызывающему абоненту сообщается, что его правильный PIN-код был неверным. Читайте dtmf["digits"].

Параметр bargeIn в действии talk позволяет получателю начать ввод до того, как сообщение закончится, что важнее, чем кажется, для любого, кто уже слышал это приглашение раньше. Это требует действия ввода (input) сразу после него.

Обратные вызовы

Когда вызов завершается, функция Lambda отправляет POST (или GET) запрос на указанный вами URL вебхука. Обратный вызов включает следующие параметры:

Параметр

Значение

Пример

Номер, на который был совершен вызов, в формате E.164

14155550100

tid

Идентификатор транзакции

6a2827c9-4c68-46fc-b179-115f055dc0eb

status

Результат вызова

Поле status будет содержать одно из следующих значений:

  • ok: вызов завершен, и получатель ввел правильный PIN-код.

ok: вызов завершен, и получатель ввел правильный PIN-код.

  • failed: вызов завершен, но получатель не ввел правильный PIN-код в течение трех попыток.

failed: вызов завершен, но получатель не ввел правильный PIN-код в течение трех попыток.

  • error: вызов не был завершен (занято, отклонено, нет ответа, отменено или сброшено до ввода PIN-кода).

error: вызов не был завершен (занято, отклонено, нет ответа, отменено или сброшено до ввода PIN-кода).

Последний пункт заслуживает особого внимания. Возникает искушение отправлять обратный вызов только тогда, когда статус вызова — completed, но completed означает, что вызов был соединен и дошел до конца. Вызов, на который так и не ответили, достигает терминального статуса unanswered, и если вы не следите за ним, ваше приложение будет вечно ждать результата, который никогда не придет. Функция обрабатывает completed, failed, rejected, busy, cancelled, unanswered и timeout как терминальные и сообщает обо всех из них.

Следующие шаги

Ваша функция работает и совершает реальные вызовы. Вот несколько вещей, которые стоит изучить:

  • Настройте количество повторных попыток. MAX_ATTEMPTS в начале app.py управляет тем, сколько попыток получает получатель. DTMF_TIMEOUT рядом с ним управляет тем, сколько времени Vonage ждет между цифрами перед отправкой.

Настройте количество повторных попыток. MAX_ATTEMPTS в начале app.py управляет тем, сколько попыток получает получатель. DTMF_TIMEOUT рядом с ним управляет тем, сколько времени Vonage ждет между цифрами перед отправкой.

  • Измените голос. Vonage Voice API поддерживает несколько языков и голосов, которые устанавливаются параметрами language и style в действии talk. (В старых примерах используется voiceName; он был заменен этой парой.) Оба параметра доступны как переменные окружения TALK_LANGUAGE и TALK_STYLE. Полный список смотрите в документации по синтезу речи (Text-to-Speech).

Измените голос. Vonage Voice API поддерживает несколько языков и голосов, которые устанавливаются параметрами language и style в действии talk. (В старых примерах используется voiceName; он был заменен этой парой.) Оба параметра доступны как переменные окружения TALK_LANGUAGE и TALK_STYLE. Полный список смотрите в документации по синтезу речи (Text-to-Speech).

  • Добавьте аутентификацию перед /call. Эндпоинт открыт по умолчанию: любой, кто узнает URL, может совершать через него вызовы. Авторизатор API Gateway или API-ключ — это минимум, который должен быть перед тем, как это попадет в продакшн.

Добавьте аутентификацию перед /call. По умолчанию эта конечная точка открыта: любой, кто узнает URL, сможет совершать через нее вызовы. Авторизатор API Gateway или API-ключ — это минимум, который должен быть настроен перед выводом в продакшн.

  • Очистите DynamoDB. Детали вызовов сохраняются в DynamoDB после каждого вызова и никогда не удаляются. Добавьте атрибут TTL или задание по очистке, чтобы контролировать расходы на хранение.

Очистите DynamoDB. Детали вызовов сохраняются в DynamoDB после каждого вызова и никогда не удаляются. Добавьте атрибут TTL или задание по очистке, чтобы контролировать расходы на хранение.

  • Удалите развертывание. Чтобы удалить все ресурсы AWS, созданные в этом руководстве, выполните chalice delete. Это удалит функцию Lambda, конечные точки API Gateway и связанную роль IAM. Таблица DynamoDB не удаляется автоматически; удалите ее отдельно через консоль AWS или с помощью AWS CLI.

Удалите развертывание. Чтобы удалить все ресурсы AWS, созданные в этом руководстве, выполните chalice delete. Это удалит функцию Lambda, конечные точки API Gateway и связанную роль IAM. Таблица DynamoDB не удаляется автоматически; удалите ее отдельно через консоль AWS или с помощью AWS CLI.

  • Изучите Voice API. Обзор Vonage Voice API охватывает полный спектр возможностей управления вызовами, включая запись, конференц-связь и обработку входящих вызовов.

Изучите Voice API. Обзор Vonage Voice API охватывает полный спектр возможностей управления вызовами, включая запись, конференц-связь и обработку входящих вызовов.

Итоги

Вот что вы реализовали:

  • Функцию Lambda, поддерживаемую API Gateway и DynamoDB, развернутую с помощью одной команды chalice deploy.

Функцию Lambda, поддерживаемую API Gateway и DynamoDB, развернутую с помощью одной команды chalice deploy.

  • Сценарий вызова, который обрабатывает вебхуки ответа, ввода PIN-кода, повторной попытки и завершения вызова без участия вашего основного приложения.

Сценарий вызова, который обрабатывает вебхуки ответа, ввода PIN-кода, повторной попытки и завершения вызова без участия вашего основного приложения.

  • Два пути аутентификации: закрытый ключ через cURL для быстрого тестирования и JWT для всего, что выполняется в коде.

Два пути аутентификации: закрытый ключ через cURL для быстрого тестирования и JWT для всего, что выполняется в коде.

  • Две ловушки при вводе NCCO: обязательный тип массива и dtmf["digits"] вместо самого dtmf.

Две ловушки при вводе NCCO: обязательный тип массива и dtmf["digits"] вместо самого dtmf.

Один входящий запрос, один исходящий колбэк. Lambda обрабатывает все, что происходит между ними.

О чём эта статья

Ещё в разделе «Телеком и сети»

Все →

Ещё от Vonage API Platform