Laten we beginnen met het nieuws. Gisteren heeft Yandex.Cloud de lancering van een serverless computing service aangekondigd. Dit betekent: je schrijft alleen de code van je service (bijvoorbeeld een webapplicatie of chatbot), terwijl de Cloud zelf de virtuele machines creëert en beheert waar deze wordt uitgevoerd, en zelfs deze repliceert als de belasting toeneemt. Je hoeft er helemaal niet over na te denken, het is heel handig. En je betaalt alleen voor de rekentijd.
Toch kunnen sommigen helemaal niet betalen. Dit zijn de ontwikkelaars , dat zijn ingebouwde chatbots. Iedereen kan zo’n skill schrijven, hosten en registreren, en vanaf vandaag hoef je skills zelfs niet meer te hosten — je hoeft alleen de code in de cloud te uploaden als een .
Maar er zijn een paar nuances. Ten eerste kan jouw Python-code afhankelijkheden vereisen, en deze naar de Cloud brengen is niet triviaal. Ten tweede heeft elke normale chatbot ergens opslag nodig voor de gespreksstatus (stateful), hoe kun je dit het beste doen in een serverless functie? Ten derde, hoe kun je snel en slordig een skill voor Alice of überhaupt een bot met een non-zero verhaal schrijven? Over deze nuances gaat deze artikel.

Morele voorbereiding
Voor de ongeduldigen: de nodige afhankelijkheden verzamel ik met een make-bestand voordat ik de functie in de cloud upload, de gespreksstatus bewaar ik in Yandex's Object Storage (het ondersteunt de S3 API), en voor het beheren van het gesprek gebruik ik mijn eigen bibliotheek . Uiteindelijk krijg je een demo-skill. Laten we dit nu iets gedetailleerder bekijken.
Voor de iets minder ongeduldigen: voordat je je in mijn verhaal over de moeilijkheden verdiept, raad ik ten zeerste aan om met iets eenvoudigs te beginnen. Namelijk, maak een één-bestand echo-bot functie, registreer en test deze in de Dialogen ontwikkelaarsconsole; details staan in de en . En dan pas beginnen.
Als je meteen wilt voelen waar we hier voor strijden, vraag Alice dan ‘‘. Er zou ongeveer de volgende dialoog moeten plaatsvinden:
Alice: Hallo! Je bevindt je in de skill 'IT Horoscoop'. Zeg 'Start' om te weten te komen wat de sterren voor jou voorspellen.
Gebruiker: Start
A: Alstublieft, noem je naam
G: Vassilij
A: Noem nu je geboortejaar. Alleen vier cijfers, niets overbodigs.
G: tweede
A: Probeer het alstublieft nog eens. Noem het jaar van uw geboorte - vier cijfers.
J: 2002
A: Geweldig! Noem nu de maand van uw geboorte.
J: Februari
A: Uitstekend! Noem tenslotte de datum van uw geboorte - slechts een getal, één of twee cijfers.
J: 18
A: Dank u, Vasisaali! Nu weten we: u bent 17 jaar oud en u bent Waterman. Dat is geluk voor u! De sterren zeggen u: wat de sterren zeggen, ontdekt u door de skill te starten.
Het probleem is dat, om zelfs zo'n eenvoudige dialoog te onderhouden, u de naam en de geboortedatum van de gebruiker moet onthouden, en in een serverless omgeving is dat niet triviaal. Het is niet mogelijk om de context in het geheugen of een bestand op de schijf op te slaan, omdat Yandex Cloud de functie op meerdere virtuele machines tegelijkertijd kan uitvoeren en willekeurig tussen hen kan schakelen. We zullen een externe opslagoplossing moeten gebruiken. Object Storage is gekozen, als een relatief goedkope en eenvoudige opslag direct in Yandex Cloud (dus waarschijnlijk snel). Als gratis alternatief kunt u bijvoorbeeld een stukje ergens ver weg proberen. Voor zowel Object Storage (dat de S3-interface ondersteunt) als voor Mongo zijn er handige Python wrappers beschikbaar.
Een ander probleem is dat, om toegang te krijgen tot zowel Object Storage, MongoDB, als andere databases of opslag, u specifieke externe afhankelijkheden nodig heeft die samen met de code van uw functie naar Yandex Functions moeten worden geüpload. En het zou prettig zijn om dit op een handige manier te doen. Helemaal handig (zoals op Heroku) zal helaas niet lukken, maar enige basiscomfort kan worden gecreëerd door een script te schrijven voor het samenstellen van de omgeving (make-bestand).
Hoe de horoscoop skill te starten
- Voorbereiding: ga naar een machine met Linux. In principe is het waarschijnlijk ook mogelijk om met Windows te werken, maar dan zult u waarschijnlijk wat moeten rommelen met het uitvoeren van een make-bestand. En in elk geval heeft u Python versie 3.6 of hoger nodig.
- Kloon de .
- Registreer jezelf bij Yandex Cloud:
- Maak twee buckets aan in , noem ze elke naam
{BUCKET NAME}entgalice-test-cold-storage(deze tweede naam is momenteel hardcoded inmain.pyvan mijn voorbeeld). De eerste bucket is alleen nodig voor de deployment, de tweede - voor het opslaan van de gespreksstatussen. - Creëren , geef hem de rol
editor, en ontvang statische referenties voor hem{KEY ID}en{KEY VALUE}— we will use them to record the state of the dialogue. All this is needed so that the function from Y.Cloud can access the storage from Y.Cloud. Hopefully, one day authorization will become automatic, but for now — this is how it is. - (Not required) to install
yc. You can create a function through the web interface, but the CLI is good because new features appear there faster. - Now, you can actually prepare the dependency build: run it in the command line from the folder with the skill example
make all. A bunch of libraries (mostly, as usual, unnecessary) will be installed in the folderdist. - Manually upload the resulting archive to Object Storage (to the bucket
{BUCKET NAME}) from the previous stepdist.zip. If desired, you can also do this from the command line, for example, using . - Create a serverless function via the web interface or using the utility
yc. For the utility, the command will look like this:
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=3sWhen manually creating a function, all parameters are filled in similarly.
Now the function you created can be tested through the developer console, and then refined and published as a skill.

What's under the hood
The Makefile actually contains a rather simple script for installing dependencies and packaging them into an archive dist.zip, approximately like this:
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 ./The rest are several simple tools wrapped in a library tgalice. The process of filling in user data is described by the config form.yaml:
form_name: 'horoscope_form'
start:
regexp: 'start|begin'
suggests:
- Start
fields:
- name: 'name'
question: Noem alstublieft uw naam.
- name: 'year'
question: Vertel me nu het jaar van uw geboorte. Alleen vier cijfers, niets meer.
validate_regexp: '^[0-9]{4}$'
validate_message: Probeer het alstublieft nog eens. Geef het jaar van uw geboorte op - vier cijfers.
- name: 'month'
question: Geweldig! Noem nu de maand van uw geboorte.
options:
- januari
- februari
- maart
- april
- mei
- juni
- juli
- augustus
- september
- oktober
- november
- december
validate_message: Wat u hebt genoemd, lijkt niet op een maand. Noem alstublieft de maand van uw geboorte, zonder andere woorden.
- name: 'day'
question: Geweldig! Tenslotte, noem me de datum van uw geboorte - alleen het getal, in totaal één of twee cijfers.
validate_regexp: '[0123]?\d$'
validate_message: Probeer het alstublieft nog eens. U moet het getal van uw geboorte noemen (bijvoorbeeld de twintigste); dit is één of twee cijfers.De verwerking van deze configuratie en het berekenen van het uiteindelijke resultaat wordt door de Python-klasse afgehandeld.
class CheckableFormFiller(tgalice.dialog_manager.form_filling.FormFillingDialogManager):
SIGNS = {
'januari': 'Steenbok',
...
}
def handle_completed_form(self, form, user_object, ctx):
response = tgalice.dialog_manager.base.Response(
text='Dank u, {}! Nu weten we: u bent {} jaar oud, en u bent {}. n'
'Dat is echt geluk voor u! De sterren zeggen u: {}'.format(
form['fields']['name'],
2019 - int(form['fields']['year']),
self.SIGNS[form['fields']['month']],
random.choice(FORECASTS),
),
user_object=user_object,
)
return responseOm precies te zijn, de basis klasse. FormFillingDialogManager is verantwoordelijk voor het invullen van de "formulier" en de methode van de subklasse. handle_completed_form geeft aan wat te doen wanneer deze klaar is.
Daarnaast moet de belangrijkste dialoogstroom van de gebruiker ook verwelkomd worden, evenals een hulpbericht gegeven bij het commando "help" en het vaardigheid verlaten bij het commando "exit". Hiervoor is er ook een sjabloon, zodat de complete dialoogmanager uit stukjes is opgebouwd: tgalice dm = tgalice.dialog_manager.CascadeDialogManager( tgalice.dialog_manager.GreetAndHelpDialogManager( greeting_message=DEFAULT_MESSAGE, help_message=DEFAULT_MESSAGE, exit_message='Vaarwel, kom terug naar de vaardigheid "IT Horoscoop"!' ), CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE) )
CascadeDialogManagerwerkt eenvoudig: probeert al zijn componenten in volgorde toe te passen op de huidige state van de dialoog en kiest de eerste passende. Als antwoord op elk bericht geeft de dialoogmanager een Python-object terug.
In antwoord op elk bericht retourneert de gesprekbeheerder een Python-object Response, wat verder kan worden geconverteerd naar platte tekst, of naar een bericht in Alice of Telegram - afhankelijk van waar de bot draait; het bevat ook de gewijzigde status van de dialoog, die moet worden opgeslagen. Al deze vreugde wordt behandeld door nog een klasse, DialogConnector, daarom ziet het directe script voor het starten van de vaardigheid op Yandex Functions er ongeveer zo uit:
...
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_handlerZoals je kunt zien, de meeste van deze code creëert een verbinding met de S3-interface voor Object Storage. Hoe deze verbinding precies wordt gebruikt, kun je lezen .
De laatste regel creëert de functie alice_handler — degene die we hebben opgedragen Yandex Cloud te trekken, toen we de parameter instelenden --entrypoint=main.alice_handler.
Dat is het eigenlijk. Make-bestanden voor de bouw, S3-achtige Object Storage voor het opslaan van de context, en een Python-bibliotheek tgalice. In combinatie met serverless functies en de expressiviteit van Python is dit voldoende voor de ontwikkeling van een gezonde vaardigheid.
Je kunt je afvragen waarom het nodig was om te maken tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.
Als je een serieuzere NLU wilt, kun je eraan denken om aan je vaardigheid toe te voegen of , maar voor hun configuratie zijn extra toeters en bellen nodig, vooral op serverless. Als je helemaal niet wilt coderen, kun je beter een visuele bouwer zoals gebruiken . Bij het creëren van tgalice dacht ik aan een soort tussenweg. Laten we zien wat daaruit komt.
Nou, meld je vandaag aan voor , lees , en maak geweldige !
Bron: habr.com
