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

Морална подготовка
За нетърпеливите: необходимите зависимости събирам с make файл преди качването на функцията в облака, състоянието на диалога съхранявам в яндексовия Object Storage (той поддържа API S3), а за управление на диалога използвам собствена библиотека . В крайна сметка получаваме демо-умение. А сега ще разгледаме всичко това малко по-подробно.
За малко по-малко нетърпеливите: преди да се потопите в моята история за трудностите, силно препоръчвам да започнете с нещо просто. А именно, създайте еднофайлова функция-ехобот, регистрирайте я и я тествайте в конзолата на разработчиците на Диалозите; подробности — в и . И едва след това да пристъпите към следващото.
Ако искате веднага да усетите за какво се борим, помолете Алисa ‘‘. Трябва да се случи приблизително следния диалог:
Алисa: Здравейте! Находите се в умението «Айтишен хороскоп». Кажете «Старт», за да разберете какво ви предсказват звездите.
Потребител: Старт
А: Моля, посочете името си.
П: Васисуалий
А: Сега ми кажете годината на вашето раждане. Само четири цифри, нищо излишно.
Ю: втори
А: Моля, опитайте отново. Кажете год на вашето раждане — четири цифри.
Ю: 2002
А: Чудесно! Сега, кажете ми месеца на вашето раждане.
Ю: Февруари
А: Отлично! Накрая, кажете ми датата на вашето раждане — само числото, едно или две цифри.
Ю: 18
А: Благодаря, Васисуалий! Сега знаем: вие сте на 17 години и сте Водолей. Наистина ви е провървяло! Звездите ви казват: какво говорят звездите, ще научите, като стартирате умението.
Проблемът е, че за поддържането дори на такъв прост диалог, е необходимо да помним името и датата на раждане на потребителя, а в средата на безсървърни функции, това не е тривиално. Да се съхранява контекст в оперативната памет или файл на диска не е възможно, тъй като Яндекс.Облако може да стартира функция на няколко виртуални машини едновременно и да превключва между тях произволно. Ще се наложи да използваме някакво външно хранилище. Избрано беше Object Storage, като доста евтино и неусложнено хранилище в Яндекс.Облако (т.е. вероятно бързо). Като безплатна алтернатива можете да опитате, например, безплатен кусочек някъде далеч. И за Object Storage (той поддържа интерфейса S3), и за Mongo съществуват удобни Python обвивки.
Друг проблем е, че за достъп до Object Storage, MongoDB и всяка друга база данни или хранилище са необходими някакви външни зависимости, които трябва да се качат на Yandex Functions заедно с кода на вашата функция. И бих искал да направим това удобно. Съвсем удобно (като на heroku), уви, няма да успее, но известен базов комфорт може да бъде създаден, написвайки скрипт за изграждане на средата (make файл).
Как да стартирате умението за хороскоп
- Подгответе се: влезте в някоя машина с Linux. Всъщност, вероятно може да се работи и с Windows, но при стартиране на make файл тогава ще трябва да се помъчим. И във всеки случай, ще ви трябва инсталиран Python не по-нисък от 3.6.
- Склонирайте от GitHub .
- Регистрирайте се в Я.Облако:
- Създайте си две кофи в , наречете ги с каквото име искате
{BUCKET NAME}иtgalice-test-cold-storage(това второ име в момента е закодирано вmain.pyмоя пример). Първата кофа ще е необходима само за деплой, втората — за съхранение на състоянията на диалога. - Създаване , дайте му роля
редактор, и да получите статични идентификационни данни{KEY ID}и{KEY VALUE}— те ще се използват за записване на състоянието на диалога. Всичко това е необходимо, за да може функцията от Yandex.Cloud да получи достъп до хранилището от Yandex.Cloud. Надявам се, че по някое време удостоверението ще стане автоматично, но за сега — така. - (Не е задължително) да инсталирате
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='Сбогом, идвайте отново в умението "IT хороскоп"!'
),
CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE)
)CascadeDialogManager работи просто: опитва последователно да приложи всички свои съставни елементи към текущото състояние на диалога и избира първото уместно.
Като отговор на всяко съобщение, диалоговият мениджър връща Python обект. Response, който по-далее може да се конвертира в чист текст или в съобщение в Алисa или Телеграм — в зависимост от това къде е активиран ботът; в него също така се съдържа и промененото състояние на диалога, което трябва да се запази. Всичко това се управлява от още един клас, 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 за съхранение на контекста и библиотека на питон. tgaliceВ комбинация с безсървърни функции и изразителността на питона, това е достатъчно за разработка на навик на здрав човек.
Можете да се запитате, защо е нужно да създаваме tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.
Ако искате по-сериозно NLU, можете да добавите към своя навик или , но за тяхната настройка са необходими допълнителни усилия, особено на безсървърно ниво. Ако съвсем не искате да програмирате, заслужава си да се възползвате от визуален конструктор, като . Когато създавах tgalice, мислех за някакъв междинен път. Нека видим какво ще излезе.
Но в момента се присъединете към , четете , и създавайте страхотни !
Източник: habr.com
