Comencemos con las noticias. Ayer, Yandex.Cloud anunció el lanzamiento del servicio de computación sin servidor. . Esto significa que solo escribes el código de tu servicio (por ejemplo, una aplicación web o un chatbot), mientras que la Nube crea y mantiene las máquinas virtuales donde se ejecuta, e incluso las replica si aumenta la carga. No necesitas pensar en nada, es muy conveniente. Y solo se paga por el tiempo de computación.
Sin embargo, algunas personas pueden no pagar en absoluto. Estos son los desarrolladores , es decir, chatbots integrados en ella. Cualquiera puede escribir, alojar y registrar esa habilidad, y a partir de hoy no es necesario alojar habilidades; simplemente puedes subir su código a la nube como .
Pero hay un par de matices. Primero, tu código en Python puede requerir ciertas dependencias, y ensamblarlas en la Nube no es trivial. En segundo lugar, cualquier chatbot normal necesita almacenar el estado del diálogo en algún lugar (por eso es stateful); ¿cómo se puede hacer esto en una función sin servidor de la manera más sencilla? Y en tercer lugar, ¿cómo se puede escribir rápida y suciamente una habilidad para Alice o algún otro bot con una trama no trivial? De estos matices trata precisamente el artículo.

Preparación moral
Para los impacientes: recojo las dependencias necesarias con un archivo make antes de cargar la función en la nube, almaceno el estado del diálogo en el Object Storage de Yandex (que soporta la API S3), y para gestionar el diálogo utilizo mi propia biblioteca . Al final, obtenemos una habilidad de demostración. Ahora desglosaremos todo esto con un poco más de detalle.
Para los que son un poco menos impacientes: antes de sumergirte en mi historia sobre los problemas, te recomiendo empezar con algo simple. Es decir, crear una función de chatbot de eco de un solo archivo, registrarla y probarla en la consola del desarrollador de Diálogos; más detalles en y . Y solo después proceder.
Si quieres experimentar de inmediato por qué luchamos aquí, pídele a Alice ‘‘. Debería ocurrir un diálogo similar al siguiente:
Alice: ¡Hola! Estás en la habilidad “Horóscopo IT”. Diga “Inicio” para saber lo que les deparan las estrellas.
Usuario: Inicio
A: Por favor, dime tu nombre.
U: Vasílual
A: Ahora dime el año de tu nacimiento. Solo cuatro dígitos, nada más.
Y: segundo
A: Por favor, inténtalo de nuevo. Indica el año de tu nacimiento — cuatro dígitos.
Y: 2002
A: ¡Maravilloso! Ahora indica el mes de tu nacimiento.
Y: Febrero
A: ¡Excelente! Finalmente, dime el día de tu nacimiento — solo un número, uno o dos dígitos.
Y: 18
A: Gracias, Vasisualiy! Ahora sabemos: tienes 17 años y eres Acuario. ¡Tienes suerte! Las estrellas te dicen: lo que las estrellas dicen, lo sabrás al iniciar la habilidad.
El problema es que para mantener incluso un diálogo tan simple, es necesario recordar el nombre y la fecha de nacimiento del usuario, y en un entorno de funciones sin servidor esto no es trivial. No se puede almacenar el contexto en la memoria operativa o en un archivo en el disco, ya que Yandex.Cloud puede iniciar la función en varias máquinas virtuales al mismo tiempo y cambiar entre ellas de forma arbitraria. Tendrás que usar algún tipo de almacenamiento externo. Se eligió Object Storage, que es un almacenamiento bastante barato y simple directamente en Yandex.Cloud (es decir, probablemente rápido). Como alternativa gratuita, puedes intentar, por ejemplo, una porción gratuita. en algún lugar lejano. Tanto para Object Storage (que soporta la interfaz S3) como para Mongo, existen envoltorios convenientes en Python.
Otro problema es que para acceder tanto a Object Storage como a MongoDB, y a cualquier otra base de datos o almacenamiento de datos, necesitas algunas dependencias externas que debes cargar en Yandex Functions junto con el código de tu función. Y sería bueno hacerlo cómodamente. Por desgracia, no se podrá lograr completamente una gran comodidad (como en heroku), pero se puede crear cierto confort básico escribiendo un script para compilar el entorno (Makefile).
Cómo iniciar la habilidad de horóscopo
- Prepararte: acceder a alguna máquina con Linux. En principio, también se podría trabajar con Windows, pero con el inicio de Makefile tendrás que hacer algunos malabarismos. Y en cualquier caso, necesitarás tener Python instalado en la versión 3.6 o superior.
- Clonar desde GitHub .
- Registrarte en Y. Cloud:
- Crear dos buckets en , llamarlos con cualquier nombre
{BUCKET NAME}ytgalice-test-cold-storage(este segundo nombre está ahora codificado enmain.pyde mi ejemplo). El primer bucket solo se necesitará para el despliegue, el segundo para almacenar los estados del diálogo. - Crear , darle el rol
editor, y obtener credenciales estáticas para él.{KEY ID}y{KEY VALUE}— los utilizaremos para registrar el estado del diálogo. Todo esto es necesario para que la función de Y.Cloud pueda acceder al almacenamiento de Y.Cloud. Espero que algún día la autorización se vuelva automática, pero por ahora — así es. - (No obligatorio) instalar
yc. Se puede crear una función a través de la interfaz web, pero el CLI es bueno porque las novedades aparecen en él más rápidamente. - Ahora podemos, en realidad, preparar la construcción de dependencias: ejecutar en la línea de comandos desde la carpeta del ejemplo de la habilidad
make all. Se instalará un montón de bibliotecas (principalmente, como de costumbre, innecesarias) en la carpetadist. - Subir manualmente al Object Storage (en el bucket
{BUCKET NAME}) el archivo que resultó del paso anterior.dist.zip. Si lo deseas, también se puede hacer desde la línea de comandos, por ejemplo, usando . - Crear una función sin servidor a través de la interfaz web o utilizando la utilidad
yc. Para la utilidad el comando se verá así:
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=3sAl crear la función manualmente, todos los parámetros se rellenan de manera similar.
Ahora la función que has creado se puede probar a través de la consola de desarrolladores, y luego mejorar y publicar la habilidad.

Qué hay debajo del capó
El archivo Make en realidad contiene un script bastante simple para instalar dependencias y empaquetarlas en un archivo. dist.zip, aproximadamente así:
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 ./El resto son algunas herramientas simples, empaquetadas en una biblioteca. tgalice. El proceso de llenar los datos del usuario se describe en la configuración form.yaml:
form_name: 'horoscope_form'
start:
regexp: 'inicio|comenzar'
suggests:
- Inicio
fields:
- name: 'name'
question: Por favor, dígame su nombre.
- name: 'year'
question: Ahora dígame el año de su nacimiento. Solo cuatro dígitos, nada más.
validate_regexp: '^[0-9]{4}$'
validate_message: Por favor, inténtelo de nuevo. Indique su año de nacimiento - cuatro dígitos.
- name: 'month'
question: ¡Maravilloso! Ahora dígame el mes de su nacimiento.
options:
- enero
- febrero
- marzo
- abril
- mayo
- junio
- julio
- agosto
- septiembre
- octubre
- noviembre
- diciembre
validate_message: Lo que ha dicho no parece un mes. Por favor, indique el mes de su nacimiento, sin otras palabras.
- name: 'day'
question: ¡Excelente! Finalmente, indíqueme la fecha de su nacimiento - solo el número, un o dos dígitos.
validate_regexp: '[0123]?d$'
validate_message: Por favor, inténtelo de nuevo. Necesita indicar el número de su nacimiento (por ejemplo, veinte); son uno o dos dígitos.El trabajo de procesar esta configuración y calcular el resultado final es responsabilidad de la clase de Python.
class CheckableFormFiller(tgalice.dialog_manager.form_filling.FormFillingDialogManager):
SIGNS = {
'enero': 'Capricornio',
...
}
def handle_completed_form(self, form, user_object, ctx):
response = tgalice.dialog_manager.base.Response(
text='Gracias, {}! Ahora sabemos que tienes {} años y eres {}. n'
'¡Tienes mucha suerte! Las estrellas te dicen: {}'.format(
form['fields']['name'],
2019 - int(form['fields']['year']),
self.SIGNS[form['fields']['month']],
random.choice(FORECASTS),
),
user_object=user_object,
)
return responseMás precisamente, la clase base. FormFillingDialogManager se encarga de llenar la “forma”, y el método de la clase hija handle_completed_form indica qué hacer cuando está lista.
Además de este flujo principal del diálogo del usuario, también debe dar la bienvenida, así como proporcionar ayuda al comando "ayuda" y salir de la habilidad al comando "salir". Para esto, en tgalice también hay una plantilla, por lo que el gestor de diálogo completo se compone de piezas:
dm = tgalice.dialog_manager.CascadeDialogManager(
tgalice.dialog_manager.GreetAndHelpDialogManager(
greeting_message=DEFAULT_MESSAGE,
help_message=DEFAULT_MESSAGE,
exit_message='¡Adiós, vuelva al skill "Horóscopo IT" pronto!'
),
CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE)
)CascadeDialogManager funciona simplemente: intenta aplicar a la situación actual del diálogo todos sus componentes uno por uno, y selecciona el primero que sea apropiado.
Como respuesta a cada mensaje, el gestor de diálogo devuelve un objeto de Python. Response, que luego se puede convertir en texto plano, o en un mensaje en Alice o Telegram, dependiendo de dónde se esté ejecutando el bot; también contiene el estado modificado del diálogo que debe ser guardado. Todo esto es manejado por otra clase, DialogConnector, por lo que el script directo para iniciar la habilidad en Yandex Functions se ve así:
...
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_handlerComo pueden ver, gran parte de este código establece la conexión con la interfaz S3 de Object Storage. Se puede leer sobre cómo se utiliza esta conexión .
La última línea crea la función alice_handler — esa misma que le dijimos a Yandex Cloud que llamara cuando especificamos el parámetro --entrypoint=main.alice_handler.
Y eso es todo. Archivos Make para la construcción, almacenamiento de objetos similar a S3 para guardar el contexto, y la biblioteca de Python tgalice. Junto con funciones sin servidor y la expresividad de Python, esto es suficiente para desarrollar la habilidad de una persona saludable.
Puede preguntarse por qué es necesario crear tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.
Si se desea un NLU más avanzado, se puede acoplar a su habilidad o , pero se requerirá una configuración adicional, especialmente en un entorno sin servidor. Si realmente no se quiere programar, es mejor usar un constructor visual como . Al crear tgalice, pensé en algún camino intermedio. Veremos qué resulta de esto.
Bueno, ahora únete al , lee , y crea maravillosas !
Fuente: habr.com
