Zacznijmy od wiadomości. Wczoraj Yandex.Cloud ogłosił uruchomienie usługi obliczeń bezserwerowych . Oznacza to, że piszesz tylko kod swojego serwisu (na przykład, aplikacji internetowej lub chatbota), a Chmura sama tworzy i obsługuje maszyny wirtualne, na których działa, a nawet replikuje je, gdy wzrasta obciążenie. Nie musisz w ogóle myśleć, to bardzo wygodne. A opłata jest naliczana tylko za czas obliczeń.
Jednak niektórzy mogą w ogóle nie płacić. To są deweloperzy , czyli wbudowanych w nią chatbotów. Każdy deweloper może napisać, hostować i zarejestrować taką umiejętność, a od dziś nie trzeba ich nawet hostować – wystarczy wrzucić ich kod do chmury w postaci .
Ale jest kilka niuansów. Po pierwsze, twój kod w Pythonie może wymagać pewnych zależności, a wciągnięcie ich do Chmury nie jest trywialne. Po drugie, każdy normalny chatbot potrzebuje gdzieś przechowywać stan dialogu (dlatego jest stateful); jak najłatwiej to zrobić w funkcji bezserwerowej? Po trzecie, jak w ogóle można szybko i brudnie napisać umiejętność dla Alisy lub jakiegokolwiek bota z niezerową fabułą? O tych niuansach jest właściwie artykuł.

Przygotowanie moralne
Dla niecierpliwych: potrzebne zależności zbieram plikiem make przed załadowaniem funkcji do chmury, stan dialogu przechowuję w Yandex Object Storage (obsługuje API S3), a do zarządzania dialogiem używam własnej biblioteki . W rezultacie mamy demo-umiejętność. A teraz omówmy to wszystko trochę dokładniej.
Dla tych, którzy są mniej niecierpliwi: zanim zanurzymy się w mojej historii o trudnościach, bardzo polecam zacząć od prostego. A mianowicie, stworzyć jednoplikiem funkcję echo-bota, zarejestrować ją i przetestować w konsoli dewelopera Dialogów; szczegóły – w i . A dopiero potem zaczynać.
Jeśli chcesz od razu poczuć, o co tu chodzi, poproś Alisę ‘‘. Powinien nastąpić mniej więcej taki dialog:
Alisa: Cześć! Jesteś w umiejętności „Techno horoskop”. Powiedz „Start”, aby dowiedzieć się, co przewidują ci gwiazdy.
Użytkownik: Start
A: Proszę podaj swoje imię
U: Wasilij
A: Teraz powiedz mi rok swojego urodzenia. Tylko cztery cyfry, nic więcej.
U: dwa
A: Proszę spróbować ponownie. Podaj rok swojego urodzenia — cztery cyfry.
J: 2002
A: Wspaniale! Teraz podaj miesiąc swojego urodzenia.
J: Luty
A: Doskonale! Na koniec podaj mi datę swojego urodzenia — tylko dzień, jedna lub dwie cyfry.
J: 18
A: Dziękuję, Wasisuali! Teraz wiemy: masz 17 lat i jesteś Wodnikiem. To ci się oczywiście udało! Gwiazdy mówią ci: a co mówią gwiazdy, dowiesz się, uruchamiając umiejętność.
Problem w tym, że aby prowadzić nawet tak prosty dialog, trzeba pamiętać imię i datę urodzenia użytkownika, a w środowisku funkcji bezserwerowych nie jest to trywialne. Nie da się przechować kontekstu w pamięci operacyjnej ani jako pliku na dysku, ponieważ Yandex.Cloud może uruchomić funkcję na kilku maszynach wirtualnych jednocześnie i swobodnie przełączać się między nimi. Będziesz musiał skorzystać z jakiegoś zewnętrznego magazynu. Wybrano Object Storage, który jest stosunkowo niedrogim i prostym magazynem bezpośrednio w Yandex.Cloud (tj. prawdopodobnie szybkim). Jako bezpłatną alternatywę można spróbować na przykład darmowy fragment gdzieś daleko. Zarówno dla Object Storage (obsługuje interfejs S3), jak i dla Mongo istnieją wygodne opakowania w Pythonie.
Inny problem — aby mieć dostęp do Object Storage, MongoDB i innych baz lub magazynów danych, potrzebne są zewnętrzne zależności, które musisz wgrać na Yandex Functions razem z kodem swojej funkcji. I chciałbyś to robić wygodnie. Całkowicie wygodnie (jak na heroku), niestety, nie uda się, ale jakiś podstawowy komfort można stworzyć, pisząc skrypt do budowy środowiska (plik make).
Jak uruchomić umiejętność-horoskop
- Przygotuj się: wejdź na jakąkolwiek maszynę z Linuxem. W zasadzie da się również pracować z Windows, ale uruchomienie pliku make może wymagać trochę magii. W każdym razie, potrzebujesz zainstalowanego Pythona w wersji 3.6 lub wyższej.
- Skopiuj z gita .
- Zarejestruj się w Yandex.Cloud:
- Stwórz dwa buckety w , nadaj im dowolną nazwę
{BUCKET NAME}itgalice-test-cold-storage(ta druga nazwa jest teraz twardo zakodowana wmain.pymoim przykładzie). Pierwszy bucket będzie potrzebny tylko do wdrożenia, drugi — do przechowywania stanów dialogu. - Utwórz , nadaj mu rolę
editor, i zdobądź do niego statyczne poświadczenia{KEY ID}i{KEY VALUE}— będziemy je używać do rejestrowania stanu rozmowy. To wszystko jest potrzebne, aby funkcja z Yandex.Cloud mogła uzyskać dostęp do przechowywania z Yandex.Cloud. Mam nadzieję, że kiedyś autoryzacja stanie się automatyczna, ale na razie — tak. - (Nieobowiązkowe) zainstalować
yc. Funkcję można stworzyć również przez interfejs internetowy, ale CLI jest dobre tym, że wszelkie nowości pojawiają się w nim szybciej. - Teraz można, właściwie, przygotować zbiór zależności: uruchomić w wierszu poleceń z folderu z przykładem umiejętności
make all. Zainstaluje się mnóstwo bibliotek (głównie, jak zwykle, niepotrzebnych) w folderzedist. - Ręcznie załadować do Object Storage (do koszyka
{BUCKET NAME}) otrzymany w poprzednim kroku archiwumdist.zip. Jeśli chcesz, można to zrobić także z wiersza poleceń, na przykład używając . - Utwórz funkcję bezserwerową przez interfejs internetowy lub używając narzędzia
yc. Dla narzędzia polecenie będzie wyglądać tak:
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=3sPrzy ręcznym tworzeniu funkcji wszystkie parametry wypełniane są analogicznie.
Teraz utworzoną przez Ciebie funkcję można testować przez konsolę dewelopera, a potem dopracować i opublikować umiejętność.

Co tam pod maską
Plik Make w rzeczywistości zawiera dość prosty skrypt do instalowania zależności i pakowania ich w archiwum dist.zip, mniej więcej tak:
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, zapakowanych w bibliotekę tgalice. Proces wypełniania danych o użytkowniku opisywany jest w pliku konfiguracyjnym form.yaml:
form_name: 'horoscope_form'
start:
regexp: 'start|begin'
suggests:
- Start
fields:
- name: 'name'
question: Proszę podać swoje imię.
- name: 'year'
question: Teraz proszę powiedzieć mi rok swojego urodzenia. Tylko cztery cyfry, nic więcej.
validate_regexp: '^[0-9]{4}$'
validate_message: Proszę spróbować ponownie. Podaj rok swojego urodzenia - cztery cyfry.
- name: 'month'
question: Świetnie! Teraz podaj miesiąc swojego urodzenia.
options:
- styczeń
- luty
- marzec
- kwiecień
- maj
- czerwiec
- lipiec
- sierpień
- wrzesień
- październik
- listopad
- grudzień
validate_message: To, co podałeś, nie przypomina miesiąca. Proszę podać miesiąc swojego urodzenia, bez innych słów.
- name: 'day'
question: Doskonale! Na koniec, proszę podać dzień swojego urodzenia - tylko cyfrę, jedną lub dwie.
validate_regexp: '[0123]?d$'
validate_message: Proszę spróbować ponownie. Musisz podać dzień swojego urodzenia (na przykład dwudziesty); to jedna lub dwie cyfry.Zarządzanie rozbiórką tej konfiguracji i obliczaniem ostatecznego wyniku przejmuje klasa Pythona
class CheckableFormFiller(tgalice.dialog_manager.form_filling.FormFillingDialogManager):
SIGNS = {
'styczeń': 'Koziorożec',
...
}
def handle_completed_form(self, form, user_object, ctx):
response = tgalice.dialog_manager.base.Response(
text='Dziękujemy, {}! Teraz wiemy, że masz {} lat, a ty {}. n'
'To na pewno szczęśliwy zbieg okoliczności! Gwiazdy mówią ci: {}'.format(
form['fields']['name'],
2019 - int(form['fields']['year']),
self.SIGNS[form['fields']['month']],
random.choice(FORECASTS),
),
user_object=user_object,
)
return responseDokładniej, klasa bazowa FormFillingDialogManager zajmuje się wypełnianiem "formularza", a metoda klasy podrzędnej handle_completed_form mówi, co robić, gdy jest gotowa.
Poza tym podstawowym wątkiem dialogu użytkownika należy również przywitać oraz wydać pomoc przy komendzie „pomoc” i wyjść z umiejętności przy komendzie „wyjście”. W tym celu tgalice również istnieje szablon, więc kompletny menedżer dialogu składa się z fragmentów:
dm = tgalice.dialog_manager.CascadeDialogManager(
tgalice.dialog_manager.GreetAndHelpDialogManager(
greeting_message=DEFAULT_MESSAGE,
help_message=DEFAULT_MESSAGE,
exit_message='Do widzenia, wróć do umiejętności "IT Horoscope" jeszcze raz!'
),
CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE)
)CascadeDialogManager działa w sposób prosty: próbuje zastosować do bieżącego stanu dialogu wszystkie swoje składniki po kolei i wybiera pierwszą odpowiednią.
Jako odpowiedź na każdą wiadomość menedżer dialogu zwraca obiekt Pythona Odpowiedź, który można następnie skonwertować na czysty tekst lub wiadomość w Alisie lub Telegramie — w zależności od tego, gdzie bot jest uruchomiony; zawiera on również zmieniony stan dialogu, który należy zachować. Całą tę potrzebę obsługuje jeszcze jedna klasa, DialogConnector, dlatego bezpośredni skrypt do uruchomienia umiejętności na 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_handlerJak widać, większość tego kodu tworzy połączenie z interfejsem S3 Object Storage. Jak to połączenie jest następnie używane, można przeczytać .
Ostatnia linia tworzy funkcję alice_handler — tę właśnie, którą kazaliśmy wywoływać Yandex.Cloud, gdy ustawialiśmy parametr --entrypoint=main.alice_handler.
To właściwie wszystko. Pliki Make do budowy, S3-podobne Object Storage do przechowywania kontekstu, oraz biblioteka w Pythonie tgalice. W połączeniu z funkcjami bezserwerowymi i ekspresyjnością Pythona to wystarczy do opracowania umiejętności zdrowego człowieka.
Możesz zapytać, po co potrzeba było tworzyć tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.
Jeśli chcesz bardziej zaawansowanego NLU, możesz do swojego projektu dodać lub , ale do ich konfiguracji będą potrzebne dodatkowe zabiegi, zwłaszcza na serverless. Jeśli nie chcesz zupełnie kodować, warto skorzystać z wizualnego konstruktora typu . Tworząc tgalice, myślałem o jakimś pośrednim rozwiązaniu. Zobaczymy, co z tego będzie.
A teraz dołączcie do , czytajcie , i twórzcie wspaniałe !
Źródło: habr.com
