Serverless e Alexa - personalizando sua assistente pessoal
Paulo Guilherme
Posted on June 26, 2022
"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.
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. 😎
O diagrama acima exemplifica bem como vai funcionar todo o fluxo saindo do usuário até a execução da lambda.
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
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
- Nodejs 12.x
- NPM
- AWS account
- Alexa Echo Dot
- Alexa developer Amazon account
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
Depois de instalado vamos criar uma pasta e rodar o seguinte comando.
$ serverless create --name ensinamentos-yoda --template aws-nodejs
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
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}
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: []
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)]
};
Durante o desenvolvimento podemos usar o comando
serverless invoke local -f yoda
para testar localmente sem provisionamento de cloud
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."
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
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
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. 😎
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'
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
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 umAMAZON.HelpIntent
. -
CancelAndStopIntentHandler
Executada sempre que o usuário enviar umAMAZON.CancelIntent
ouAMAZON.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)
}
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
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
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.
Prontinho! temos nosso backend agora só precisamos adicionar um gatilho para a função.
- Clique em Visão geral da função.
- Clique em Adicionar gatilho.
- Selecione Alexa.
- Para Skill ID verification, selecione Disable.
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---
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
- Abra o aplicativo Amazon Alexa.
- No menu Mais acesse Skills & Games.
- Selecione a aba Suas Skills.
- Clique no card Desenv..
- Clique em ensinamentos-yoda.
- Clique em Ativar Skill.
- Diga "Alexa abrir ensinamentos yoda"
- Diga alguma das frases que configuramos, por exemplo "O que o Yoda diria"
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
- Projeto pessoal criando uma skill que diz em que plataforma de streaming filmes e séries estão disponíveis
- Tutorial oficial da API Amazon
- Blog serverless
Happy Coding!
Posted on June 26, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.