https://d226lax1qjow5r.cloudfront.net/blog/blogposts/serverless-paging-amazon-transcribe-dr/Pager-Hack-the-Planet.png

Aufbau einer serverlosen Paging-Anwendung mit Amazon Transcribe

Zuletzt aktualisiert am May 4, 2021

Lesedauer: 8 Minuten

In den 90er Jahren gab es einige großartige Technologien: MiniDisc, Tamagotchi, und PAGERS!

OK, bei den meisten Dingen haben unsere Smartphones in den letzten 20 Jahren einiges bewegt, aber die Pager haben eine gewisse Nostalgie.

Und jeder, der schon einmal im Bereitschaftsdienst war und um 2 Uhr morgens von einem Anruf geweckt wurde, in dem er verzweifelt um Hilfe gebeten wurde, weiß, dass Pager einen Vorteil hatten, den wir mit den Telefonen verloren haben: Man bekam die Nachricht und rief die Person zurück.

Für diejenigen, die zu jung sind, um sich an Pager zu erinnern: Man rief die Pager-Nummer der Person an, und der Anruf wurde von einem Mitarbeiter entgegengenommen, der dann eine Nachricht aufnahm, diese in sein Pult eintippte und die Nachricht kurz darauf auf dem Bildschirm des Pagers erschien.

In diesem Tutorial zeige ich Ihnen, wie Sie diesen Messaging-Service mit Nexmo und AWS neu erstellen können.

KI hat in letzter Zeit einen langen Weg zurückgelegt, und jetzt können wir den teuren Paging-Bürobetreiber durch eine API ersetzen; für dieses Beispiel werde ich Amazon Transcribe verwenden. Wir verwenden Nexmo, um den eingehenden Anruf entgegenzunehmen, ihn zu beantworten und eine Nachricht aufzuzeichnen, dann diese Aufnahme an Transcribe weiterzugeben und wenn wir den Text zurückbekommen, senden wir ihn mit Nexmo SMS an Ihr Telefon.

Um die Teile zusammenzufügen, werden wir eine Python-Anwendung mit dem Chalice-Framework erstellen, die es uns dann ermöglicht, das Ganze auf AWS Lambda und S3 bereitzustellen und auszuführen.

Die Vorteile dieser Architektur liegen darin, dass unsere Hosting-Kosten sehr gering sein werden. Wenn Sie dies nur für den persönlichen Gebrauch betreiben, werden Sie wahrscheinlich innerhalb der kostenlosen Ebenen von AWS bleiben, und wenn Sie es ausbauen möchten, wird der serverlose Stack auf massive Volumina skalieren.

Voraussetzungen

Für diesen Lehrgang benötigen Sie Folgendes:

  • Ein AWS Account (Sie können dies auf der kostenlosen Stufe ausführen)

  • Das AWS CLI-Werkzeug und Chalice sind auf Ihrem Rechner installiert und konfiguriert

  • Ein Nexmo Account mit dem nexmo CLI-Werkzeug installiert und konfiguriert

Vonage API-Konto

Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.

In diesem Lernprogramm wird auch eine virtuelle Telefonnummer verwendet. Um eine zu erwerben, gehen Sie zu Rufnummern > Rufnummern kaufen und suchen Sie nach einer Nummer, die Ihren Anforderungen entspricht.

Einrichtung

Bevor wir unsere Funktionalität implementieren, müssen wir einige Einstellungen vornehmen.

Nexmo Voice-Anwendung

Wir müssen eine neue Sprachanwendung entweder im nexmo-Dashboard oder mit dem Kommandozeilentool erstellen

nexmo app:create “Paging Service” http://example.com/answer http://example.com/event --keyfile private.key

Notieren Sie sich die zurückgegebene Anwendungs-ID; damit wird auch der private Schlüssel in einer Datei gespeichert.

Für den Moment werden wir Dummy-Werte für die Webhooks verwenden, oder wenn Sie lokal testen möchten, können Sie ngrok für Ihren Host verwenden.

S3-Eimer

Außerdem müssen wir einen S3-Bucket erstellen, in dem wir die Aufnahmen für die Transkription ablegen. Wir werden unsere Ressourcen in der AWS-Region us-east-1 erstellen.

aws s3api create-bucket --bucket pagingservice --region us-east-1

Kelch Anwendung

Als erstes werden wir ein neues Kelchprojekt erstellen:

chalice new-project

Wenn Sie dazu aufgefordert werden, geben Sie Ihrem Projekt einen Namen, z. B. paging-service. Sie haben nun eine grundlegende Vorlage für eine Kelch-Anwendung im Ordner mit dem Namen Ihres Projekts erstellt.

In diesem Ordner befinden sich drei wichtige Dateien:

  • app.py: der Hauptcode für die Anwendung

  • requirements.txtpython: listet die von Ihnen verwendeten Python-Module auf

  • .chalice\config.json: enthält verschiedene Einstellungen, die sich auf Ihr Projekt beziehen

Module importieren

Die Vorlage hat bereits das Chalice-Modul importiert. Wir benötigen auch das boto3-Modul für die Verbindung zu S3 und Transcribe sowie das Nexmo-Modul zum Abrufen der Aufzeichnung und zum Senden der SMS. Wir importieren auch das os-Modul für den Zugriff auf Umgebungsvariablen.

import boto3
import nexmo
import json
import os

Wir müssen auch boto3 und nexmo in die requirements.txt Datei hinzufügen, damit Lambda weiß, dass diese installiert werden sollen, wenn die Anwendung bereitgestellt wird.

Setup-Variablen

APPLICATION_ID = os.environ['APPLICATION_ID']
API_KEY = os.environ['API_KEY']
API_SECRET = os.environ['API_SECRET']
NAME = os.environ['NAME']
NUMBER = os.environ['NUMBER']
NEXMO_NUMBER = os.environ['NEXMO_NUMBER']
S3_BUCKET = 'pagingdemo'

Wir setzen die meisten unserer Variablen aus Umgebungsvariablen, Sie können sehen, wie man diese mit Chalice aus dem README auf Github setzt, alternativ können Sie auch einfach Ihre eigenen Werte hier setzen.

Clients initialisieren

Wir werden als Teil unserer Anwendung eine Verbindung zu drei externen Services herstellen: AWS S3, Amazon Transcribe und Nexmo. Wir werden diese Verbindungen hier erstellen:

S3 = boto3.client('s3')
TRANSCRIBE = boto3.client('transcribe')
NEXMO = nexmo.Client(
    key=API_KEY,
    secret=API_SECRET,
    application_id=APPLICATION_ID,
    private_key='chalicelib/private.key',
)

Wir müssen keine Anmeldeinformationen für die Amazon-Dienste angeben, da boto und chalice dies automatisch tun, wenn wir auf Lambda bereitstellen.

Schreiben des Handler-Codes

Für diese Anwendung gibt es drei Phasen, die wir durchlaufen müssen, um eine Sprachnachricht zu transkribieren; diese werden mit drei separaten Handlern in unserem Anwendungscode übereinstimmen.

Der erste ist ein Webhook-Handler, der die eingehende Anrufanfrage von Nexmo beantwortet und ein NCCO (Nexmo Call Control Object) zurückgibt, das eine Liste von Aktionen ist, die mit dem Anruf durchgeführt werden sollen, dargestellt als JSON-Objekt.

@app.route('/answer')
def answer():
    req = app.current_request.to_dict()
    ncco =[
            {
                'action': 'talk',
                'text': "Welcome to {}s messaging service, please leave a short message after the tone".format(NAME),
            },
            {
                'action': 'record',
                'endOnSilence': 3,
                'endOnKey': '#',
                'beepStart' : True,
                'eventUrl' : [req['headers']['x-forwarded-proto'] + "://" + req['headers']['host'] + "/api/recording?from=" +req['query_params']['from']]
            },
            {
                'action': 'talk',
                'text': "thankyou, your message has been forwarded"
            }
        ]
    return ncco

Der @app.route Dekorator definiert den Pfad, auf den dieser Handler reagieren wird. Wir konvertieren die Parameter in der eingehenden Anfrage in ein req Dictionary Objekt um, da wir einige dieser Daten später verwenden wollen. Dann erstellen wir unsere NCCO-Antwort.

Die erste Aktion ist die talk das ist die erste Begrüßung, die der Anrufer hört. Wir geben hier den Parameter NAME ein, um die Begrüßung zu personalisieren.

Wir haben dann eine record Aktion, mit der wir die Nachricht des Anrufers erfassen. Ich habe endOnSilence auf drei Sekunden eingestellt, so dass es weitergeht, wenn der Anrufer zu Ende gesprochen hat, oder er kann mit der endonKey um # zu drücken. beepStart ist auf True gesetzt, damit der Anrufer weiß, wann er zu sprechen beginnt.

Der eventUrl Parameter sieht ein wenig kompliziert aus, aber alles, was ich hier tue, ist die Erstellung der URL, die den gleichen Host und das gleiche Protokoll wie der eingehende Webhook auf dem API-Gateway verwendet, so dass wir nicht brauchen, um hart Code, dass. Der Pfad lautet /api/recording, wobei api die Vorgabe für API-Gateway ist. Schließlich fügen wir das Tag from als Abfrageparameter, so dass wir die ursprüngliche Anrufer-ID des Anrufs kennen, wenn der Aufzeichnungs-Webhook eingeht (da Nexmo diese nicht standardmäßig in Aufzeichnungsereignissen weitergibt).

Wir beenden unseren NCCO mit einer einfachen talk Aktion ab, damit der Anrufer weiß, dass seine Nachricht aufgezeichnet wurde und er auflegen kann. Wenn der Anrufer auflegt, während die Aufnahme noch aktiv ist, wird die Nachricht trotzdem zugestellt.

In unserem nächsten Handler werden wir das eingehende Aufnahme-Ereignis von Nexmo empfangen, die Aufnahme abrufen und in S3 speichern und dann die Transkription starten.

@app.route('/recording', methods=['POST'])
def recording():
  qparams=  app.current_request.query_params
  data =  app.current_request.json_body
  recfile = NEXMO.get_recording(data['recording_url'])
  S3.put_object(
      Bucket=S3_BUCKET,
      Key=data['conversation_uuid']+".mp3",
      Body=recfile,
      ContentType='audio/mp3',
      Metadata={
        'callerid': qparams['from'],
        'time' : data['end_time']
      }
  )
  response = TRANSCRIBE.start_transcription_job(
      TranscriptionJobName=data['conversation_uuid'],
      LanguageCode='en-GB',
      MediaFormat='mp3',
      Media={
          'MediaFileUri': 'https://s3.amazonaws.com/{}/{}'.format(S3_BUCKET, data['conversation_uuid']+".mp3")
      },
      OutputBucketName=S3_BUCKET,
  )
  return "ok"

Wir haben den gleichen @app.route Dekorator, aber wir geben auch an, dass dieser eine POST-Anfrage bearbeiten wird (Chalice ist standardmäßig auf GET eingestellt).

Wir erfassen die Parameter der Abfragezeichenfolge, in der wir die from Details in ein Wörterbuch namens qparams und dann den JSON-Body des Webhooks in ein Objekt namens data.

Wir verwenden das NEXMO Objekt, das wir als Verbindung zu Nexmo erstellt haben, um die Aufzeichnung abzurufen, und speichern diese dann im S3-Bucket, wobei wir die UUID der Konversation als unseren Schlüssel verwenden. Wir setzen auch ein paar Metadaten gegen das Objekt, nämlich die ursprüngliche Anrufer-ID (from) und die Zeit der Aufzeichnung.

Schließlich starten wir einen Transkriptionsauftrag, der auf unsere neue Aufnahme in S3 verweist. Wir müssen einen Namen für den Auftrag angeben (wieder verwenden wir die UUID des Gesprächs), die Sprache, in der das Audio vorliegt (in diesem Fall Englisch - Britisch), das Medienformat und den URI für die Datei in S3. Dieses Format hängt von der Region ab, in der Sie Ihren Bucket erstellt haben. Das Beispiel hier ist für us-east-1. Schließlich geben wir den Ausgabe-Bucket an, in den die resultierende Transkription geschrieben werden soll: Wir verwenden denselben Bucket wie die Aufnahmen.

Beim letzten Handler lösen wir etwas anders aus. Diesmal ist es kein Webhook, sondern das Eintreffen des Transkriptionsergebnisses in unserem S3-Bucket, das unseren Code aufruft.

@app.on_s3_event(bucket=S3_BUCKET, events=['s3:ObjectCreated:*'], suffix='.json')
def transcribed(event):
  # Get transcription from S3
  obj = S3.get_object( Bucket=S3_BUCKET, Key=event.key)
  data = json.loads(obj['Body'].read())
  # Make recording public
  S3.put_object_acl(ACL='public-read', Bucket=S3_BUCKET, Key= data['jobName']+".mp3")
  #Build SMS
  text = data['results']['transcripts'][0]['transcript'].upper()
  obj = S3.get_object( Bucket=S3_BUCKET, Key=data['jobName']+".mp3")
  callerid = obj['ResponseMetadata']['HTTPHeaders']['x-amz-meta-callerid']
  url = 'https://s3.amazonaws.com/{}/{}'.format(S3_BUCKET, data['jobName']+".mp3")
  message = "[From: +{}]\n\n{}\n\n{}".format(callerid, text, url)
  #Send SMS
  NEXMO.send_message({'from': NEXMO_NUMBER, 'to': NUMBER, 'text': message})

Sie werden feststellen, dass der Dekorator ein anderes Format hat: on_s3_event. Wir geben auch den Bucket an, an dem wir interessiert sind, den Typ des Ereignisses, wenn ein neues Objekt erstellt wird, und das Suffix dieser Objekte als JSON, um nicht auszulösen, wenn die Aufnahme .mp3 Objekte zum Bucket hinzugefügt werden.

Wir holen dann das neue Objekt ab, das eine JSON-Antwort unserer Transkription ist, und halten es in data. Wir machen die mp3-Aufnahmedatei öffentlich lesbar und beginnen dann mit dem Aufbau unserer Benachrichtigungsnachricht. Ich mag es, den Text der Transkription in GROSSBUCHSTABEN zu sehen, da es ein bisschen mehr wie der Retro-Pager-Service wirkt. Wir fügen auch die ursprüngliche Anrufer-ID am Anfang der Nachricht ein und fügen schließlich die URL zur Audioaufnahme am Ende der Nachricht hinzu, nur für den Fall, dass die Transkription nicht perfekt ist und Sie hören möchten, was der Anrufer ursprünglich gesagt hat.

Schließlich versenden wir die SMS mit Hilfe des zuvor erstellten NEXMO-Client-Objekts.

Einsatz

Jetzt, wo wir die Anwendung erstellt haben, müssen wir sie nur noch in AWS bereitstellen, indem wir sie ausführen:

chalice deploy

Dadurch wird eine Lambda-Funktion erstellt und die API-Gateway-Regeln werden konfiguriert. Außerdem werden die S3-Bucket-Ereignisse erstellt und die zugehörigen IAM-Sicherheitsrichtlinien automatisch für uns eingerichtet.

Sie sollten dann eine Ausgabe erhalten, die die URL des API-Gateways enthält, z. B.:

Rest API URL: https://3u9ucalu05.execute-api.us-east-1.amazonaws.com/api/

Aktualisieren Sie Ihre Nexmo-Anwendung auf der Grundlage dieser URL, um den Antwort-Webhook so einzustellen, dass er auf Ihre eingesetzte Anwendung verweist. Hierfür benötigen Sie Ihre Anwendungs-ID:

nexmo app:update [APPLICATION UUID] “Paging Service” `chalice url`answer `chalice url`event

Vergewissern Sie sich schließlich, dass Ihre Nexmo-Nummer mit der Anwendung verknüpft ist, und rufen Sie dann an. Sie sollten Ihre Begrüßung hören und dann eine Nachricht hinterlassen können. Kurze Zeit später erhalten Sie eine Textnachricht mit Ihrer Transkription und einem Link zur Audiodatei:

text message screenshottext message

Nächste Schritte

Es gibt verschiedene Möglichkeiten, diese Anwendung zu erweitern. Einige Ideen, die Sie in Betracht ziehen könnten, sind das Hinzufügen von Unterstützung für mehrere Benutzer, indem Sie eine Zuordnung von eingehender Nummer zu Benachrichtigungsnummer und Begrüßung erstellen, oder das Austauschen der SMS-Benachrichtigung gegen eine E-Mail, wenn dies besser zu Ihrem Anwendungsfall passt.

Außerdem werden die MP3- und Transkriptionsdateien dauerhaft in Ihrem S3-Bucket gespeichert, so dass Sie vielleicht nach einer Möglichkeit suchen sollten, sie nach einer bestimmten Zeit zu entfernen oder ablaufen zu lassen.

Es ist auch erwähnenswert, dass der Amazon Transcribe-Service nicht der schnellste ist - insbesondere bei kurzen Audioclips. In meinen Tests habe ich eine 1-2-minütige Verzögerung bei der Transkription einer kurzen Sprachnachricht festgestellt, also bedenken Sie dies, wenn Sie es für zeitkritische Benachrichtigungen verwenden möchten.

Sie finden den gesamten Quellcode für die Anwendung zusammen mit der Chalice-Konfiguration im GitHub Repository.

Teilen Sie:

https://a.storyblok.com/f/270183/384x384/7fbbc7293b/sammachin.png
Sam MachinVonage Ehemalige