Oprettelse af stateful færdigheder til Alice ved hjælp af serverless funktioner fra Yandex Cloud og Python

Lad os starte med nyhederne. I går annoncerede Yandex.Cloud lanceringen af en serverløs beregningsservice. Yandex Cloud Functions. Det betyder: du skriver kun koden til din service (for eksempel en webapplikation eller chatbot), og skyen opretter og vedligeholder selv virtuelle maskiner, hvor den kører, og endda replikerer dem, hvis belastningen stiger. Du skal slet ikke tænke over det, det er meget bekvemt. Og du betaler kun for beregningstiden.

Men nogle kan slet ikke betale. Det er udviklerne af eksterne færdigheder for Alice, altså de indbyggede chatbots. Enhver udvikler kan skrive, hoste og registrere en sådan færdighed, og fra i dag af behøver færdighederne endda ikke at blive hosted – det er nok at uploade deres kode til skyen i form af denne serverløse funktion..

Men der er et par nuancer. For det første kan din Python-kode have brug for afhængigheder, og at trække dem ind i skyen er ikke trivielt. For det andet har enhver normal chatbot brug for at gemme dialogens tilstand et sted (deraf stateful); hvordan kan man nemmest gøre det i en serverløs funktion? For det tredje, hvordan kan man overhovedet hurtigt og snavset skrive en færdighed til Alice eller enhver bot med en ikke-nul plot? Disse nuancer er faktisk emnet for artiklen.

Oprettelse af stateful færdigheder til Alice ved hjælp af serverless funktioner fra Yandex Cloud og Python

Moralsk forberedelse

For de utålmodige: de nødvendige afhængigheder samler jeg med en make-fil før jeg uploader funktionen til skyen, dialogens tilstand gemmer jeg i Yandex's Object Storage (det understøtter S3 API), og til at styre dialogen bruger jeg mit eget bibliotek tgalice. Resultatet er sådan en demo-færdighed. Og nu skal vi gennemgå alt dette lidt mere detaljeret.

For de lidt mindre utålmodige: før du dykker ned i min historie om komplikationer, anbefaler jeg stærkt at starte med det enkle. Nemlig at lave en engangs-funktion-echo-bot, registrere og teste den i Dialogudviklerens konsol; detaljer findes i Dialogernes blog og dokumentation for Skyen. Og først derefter kan du begynde.

Hvis du vil føle, hvad vi kæmper for, så bed Alice om ‘tænd færdigheden „it-horoskop“‘. Der burde ske omtrent følgende dialog:

Alice: Hej! Du er i færdigheden „IT-horoskop“. Sig „Start“, for at finde ud af, hvad stjernerne spår dig.
Bruger: Start
A: Venligst oplys dit navn.
B: Vasisualy
A: Sig nu året for din fødsel. Kun fire cifre, intet ekstra.
Yu: anden
A: Prøv venligst igen. Angiv dit fødselsår — fire cifre.
Yu: 2002
A: Fantastisk! Nu skal du angive måneden for din fødsel.
Yu: Februar
A: Fremragende! Til slut, angiv venligst datoen for din fødsel — kun et nummer, maks to cifre.
Yu: 18
A: Tak, Vasisauliy! Nu ved vi: du er 17 år, og du er Vandmand. Du har været heldig! Stjernerne siger til dig: hvad stjernerne siger, finder du ud af ved at starte færdigheden.

Problemet er, at for at opretholde selv en så simpel dialog skal man huske brugerens navn og fødselsdato, og i et serverløst miljø er dette ikke trivielt. At gemme konteksten i RAM eller en fil på disken virker ikke, da Yandex.Cloud kan starte funktionen på flere virtuelle maskiner samtidig og skifte mellem dem vilkårligt. Man må bruge noget eksternt lager. Valget faldt på Object Storage, som er en relativt billig og simpel lagerløsning direkte i Yandex.Cloud (dvs. hurtig). Som en gratis alternativ kan man prøve f.eks. et gratis stykke cloud Mongo et sted langt væk. Og til Object Storage (den understøtter S3-grænsefladen) og til Mongo findes der brugervenlige Python-wraps.

Et andet problem er, at for at interagere med både Object Storage, MongoDB, og enhver anden database eller dataopbevaring, er der behov for nogle eksterne afhængigheder, som skal uploades til Yandex Functions sammen med koden for sin funktion. Og man ønsker at gøre dette på en bekvem måde. Det vil desværre ikke være helt ligesom heroku, men man kan skabe en vis grundlæggende komfort ved at skrive et script til at opbygge miljøet (make-fil).

Sådan starter du horoskopfærdigheden

  1. Forberedelse: log ind på en hvilken som helst maskine med Linux. I princippet kan man sikkert også arbejde med Windows, men man skal beskæftige sig med at køre make-filen. Og i alle tilfælde vil du have brug for installeret Python, minimum version 3.6.
  2. Klon fra GitHub eksemplet på horoskopfærdigheden.
  3. Registrer dig i Y. Cloud: https://cloud.yandex.ru
  4. Opret to buckets i Object Storage, kald dem hvad som helst {BUCKET NAME} og tgalice-test-kold-lagring (det andet navn er nu hardkodet i main.py min. det første bucket er kun nødvendigt til deployment, det andet — til opbevaring af dialogs tilstande.
  5. Oprette servicekonto, giv den rollen editor, og få fat i statiske legitimationsoplysninger {KEY ID} og {KEY VALUE} — de vil blive brugt til at registrere dialogens tilstand. Alt dette er nødvendigt, så funktionen fra Я.Облака kan få adgang til lageret fra Я.Облака. Engang, håber jeg, at autorisationen bliver automatisk, men indtil da — sådan.
  6. (Ikke nødvendigt) at installere kommandolinjegrænsefladen yc. Man kan også oprette funktionen via webgrænsefladen, men CLI er godt, fordi nye funktioner dukker op hurtigere der.
  7. Nu kan vi, faktisk, forberede samlingen af afhængigheder: køre i kommandolinjen fra mappen med eksemplet på færdighederne make all. En masse biblioteker (for det meste, som sædvanligt, unødvendige) vil blive installeret i mappen dist.
  8. Manuelt uploade til Object Storage (til bucket {BUCKET NAME}) den resulterende arkiv fra det foregående trin dist.zip. Hvis ønskes, kan dette også gøres fra kommandolinjen, for eksempel ved at bruge AWS CLI.
  9. Opret en serverløs funktion via webgrænsefladen eller ved at bruge værktøjet yc. For værktøjet vil kommandoen se sådan ud:

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

Ved manuel oprettelse af funktion udfyldes alle parametre på samme måde.

Nu kan den funktion, du har oprettet, testes via udviklerkonsollen, og derefter forbedres og offentliggøres færdigheden.

Oprettelse af stateful færdigheder til Alice ved hjælp af serverless funktioner fra Yandex Cloud og Python

Hvad der gemmer sig under hætten

Make-filen indeholder faktisk et ret simpelt script til installation af afhængigheder og pakning i et arkiv dist.zip, omtrent sådan her:

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 ./

Det øvrige — et par simple værktøjer, indkapslet i et bibliotek tgalice. Proces til udfyldelse af brugeroplysninger beskrives i konfigurationen form.yaml:

form_name: 'horoscope_form'
start:
  regexp: 'start|beg(inn|in)'
  suggests:
    - Start
fields:
  - name: 'name'
    question: Venligst oplys dit navn.
  - name: 'year'
    question: Fortæl mig nu dit fødselsår. Kun fire cifre, intet mere.
    validate_regexp: '^[0-9]{4}$'
    validate_message: Venligst prøv igen. Oplys dit fødselsår - fire cifre.
  - name: 'month'
    question: Fantastisk! Nævn nu måneden, hvor du blev født.
    options:
      - januar
      ...
      - december
    validate_message: Det, du har nævnt, lader ikke til at være en måned. Venligst oplys måneden for din fødsel, uden andre ord.
  - name: 'day'
    question: Fremragende! Til slut, nævn datoen for din fødsel - kun tal, op til to cifre.
    validate_regexp: '[0123]?d$'
    validate_message: Venligst prøv igen. Du skal oplyse dit fødselsnummer (for eksempel den tyvende); det er et eller to cifre.

Python-klassen tager sig af at analysere denne konfiguration og beregne det endelige resultat.

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

    def handle_completed_form(self, form, user_object, ctx):
        response = tgalice.dialog_manager.base.Response(
            text='Tak, {}! Nu ved vi: du er {} år gammel, og du er {}.
            Held og lykke! Stjernerne siger til dig: {}'.format(
                form['fields']['name'],
                2019 - int(form['fields']['year']),
                self.SIGNS[form['fields']['month']],
                random.choice(FORECASTS),
            ),
            user_object=user_object,
        )
        return response

Nærmere bestemt, baseklassen FormFillingDialogManager håndterer udfyldningen af "formen", mens metoden i underklassen handle_completed_form angiver, hvad der skal gøres, når den er klar.

Udover denne primære dialogstrøm skal brugeren også bydes velkommen, og der skal gives vejledning ved kommandoen "hjælp" og afsluttes fra færdigheden med kommandoen "exit". Til dette formål tgalice er der også en skabelon, så hele dialogmanageren er bygget op af stykker:

dm = tgalice.dialog_manager.CascadeDialogManager(
    tgalice.dialog_manager.GreetAndHelpDialogManager(
        greeting_message=DEFAULT_MESSAGE,
        help_message=DEFAULT_MESSAGE,
        exit_message='Farvel, kom tilbage til færdigheden "IT Horoskop" igen!'
    ),
    CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE)
)

CascadeDialogManager fungerer simpelt: forsøger at anvende alle sine komponenter på den nuværende dialogtilstand i rækkefølge og vælger den første relevante.

Som svar på hver meddelelse returnerer dialogmanageren et Python-objekt. Svar, som derefter kan konverteres til ren tekst, eller til en besked i Alice eller Telegram — afhængigt af hvor bot'en er startet; den indeholder også den ændrede dialogtilstand, som skal bevares. Alt dette håndteres af endnu en klasse, DialogConnector, derfor ser det direkte script til at starte færdigheden på Yandex Functions således ud:

...
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

Som I kan se, skaber størstedelen af denne kode en forbindelse til S3-grænsefladen for Object Storage. Hvordan denne forbindelse bruges direkte, kan du læse i koden tgalice.
Den sidste linje opretter funktionen alice_handler — den, vi bad Yandex.Cloud om at trække, da vi angav parameteren --entrypoint=main.alice_handler.

Og det er faktisk det hele. Make-filer til bygning, S3-lignende Object Storage til at gemme konteksten, og Python-biblioteket tgalice. I kombination med serverløse funktioner og Pythons ekspressivitet er dette tilstrækkeligt til at udvikle en sund menneskes færdighed.

Du må spørge, hvorfor det var nødvendigt at lave tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.

Hvis du ønsker en mere seriøs NLU, kan du tilknytte til din færdighed Rasa eller DeepPavlov, men for deres opsætning vil der kræves ekstra dansetrin, især med serverløse systemer. Hvis du slet ikke ønsker at kode, bør du bruge en visuel builder som Aimylogic. Da jeg skabte tgalice, tænkte jeg på en form for mellemvej. Lad os se, hvad der kommer ud af det.

Men nu, meld jer ind i chatten for udviklere af Alice-færdigheder, læs dokumentationen, og skab fantastiske færdigheder!

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster