Serverless e Alexa - personalizando sua assistente pessoal

mrmorales

Paulo Guilherme

Posted on June 26, 2022

Serverless e Alexa - personalizando sua assistente pessoal

"Alexa o que iremos fazer hoje?"
"-Vamos desenvolver nossa própria skill usando Nodejs e AWS Lambda!"

As assistentes pessoais já fazem parte dos nossos dias, com suas inúmeras utilidades e possibilidades a Alexa tem se tornado a queridinha das assistentes.

Quer aprender como desenvolver sua própria Skill e ter a Alexa um pouquinho mais com a sua cara?
Então vem comigo, que vou mostrar um pouco como foi minha experiência desenvolvendo com a Alexa.😉

Antes de começarmos ...

Todo comando que fazemos para Alexa é dividido em pequenas partes.

diagrama exibindo o fluxo de comando da alexa

intent: Comando de voz.

utterances: Enunciado que identifica a sua intenção.

slots: Valor do comando que será enviado para API.

A Alexa funciona dentro de uma arquitetura serverless, então para termos nossa própria skill vamos precisar de uma function AWS Lambda e podemos aproveitar do Free Tier da AWS para desenvolver e hospedar a nossa funçãozinha. 😎

alexa cloud system

O diagrama acima exemplifica bem como vai funcionar todo o fluxo saindo do usuário até a execução da lambda.

jin carry digitando super rápido

Show me the code

Chega de conversa e vamos para o código, para esse tutorial vamos desenvolver uma skill que nos responda com um ensinamento aleatório do nosso querido Mestre Yoda.

O código dessa solução está disponível no Github, clique no link do repositório

Mestre Yoda - Star Wars

Vamos usar o framework Serverles para nos ajudar nessa missão.
Com ele vamos conseguir atualizar e fazer o deploy da lambda que será executada sempre que falarmos com a Alexa.

Ingredientes

Com todos os ingredientes no balcão e as contas devidamente criadas vamos ao modo de preparo.

Criando o projeto

Vamos começar fazendo o download das ferramentas que vamos utilizar.

$ npm install -g serverless
Enter fullscreen mode Exit fullscreen mode

Depois de instalado vamos criar uma pasta e rodar o seguinte comando.

$ serverless create --name ensinamentos-yoda --template aws-nodejs
Enter fullscreen mode Exit fullscreen mode

Vale lembrar que o serverless utiliza as configurações e o usuário configurado no AWS CLI, caso não tenha baixado é super fácil existem vários exemplos na documentação oficial ou na nossa comunidade.

O framework Serverless tem uma variedade de plugins para nos ajudar durante o desenvolvimento, nessa missão vamos precisar do serverless-alexa-skills.

$ serverless plugin install --name serverless-alexa-skills
Enter fullscreen mode Exit fullscreen mode

Pronto! temos nosso projeto, repare na estrutura criada, temos um arquivo chamado serverless.yml e outro handler.js esses carinhas merecem uma atenção especial por que são os responsáveis por toda a execução da nossa lambda.

handler.js - contém a função main que será chamada sempre que a lambda for disparada.

serverless.yml - arquivo de configuração responsável por toda estrutura, plugins, deploy e publicação da lambda.

Para ver a lambda funcionando em ambiente local é só executar o comando a seguir na pasta do projeto.

$ serverless invoke local -f {$FUNCTION_NAME}
Enter fullscreen mode Exit fullscreen mode

Normalmente a função criada por padrão tem o nome de hello, mas durante o projeto vamos precisar renomear.

Com a função criada vamos adicionar mais alguns detalhes para conseguirmos acessar a SDK. 🚀

Usando o NPM conseguimos instalar a lib da SDK que vamos utilizar para programar a Alexa. Na mesma pasta execute npm install ask-sdk-core.

serverless.yml

Vamos especificar algumas configurações logo de início.

service: ensinamentos-yoda #nome do projeo
frameworkVersion: "3"

provider:
  name: aws
  runtime: nodejs12.x 
  stage: dev 
  region: us-east-2

functions:
  yoda:
    handler: handler.yoda #nome da função que será disparada

plugins:
  - serverless-alexa-skills

custom:
  alexa: []

Enter fullscreen mode Exit fullscreen mode

handler.js

'use strict';

const ensinamentos = [
  'Muito a aprender você ainda tem, jovem Padawan.',
  'Faça. Ou não faça. Não existe a tentativa',
  'O medo leva a raiva, a raiva leva ao ódio, o ódio leva ao sofrimento',
  'Paciência você deve ter meu jovem Padawan',
  'Um Jedi usa a Força para o conhecimento e defesa, nunca para o ataque.',
  'Raiva, medo, agressão. Ao lado sombrio elas pertencem'
]

module.exports.yoda = async (event) => {
  console.log(`[INFO] ${Date(Date.now())} Lambda disparada `)

  return 'Lembre-se do que o mestre Yoda disse: ' +
    ensinamentos[Math.floor(Math.random() * ensinamentos.length)]
};

Enter fullscreen mode Exit fullscreen mode

Durante o desenvolvimento podemos usar o comando serverless invoke local -f yoda para testar localmente sem provisionamento de cloud

mestre Yoda dançando

Round 1

Primeira etapa concluída! temos nossa regra de negócio pronta, sempre que executada a lambda responde com algum ensinamento aleatório da lista.

$ serverless invoke local -f yoda
[INFO] Sun Jun 26 2022 01:48:21 GMT-0300 (Horário Padrão de Brasília) Lambda disparada 
"Lembre-se do que o mestre Yoda disse: Muito a aprender você ainda tem, jovem Padawan."

Enter fullscreen mode Exit fullscreen mode

Round 2 - Criando nossa skill

Vamos nos autenticar com a API da Alexa, nosso amigo serverless nos ajudará muito nessa missão.

$ serverless alexa auth
Enter fullscreen mode Exit fullscreen mode

Após ser redirecionado para a página do console vamos fazer o login com nossa conta Amazon, de preferência com a mesma conta que configuramos nosso Amazon Echo.

Depois de efetuar o login com sucesso receberemos a mensagem "Thank you for using Serverless Alexa Skills Plugin!!".

Agora autenticados podemos criar nossa skill na plataforma da Amazon.

$ serverless alexa create --name ensinamentos-yoda --locale pt-BR --type custom
Enter fullscreen mode Exit fullscreen mode

Se tudo der certo teremos o ID da nossa skill exibido no console ao fim da execução.

Acessando a página Amazon Alexa Console conseguimos visualizar nossa skill criada e prontinha para o show. 😎

screenshot exibindo o painel do console

Fique à vontade para explorar o painel, repare que também podemos editar e configurar nossa skill pela plataforma, mas vamos seguir usando o plugin do serverless para o tutorial.

Com a skill criada vamos adicionar mais algumas configurações no serverless.yml.

#---resto do código---
functions:
  yoda:
    handler: handler.yoda #nome da função que será disparada

plugins:
  - serverless-alexa-skills #plugin alexa skill

custom:
  alexa:
    skills:
      - id: ${env:ALEXA_SKILL_ID} #ID informado no comando create
        manifest:
          apis:
            custom: {}
          publishingInformation:
            locales:
              pt-BR:
                name: ensinamentos-yoda
          manifestVersion: '1.0'
        models:
          pt-BR:
            interactionModel:
              languageModel:
                invocationName: ensinamentos yoda #Nome usado para invocar a skill 'Alexa, abrir ensinamentos Yoda'
                intents: 
                  - name: AMAZON.CancelIntent
                    samples: []
                  - name: AMAZON.HelpIntent
                    samples: []
                  - name: AMAZON.StopIntent
                    samples: []
                  - name: PedirEnsinamentoIntent #Intenção que criaremos para executar nossa regra
                    samples: #Frases que usaremos para pedir um ensinamento, Seja Criativo!
                      - 'O que o yoda diria'
                      - 'me fale um ensinamento'
                      - 'qual o pensamento do dia'

Enter fullscreen mode Exit fullscreen mode

Lembra das intent que comentamos lá em cima? A API por padrão define algumas padrões para qualquer skill: AMAZON.CancelIntent, AMAZON.HelpIntent, AMAZON.StopIntent

Depois de editado o arquivo vamos executar mais dois comandinhos, eles serão nossos companheiros durante todo o desenvolvimento.

$ serverless alexa update
$ serverless alexa build
Enter fullscreen mode Exit fullscreen mode

Sempre que um comando é enviado para a API da Alexa precisaremos de um handler próprio para resolve-lo.

Para esse projeto teremos os seguintes handlers:

  • LaunchRequestHandler Quando o usuário invoca sua skill sem uma intenção específica, a Alexa envia para sua skill um payload de LaunchRequest.
  • PedirEnsinamentoIntent Executada sempre que o usuário pedir um ensinamento do mestre Yoda.
  • HelpIntentHandler Executada sempre que o usuário enviar um AMAZON.HelpIntent.
  • CancelAndStopIntentHandler Executada sempre que o usuário enviar um AMAZON.CancelIntent ou AMAZON.StopIntent.
  • SessionEndedRequestHandler Executada sempre que o usuário desejar encerrar a skill
'use strict';
const Alexa = require('ask-sdk-core')

const ensinamento = () => {
  const ensinamentos = [
    'Muito a aprender você ainda tem, jovem Padawan.',
    'Faça. Ou não faça. Não existe a tentativa',
    'O medo leva a raiva, a raiva leva ao ódio, o ódio leva ao sofrimento',
    'Paciência você deve ter meu jovem Padawan',
    'Um Jedi usa a Força para o conhecimento e defesa, nunca para o ataque.',
    'Raiva, medo, agressão. Ao lado sombrio elas pertencem'
  ]

  return 'Lembre-se do que o mestre Yoda disse: ' +
    ensinamentos[Math.floor(Math.random() * ensinamentos.length)]
}

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'
  },
  handle(handlerInput) {
    const speechText = 'Olá, que a força esteja com você, me peça um ensinamento do mestre Yoda'

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard('Olá, que a força esteja com você, me peça um ensinamento do mestre Yoda', speechText)
      .getResponse()
  }
}


const PedirEnsinamentoIntent = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
      && Alexa.getIntentName(handlerInput.requestEnvelope) === 'PedirEnsinamentoIntent'
  },
  async handle(handlerInput) {

    const speechText = ensinamento() //chamada a nossa função

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('Mestre Yoda.', speechText)
      .getResponse()
  }
}

const CancelAndStopIntentHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
      && (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
        || Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent')
  },
  handle(handlerInput) {
    const speechText = 'Que a força esteja com você!'

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('Que a força esteja com você!', speechText)
      .withShouldEndSession(true)
      .getResponse()
  }
}

const SessionEndedRequestHandler = {
  canHandle(handlerInput) {
    return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest'
  },
  handle(handlerInput) {
    // Any clean-up logic goes here.
    return handlerInput.responseBuilder.getResponse()
  }
}


const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('Encontrei um probleminha, vamos tentar de novo ?')
      .reprompt('Encontrei um probleminha, vamos tentar de novo ?')
      .getResponse();
  }
}


let skill

module.exports.yoda = async (event) => {
  console.log(`[INFO] ${Date(Date.now())} Lambda disparada `)

  if (!skill) {
    skill = Alexa.SkillBuilders.custom()
      .addRequestHandlers(
        LaunchRequestHandler, //responsável por abrir nossa skill
        PedirEnsinamentoIntent,
        CancelAndStopIntentHandler,
        SessionEndedRequestHandler //responsável para encerrar nossa skill
      )
      .addErrorHandlers(ErrorHandler)//mensagem para caso de erros
      .create()
  }

  return await skill.invoke(event)
}

Enter fullscreen mode Exit fullscreen mode

Rolou muitas mudanças dessa vez, recomendo fortemente dar uma olhada na documentação oficial onde tem vários exemplos e explicações sobre as etapas e funções desse código.

Nesse ponto da nossa aventura temos uma lambda capaz de receber um comando de voz da Alexa e responder com um ensinamento do mestre Yoda.

Não esqueça dos nossos amigos.

$ serverless alexa update
$ serverless alexa build
Enter fullscreen mode Exit fullscreen mode

Todo poderoso tomando café enquanto usa o computador

Estamos quase lá

Já temos nosso código e nossa skill agora vamos fazer todas as peças se encaixarem?

Partiu!

Deploy

O nosso amigo serverless também nos ajudará na etapa do deploy, vamos precisar publicar nossa lambda para cloud e adicionar o gatilho de execução pela Alexa.

$ serverless deploy
Enter fullscreen mode Exit fullscreen mode

Hora da mágica!🧙‍♂️ esse comando vai executar todas as etapas necessária de publicação e os recursos necessários para termos o serviço na nossa conta AWS.

painel AWS Lambda

Prontinho! temos nosso backend agora só precisamos adicionar um gatilho para a função.

  1. Clique em Visão geral da função.
  2. Clique em Adicionar gatilho.
  3. Selecione Alexa.
  4. Para Skill ID verification, selecione Disable.

detalhes da função no painel AWS

Depois de criada vamos editar mais uma vez o arquivo serverless.yml e adicionar o ARN da função para ser chamada pela Alexa.

#---resto do código---
 apis:
  custom: 
   endpoint:
    uri: ${env:AWS_LAMBDA_ARN}
#---resto do código---

Enter fullscreen mode Exit fullscreen mode

Lembre-se de atualizar e fazer o build da skill mais uma vez.

Testando

Essa jornada foi longa mas chegamos ao final.

Para testarmos nossa skill podemos usar o aplicativo, Amazon Echo ou a página de teste do Alexa Developer Console.

Usando o Amazon Echo Dot

  1. Abra o aplicativo Amazon Alexa.
  2. No menu Mais acesse Skills & Games.
  3. Selecione a aba Suas Skills.
  4. Clique no card Desenv..
  5. Clique em ensinamentos-yoda.
  6. Clique em Ativar Skill.
  7. Diga "Alexa abrir ensinamentos yoda"
  8. Diga alguma das frases que configuramos, por exemplo "O que o Yoda diria"

Michael Scott dançando

Conclusão

Desenvolver com a Alexa é uma aventura muito divertida, existem inúmeras opções e funcionalidades que podemos explorar.

O objetivo desse tutorial foi tentar mostrar os primeiros passos para conseguir usar a API da Amazon e sua integração com o serviço AWS Lambda.

Espero que este post tenha inspirado você a explorar o Alexa Skill Kit e criar algo para ela.😊

Recomendo a leitura das documentações oficiais e os links que deixarei abaixo para definições mais técnicas e detalhadas.

Links

isso é tudo pessoal

Happy Coding!

💖 💪 🙅 🚩
mrmorales
Paulo Guilherme

Posted on June 26, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related