Implementar Componentes Personalizados
Para implementar componentes personalizados, use o Node.js SDK do Oracle Digital Assistant para fazer interface com o serviço de componente personalizado do Digital Assistant.
Veja como implementar componentes personalizados que podem ser implantados no contêiner incorporado do Digital Assistant, no Oracle Cloud Infrastructure Functions, em um backend do Mobile Hub ou em um servidor Node.js:
Se você planeja implantar o pacote de componentes personalizados em um serviço de componente personalizado incorporado, cada habilidade à qual você adicionar o pacote será contada como um serviço separado. Há um limite de quantos serviços de componentes personalizados incorporados uma instância pode ter. Se você não souber o limite, peça ao administrador de serviço para obter o
embedded-custom-component-service-count
para você, conforme descrito em Exibir Limites do Serviço na Console de Infraestrutura. Considere empacotar vários componentes por pacote para minimizar o número de serviços de componentes incorporados que você usa. Se você tentar adicionar um serviço de componente depois de atingir esse limite, a criação do serviço falhará.
Etapa 1: Instalar o Software para Criar Componentes Personalizados
Para criar um pacote de componentes personalizados, é necessário o Node.js, o Gerenciador de Pacotes de Nós e o Oracle Digital Assistant Bots Node.js SDK.
No Windows, o SDK do Nó de Bots não funcionará no Windows se a instalação do Nó for da versão 20.12.2 ou superior por causa de uma alteração incompatível com versões anteriores em Node.js. Se você já tiver o Node versão 20.12.2 ou posterior instalado, será necessário desinstalá-lo e, em seguida, instalar a versão 20.12.1 ou anterior para que o Bots Node SDK funcione.
Etapa 2: Criar o Pacote de Componentes Personalizados
Para iniciar um projeto, use o comando bots-node-sdk init
na interface de linha de comando (CLI) do SDK para criar a estrutura de arquivos e diretórios necessários para sua estrutura de componentes.
O comando init
tem algumas opções, como o uso de JavaScript (o padrão) ou TypeScript, e o nome do arquivo JavaScript do componente inicial. Essas opções são descritas em Ferramentas do Desenvolvedor da CLI. Este é o comando básico para iniciar um projeto JavaScript:
bots-node-sdk init <top-level folder path> --name <component service name>
Esse comando executa as seguintes ações para um pacote JavaScript:
-
Criar a pasta de nível superior.
-
Cria uma pasta
components
e adiciona um arquivo JavaScript do componente de amostra chamadohello.world.js
. É aqui que você colocará seus arquivos JavaScript do componente. -
Adiciona um arquivo
package.json
, que especificamain.js
como o ponto de entrada principal e lista@oracle/bots-node-sdk
comodevDependency
. O arquivo de pacote também aponta para alguns scriptsbots-node-sdk
.{ "name": "myCustomComponentService", "version": "1.0.0", "description": "Oracle Bots Custom Component Package", "main": "main.js", "scripts": { "bots-node-sdk": "bots-node-sdk", "help": "npm run bots-node-sdk -- --help", "prepack": "npm run bots-node-sdk -- pack --dry-run", "start": "npm run bots-node-sdk -- service ." }, "repository": {}, "dependencies": {}, "devDependencies": { "@oracle/bots-node-sdk": "^2.2.2", "express": "^4.16.3" } }
-
Adiciona um arquivo
main.js
, que exporta as definições do pacote e aponta para a pasta de componentes do local dos componentes, à pasta de nível superior. -
Adiciona um arquivo
.npmignore
à pasta de nível superior. Esse arquivo é usado quando você exporta o pacote de componentes. Ele deve excluir arquivos.tgz
do pacote. Por exemplo:*.tgz
. -
Para algumas versões do npm, cria um arquivo
package-lock.json
. - Instala todas as dependências de pacote na subpasta
node_modules
.
Se você não usar o comando
bots-node-sdk init
para criar a pasta do pacote, certifique-se de que a pasta de nível superior contenha um arquivo .npmignore
que contenha uma entrada *.tgz
. Por exemplo :*.tgz
spec
service-*
Caso contrário, toda vez que você compactar os arquivos em um arquivo TGZ, será incluído o arquivo TGZ que já existe na pasta de nível superior e seu arquivo TGZ continuará dobrando de tamanho.
Se você planeja implantar no contêiner incorporado, seu pacote deve ser compatível com o Nó 14.17.0.
Etapa 3: Criar e Desenvolver um Componente Personalizado
Estas são as etapas para a criação de cada componente personalizado do seu pacote:
Criar o Arquivo do Componente
Use o comando init component
da CLI do SDK para criar um arquivo JavaScript ou TypeScript com o framework para trabalhar com o Node.js SDK do Oracle Digital Assistant na criação de um componente personalizado. O idioma especificado quando você executou o comando init
para criar o pacote do componente determina se um arquivo JavaScript ou TypeScript será criado.
Por exemplo, para criar um arquivo para o componente personalizado, em uma janela de terminal, vá para a pasta de nível superior do pacote e digite o seguinte comando, substituindo <component name>
pelo nome do seu componente:
bots-node-sdk init component <component name> c components
Para JavaScript, esse comando adiciona o <component name>.js
ao components folder
. Para TypeScript, o arquivo é adicionado à pasta src/components
. O argumento c
indica que o arquivo se destina a um componente personalizado.
Observe que o nome do componente não pode exceder 100 caracteres. Só é possível usar caracteres alfanuméricos e sublinhados no nome. Não é possível utilizar hifens. O nome não pode ter um prefixo System.
. O Oracle Digital Assistant não permitirá que você adicione um serviço de componente personalizado que tenha nomes de componente inválidos.
Para obter detalhes adicionais, consulte https://github.com/oracle/bots-node-sdk/blob/master/bin/CLI.md
.
Adicionar Código às Funções metadados e chamada
Seu componente personalizado deve exportar dois objetos:
metadata
: Fornece à habilidade as informações a seguir do componente.- Nome do componente
- Propriedades suportadas
- Ações de transição suportadas
Para fluxos de caixas de diálogo baseados em YAML, o componente personalizado suporta as propriedades a seguir por padrão. Essas propriedades não estão disponíveis para habilidades projetadas no modo de diálogo Visual.
autoNumberPostbackActions
: Booliano. Não é obrigatório. Quandotrue
, os botões e as opções de lista são numerados automaticamente. O padrão éfalse
. Consulte Numeração Automática para Canais Somente Texto em Fluxos de Caixas de Diálogo YAML.insightsEndConversation
: Booliano. Não é obrigatório. Quandotrue
, a sessão interrompe a gravação da conversa para geração de relatório de informações. O padrão éfalse
. Consulte Modelar o Fluxo de Caixas de Diálogo.insightsInclude
: Booliano. Não é obrigatório. Quandotrue
, o estado é incluído no relatório de informações. O padrão étrue
. Consulte Modelar o Fluxo de Caixas de Diálogo.translate
: Booliano. Não é obrigatório. Quandotrue
, a tradução automática é ativada para este componente. O padrão é o valor da variável de contextoautotranslation
. Consulte Serviços de Tradução em Habilidades.
invoke
: Contém a lógica a ser executada. Neste método, você pode ler e gravar variáveis de contexto de habilidade, criar mensagens de conversa, definir transições de estado, fazer chamadas REST e muito mais. Normalmente, você usaria a palavra-chaveasync
com essa função para tratar promessas. A funçãoinvoke
usa o seguinte argumento:context
, que nomeia a referência ao objetoCustomComponentContext
no Node.js SDK do Digital Assistant. Essa classe é descrita na documentação do SDK em https://oracle.github.io/bots-node-sdk/. Nas versões anteriores do SDK, o nome eraconversation
. Você pode usar qualquer um dos nomes.
Observação
Se você estiver usando uma biblioteca JavaScript que não suporta promessas (e, portanto, não está usando a palavra-chaveasync
), também será possível adicionar um argumentodone
como um callback que o componente chama quando terminar o processamento.
Veja aqui um exemplo:
'use strict';
module.exports = {
metadata: {
name: 'helloWorld',
properties: {
human: { required: true, type: 'string' }
},
supportedActions: ['weekday', 'weekend']
},
invoke: async(context) => {
// Retrieve the value of the 'human' component property.
const { human } = context.properties();
// determine date
const now = new Date();
const dayOfWeek = now.toLocaleDateString('en-US', { weekday: 'long' });
const isWeekend = [0, 6].indexOf(now.getDay()) > -1;
// Send two messages, and transition based on the day of the week
context.reply(`Greetings ${human}`)
.reply(`Today is ${now.toLocaleDateString()}, a ${dayOfWeek}`)
.transition(isWeekend ? 'weekend' : 'weekday');
}
}
Para saber mais e explorar alguns exemplos de código, consulte Criando Componentes Personalizados na documentação do SDK do Nó de Bots.
Controlar o Fluxo com keepTurn e transição
Use diferentes combinações das funções Bots Node SDK keepTurn
e transition
para definir como o componente personalizado interage com um usuário e como a conversa continua depois que o componente retorna o controle de fluxo para a habilidade.
-
keepTurn(boolean)
especifica se a conversa deve fazer a transição para outro estado sem primeiro solicitar a entrada do usuário.Observe que se você quiser definir
keepTurn
como true, você deve chamarkeepTurn
depois de chamarreply
, porquereply
define implicitamentekeepTurn
comofalse
. -
transition(action)
faz com que a caixa de diálogo faça a transição para o próximo estado depois que todas as respostas, se houver, forem enviadas. O argumento opcionalaction
nomeia essa ação (resultado) que o componente retorna.Se você não chamar
transition()
, a resposta será enviada, mas a caixa de diálogo permanecerá no estado e a entrada subsequente do usuário retornará para esse componente. Ou seja,invoke()
é chamado novamente.
invoke: async (context) ==> {
...
context.reply(payload);
context.keepTurn(true);
context.transition ("success");
}
Aqui estão alguns casos de uso comuns em que você usaria keepTurn
e transition
para controlar o fluxo de caixas de diálogo:
Caso de Uso | Valores Definidos para keepTurn e transição |
---|---|
Um componente personalizado que faz a transição para outro estado sem primeiro solicitar a entrada do usuário. |
Por exemplo, este componente personalizado atualiza uma variável com uma lista de valores a ser exibida imediatamente pelo próximo estado no fluxo de caixas de diálogo.
|
Um componente personalizado que permite que a habilidade aguarde a entrada depois que o controle retorna à habilidade e antes que a habilidade faça a transição para outro estado. |
Por exemplo :
|
Um componente personalizado que obtém entrada do usuário sem retornar o controle de fluxo para a habilidade. Por exemplo :
|
Por exemplo, esse componente personalizado gera uma cotação e, em seguida, exibe os botões
Yes e No para solicitar outra cotação. Ele faz a transição de volta para a habilidade quando o usuário clica em No .
Se um componente não fizer a transição para outro estado, ele precisará controlar seu próprio estado, conforme mostrado no exemplo acima. Para um tratamento de estado mais complexo, como dar ao usuário a opção de cancelar se uma recuperação de dados estiver demorando muito, você pode criar e usar uma variável de contexto. Por exemplo: Observe que, desde que você não faça a transição, todos os valores informados como propriedades do componente estarão disponíveis. |
A chamada do componente se repete sem entrada do usuário. Por exemplo :
|
Aqui está um exemplo criado que mostra como repetir a invocação sem esperar pela entrada do usuário e, em seguida, como fazer a transição quando terminar:
|
Acessar o Backend
Você descobrirá que existem várias bibliotecas Node.js que foram criadas para facilitar as solicitações HTTP e que a lista é alterada com frequência. Você deve rever os prós e contras das bibliotecas atualmente disponíveis e decidir qual delas funciona melhor para você. Recomendamos o uso de uma biblioteca que suporte promessas para que você possa utilizar a versão async
do método invoke
, que foi introduzida na versão 2.5.1, e use a palavra-chave await
para gravar suas chamadas REST de forma síncrona.
Uma opção é a API de node-fetch pré-instalada com o Bots Node-SDK. Acessar o Backend Usando Chamadas HTTP REST na documentação do Bots Node SDK contém alguns exemplos de código.
Usar o SDK para Acessar Payloads de Solicitação e Resposta
Use os métodos de instância CustomComponentContext
para obter o contexto da chamada, acessar e alterar variáveis e enviar os resultados de volta para o mecanismo de caixa de diálogo.
Você pode encontrar vários exemplos de código para usar esses métodos em Criando Componentes Personalizados e Mensagens de Conversa na documentação do Bots Node SDK
A documentação de referência do SDK está em https://github.com/oracle/bots-node-sdk
.
Componentes personalizados para habilidades em vários idiomas
Ao projetar um componente personalizado, você deve considerar se o componente será usado por uma habilidade que suporte mais de um idioma.
Se o componente personalizado tiver que suportar habilidades em vários idiomas, você precisará saber se as habilidades estão configuradas para suporte ao idioma nativo ou serviço de tradução.
Ao usar um serviço de tradução, você pode traduzir o texto da habilidade. Estas são as opções disponíveis:
-
Defina a propriedade
translate
no estado do componente personalizado como verdadeira para traduzir a resposta do componente, conforme descrito em Enviar Respostas Diretamente ao Serviço de Tradução. -
Envie dados brutos de volta para a habilidade em variáveis e use os valores das variáveis em um componente do sistema que compõe a saída. Defina a propriedade
translate
desse componente como verdadeira. Consulte Usar um Componente do Sistema para Passar a Mensagem ao Serviço de Tradução. -
Envie dados brutos de volta para a habilidade em variáveis e use os valores das variáveis em um componente do sistema que use a chave do pacote de recursos para o idioma. Consulte Usar um Componente de Sistema para Fazer Referência a um Pacote de Recursos.
Para habilidades de idioma nativo, você tem estas opções:
-
Informe os dados de volta à habilidade em variáveis e, em seguida, gere o texto de um componente do sistema informando os valores das variáveis para uma chave do pacote de recursos, conforme descrito em Usar um Componente do Sistema para Fazer Referência a um Pacote de Recursos. Com essa opção, o componente personalizado deve ter propriedades de metadados para que a habilidade informe os nomes das variáveis nas quais armazenar os dados.
-
Use o pacote de recursos do componente personalizado para compor a resposta do componente personalizado, conforme descrito em Reference Resource Bundles from the Custom Component. Você usa o método
conversation.translate()
para obter a string do pacote de recursos a ser usada para sua chamada paracontext.reply()
. Esta opção só é válida para definições de pacotes de recursos que usam parâmetros posicionais (numerados). Não funciona para parâmetros nomeados. Com essa opção, o componente personalizado deve ter uma propriedade de metadados para o nome da chave do pacote de recursos, e os parâmetros da chave do pacote de recursos nomeado devem corresponder aos usados na chamada paracontext.reply()
.
Veja um exemplo de como usar o pacote de recursos do componente personalizado. Neste exemplo, fmTemplate
seria definido como algo como ${rb('date.dayOfWeekMessage', 'lundi', '19 juillet 2021')}
.
'use strict';
var IntlPolyfill = require('intl');
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat;
module.exports = {
metadata: () => ({
name: 'Date.DayOfWeek',
properties: {
rbKey: { required: true, type: 'string' }
},
supportedActions: []
}),
invoke: (context, done) => {
const { rbKey } = context.properties();
if (!rbKey || rbKey.startsWith('${')){
context.transition();
done(new Error('The state is missing the rbKey property or it uses an invalid expression to pass the value.'));
}
//detect user locale. If not set, define a default
const locale = context.getVariable('profile.locale') ?
context.getVariable('profile.locale') : 'en-AU';
const jsLocale = locale.replace('_','-');
//when profile languageTag is set, use it. If not, use profile.locale
const languageTag = context.getVariable('profile.languageTag')?
context.getVariable('profile.languageTag') : jslocale;
/* =============================================================
Determine the current date in local format and
the day name for the locale
============================================================= */
var now = new Date();
var dayTemplate = new Intl.DateTimeFormat(languageTag,
{ weekday: 'long' });
var dayOfWeek = dayTemplate.format(now);
var dateTemplate = new Intl.DateTimeFormat(languageTag,
{ year: 'numeric', month: 'long', day: 'numeric'});
var dateToday = dateTemplate.format(now);
/* =============================================================
Use the context.translate() method to create the ${Freemarker}
template that's evaluated when the reply() is flushed to the
client.
============================================================= */
const fmTemplate = context.translate(rbKey, dateToday, dayOfWeek );
context.reply(fmTemplate)
.transition()
.logger().info('INFO : Generated FreeMarker => '
+ fmTemplate);
done();
}
};
Certificar-se de que o Componente Funciona em Assistentes Digitais
Em uma conversa com assistente digital, um usuário pode interromper um fluxo de conversa alterando o assunto. Por exemplo, se um usuário iniciar um fluxo para fazer uma compra, ele poderá interromper esse fluxo para perguntar quanto ele tem de crédito em um cartão-presente. Nós chamamos isso de não sequenciadores. Para permitir que o assistente digital identifique e manipule não sequenciadores, chame o método context.invalidInput(payload)
quando uma resposta do usuário não for compreendida no contexto do componente.
Em uma conversa digital, o runtime determina se uma entrada inválida é um não sequenciador, procurando correspondências de resposta em todas as habilidades. Se encontrar correspondências, ele redirecionará o fluxo. Caso contrário, ele exibirá a mensagem, se fornecida, solicitará ao usuário a entrada e, em seguida, executará o componente novamente. A nova entrada é transmitida ao componente na propriedade text
.
Em uma conversa de habilidade stand-alone, o runtime exibe a mensagem, se fornecida, solicita ao usuário a entrada e, em seguida, executa o componente novamente. A nova entrada é transmitida ao componente na propriedade text
.
Esse código de exemplo chama context.invalidInput(payload)
, sempre que a entrada não é convertida em um número.
"use strict"
module.exports = {
metadata: () => ({
"name": "AgeChecker",
"properties": {
"minAge": { "type": "integer", "required": true }
},
"supportedActions": [
"allow",
"block",
"unsupportedPayload"
]
}),
invoke: (context, done) => {
// Parse a number out of the incoming message
const text = context.text();
var age = 0;
if (text){
const matches = text.match(/\d+/);
if (matches) {
age = matches[0];
} else {
context.invalidUserInput("Age input not understood. Please try again");
done();
return;
}
} else {
context.transition('unsupportedPayload");
done();
return;
}
context.logger().info('AgeChecker: using age=' + age);
// Set action based on age check
let minAge = context.properties().minAge || 18;
context.transition( age >= minAge ? 'allow' : 'block' );
done();
}
};
Este é um exemplo de como um assistente digital trata uma entrada inválida no runtime. Para a primeira resposta de idade (twentyfive
), não há correspondência em habilidade alguma registrada no assistente digital; dessa forma, a conversa exibe a mensagem context.invalidUserInput
especificada. Na segunda resposta de idade (send money
), o assistente digital encontra uma correspondência e pergunta se ele deve redirecionar para esse fluxo.

Descrição da ilustração components-nonsequitur-conversation.png
Chame context.invalidInput()
ou context.transition()
. Se você chamar as duas operações, certifique-se de que a variável system.invalidUserInput
ainda esteja definida se qualquer mensagem adicional for enviada. Observe também que os componentes de entrada do usuário (como os componentes Resposta Comum e Resolver Entidades) redefinem system.invalidUserInput
.
Digamos, por exemplo, que modificamos o componente AgeChecker conforme mostrado a seguir e chamamos context.transition()
após context.invalidInput()
.
if (matches) { age = matches[0]; } else {
context.invalidUserInput("Age input not understood. Please try again");
context.transition("invalid");
context.keepTurn(true);
done();
return;
}
Nesse caso, o fluxo de dados precisa fazer uma nova transição para askage
para que o usuário receba duas mensagens de saída – "Entrada de idade não compreendida. Tente novamente" seguido por "Quantos anos você tem?". Veja como isso pode ser tratado em um fluxo de caixas de diálogo no modo YAML.
askage:
component: "System.Output"
properties:
text: "How old are you?"
transitions:
next: "checkage"
checkage:
component: "AgeChecker"
properties:
minAge: 18
transitions:
actions:
allow: "crust"
block: "underage"
invalid: "askage"
Executar o Serviço do Componente em um Ambiente de Desenvolvimento
Durante a fase de desenvolvimento, você pode iniciar um serviço local para expor o pacote de componentes personalizados.