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:

  1. Instale o software para criar os componentes personalizados.

  2. Crie o pacote de componentes personalizados.

  3. Crie e desenvolva um componente personalizado.

Observação

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.

Observação

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.
  1. Caso ainda não tenha feito, faça o download do Node.js em https://nodejs.org e instale-o para acesso global. O Gerenciador de Pacotes de Nós (npm) é distribuído com o Node.js.

    Para testar se o Node.js e o npm estão instalados, abra uma janela de terminal e digite estes comandos:

    node –v 
    npm –v
  2. Para instalar o Oracle Digital Assistant Bots Node.js SDK para acesso global, digite este comando em uma janela de terminal:
    npm install -g @oracle/bots-node-sdk

    Em um Mac, use o comando sudo:

    sudo npm install -g @oracle/bots-node-sdk

    Ao usar a opção -g (global), você tem acesso direto à interface de linha de comando bots-node-sdk. Caso contrário, use npx @oracle/bots-node-sdk.

  3. Para verificar sua instalação do Oracle Digital Assistant Bots Node.js SDK, digite o seguinte comando:
    bots-node-sdk -v
    O comando deve imprimir a versão do Oracle Digital Assistant Bots Node.js SDK.

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 chamado hello.world.js. É aqui que você colocará seus arquivos JavaScript do componente.

  • Adiciona um arquivo package.json, que especifica main.js como o ponto de entrada principal e lista @oracle/bots-node-sdk como devDependency. O arquivo de pacote também aponta para alguns scripts bots-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.
Observação

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

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.

  • 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-chave async com essa função para tratar promessas. A função invoke usa o seguinte argumento:
    • context, que nomeia a referência ao objeto CustomComponentContext 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 era conversation. 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-chave async), também será possível adicionar um argumento done 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 chamar keepTurn depois de chamar reply, porque reply define implicitamente keepTurn como false.

  • 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 opcional action 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.

  1. Se aplicável, use context.reply(<reply>) para enviar uma resposta.

  2. Defina context.keepTurn(true).

  3. Defina context.transition com uma string supportedActions (e.g., context.transition("success")) ou sem argumentos (e.g., context.transition()).

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.
invoke: async (context) => {
    const listVariableName = context.properties().variableName;
    ...
    // Write list of options to a context variable
    context.variable(listVariableName, list);
   // Navigate to next state without 
   // first prompting for user interaction.
   context.keepTurn(true);
   context.transition();
 }

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.

  1. Se aplicável, use context.reply(<reply>) para enviar uma resposta.

  2. Defina context.keepTurn(false).

  3. Defina context.transition com uma string supportedActions string(context.transition("success")) ou sem argumentos (context.transition()).

Por exemplo :
context.keepTurn(false);
context.transition("success");
Um componente personalizado que obtém entrada do usuário sem retornar o controle de fluxo para a habilidade. Por exemplo :
  • Um componente informa a entrada do usuário para consultar um mecanismo de pesquisa de backend. Se a habilidade só puder acomodar um único resultado, mas a consulta retornar várias ocorrências, o componente solicitará ao usuário mais entrada para filtrar os resultados. Nesse caso, o componente personalizado continua a tratar a entrada do usuário; ele mantém a conversa até que o mecanismo de pesquisa retorne um único acerto. Quando um único resultado é obtido, o componente chama context.transition() para passar para outro estado, conforme estabelecido na definição do fluxo de caixas de diálogo.

  • Um componente processa um questionário e só faz a transição para outro próximo estado quando todas as perguntas são respondidas.

  1. Não chame transition.

  2. Defina keepTurn(false).

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.
  invoke: async (context) => {
    // Perform conversation tasks.
    const tracking_token = "a2VlcHR1cm4gZXhhbXBsZQ==";    
    const quotes = require("./json/Quotes.json");
    const quote = quotes[Math.floor(Math.random() * quotes.length)];
    
    // Check if postback action is issued. If postback action is issued, 
    // check if postback is from this component rendering. This ensures
    // that the component only responds to its own postback actions.     
    if (context.postback() && context.postback().token == tracking_token && context.postback().isNo) {
      context.keepTurn(true);
      context.transition();
    } else {
      // Show the quote of the day.
      context.reply("'" + quote.quote + "'");
      context.reply(" Quote by: " + quote.origin);
      // Create a single message with two buttons to 
      // request another quote or not.
      const mf = context.getMessageFactory();
      const message = mf.createTextMessage('Do you want another quote?')
        .addAction(mf.createPostbackAction('Yes', { isNo: false, token: tracking_token }))
        .addAction(mf.createPostbackAction('No', { isNo: true, token: tracking_token })); 
      context.reply(message);
      // Although reply() automatically sets keepTurn to false, 
      // it's good practice to explicitly set it so that it's
      // easier to see how you intend the component to behave.
      context.keepTurn(false);
    };
  }

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: context.variable("InternalComponentWaitTime", time). Se você usar uma variável de contexto, não se esqueça de redefini-la ou defini-la como nula antes de chamar context.transition.

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 :

  • Um componente faz ping em um serviço remoto para obter o status de um pedido até o status ser retornado como accepted ou o componente atingir o timeout. Se o status accepted não for retornado após o quinto ping, o componente fará a transição com o status failedOrder.

  • O componente personalizado passa o usuário a um agente ao vivo. Nesse caso, a entrada do usuário e as respostas são enviadas ao agente. O componente faz a transição para outro estado quando o usuário ou o agente encerra a sessão.

  • Não chame transition.

  • Defina context.keepTurn(true).

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:
invoke: async (context) => {

  const quotes = require("./json/Quotes.json");
  const quote = quotes[Math.floor(Math.random() * quotes.length)];
  
  // Check if postback action is issued and postback is from this component rendering. 
  // This ensures that the component only responds to its own postback actions.     
  const um = context.getUserMessage()
  if (um instanceof PostbackMessage && um.getPostback() && um.getPostback()['system.state'] === context.getRequest().state && um.getPostback().isNo) {
    context.keepTurn(true);
    context.transition();
  } else {
    // Show the quote of the day.
    context.reply(`'${quote.quote}'`);
    context.reply(`Quote by: ${quote.origin}`);
    // Create a single message with two buttons to request another quote or not.
    let actions = [];

    const mf = context.getMessageFactory();
    const message = mf.createTextMessage('Do you want another quote?')
      .addAction(mf.createPostbackAction('Yes', { isNo: false }))
      .addAction(mf.createPostbackAction('No', { isNo: true }));
    context.reply(message);
    // Although reply() automatically sets keepTurn to false, it's good practice to explicitly set it so that it's
    // easier to see how you intend the component to behave.
    context.keepTurn(false);
  }
}

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:

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


Segue a descrição da ilustração components-nonsequitur-conversation.png
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.

  1. Na pasta de nível superior, abra uma janela de terminal e execute estes comandos para iniciar o serviço:
    npm install
    npm start
  2. Para verificar se o serviço está em execução, digite o seguinte URL em um browser:
    localhost:3000/components

    O browser exibe os metadados do componente.

  3. Se você tiver acesso direto à Internet, poderá acessar o ambiente de desenvolvimento usando uma habilidade:
    1. Instalar um túnel, como ngrok ou Localtunnel.
    2. Se você tiver um proxy, vá para http://www.whatismyproxy.com/ para obter o endereço IP externo do seu proxy e, em seguida, na janela de terminal que você usará para iniciar o túnel, digite estes comandos:
      export https_proxy=http://<external ip>:80
      export http_proxy=http://<external ip>:80
    3. Inicie o túnel e configure-o para expor a porta 3000.
    4. No Oracle Digital Assistant, vá até a guia Componentes Ícone Componentes da habilidade e adicione um serviço de componente Externo com o URL de metadados definido como https://<tunnel-url>/components.
      Você pode usar qualquer valor para o nome do usuário e a senha.
Agora você pode adicionar estados dos componentes do serviço ao fluxo de caixas de diálogo e testá-los na página Visualizar da habilidade.