Vamos começar com as notícias. Ontem, a Yandex.Cloud anunciou o lançamento do serviço de computação serverless . Isso significa: você escreve apenas o código do seu serviço (por exemplo, uma aplicação web ou um chatbot), e a Cloud cria e mantém as máquinas virtuais onde ele é executado, até mesmo replicando-as se a carga aumentar. Não é necessário pensar em nada, é muito conveniente. E a cobrança é apenas pelo tempo de computação.
No entanto, alguns podem não pagar nada. Esses são os desenvolvedores , ou seja, chatbots integrados. Qualquer desenvolvedor pode escrever, hospedar e registrar tal habilidade, e a partir de hoje, não é mais necessário hospedar as habilidades — basta carregar o código na nuvem na forma de .
Mas há algumas nuances. Primeiro, seu código em Python pode exigir algumas dependências, e trazê-las para a Cloud não é trivial. Em segundo lugar, qualquer chatbot normal precisa armazenar o estado do diálogo em algum lugar (por isso é stateful); como fazer isso da forma mais simples em uma função serverless? Em terceiro lugar, como é possível escrever rapidamente uma habilidade para a Alice ou para qualquer bot com uma história não nula? Sobre essas nuances, na verdade, trata-se do artigo.

Preparação moral
Para os impacientes: eu reúno as dependências necessárias com um Makefile antes de carregar a função na nuvem, armazeno o estado do diálogo no Object Storage da Yandex (que suporta a API S3) e uso minha própria biblioteca . No final, obtemos habilidade demo. Agora, vamos analisar tudo isso com um pouco mais de detalhes.
Para os menos impacientes: antes de mergulhar na minha história sobre as dificuldades, eu recomendo começar pelo básico. Ou seja, criar uma função de echo de um arquivo, registrar e testá-la no console do desenvolvedor de Diálogos; detalhes estão no e . E só depois começar.
Se você quer imediatamente sentir pelo que estamos lutando, peça à Alice ‘‘. Deve ocorrer aproximadamente o seguinte diálogo:
Alice: Olá! Você está na habilidade ‘Horóscopo de TI’. Diga ‘Iniciar’ para descobrir o que as estrelas reservam para você.
Usuário: Iniciar
A: Por favor, diga seu nome
U: Vasisiualiy
A: Agora, diga-me o ano do seu nascimento. Apenas quatro dígitos, nada mais.
U: segundo
A: Por favor, tente novamente. Diga o ano do seu nascimento — quatro dígitos.
U: 2002
A: Maravilhoso! Agora, diga-me o mês do seu nascimento.
U: Fevereiro
A: Ótimo! Finalmente, diga-me a data do seu nascimento — apenas o número, de uma ou duas dígitos.
U: 18
A: Obrigado, Vasisuály! Agora sabemos: você tem 17 anos e é Aquário. Você realmente teve sorte! As estrelas dizem para você: o que as estrelas dizem, você descobrirá ao ativar a habilidade.
O problema é que, para manter mesmo um diálogo tão simples, é necessário lembrar o nome e a data de nascimento do usuário, e em um ambiente de funções sem servidor, isso não é trivial. Não é possível armazenar o contexto na memória ou em um arquivo no disco, pois o Yandex.Cloud pode executar a função em várias máquinas virtuais simultaneamente e alternar entre elas de forma arbitrária. Será necessário usar algum armazenamento externo. O Object Storage foi escolhido, como um armazenamento bastante barato e simples diretamente no Yandex.Cloud (ou seja, provavelmente rápido). Como uma alternativa gratuita, você pode tentar, por exemplo, um pedaço grátis longe. E para o Object Storage (que suporta a interface S3) e para o Mongo existem convenientemente wrappers em Python.
Outro problema é que, para navegar tanto no Object Storage quanto no MongoDB, e em qualquer outro banco de dados ou armazenamento de dados, são necessárias algumas dependências externas que precisam ser enviadas para o Yandex Functions junto com o código da sua função. E gostaríamos de fazer isso de forma conveniente. Totalmente conveniente (como no Heroku), infelizmente, não será possível, mas algum conforto básico pode ser criado escrevendo um script para montar o ambiente (um arquivo make).
Como ativar a habilidade de horóscopo
- Preparação: acesse alguma máquina com Linux. Em princípio, também pode ser feito com Windows, mas ao executar o arquivo make, será necessário um pouco de mágica. E, de qualquer forma, você precisará ter o Python instalado, versão 3.6 ou superior.
- Clone para si mesmo do GitHub .
- Cadastre-se no Y. Cloud:
- Crie para si dois buckets em , nomeie-os com qualquer nome
{BUCKET NAME}etgalice-test-cold-storage(este segundo nome está atualmente codificado emmain.pydo meu exemplo). O primeiro bucket será necessário apenas para o deployment, o segundo — para armazenar os estados do diálogo. - Criar , conceder a ela o papel
editor, e obter as credenciais estáticas{KEY ID}e{KEY VALUE}— usaremos isso para registrar o estado do diálogo. Tudo isso é necessário para que a função do Yandex.Cloud possa acessar o armazenamento do Yandex.Cloud. Um dia, espero, a autenticação se tornará automática, mas por enquanto — assim. - (Opcional) instalar
yc. A função também pode ser criada através da interface da web, mas o CLI é bom porque novidades aparecem nele mais rápido. - Agora podemos, na verdade, preparar a montagem das dependências: executar na linha de comando a partir da pasta com o exemplo da habilidade
make all. Uma série de bibliotecas (na maioria, como de costume, desnecessárias) será instalada na pastadist. - Carregue manualmente no Object Storage (no bucket
{BUCKET NAME}) o arquivo gerado na etapa anteriordist.zip. Se desejado, isso também pode ser feito a partir da linha de comando, por exemplo, usando . - Criar uma função serverless através da interface da web ou usando a ferramenta
yc. Para a ferramenta, o comando será assim:
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=3sAo criar a função manualmente, todos os parâmetros são preenchidos de forma semelhante.
Agora, a função que você criou pode ser testada através do console do desenvolvedor, e depois você pode aprimorar e publicar a habilidade.

O que está por trás
O arquivo Make contém na verdade um script bastante simples para instalar dependências e empacotar tudo em um arquivo zip dist.zip, aproximadamente assim:
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 é um conjunto de ferramentas simples, empacotadas em uma biblioteca tgalice. O processo de preenchimento dos dados do usuário é descrito na configuração form.yaml:
form_name: 'horoscope_form'
start:
regexp: 'iniciar|começar'
suggests:
- Iniciar
fields:
- name: 'name'
question: Por favor, diga o seu nome.
- name: 'year'
question: Agora, diga-me o ano do seu nascimento. Apenas quatro dígitos, nada mais.
validate_regexp: '^[0-9]{4}$'
validate_message: Por favor, tente novamente. Diga o ano do seu nascimento - quatro dígitos.
- name: 'month'
question: Maravilha! Agora, diga o mês do seu nascimento.
options:
- janeiro
...
- dezembro
validate_message: O que você mencionou não parece um mês. Por favor, diga o mês do seu nascimento, sem outras palavras.
- name: 'day'
question: Ótimo! Finalmente, diga-me a data do seu nascimento - apenas o número, um ou dois dígitos.
validate_regexp: '[0123]?d$'
validate_message: Por favor, tente novamente. Você precisa dizer o número do seu nascimento (por exemplo, vigésimo); são um ou dois dígitos.O trabalho de analisar essa configuração e calcular o resultado final é feito pela classe Python.
class CheckableFormFiller(tgalice.dialog_manager.form_filling.FormFillingDialogManager):
SIGNS = {
'janeiro': 'Capricórnio',
...
}
def handle_completed_form(self, form, user_object, ctx):
response = tgalice.dialog_manager.base.Response(
text='Obrigado, {}! Agora sabemos: você tem {} anos, e você {}. n'
'Você realmente teve sorte! As estrelas dizem a você: {}'.format(
form['fields']['name'],
2019 - int(form['fields']['year']),
self.SIGNS[form['fields']['month']],
random.choice(FORECASTS),
),
user_object=user_object,
)
return responseMais precisamente, a classe base. FormFillingDialogManager é responsável pelo preenchimento da "formulário", enquanto o método da classe derivada handle_completed_form diz o que fazer quando está pronto.
Além desse fluxo principal do diálogo, o usuário também precisa ser saudado, e uma ajuda deve ser fornecida no comando "ajuda" e sair da habilidade com o comando "sair". Para isso, em tgalice também há um modelo, então o gerenciador de diálogo completo é composto por partes:
dm = tgalice.dialog_manager.CascadeDialogManager(
tgalice.dialog_manager.GreetAndHelpDialogManager(
greeting_message=DEFAULT_MESSAGE,
help_message=DEFAULT_MESSAGE,
exit_message='Adeus, volte ao habilidade "Horóscopo da TI" novamente!'
),
CheckableFormFiller(`form.yaml`, default_message=DEFAULT_MESSAGE)
)CascadeDialogManager funciona simplesmente: tenta aplicar à condição atual do diálogo todas as suas partes em ordem e escolhe a primeira apropriada.
Como resposta a cada mensagem, o gerenciador de diálogo retorna um objeto Python. Response, que pode ser convertido em texto puro, ou em uma mensagem no Alice ou Telegram — dependendo de onde o bot está rodando; também contém o estado alterado do diálogo que precisa ser salvo. Todo esse trabalho é gerenciado por outra classe, DialogConnector, portanto, o script que inicia a habilidade no Yandex Functions fica assim:
...
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 você pode ver, a maior parte deste código cria uma conexão com a interface S3 de Object Storage. Como essa conexão é usada diretamente pode ser lido .
A última linha cria a função alice_handler — aquela que pedimos ao Yandex.Cloud para acionar quando definimos o parâmetro --entrypoint=main.alice_handler.
E é isso. Makefiles para compilação, armazenamento de objetos tipo S3 para guardar o contexto, e uma biblioteca em Python tgalice. Junto com funções serverless e a expressividade do Python, isso é suficiente para desenvolver uma habilidade de qualidade.
Você pode se perguntar por que foi necessário criar tgalice? Весь скучный код, перекладывающий JSON’ы из запроса в ответ и из хранилища в память и обратно, лежит в ней. Там же лежит применялка регулярок, функция для понимания того, что «феврарь» похоже на «февраль», и прочее NLU для бедных. По моей задумке, этого уже должно быть достаточно, чтобы можно было набрасывать прототипы навыков в yaml-файлах, не слишком отвлекаясь на технические детали.
Se você deseja um NLU mais avançado, pode integrar ao seu projeto ou , mas para configurá-los serão necessárias algumas manobras complicadas, especialmente em serverless. Se você realmente não quer codificar, vale a pena usar um construtor visual como . Ao criar tgalice, pensei em algum caminho intermediário. Vamos ver o que resulta disso.
Bem, atualmente entre no , leia , e crie habilidades incríveis !
Fonte: habr.com
