Création d'une compétence stateful pour Alice sur des fonctions serverless de Yandex.Cloud et Python

Commençons par les nouvelles. Hier, Yandex.Cloud a annoncé le lancement d'un service de calcul sans serveur. Yandex Cloud Functions. Cela signifie que vous écrivez seulement le code de votre service (par exemple, une application web ou un chatbot), et le Cloud crée et gère lui-même les machines virtuelles sur lesquelles il s'exécute, et même les réplique si la charge augmente. Il n'y a pas besoin de réfléchir, c'est très pratique. Et vous ne payez que pour le temps de calcul.

Cependant, certains peuvent ne rien payer du tout. Ce sont les développeurs des compétences externes d'Alexa, c'est-à-dire des chatbots intégrés. Écrire, héberger et enregistrer une telle compétence peut être fait par n'importe quel développeur, et depuis aujourd'hui, il n'est même plus nécessaire d'héberger les compétences - il suffit de télécharger leur code dans le Cloud sous forme de cette fameuse fonction sans serveur..

Mais il y a quelques nuances. Premièrement, votre code Python peut nécessiter certaines dépendances, et les intégrer dans le Cloud n'est pas trivial. Deuxièmement, tout bon chatbot a besoin de stocker l'état du dialogue quelque part; comment faire cela dans une fonction sans serveur de la manière la plus simple ? Troisièmement, comment peut-on rapidement et efficacement écrire une compétence pour Alexa ou pour tout autre bot avec une trame narrative quelconque ? En fait, cet article porte sur ces nuances.

Création d'une compétence stateful pour Alice sur des fonctions serverless de Yandex.Cloud et Python

Préparation morale

Pour les impatients : je rassemble les dépendances nécessaires avec un fichier Make avant de télécharger la fonction dans le Cloud, je stocke l'état du dialogue dans le Yandex Object Storage (qui prend en charge l'API S3), et j'utilise ma propre bibliothèque tgalice. Au final, cela donne . Et pour l'authentification à deux facteurs dans les systèmes d'exploitation Windows, Linux et MacOS, des une compétence de démonstration. Maintenant, examinons tout cela un peu plus en détail.

Pour ceux qui sont un peu moins impatients : avant de plonger dans mon histoire sur les difficultés, je recommande vivement de commencer par quelque chose de simple. À savoir, créer une fonction de bot écho dans un fichier unique, l'enregistrer et la tester dans la console développeur Dialogues; les détails se trouvent dans le blog des Dialogues et la documentation du Cloud. Et seulement ensuite entrer dans le vif du sujet.

Si vous souhaitez immédiatement ressentir pour quoi nous nous battons ici, demandez à Alexa ‘active la compétence « horoscope informatique »‘. Cela devrait donner lieu à un dialogue comme suit :

Alexa : Bonjour ! Vous êtes dans la compétence « Horoscope informatique ». Dites « Démarrer » pour savoir ce que les étoiles vous prédisent.
Utilisateur : Démarrer.
A : S'il vous plaît, indiquez votre nom.
U : Vasilij.
A : Maintenant, dites-moi votre année de naissance. Juste quatre chiffres, rien de plus.
U : Deuxième.
A : Veuillez réessayer. Indiquez l'année de votre naissance — quatre chiffres.
J : 2002
A : Super ! Maintenant, indiquez le mois de votre naissance.
J : Février
A : Excellent ! Enfin, indiquez-moi la date de votre naissance — juste un chiffre, un ou deux au maximum.
J : 18
A : Merci, Vassilissy ! Maintenant nous savons : vous avez 17 ans et vous êtes Verseau. Quelle chance ! Les étoiles vous disent : et ce que les étoiles disent, vous le saurez en lançant la compétence.

Le problème est que, même pour maintenir un dialogue aussi simple, il faut se souvenir du nom et de la date de naissance de l'utilisateur, ce qui n'est pas trivial dans un environnement de fonctions sans serveur. Il n'est pas possible de stocker le contexte en mémoire ou dans un fichier sur le disque, car Yandex.Cloud peut lancer la fonction sur plusieurs machines virtuelles simultanément et basculer entre elles de manière arbitraire. Il faudra utiliser un stockage externe. Le choix s'est porté sur Object Storage, qui est un stockage relativement bon marché et simple directement dans Yandex.Cloud (c'est-à-dire probablement rapide). Comme alternative gratuite, vous pouvez essayer, par exemple, une petite partie de Mongo Cloud quelque part au loin. Et pour Object Storage (qui prend en charge l'interface S3) et pour Mongo, il existe des wrappers Python pratiques.

Un autre problème est que pour accéder à Object Storage, MongoDB ou toute autre base ou stockage de données, des dépendances externes sont nécessaires, qui doivent être chargées sur Yandex Functions avec le code de votre fonction. Et il serait souhaitable de le faire de manière pratique. Très pratique (comme sur Heroku), malheureusement, cela ne sera pas possible, mais un certain confort de base peut être créé en écrivant un script de construction d'environnement (un fichier make).

Comment lancer la compétence astrologique

  1. Préparez-vous : connectez-vous à une machine Linux. En principe, il est probablement possible de travailler aussi sous Windows, mais avec le lancement du fichier make, il faudra la jouer avec un peu de magie. Et de toute façon, vous aurez besoin de Python version 3.6 ou supérieure installé.
  2. Clonez depuis GitHub un exemple de compétence astrologique.
  3. Inscrivez-vous sur Yandex.Cloud : https://cloud.yandex.ru
  4. Créez deux buckets dans Object Storage, nommez-les comme vous le souhaitez {BUCKET NAME} et tgalice-test-cold-storage (ce deuxième nom est actuellement codé en dur dans main.py de mon exemple). Le premier bucket sera nécessaire uniquement pour le déploiement, le second — pour stocker les états du dialogue.
  5. Créer compte de service, donnez-lui le rôle éditeur, et obtenez ses identifiants statiques {KEY ID} et {KEY VALUE} — nous allons les utiliser pour enregistrer l'état du dialogue. Tout cela est nécessaire pour que la fonction de Y.Cloud puisse accéder au stockage de Y.Cloud. J'espère qu'un jour, l'authentification deviendra automatique, mais pour l'instant, c'est ainsi.
  6. (Pas obligatoire) installer interface de ligne de commande yc. Il est possible de créer une fonction aussi via l'interface web, mais le CLI a l'avantage que les nouveautés y apparaissent plus rapidement.
  7. Maintenant, il est temps de préparer l'assemblage des dépendances : exécutez dans la ligne de commande depuis le dossier contenant l'exemple de compétence make all. Un tas de bibliothèques (principalement inutiles, comme d'habitude) sera installé dans le dossier dist.
  8. Téléchargez à la main dans Object Storage (dans le bucket {BUCKET NAME}) l'archive obtenue à l'étape précédente dist.zip. Si vous le souhaitez, cela peut également être fait depuis la ligne de commande, par exemple en utilisant CLI AWS.
  9. Créer une fonction sans serveur via l'interface web ou en utilisant l'utilitaire yc. Pour l'utilitaire, la commande ressemblera à ceci :

yc serverless function version create
    --function-name=horoscope
    --environment=AWS_ACCESS_KEY_ID={KEY ID},AWS_SECRET_ACCESS_KEY={KEY VALUE}
    --runtime=python37
    --package-bucket-name={BUCKET NAME}
    --package-object-name=dist.zip
    --entrypoint=main.alice_handler
    --memory=128M
    --execution-timeout=3s

Lors de la création manuelle de la fonction, tous les paramètres sont remplis de manière similaire.

Maintenant, la fonction que vous avez créée peut être testée via la console développeur, puis améliorée et publiée.

Création d'une compétence stateful pour Alice sur des fonctions serverless de Yandex.Cloud et Python

Ce qu'il y a sous le capot

Le fichier Make contient en réalité un script assez simple pour installer les dépendances et les empaqueter dans une archive dist.zip, à peu près comme ceci :

mkdir -p dist/
pip3 install -r requirements.txt --target dist/
cp main.py dist/main.py
cp form.yaml dist/form.yaml
cd dist && zip --exclude '*.pyc' -r ../dist.zip ./*

Le reste est constitué de quelques outils simples, emballés dans une bibliothèque tgalice. Le processus de remplissage des données utilisateur est décrit par la configuration form.yaml:

form_name: 'horoscope_form'
start:
  regexp: 'début|commencer'
  suggests:
    - Début
fields:
  - name: 'name'
    question: Veuillez indiquer votre nom.
  - name: 'year'
    question: Maintenant, dites-moi l’année de votre naissance. Seulement quatre chiffres, rien de plus.
    validate_regexp: '^[0-9]{4}$'
    validate_message: Veuillez réessayer. Indiquez l’année de votre naissance - quatre chiffres.
  - name: 'month'
    question: Super! Maintenant, indiquez le mois de votre naissance.
    options:
      - janvier
      ...
      - décembre
    validate_message: Ce que vous avez dit ne ressemble pas à un mois. Veuillez indiquer le mois de votre naissance, sans autres mots.
  - name: 'day'
    question: Excellent! Enfin, donnez-moi la date de votre naissance - seulement un nombre, un ou deux chiffres au maximum.
    validate_regexp: '[0123]?d$'
    validate_message: Veuillez réessayer. Vous devez indiquer le nombre de votre naissance (par exemple, vingt); c'est un ou deux chiffres.

Le travail d'interprétation de cette config et de calcul du résultat final est pris en charge par la classe Python.

class CheckableFormFiller(tgalice.dialog_manager.form_filling.FormFillingDialogManager):
    SIGNS = {
        'janvier': 'Capricorne',
        ...
    }

    def handle_completed_form(self, form, user_object, ctx):
        response = tgalice.dialog_manager.base.Response(
            text='Merci, {}! Maintenant nous savons: vous avez {} ans, et vous êtes {}. n'
                 'Vous avez vraiment de la chance! Les étoiles vous disent: {}'.format(
                form['fields']['name'],
                2019 - int(form['fields']['year']),
                self.SIGNS[form['fields']['month']],
                random.choice(FORECASTS),
            ),
            user_object=user_object,
        )
        return response

Plus précisément, la classe de base. FormFillingDialogManager s'occupe de remplir le « formulaire », et la méthode de la classe dérivée handle_completed_form indique ce qu'il faut faire lorsque celui-ci est prêt.

De plus, le flux principal du dialogue de l'utilisateur doit également accueillir, ainsi que fournir une aide sur la commande « aide » et sortir de la compétence sur la commande « sortie ». Pour cela, il y a aussi un modèle, donc le gestionnaire de dialogue complet est composé de morceaux: tgalice dm = tgalice.dialog_manager.CascadeDialogManager( tgalice.dialog_manager.GreetAndHelpDialogManager( greeting_message=DEFAULT_MESSAGE, help_message=DEFAULT_MESSAGE, exit_message='Au revoir, revenez au skill "Horoscope IT" bientôt!' ), CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE) )

CascadeDialogManager

fonctionne simplement: essaie d'appliquer à l'état actuel du dialogue tous ses composants à tour de rôle, et choisit le premier approprié. En réponse à chaque message, le gestionnaire de dialogue retourne un objet Python.

Avec la gestion du dialogue, les utilisateurs ont une expérience interactive agréable. Response, que l'on peut ensuite convertir en texte brut, ou en message dans Alice ou Telegram — selon où le bot est lancé ; il contient également l'état modifié du dialogue qui doit être préservé. Tout cela est géré par une autre classe, DialogConnector, donc le script pour lancer la compétence sur Yandex Functions ressemble à ceci :

...
session = boto3.session.Session()
s3 = session.client(
    service_name='s3',
    endpoint_url='https://storage.yandexcloud.net',
    aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'],
    region_name='ru-central1',
)
storage = tgalice.session_storage.S3BasedStorage(s3_client=s3, bucket_name='tgalice-test-cold-storage')
connector = tgalice.dialog_connector.DialogConnector(dialog_manager=dm, storage=storage)
alice_handler = connector.serverless_alice_handler

Comme vous le voyez, la majeure partie de ce code crée une connexion à l'interface S3 de l'Object Storage. Pour savoir comment cette connexion est utilisée, vous pouvez lire dans le code de tgalice.
La dernière ligne crée la fonction alice_handler — celle que nous avons demandé d'appeler à Yandex.Cloud lorsque nous avons défini le paramètre --entrypoint=main.alice_handler.

Voilà, c'est tout. Les fichiers Make pour la construction, un Object Storage de type S3 pour stocker le contexte, et une bibliothèque Python tgalice. Associés à des fonctions sans serveur et à l'expressivité de Python, cela suffit pour développer une compétence de qualité.

Vous pourriez vous demander pourquoi il était nécessaire de créer tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.

Si vous recherchez un NLU plus sophistiqué, vous pouvez adjoindre à votre compétence Rasa ou DeepPavlov, mais leur configuration nécessitera quelques contorsions supplémentaires, surtout en serverless. Si vous ne voulez vraiment pas coder, il vaut mieux utiliser un constructeur visuel comme Aimylogic. En créant tgalice, je pensais à un chemin intermédiaire. Voyons ce que cela donnera.

Et aujourd'hui, rejoignez le chat des développeurs de compétences Alice, lisez documentation, et créez des choses formidables les compétences!

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster