Începem cu știrile. Ieri, Yandex.Cloud a anunțat lansarea serviciului de calcul fără server. . Asta înseamnă că: tu scrii doar codul serviciului tău (de exemplu, aplicații web sau chatbot-uri), iar Cloud creează și întreține singur mașinile virtuale pe care acesta rulează și chiar le replică dacă crește încărcarea. Nu trebuie să te gândești deloc, este foarte convenabil. Și plata se face doar pentru timpul de calcul.
Totuși, unii ar putea să nu plătească deloc. Aceștia sunt dezvoltatorii , adică chatbot-urile încorporate. Orice dezvoltator poate scrie, găzdui și înregistra un astfel de skill, iar de astăzi, nu mai este nevoie să le găzduiești — este suficient să încarci codul acestora în cloud sub forma .
Dar există câteva nuanțe. În primul rând, codul tău Python ar putea necesita anumite dependențe, iar aducerea acestora în Cloud nu este simplă. În al doilea rând, orice chatbot normal trebuie să păstreze undeva starea dialogului (de aceea este stateful); cum se poate face acest lucru cel mai simplu într-o funcție serverless? În al treilea rând, cum poți scrie rapid și eficient un skill pentru Alice sau, în general, pentru un bot cu o poveste non-zero? Despre aceste nuanțe, de fapt, este vorba în articol.

Pregătirea morală
Pentru cei răbdători: dependențele necesare le adun cu un fișier make înainte de a încărca funcția în cloud, păstrez starea dialogului în Object Storage de la Yandex (care suportă API S3), iar pentru gestionarea dialogului folosesc propria bibliotecă . În final, obținem un skill demo. Acum să detaliem toate acestea puțin mai mult.
Pentru cei mai puțin răbdători: înainte de a te adânci în povestea mea despre dificultăți, îți recomand foarte mult să începi cu ceva simplu. Adică, să creezi o funcție echo într-un singur fișier, să o înregistrezi și să o testezi în consola dezvoltatorului Dialoguri; detaliile — în și . Și abia apoi să te apuci.
Dacă vrei să simți imediat pentru ce luptăm aici, cere-i lui Alice ‘‘. Ar trebui să aibă loc un dialog asemănător cu următorul:
Alice: Bună! Te afli în skill-ul „Horoscop IT”. Spune „Start” pentru a afla ce îți promit stelele.
User: Start
A: Te rog, numește-ți numele
U: Vasisuvaliy
A: Acum spuneți-mi anul nașterii dumneavoastră. Numai patru cifre, nimic în plus.
U: al doilea
A: Vă rog, încercați din nou. Menționați anul nașterii dumneavoastră — patru cifre.
U: 2002
A: Minunat! Acum spuneți-mi luna nașterii dumneavoastră.
U: Februarie
A: Excelent! În sfârșit, spuneți-mi ziua nașterii dumneavoastră — doar numărul, total o sau două cifre.
U: 18
A: Mulțumesc, Vasisualiy! Acum știm: ai 17 ani și ești Vărsător. Cu siguranță ai noroc! Stelele îți spun: iar ce spun stelele, vei afla lansând skill-ul.
Problema este că pentru a menține chiar și un dialog așa simplu trebuie să reții numele și data nașterii utilizatorului, iar în mediul funcțiilor fără server, aceasta nu este simplu. Nu poți stoca contextul în memoria volatile sau într-un fișier pe disc, deoarece Yandex.Cloud poate lansa funcția pe mai multe mașini virtuale simultan și poate comuta între ele în mod aleatoriu. Va trebui să utilizezi un stoc exterior. S-a ales Object Storage, ca un depozit destul de ieftin și simplu direct în Yandex.Cloud (adică probabil rapid). Ca alternativă gratuită, poți încerca, de exemplu, o bucată gratuită undeva departe. Și pentru Object Storage (care suportă interfața S3), și pentru Mongo există wrapper-uri Python convenabile.
O altă problemă este că pentru a accesa atât Object Storage, cât și MongoDB, dar și orice altă bază sau depozit de date, ai nevoie de anumite dependențe externe, care trebuie încărcate pe Yandex Functions împreună cu codul funcției tale. Și ar fi bine să faci acest lucru într-un mod convenabil. Din păcate, foarte convenabil (ca pe heroku) nu va fi posibil, dar un anumit confort de bază poate fi creat, scriind un script pentru a construi mediu (un fișier make).
Cum să lansezi skill-ul de horoscop
- Pregătește-te: intră pe o mașină cu Linux. Practic, se poate lucra și cu Windows, dar va trebui să faci câteva trucuri pentru a lansa fișierul make. Și în orice caz, îți va trebui Python instalat, versiunea 3.6 sau mai mare.
- Clonează de pe GitHub .
- Înregistrează-te în Y. Cloud:
- Creează-ți două bucket-uri în , numește-le orice
{BUCKET NAME}șitgalice-test-cold-storage(acest al doilea nume este acum hardcodat înmain.pyexemplul meu). Primul bucket va fi necesar doar pentru deploy, iar al doilea — pentru stocarea stărilor dialogului. - Creare , atribuiți-i un rol
editor, și obțineți acreditive statice pentru acesta{KEY ID}și{KEY VALUE}— le vom folosi pentru a înregistra starea dialogului. Totul este necesar astfel încât funcția din Ya.Cloud să poată accesa stocarea din Ya.Cloud. Sper că, într-o zi, autorizarea va deveni automată, dar momentan — așa. - (Nu este obligatoriu) să instalați
yc. Funcția poate fi creată și prin interfața web, dar CLI este bun pentru că diverse inovații apar aici mai repede. - Acum putem, de fapt, pregăti construirea dependențelor: să o lansăm în linia de comandă din folderul cu exemplul abilității
make all. Se vor instala o mulțime de biblioteci (în principal, ca de obicei, inutile) în folderuldist. - Să le urcăm manual în Object Storage (în bucket
{BUCKET NAME}) arhiul rezultat la pasul precedentdist.zip. Dacă doriți, se poate face și din linia de comandă, de exemplu, folosind . - Crearea unei funcții serverless prin interfața web sau folosind utilitarul
yc. Pentru utilitar, comanda va arăta astfel:
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=3sLa crearea manuală a funcției, toți parametrii se umplu similar.
Acum funcția creată de dvs. poate fi testată prin consola dezvoltatorului, iar apoi îmbunătățită și publicată ca abilitate.

Ce se află sub capotă
Fișierul Make conține, de fapt, un script destul de simplu pentru instalarea dependențelor și aranjarea lor într-un arhivă dist.zip, aproximativ astfel:
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 ./Restul — câteva instrumente simple, împachetate într-o bibliotecă tgalice. Procesul de completare a datelor despre utilizator este descris în configurarea form.yaml:
form_name: 'horoscope_form'
start:
regexp: 'start|begin'
suggests:
- Start
fields:
- name: 'name'
question: Vă rog, numiți-vă numele.
- name: 'year'
question: Acum spuneți-mi anul nașterii dumneavoastră. Doar patru cifre, nimic mai mult.
validate_regexp: '^[0-9]{4}$'
validate_message: Vă rugăm să încercați din nou. Numiti anul nașterii dumneavoastră - patru cifre.
- name: 'month'
question: Minunat! Acum numiți luna nașterii dumneavoastră.
options:
- ianuarie
- februarie
- martie
- aprilie
- mai
- iunie
- iulie
- august
- septembrie
- octombrie
- noiembrie
- decembrie
validate_message: Ceea ce ați menționat nu pare a fi o lună. Vă rugăm să numiți luna nașterii dumneavoastră, fără alte cuvinte.
- name: 'day'
question: Excelent! În cele din urmă, numiți-mi data nașterii dumneavoastră - doar numărul, un singur sau două cifre.
validate_regexp: '[0123]?\d$'
validate_message: Vă rugăm să încercați din nou. Trebuie să menționați ziua nașterii dumneavoastră (de exemplu, a douăzecea); este un singur sau două cifre.Responsabilitatea de a analiza acest config și de a calcula rezultatul final revine clasei Python.
class CheckableFormFiller(tgalice.dialog_manager.form_filling.FormFillingDialogManager):
SIGNS = {
'ianuarie': 'Capricorn',
...
}
def handle_completed_form(self, form, user_object, ctx):
response = tgalice.dialog_manager.base.Response(
text='Mulțumesc, {}! Acum știm: aveți {} ani, și sunteți {}.
'Aveți noroc! Stelele vă spun: {}'.format(
form['fields']['name'],
2019 - int(form['fields']['year']),
self.SIGNS[form['fields']['month']],
random.choice(FORECASTS),
),
user_object=user_object,
)
return responseMai precis, clasa de bază. FormFillingDialogManager se ocupă de completarea „formularului”, iar metoda clasei derivate handle_completed_form indică ce trebuie făcut când este gata.
În plus față de acest flux principal de dialog, utilizatorul trebuie întâmpinat și să i se ofere asistență prin comanda „ajutor” și să fie scos din skill prin comanda „ieșire”. Pentru aceasta, tgalice există și un șablon, astfel încât managerul de dialog complet este format din părți:
dm = tgalice.dialog_manager.CascadeDialogManager(
tgalice.dialog_manager.GreetAndHelpDialogManager(
greeting_message=DEFAULT_MESSAGE,
help_message=DEFAULT_MESSAGE,
exit_message='La revedere, reveniți la skillul "Horoscopul IT"!
),
CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE)
)CascadeDialogManager funcționează simplu: încearcă să aplice toate componentele sale pe rând la starea curentă a dialogului și alege prima care se potrivește.
Ca răspuns la fiecare mesaj, managerul de dialog returnează un obiect Python. Response, care poate fi apoi convertit în text simplu, sau într-un mesaj în Alice sau Telegram — în funcție de unde este pornit bot-ul; acesta conține și starea modificată a dialogului, care trebuie păstrată. Toată această gestionare se face de o altă clasă, DialogConnector, astfel încât scriptul direct pentru activarea abilității pe Yandex Functions arată așa:
...
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_handlerDupă cum vedeți, cea mai mare parte a acestui cod creează o conexiune la interfața S3 a Object Storage. Cum să folosiți efectiv această conexiune, puteți citi .
Ultima linie creează funcția alice_handler — aceea pe care am spus să o apelăm în Yandex.Cloud, când am setat parametru --entrypoint=main.alice_handler.
Asta e tot. Makefile-uri pentru construirea, stocare de tip S3 pentru păstrarea contextului și o bibliotecă Python tgalice. Împreună cu funcțiile serverless și expresivitatea lui Python, acest lucru este suficient pentru dezvoltarea unei abilități eficiente.
Puteți întreba, de ce a fost necesar să creăm tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.
Dacă doriți un NLU mai serios, puteți integra în abilitatea dumneavoastră sau , dar pentru configurarea lor vor fi necesare manevre suplimentare, în special pe serverless. Dacă nu doriți deloc să programați, ar trebui să folosiți un constructor vizual de tipul . Când am creat tgalice, m-am gândit la un fel de cale intermediară. Vom vedea ce va ieși din asta.
Ei bine, acum alăturați-vă , citiți , și creați abilități uimitoare !
Sursa: habr.com
