Criar fulfillment usando webhook

O fulfillment do webhook no Dialogflow proporciona um grande controle sobre o fluxo do nosso agente. Neste tutorial, você precisa de um webhook para validar as sequências alfanuméricas coletadas na intent SequenceSequence". O webhook repetirá esse intent repetidamente para coletar uma sequência longa em iterações mais gerenciáveis.

Criar um webhook com o editor in-line

O Dialogflow tem um editor in-line no console que permite escrever diretamente o código NodeJS, que pode ser implantado e executado como um webhook no Cloud Functions.

Para criar um webhook usando o editor in-line do Dialogflow, siga estas etapas:

  1. Clique na guia Fulfillment na barra de navegação para acessar a página de fulfillment.
  2. Alterne o botão do editor in-line para ATIVADO.
  3. Exclua o conteúdo existente na guia package.json do editor in-line.
  4. Copie e cole o conteúdo JSON abaixo na guia package.json do editor in-line:

    {
      "name": "DialogflowFirebaseWebhook",
      "description": "Firebase Webhook dependencies for a Dialogflow agent.",
      "version": "0.0.1",
      "private": true,
      "license": "Apache Version 2.0",
      "author": "Google Inc.",
      "engines": {
        "node": "10"
      },
      "scripts": {
        "lint": "semistandard --fix \"**/*.js\"",
        "start": "firebase deploy --only functions",
        "deploy": "firebase deploy --only functions"
      },
      "dependencies": {
        "firebase-functions": "^2.0.2",
        "firebase-admin": "^5.13.1"
      }
    }
    
  5. Exclua o código da guia index.js do editor in-line.

  6. Copie e cole o código abaixo na guia index.js do editor in-line:

    /**
     * Copyright 2020 Google Inc. All Rights Reserved.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    'use strict';
    
    const functions = require('firebase-functions');
    
    // TODO: set this to the minimum valid length for your sequence.
    // There's no logic in here to enforce this length, but once the
    // user has said this many digits, the slot-filling prompt will
    // also instruct the user to say "that's all" to end the slot-filling.
    const MIN_SEQUENCE_LENGTH = 10;
    
    exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
      let dfRequest = request.body;
      let action = dfRequest.queryResult.action;
      switch (action) {
        case 'handle-sequence':
          handleSequence(dfRequest, response);
          break;
        case 'validate-sequence':
          validateSequence(dfRequest, response);
          break;
        default:
          response.json({
            fulfillmentText: `Webhook for action "${action}" not implemented.`
          });
      }
    });
    
    ////
    // Helper functions
    
    /* Send an SSML response.
     * @param request: Dialogflow WebhookRequest JSON with camelCase keys.
     *     See https://cloud.google.com/dialogflow/es/docs/reference/common-types#webhookrequest
     * @param response: Express JS response object
     * @param ssml: SSML string.
     * @example: sendSSML(request, response, 'hello')
     *     Will call response.json() with SSML payload '<speak>hello</speak>'
     */
    function sendSSML(request, response, ssml) {
      ssml = `<speak>${ssml}</speak>`;
    
      if (request.originalDetectIntentRequest.source == 'GOOGLE_TELEPHONY') {
        // Dialogflow Phone Gateway Response
        // see https://cloud.google.com/dialogflow/es/docs/reference/rpc/google.cloud.dialogflow.v2beta1#google.cloud.dialogflow.v2beta1.Intent.Message.TelephonySynthesizeSpeech
        response.json({
          fulfillmentMessages: [{
            platform: 'TELEPHONY',
            telephonySynthesizeSpeech: {ssml: ssml}
          }]
        });
      }
      else {
        // Some CCAI telephony partners accept SSML in a plain text response.
        // Check your specific integration and customize the payload here.
        response.json({
          fulfillmentText: ssml
        });
      }
    }
    
    /* Extract an output context from the incoming WebhookRequest.
     * @param request: Dialogflow WebhookRequest JSON with camelCase keys.
     *     See https://cloud.google.com/dialogflow/es/docs/reference/common-types#webhookrequest
     * @param name: A string
     * @return: The context object if found, or undefined
     * @see: https://cloud.google.com/dialogflow/es/docs/reference/rpc/google.cloud.dialogflow.v2#google.cloud.dialogflow.v2.Context
     *     and note this webhook uses JSON camelCase instead of RPC snake_case.
     * @example:
     *     // Modify an existing output content
     *     let context = getOutputContext(request, 'some-context');
     *     context.lifespanCount = 5;
     *     context.parameters.some_parameter = 'new value';
     *     response.json({
     *       fulfillmentText: 'new value set',
     *       outputContexts: [context]
     *     });
     */
    function getOutputContext(request, name) {
      return request.queryResult.outputContexts.find(
          context => context.name.endsWith(`/contexts/${name}`)
      );
    }
    
    ////
    // Action handler functions
    
    /*
     * Fulfillment function for:
     *     actions: handle-sequence
     *     intents: "Sequence", "Sequence - Edit"
     * @param request: Dialogflow WebhookRequest JSON with camelCase keys.
     *     See https://cloud.google.com/dialogflow/es/docs/reference/common-types#webhookrequest
     * @param response: Express JS response object
     */
    function handleSequence(request, response) {
      let parameters = request.queryResult.parameters;
      let isSlotFilling = !request.queryResult.allRequiredParamsPresent;
      let isEditing = getOutputContext(request, 'editing-sequence');
      console.log(request.queryResult.action + ': ' + JSON.stringify(parameters));
    
      if (isSlotFilling) {
        // Prompt the user for the sequence
    
        let verbatim = `<prosody rate="slow"><say-as interpret-as="verbatim">${parameters.existing_sequence}</say-as></prosody>`;
    
        if (!parameters.existing_sequence && !parameters.new_sequence) {
          // Initial prompt
          response.json({
            fulfillmentText: "What is your sequence? Please pause after a few characters so I can confirm as we go."
          });
        }
        else if (!isEditing) {
          // Confirm what the system heard with the user. We customize the response
          // according to how many sequences we've heard to make the prompts less
          // verbose.
          if (!parameters.previous_sequence) {
            // after the first input
            sendSSML(request, response,
                `Say "no" to correct me at any time. Otherwise, what comes after ${verbatim}`);
          }
          else if (parameters.existing_sequence.length < MIN_SEQUENCE_LENGTH) {
            // we know there are more characters to go
            sendSSML(request, response,
                `${verbatim} What's next?`);
          }
          else {
            // we might have all we need
            sendSSML(request, response,
                `${verbatim} What's next? Or say "that's all".`);
          }
        }
        else {
          // User just said "no"
          sendSSML(request, response,
              `Let's try again. What comes after ${verbatim}`);
        }
      }
      else {
        // Slot filling is complete.
    
        // Construct the full sequence.
        let sequence = (parameters.existing_sequence || '') + (parameters.new_sequence || '');
    
        // Trigger the follow up event to get back into slot filling for the
        // next sequence.
        response.json({
          followupEventInput: {
            name: 'continue-sequence',
            parameters: {
              existing_sequence: sequence,
              previous_sequence: parameters.existing_sequence || ''
            }
          }
        });
    
        // TODO: CHALLENGE: consider validating the sequence here.
        // The user has already confirmed existing_sequence, so if you find a unique
        // record in your database with this existing_sequence prefix, you could send
        // a followUpEventInput like 'validated-sequence' to skip to the next part
        // of the flow. You could either create a new intent for this event, or
        // reuse the "Sequence - done" intent. If you reuse the "done" intent, you
        // could add another parameter "assumed_sequence" with value
        // "#validated-sequence.sequence", then modify the validateSequence function
        // below to customize the response for this case.
      }
    }
    
    /*
     * Fulfillment function for:
     *     action: validate-sequence
     *     intents: "Sequence - Done"
     * @param request: Dialogflow WebhookRequest JSON with camelCase keys.
     *     See https://cloud.google.com/dialogflow/es/docs/reference/common-types#webhookrequest
     * @param response: Express JS response object
     */
    function validateSequence(request, response) {
      let parameters = request.queryResult.parameters;
      // TODO: add logic to validate the sequence and customize your response
      let verbatim = `<say-as interpret-as="verbatim">${parameters.sequence}</say-as>`;
      sendSSML(request, response, `Thank you. Your sequence is ${verbatim}`);
    }
    
  7. Clique em IMPLANTAR.

Agora você pode testar a integração chamando o agente. Este é um bom momento para configurar uma das integrações de telefonia com um clique dos nossos parceiros ou configurar o telefone do Dialogflow Phone Gateway para testar o agente por telefone.

Noções básicas sobre o código

Como o ponto de entrada para o webhook, a função dialogflowFirebaseFulfillment é chamada sempre que o webhook é acionado. Em cada solicitação, o Dialogflow envia o nome "ação" que você especificou no console do Dialogflow para uma intent. O código usa esse nome de ação para determinar qual função de webhook chamar, handleSequence ou validateSequence.

Processar sequência

handleSequence é a função principal deste tutorial. Ele é responsável por todos os aspectos do preenchimento de slot da sequência, incluindo:

  • Falar as instruções iniciais quando uma sessão entra pela primeira vez na intent.
  • Repetir a sequência antes de solicitar o próximo conjunto.
  • Dizer aos usuários finais como corrigir o bot.
  • Reconhecimento quando há dígitos suficientes para uma sequência válida e informa ao usuário final como finalizar a entrada (consulte "MIN_SEQUENCE_LENGTH" no código).
  • Repetir o preenchimento de slot para coletar várias sequências parciais.
  • Concatenar as sequências parciais em uma sequência longa.

Validar sequência

validateSequence é onde você quer adicionar uma conexão ao seu armazenamento de dados para validar a sequência final e retornar uma mensagem personalizada ao usuário com base nesses dados. Se você estiver criando um agente de pesquisa de pedidos, por exemplo, poderá personalizar a resposta para dizer:

Thank you. Your order ${verbatim} will arrive on ${lookup.date} and will ${lookup.require_signature ? '' : 'not'} require a signature.

Em que lookup é algum objeto encontrado no armazenamento de dados para esse pedido.

Funções auxiliares

Este exemplo não usa nenhuma dependência específica do Dialogflow. Em vez disso, siga a referência de WebhookRequest para saber o que esperar em request.body, e a referência WebhookResponse para saber o que responder. com response.json({...}).

O código inclui duas funções auxiliares para facilitar a:

  • Envie o JSON de resposta apropriado para a plataforma atual passando uma string para sendSSML.
  • Procure um contexto ativo do Dialogflow da solicitação transmitindo o nome do contexto para getOutputContext.

Outras melhorias

Isso ajudará você a começar a usar o webhook para casos de uso avançados. Você projetou um agente que pode repetir uma solicitação de sequência enquanto um usuário final fala a sequência, mantendo-se assim enquanto o agente virtual os escuta corretamente.

Veja algumas ideias para melhorar ainda mais a experiência:

  • Altere algumas respostas do webhook para corresponder à sua marca. Por exemplo, em vez da opção genérica "Qual é sua sequência?...", edite o código para dizer "Qual é o número do pedido? Você pode encontrá-lo em ...".
  • Considere adicionar outro contexto de saída à intent "Sequence - Concluído" e, em seguida, crie alguns novos intents nesse contexto de entrada para permitir que os usuários façam perguntas sobre a ordem deles.
  • Se você quiser se aprofundar ainda mais neste caso de uso, confira o TODO: CHALLENGE no código de exemplo acima para ver como você pode melhorar ainda mais essa experiência para seus usuários.