Créer un processus "fulfillment" à l'aide d'un webhook

Le fulfillment de webhook dans Dialogflow nous offre beaucoup de contrôle sur le flux de l'agent. Dans ce tutoriel, vous avez besoin d'un webhook pour valider les séquences alphanumériques collectées dans l'intent "Sequence". Le webhook effectuera une boucle dans l'intent de manière répétée afin de collecter une longue séquence dans des itérations plus gérables.

Créer un webhook avec l'éditeur intégré

Dialogflow dispose d'un éditeur intégré dans la console qui vous permet d'écrire directement du code NodeJS, qui peut ensuite être déployé en tant que webhook sur Cloud Functions.

Pour créer un webhook à l'aide de l'éditeur intégré de Dialogflow, procédez comme suit :

  1. Cliquez sur l'onglet Fulfillment dans la barre de navigation pour accéder à la page de fulfillment.
  2. Réglez le bouton de l'éditeur intégré sur ENABLED (ACTIVÉ).
  3. Supprimez le contenu existant dans l'onglet package.json de l'éditeur intégré.
  4. Copiez et collez le contenu JSON ci-dessous dans l'onglet package.json de l'éditeur intégré :

    {
      "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. Supprimez le code existant dans l'onglet index.js de l'éditeur intégré.

  6. Copiez et collez le code ci-dessous dans l'onglet index.js de l'éditeur intégré :

    /**
     * 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. Cliquez sur DÉPLOYER.

Vous devriez maintenant pouvoir tester l'intégration en appelant l'agent. Si ce n'est pas déjà fait, le moment est venu de configurer l'une des intégrations téléphoniques en un clic de nos partenaires ou de configurer la passerelle de téléphonie Dialogflow permettant de tester votre agent par téléphone.

Comprendre le code

En tant que point d'entrée du webhook, la fonction dialogflowFirebaseFulfillment appelée ici est appelée à chaque déclenchement du webhook. Avec chaque requête, Dialogflow envoie le nom d'action que vous avez spécifié dans la console Dialogflow pour un intent. Le code utilise ce nom d'action pour déterminer quelle fonction webhook à appeler, handleSequence ou validateSequence.

Gérer les séquences

handleSequence est la fonction principale de ce tutoriel. Il est responsable de tous les aspects du remplissage de séquence, y compris:

  • Énoncer les instructions initiales lorsqu'une session accède à l'intent pour la première fois
  • Répétez la séquence avant de demander l'ensemble suivant.
  • Indiquer aux utilisateurs finaux comment corriger le bot.
  • Reconnaître lorsqu'il y a suffisamment de chiffres pour une séquence valide et indiquer à l'utilisateur final comment finaliser l'entrée (voir "MIN_SEQUENCE_LENGTH" dans le code).
  • Boucler le remplissage de cases pour recueillir plusieurs séquences partielles
  • En concaténant les séquences partielles en une seule séquence.

Valider la séquence

validateSequence est l'endroit où vous souhaitez ajouter une connexion à votre datastore pour valider la séquence finale et renvoyer un message personnalisé à l'utilisateur en fonction de ces données. Si vous créez un agent de recherche de commandes, par exemple, vous pouvez personnaliser la réponse ici pour dire:

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

lookup est un objet que vous avez trouvé dans votre datastore pour cette commande.

Fonctions de l'outil d'aide

Cet exemple n'utilise aucune dépendance spécifique à Dialogflow. À la place, suivez la Documentation de référence sur WebhookRequest pour savoir à quoi vous attendre dans request.body, et la Documentation de référence sur WebhookResponse pour savoir quoi répondre avec response.json({...}).

Le code contient deux fonctions d'assistance qui facilitent les opérations:

  • Envoyez la réponse JSON appropriée pour la plate-forme actuelle en transmettant une chaîne à sendSSML.
  • Recherchez un contexte Dialogflow actif à partir de la requête en transmettant le nom du contexte à getOutputContext.

Autres améliorations

Cela vous permettra de commencer à utiliser le webhook pour des cas d'utilisation avancés. Vous avez conçu un agent pouvant mettre en boucle une invite de séquence pendant qu'un utilisateur final prononce sa séquence, en veillant à ce que l'agent virtuel les entend correctement.

Voici quelques idées pour améliorer encore davantage votre expérience:

  • Modifiez certaines réponses webhook pour qu'elles correspondent à votre marque. Par exemple, au lieu de l'invite générique "Quelle est votre séquence ?", vous pouvez modifier le code pour demander "Quel est votre numéro de commande ? Vous le trouverez plutôt sur ...".
  • Pensez à ajouter un contexte de sortie à l'intent "Sequence - Done" (Séquence - OK), puis créez des intents sous ce contexte d'entrée pour permettre aux utilisateurs de poser des questions complémentaires sur leur commande.
  • Si vous souhaitez approfondir ce cas d'utilisation, consultez le TODO: CHALLENGE dans l'exemple de code ci-dessus pour découvrir comment améliorer davantage cette expérience pour vos utilisateurs.