Нека започнем с новините. Вчера Yandex.Cloud анонсира старта на услугата за безсървърни изчисления . Това означава: вие пишете само кода на своята услуга (например, уеб-приложение или чатбот), а Облачната платформа сама създава и обслужва виртуални машини, на които тя се изпълнява, и дори ги репликира, ако натоварването се увеличи. Не ви се налага да мислите, много е удобно. И се плаща само за времето на изчисленията.
Въпреки това, някои хора могат изобщо да не плащат. Това са разработчиците , т.е. вградени в нея чатботове. Всеки разработчик може да напише, хостне и регистрира такова умение, а от днешния ден дори не е нужно да се хоства — достатъчно е просто да качите кода му в облака под формата на .
. Но има няколко нюанса. Първо, вашият питон код може да изисква някакви зависимости, а внасянето им в Облачната платформа не е просто. Второ, на всеки нормален чатбот му е нужно да съхранява някъде състоянието на диалога (stateful); как най-лесно да направите това в безсървърна функция? На трето място, как може бързо и мръсно да се напише умение за Алисa или изобщо за някакъв бот с ненулев сюжет? За тези нюанси всъщност е статията.

Морална подготовка
За нетърпеливите: необходимите зависимости събирам с make файл преди да заредя функцията в облака, състоянието на диалога съхранявам в Yandex Object Storage (той поддържа S3 API), а за управление на диалога използвам собствена библиотека . В резултат на това получавате демо умение. А сега нека разгледаме всичко това по-подробно.
За малко по-малко нетърпеливите: преди да се потопите в моята история за трудностите, горещо препоръчвам да започнете с нещо просто. А именно, да създадете функция-ехо в един файл, да я регистрирате и да я тествате в конзолата на разработчика на Диалозите; подробности — в и . И едва след това да започнете.
Ако искате веднага да усетите за какво се борим, помолете Алисa ‘‘. Трябва да се получи приблизително следния диалог:
Алисa: Здравейте! Вие сте в умението „Айтишен хороскоп“. Кажете „Старт“, за да разберете какво ви предсказват звездите.
Юзер: Старт
А: Моля, посочете вашето име
Ю: Васисусий
А: Сега ми кажете годината на вашето раждане. Само четири цифри, нито повече.
Ю: втори
А: Моля, опитайте отново. Посочете годината на вашето раждане — четири цифри.
Ю: 2002
А: Прекрасно! Сега посочете месеца на вашето раждане.
Ю: Февруари
А: Отлично! Накрая, посочете ми датата на вашето раждане — само числото, само една или две цифри.
Ю: 18
А: Благодаря, Васисуалий! Сега знаем: на вас са 17 години, и сте Водолей. Ето, това ви се пада! Звездите ви казват: какво казват звездите, ще разберете, като стартирате умението.
Проблемът е, че за поддържане дори на такъв прост диалог, трябва да помните името и датата на раждане на потребителя, а в среда на безсървизни функции това не е тривиално. Не е възможно да се съхранява контекста в оперативната памет или файл на диска, тъй като Яндекс.Облако може да стартира функция на няколко виртуални машини едновременно и да превключва между тях произволно. Ще трябва да се използва някакво външно хранилище. Избрано беше Object Storage, като доста евтино и лесно хранилище директно в Яндекс.Облако (т.е. вероятно бързо). Като безплатна алтернатива можете да опитате, например, безплатен парче някъде далеч. И за Object Storage (той поддържа интерфейса S3), и за Mongo съществуват удобни Python обвивки.
Друг проблем е, че за достъп и в Object Storage, и в MongoDB, и в която и да е друга база или хранилище от данни, са необходими някакви външни зависимости, които трябва да бъдат качени на Yandex Functions заедно с кода на вашата функция. И би било хубаво да се направи това удобно. Напълно удобно (като на heroku), за съжаление, няма да стане, но някакъв базов комфорт може да се създаде, като напишете скрипт за събиране на средата (make файл).
Как да стартираме умението-гороскоп
- Подготовка: да влезете на някаква машина с линукс. В принцип, с Windows също вероятно може да се работи, но за пускането на make файла тогава ще трябва да се поработи. И в какъвто и да е случай, ще ви трябва инсталиран Python не по-нисък от 3.6.
- Клонирайте си от GitHub .
- Регистрирайте се в Я.Облако:
- Създайте си две бакета в , дайте им каквото име искате
{BUCKET NAME}иtgalice-test-cold-storage(това второ име в момента е хардкоднато вmain.pyна моя пример). Първият бакет ще е необходим само за деплой, вторият — за съхранение на състоянията на диалога. - Създайте , дайте му роля
editor, и да получим статични креденшали{KEY ID}и{KEY VALUE}— ще ги използваме за запис на състоянието на диалога. Всичко това е необходимо, за да може функцията от Я.Облака да получи достъп до хранилището от Я.Облака. Надявам се, че в бъдеще авторизацията ще стане автоматична, но за сега — така. - (Не е задължително) да инсталирате
yc. Можете да създадете функция и чрез уеб интерфейса, но CLI е добър, защото нововъведенията се появяват в него по-бързо. - Сега, всъщност, можете да подготвите сборката на зависимостите: стартирайте в командния ред от папката с примера на умението
make all. Ще се инсталират много библиотеки (обикновено ненужни) в папкатаdist. - Ръчно да качите в Object Storage (в кошницата
{BUCKET NAME}) архива, получен от предишната стъпкаdist.zip. При желание, можете да направите това и от командния ред, например, използвайки . - Създайте безсървърна функция чрез уеб интерфейса или използвайки утилита
yc. За утилитата командата ще изглежда така:
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При ръчно създаване на функцията всички параметри се попълват по аналогичен начин.
Сега създадената от вас функция може да се тества през конзолата на разработчика, а след това да се подобри и публикува умението.

Какво има под капака
Make файлът в действителност съдържа доста прост скрипт за инсталиране на зависимости и тяхното опаковане в архив dist.zip, приблизително такъв:
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 ./*Останалото — няколко прости инструмента, опаковани в библиотека tgalice. Процесът на попълване на данните за потребителя се описва от конфигурационния файл 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: Моля, опитайте отново. Трябва да посочите числото на вашето раждане (например, двадесето); това са една или две цифри.Работата по анализа на този конфигурационен файл и изчисляването на финалния резултат се поема от класа на Python.
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По-точно, основният клас FormFillingDialogManager се занимава със запълването на „формата”, а методът на наследяващия клас handle_completed_form казва какво да правим, когато тя е готова.
Освен този основен поток на диалога, трябва още да приветстваме потребителя, да предоставим информация по командата „помощ” и да изведем от умението по командата „изход”. За това в tgalice също има шаблон, поради което целият диалогов мениджър е съставен от парчета:
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 работи просто: опитва се да приложи последователно всички свои компоненти към текущото състояние на диалога и избира първия подходящ.
В отговор на всяко съобщение диалоговият мениджър връща обект на Python. Отговор, който може да бъде конвертиран в обикновен текст или в съобщение в Алиса или Телеграм — в зависимост от това къде е стартиран ботът; той съдържа и промененото състояние на диалога, което трябва да бъде запазено. Със всичко това се занимава още един клас, DialogConnector, затова непосредственият скрипт за стартиране на умението на Yandex Functions изглежда така:
...
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Както виждате, голяма част от този код създава връзка към S3 интерфейса на Object Storage. Как точно се използва тази връзка, можете да прочетете .
Последният ред създава функция alice_handler — именно тази, на която наредихме на Яндекс.Облака да се обадят, когато задавахме параметъра --entrypoint=main.alice_handler.
Това е всичко. Make файловете за компилация, S3 подобен Object Storage за съхранение на контекста и Python библиотеката tgalice. В комбинация с безсървърните функции и изразителността на Python, това е достатъчно за разработването на умение на здравия разум.
Можете да попитате, защо е необходимо да се създава tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.
Ако искате по-сериозно NLU, можете да прикрепите към вашето умение или , но за настройката им ще са необходими допълнителни усилия, особено на серверс. Ако не искате изобщо да кодите, струва си да се възползвате от визуален конструктор като . Когато създавах tgalice, мислех за някакъв междинен подход. Нека видим какво ще излезе.
Но сега се присъединявайте към , читайте , и създавайте невероятни !
Източник: habr.com
