Tworzenie stanowej umiejętności dla Alice w bezserwerowych funkcjach Yandex.Cloud i Python

Zacznijmy od wiadomości. Wczoraj Yandex.Cloud ogłosił uruchomienie usługi przetwarzania bezserwerowego Funkcje chmury Yandex. Oznacza to: piszesz tylko kod swojej usługi (na przykład aplikacji internetowej lub chatbota), a Chmura sama tworzy i utrzymuje maszyny wirtualne, na których działa, a nawet replikuje je w przypadku wzrostu obciążenia. Nie musisz w ogóle o tym myśleć, jest to bardzo wygodne. A płatność dotyczy tylko czasu obliczeń.

Niektórzy jednak mogą w ogóle nie płacić. To są deweloperzy Umiejętności zewnętrzne Alicji, czyli wbudowane w niego chatboty. Każdy programista może napisać, hostować i zarejestrować taką umiejętność, a od dziś umiejętności nie muszą nawet być hostowane — wystarczy przesłać swój kod do chmury w formularzu ta sama funkcja bezserwerowa.

Ale jest kilka niuansów. Po pierwsze, Twój kod pit może wymagać pewnych zależności, a przeciągnięcie ich do chmury nie jest trywialne. Po drugie, każdy normalny chatbot musi gdzieś przechowywać stan dialogu (a zatem stanowy); Jak najłatwiej to zrobić w funkcji bezserwerowej? Po trzecie, jak można napisać szybką umiejętność dla Alicji lub nawet jakiegoś bota z niezerową fabułą? Artykuł dotyczy w rzeczywistości tych niuansów.

Tworzenie stanowej umiejętności dla Alice w bezserwerowych funkcjach Yandex.Cloud i Python

przygotowanie moralne

Dla niecierpliwych: zbieram niezbędne zależności za pomocą pliku makefile przed przesłaniem funkcji do chmury, przechowuję stan okna dialogowego w Yandex Object Storage (obsługuje API S3), a do zarządzania oknem wykorzystuję własną bibliotekę galice. W rezultacie okazuje się takie umiejętność demonstracyjna. A teraz przeanalizujmy to wszystko bardziej szczegółowo.

Tym, którzy są mniej niecierpliwi, zanim zagłębią się w moją historię o złożoności, gorąco polecam zacząć od czegoś prostego. Mianowicie utwórz jednoplikową funkcję echobota, zarejestruj ją i przetestuj w konsoli programisty Dialogs; szczegóły - w Dialogi blogowe и dokumentacja w chmurze. I dopiero wtedy zacznij.

Jeśli chcesz od razu poczuć o co tu walczymy, zapytaj Alicję”włącz umiejętność „Horoskop IT”'. Powinien nastąpić następujący dialog:

Alicja: Cześć! Jesteś w umiejętności „Horoskop IT”. Powiedz „Start”, aby dowiedzieć się, co obiecują Ci gwiazdy.
Użytkownik: Zacznij
O: Proszę podać swoje imię i nazwisko
Yu: Wasilij
Odp.: Teraz podaj mi rok swoich urodzin. Tylko cztery cyfry, nic więcej.
Yu: drugi
O: Spróbuj ponownie. Podaj rok urodzenia – cztery cyfry.
Yu: 2002
Wspaniały! Teraz podaj miesiąc swoich urodzin.
Yu: Luty
Wspaniały! Na koniec podaj swoją datę urodzenia – tylko liczbę, jedną lub dwie cyfry.
Yu: 18
O: Dziękuję, Vasisualy! Teraz już wiemy: masz 17 lat i jesteś Wodnikiem. Cóż, oczywiście, masz szczęście! Gwiazdy mówią Ci: а что говорят звёзды, вы узнаете, запустив навык.

Problem w tym, że aby utrzymać nawet tak prosty dialog, trzeba pamiętać imię i nazwisko oraz datę urodzenia użytkownika, a w środowisku bezserwerowym nie jest to trywialne. Nie będzie działać przechowywanie kontekstu w pamięci RAM lub jako plik na dysku, ponieważ Yandex.Cloud może uruchomić tę funkcję na kilku maszynach wirtualnych jednocześnie i dowolnie przełączać się między nimi. Będziesz musiał użyć jakiejś zewnętrznej pamięci masowej. Object Storage został wybrany jako dość niedrogi i prosty magazyn bezpośrednio w Yandex.Cloud (czyli prawdopodobnie szybki). Jako bezpłatną alternatywę możesz wypróbować na przykład darmowy kawałek Pochmurno Mongi gdzieś daleko stąd. Zarówno Object Storage (obsługuje interfejs S3), jak i Mongo mają wygodne opakowania Pythona.

Innym problemem jest to, że aby przejść do Object Storage, MongoDB i dowolnej innej bazy danych lub magazynu danych, potrzebujesz zewnętrznych zależności, które musisz przesłać do Yandex Functions wraz z kodem funkcji. A chciałbym to zrobić wygodnie. Jest to całkowicie wygodne (jak na Heroku), niestety nie zadziała, ale możesz stworzyć sobie pewien podstawowy komfort pisząc skrypt budujący środowisko (tworzący plik).

Jak rozpocząć umiejętność horoskopu

  1. Przygotuj się: idź na jakąś maszynę z Linuksem. W zasadzie pewnie też da się pracować z Windowsem, ale wtedy trzeba się wyczarować z uruchomieniem pliku makefile. W każdym razie będziesz potrzebować co najmniej zainstalowanego Pythona w wersji 3.6.
  2. Klon z githuba przykład umiejętności horoskopowej.
  3. Zarejestruj się w Ya.Cloud: https://cloud.yandex.ru
  4. Stwórz sobie dwa wiadra Przechowywanie obiektów, nazywaj ich dowolnym imieniem {BUCKET NAME} и tgalice-test-cold-storage (to drugie imię jest teraz zakodowane na stałe w main.py mój przykład). Pierwszy segment będzie potrzebny tylko do wdrożenia, drugi - do przechowywania stanów dialogowych.
  5. Utworzyć konto serwisowe, daj mu rolę editori uzyskaj dla niego poświadczenia statyczne {KEY ID} и {KEY VALUE} - wykorzystamy je do zarejestrowania stanu dialogu. Wszystko to jest potrzebne, aby funkcja Ya.Cloud mogła uzyskać dostęp do pamięci Ya.Cloud. Mam nadzieję, że kiedyś autoryzacja stanie się automatyczna, ale na razie – tak.
  6. (Opcjonalnie) zainstaluj interfejs linii komend yc. Funkcję można też stworzyć poprzez interfejs WWW, jednak CLI jest dobre, bo szybciej pojawiają się w nim wszelkiego rodzaju innowacje.
  7. Teraz możesz już właściwie przygotować zestaw zależności: uruchom go w wierszu poleceń z folderu z przykładem umiejętności make all. W folderze zostanie zainstalowanych kilka bibliotek (w większości jak zwykle niepotrzebnych). dist.
  8. Wypełnij długopisami magazyn obiektów (do wiadra {BUCKET NAME}) archiwum uzyskane w poprzednim kroku dist.zip. W razie potrzeby możesz to również zrobić z wiersza poleceń, na przykład za pomocą AWS CLI ..
  9. Utwórz funkcję bezserwerową za pośrednictwem interfejsu internetowego lub narzędzia yc. W przypadku narzędzia polecenie będzie wyglądać następująco:

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

Podczas ręcznego tworzenia funkcji wszystkie parametry wypełniane są w ten sam sposób.

Teraz utworzoną funkcję można przetestować za pomocą konsoli programisty, a następnie sfinalizować i opublikować umiejętności.

Tworzenie stanowej umiejętności dla Alice w bezserwerowych funkcjach Yandex.Cloud i Python

Co kryje się pod maską

Plik makefile faktycznie zawiera dość prosty skrypt do instalowania zależności i umieszczania ich w archiwum. dist.zip, coś takiego:

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

Reszta to kilka prostych narzędzi zamkniętych w bibliotece tgalice. Proces uzupełniania danych użytkownika opisuje konfiguracja form.yaml:

form_name: 'horoscope_form'
start:
  regexp: 'старт|нач(ать|ни)'
  suggests:
    - Старт
fields:
  - name: 'name'
    question: Пожалуйста, назовите своё имя.
  - name: 'year'
    question: Теперь скажите мне год вашего рождения. Только четыре цифры, ничего лишнего.
    validate_regexp: '^[0-9]{4}$'
    validate_message: Пожалуйста, попробуйте ещё раз. Назовите год вашего рождения - четыре цифры.
  - name: 'month'
    question: Замечательно! Теперь назовите месяц вашего рождения.
    options:
      - январь
     ...
      - декабрь
    validate_message: То, что вы назвали, не похоже на месяц. Пожалуйста, назовите месяц вашего рождения, без других слов.
  - name: 'day'
    question: Отлично! Наконец, назовите мне дату вашего рождения - только число, всего одна или две цифры.
    validate_regexp: '[0123]?d$'
    validate_message: Пожалуйста, попробуйте ещё раз. Вам нужно назвать число своего рождения (например, двадцатое); это одна или две цифры.

Pracę związaną z analizą tej konfiguracji i obliczeniem końcowego wyniku przejmuje klasa Pythona

class CheckableFormFiller(tgalice.dialog_manager.form_filling.FormFillingDialogManager):
    SIGNS = {
        'январь': 'Козерог',
        ...
    }

    def handle_completed_form(self, form, user_object, ctx):
        response = tgalice.dialog_manager.base.Response(
            text='Спасибо, {}! Теперь мы знаем: вам {} лет, и вы {}. n'
                 'Вот это вам, конечно, повезло! Звёзды говорят вам: {}'.format(
                form['fields']['name'],
                2019 - int(form['fields']['year']),
                self.SIGNS[form['fields']['month']],
                random.choice(FORECASTS),
            ),
            user_object=user_object,
        )
        return response

Dokładniej, klasa bazowa FormFillingDialogManager zajmuje się wypełnianiem „formularza” i metody klasy podrzędnej handle_completed_form mówi, co ma zrobić, gdy będzie gotowa.

Oprócz tego głównego toku dialogu należy także przywitać użytkownika, udzielić mu pomocy komendą „pomoc” i zwolnić go z umiejętności komendą „wyjdź”. W tym celu w tgalice istnieje również szablon, więc cały menedżer okien dialogowych składa się z elementów:

dm = tgalice.dialog_manager.CascadeDialogManager(
    tgalice.dialog_manager.GreetAndHelpDialogManager(
        greeting_message=DEFAULT_MESSAGE,
        help_message=DEFAULT_MESSAGE,
        exit_message='До свидания, приходите в навык "Айтишный гороскоп" ещё!'
    ),
    CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE)
)

CascadeDialogManager działa po prostu: stara się zastosować do aktualnego stanu dialogu po kolei wszystkie jego elementy i wybiera pierwszy odpowiedni.

W odpowiedzi na każdą wiadomość menedżer okna dialogowego zwraca obiekt Pythona Response, który można następnie zamienić na zwykły tekst lub na wiadomość w Alicji lub Telegramie – w zależności od tego, gdzie bot działa; zawiera także zmieniony stan okna dialogowego, który należy zapisać. Całą tą kuchnią zajmuje się inna klasa, DialogConnector, więc bezpośredni skrypt uruchamiania umiejętności w Yandex Functions wygląda następująco:

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

Jak widać, większość tego kodu tworzy połączenie z interfejsem Object Storage S3. Jak to połączenie jest bezpośrednio wykorzystywane, możesz przeczytać w kodzie tgalice.
Ostatnia linia tworzy funkcję alice_handler - ten, który zleciliśmy pobranie Yandex.Cloud, gdy ustawimy parametr --entrypoint=main.alice_handler.

Właściwie to wszystko. Pliki Makefile do montażu, obiektowa pamięć masowa podobna do S3 do przechowywania kontekstu i biblioteka Pythona tgalice. W połączeniu z funkcjami bezserwerowymi i ekspresją Pythona wystarczy to, aby rozwinąć umiejętności zdrowej osoby.

Możesz zapytać, dlaczego musisz tworzyć tgalice? Cały nudny kod, który przenosi JSON z żądania do odpowiedzi oraz z pamięci do pamięci i z powrotem, leży w nim. Istnieje również regularna aplikacja, funkcja zrozumienia, że ​​„luty” jest podobny do „lutego” i inne NLU dla biednych. Według mojego pomysłu powinno to już wystarczyć, aby móc szkicować prototypy umiejętności w plikach yaml bez nadmiernego rozpraszania się szczegółami technicznymi.

Jeśli chcesz poważniejszego NLU, możesz to przykręcić do swoich umiejętności Rasa lub GłębokoPawłow, ale ich ustawienie będzie wymagało dodatkowych tańców z tamburynem, zwłaszcza na serwerze bez serwera. Jeśli w ogóle nie masz ochoty na kodowanie, powinieneś użyć konstruktora wizualnego, takiego jak Aimylogic. Tworząc tgalice myślałem o jakiejś pośredniej ścieżce. Zobaczmy co się stanie.

Cóż, teraz dołącz Czat dla programistów umiejętności Aliy, czytać dokumentacjai twórz niesamowite umiejętności!

Źródło: www.habr.com

Dodaj komentarz