Criando uma habilidade stateful para Alice nas funções sem servidor do Yandex.Cloud e Python

Vamos começar com as notícias. Ontem, o Yandex.Cloud anunciou o lançamento do serviço de computação sem servidor Yandex Cloud Functions . Isso significa: você escreve apenas o código do seu serviço (por exemplo, um aplicativo da web ou chatbot), e a própria nuvem cria e mantém máquinas virtuais onde é iniciada e até as replica se a carga aumentar. Não é necessário pensar, é muito conveniente. E a taxa vai apenas durante o cálculo.


No entanto, alguns podem não pagar nada. Esses são os desenvolvedores das habilidades externas de Alice , ou seja, os chatbots incorporados a ela. Qualquer desenvolvedor pode escrever, hospedar e registrar essa habilidade, e a partir de hoje as habilidades nem precisam ser hospedadas - basta carregar seu código na nuvem na forma da função sem servidor .


Mas existem algumas nuances. Primeiro, seu código de estimação pode exigir algumas dependências, e puxá-las para a nuvem não é trivial. Em segundo lugar, qualquer chatbot normal precisa armazenar o estado do diálogo em algum lugar (portanto, com estado); como fazer isso em uma função sem servidor o mais fácil? Em terceiro lugar, como você pode escrever de maneira rápida e suja uma habilidade para Alice ou algum tipo de bot com um gráfico diferente de zero? Sobre essas nuances, de fato, um artigo.


imagem

Treinamento moral


Para os impacientes: coleciono as dependências necessárias com um arquivo make antes de carregar a função na nuvem, armazeno o estado do diálogo no Yandex Object Storage (ele suporta a API S3) e, para gerenciar o diálogo, uso minha própria biblioteca tgalice . O resultado é uma habilidade de demonstração. Agora vamos dar uma olhada em tudo isso em mais detalhes.


Para um pouco menos impaciente: antes de mergulhar na minha história sobre dificuldades, recomendo começar com uma simples. Ou seja, crie uma função de sonda de eco de arquivo único, registre e teste-a no console do desenvolvedor de Dialogs; detalhes estão no Cloud Dialogs e no blog de documentação . E só então para prosseguir.


Se você quiser sentir imediatamente pelo que estamos lutando, peça a Alice para ativar a habilidade "horóscopo de TI " . A seguinte caixa de diálogo deve acontecer:


Alice: Oi! Você está na habilidade do horóscopo de Aishish. Diga "Iniciar" para descobrir o que as estrelas prometem.
Usuário: Iniciar
A: Por favor, dê o seu nome.
Yu: Vasisuali
A: Agora me diga o ano do seu nascimento. Apenas quatro números, nada mais.
Yu: segundo
A: Por favor, tente novamente. Qual é o ano do seu nascimento - quatro dígitos.
U: 2002
A: Ótimo! Agora, nomeie o mês do seu nascimento.
U: fevereiro
A: Ótimo! Finalmente, diga-me a data de seu nascimento - apenas um número, apenas um ou dois dígitos.
U: 18
A: Obrigado, Vasisuali! Agora sabemos: você tem 17 anos e é Aquário. Aqui está, claro, sorte! As estrelas dizem: o , , .


O problema é que, para manter um diálogo tão simples, é necessário lembrar o nome e a data de nascimento do usuário e, em um ambiente sem servidor, isso não é trivial. Armazene o contexto na RAM ou um arquivo no disco não funcionará, porque O Yandex.Cloud pode executar a função em várias máquinas virtuais ao mesmo tempo e alternar entre elas arbitrariamente. Teremos que usar algum tipo de armazenamento externo. O Armazenamento de Objeto foi escolhido como um armazenamento bastante barato e sem complicações, no Yandex.Cloud (ou seja, provavelmente rápido). Como alternativa gratuita, você pode tentar, por exemplo, um pedaço de Monga nublado em algum lugar distante. O Object Storage (que suporta a interface S3) e o Mongo possuem wrappers Python convenientes.


Outro problema é que, para caminhar no Armazenamento de Objetos, no MongoDB e em qualquer outro banco de dados ou data warehouse, você precisa de algumas dependências externas que precisam ser carregadas no Yandex Functions junto com o seu código de função. E eu gostaria de fazê-lo convenientemente. É bastante conveniente (como o heroku), infelizmente, não vai funcionar, mas você pode criar algum conforto básico escrevendo um script para criar o ambiente (make-file).


Como executar uma habilidade horóscopo


  1. Prepare-se: vá para alguma máquina Linux. Em princípio, você provavelmente também pode trabalhar com o Windows, mas depois precisa invocar o lançamento do make-file. E, em qualquer caso, você precisará instalar o Python não inferior a 3,6.
  2. Clone-se no github como um exemplo de uma habilidade horóscopo .
  3. Registre-se no Y. Cloud: https://cloud.yandex.ru
  4. Crie dois buckets para você no Object Storage , nomeie-os com qualquer nome {BUCKET NAME} e tgalice-test-cold-storage (esse segundo nome agora está codificado no main.py meu exemplo). O primeiro bucket será necessário apenas para implantação, o segundo - para armazenar estados de diálogo.
  5. Crie uma conta de serviço , dê a ela a função de editor e obtenha cartões de crédito estáticos {KEY ID} e {KEY VALUE} - nós os usaremos para registrar o status do diálogo. Tudo isso é necessário para que uma função da Y. Cloud possa acessar o repositório da Y. Cloud. Algum dia, espero que a autorização se torne automática, mas por enquanto - então.
  6. (Opcional) instale a interface da linha de comandos yc . Você pode criar uma função por meio da interface da web, mas a CLI é boa, pois todos os tipos de inovações aparecem nela mais rapidamente.
  7. Agora você pode, de fato, preparar o assembly de dependência: execute no prompt de comando em uma pasta com o exemplo make all skill. Um monte de bibliotecas (principalmente, como de costume, desnecessárias) será instalado na pasta dist .
  8. Despeje no armazenamento de objetos (no {BUCKET NAME} ) o arquivo dist.zip obtido na etapa anterior. Se desejar, você pode fazer isso na linha de comando, por exemplo, usando a CLI da AWS .
  9. Crie uma função sem servidor por meio da interface da web ou usando o utilitário yc . Para o utilitário, o comando terá a seguinte aparência:

 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 

Ao criar manualmente uma função, todos os parâmetros são preenchidos da mesma maneira.


Agora, a função que você criou pode ser testada no console do desenvolvedor e, em seguida, modificar e publicar a habilidade.



O que há sob o capô


O makefile na verdade contém um script bastante simples para instalar as dependências e colocá-las no arquivo dist.zip , algo como isto:


 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 ./* 

O restante são algumas ferramentas simples tgalice biblioteca tgalice . O processo de preenchimento de dados do usuário é descrito na form.yaml 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: ,   .       (, );     . 

O trabalho de analisar essa configuração e calcular o resultado final é realizado pela classe 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 

Mais precisamente, a classe base FormFillingDialogManager está envolvida no preenchimento do "formulário", e o método da classe filho handle_completed_form diz o que fazer quando estiver pronto.


Além desse fluxo principal de diálogo do usuário, você também precisa cumprimentar e também dar ajuda no comando "help" e liberar a habilidade no comando "exit". tgalice também possui um modelo para isso, portanto todo o gerenciador de diálogo é composto de partes:


 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 funciona simplesmente: ele tenta aplicar todos os seus componentes ao estado atual do diálogo, por sua vez, e seleciona o primeiro apropriado.


Como resposta a cada mensagem, o gerenciador de diálogo retorna um objeto Response , que pode ser convertido em texto simples ou em uma mensagem em Alice ou Telegram, dependendo de onde o bot é iniciado; Ele também contém o estado alterado do diálogo, que deve ser salvo. Outra classe, DialogConnector , está envolvida em toda essa cozinha; portanto, o script direto para iniciar a habilidade no Yandex Functions se parece com:


 ... 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 

Como você pode ver, a maior parte desse código cria uma conexão com a interface do Object Storage S3. Como essa conexão é usada diretamente pode ser lida no código tgalice .
A última linha cria a função alice_handler - aquela que pedimos ao Yandex.Cloud para puxar quando definimos o --entrypoint=main.alice_handler .


Isso, de fato, é tudo. Makefiles para o assembly, armazenamento de objetos do tipo S3 para armazenamento de contexto e a biblioteca python tgalice. Juntamente com as funções sem servidor e a expressividade do python, isso é suficiente para desenvolver a habilidade de uma pessoa saudável.


Você pode perguntar, por tgalice você precisou criar tgalice ? Todo o código chato que transfere JSONs de solicitação para resposta e de armazenamento para memória e vice-versa está nele. Há também um aplicativo de controlador regular, uma função para entender como é “fevereiro” e “fevereiro” e outras NLU para os pobres. De acordo com minha idéia, isso já deve ser suficiente para que você possa esboçar protótipos de habilidades em arquivos yaml sem se distrair com os detalhes técnicos.


Se você quer um NLU mais sério, pode ferrar Rasa ou DeepPavlov com sua habilidade, mas para configurá-los, você precisará de danças adicionais com um pandeiro, especialmente em servidores. Se você não deseja codificar, use um construtor visual como o Aimylogic . Ao criar tgalice, eu estava pensando em algum tipo de caminho intermediário. Vamos ver o que acontece.


Bem, agora participe do bate-papo dos desenvolvedores de suas habilidades , leia a documentação e crie habilidades maravilhosas!

Source: https://habr.com/ru/post/pt469723/


All Articles