Auftragsausführung mithilfe eines Webhooks erstellen

Die Webhook-Auftragsausführung in Dialogflow gibt uns viele Kontrolle über den Ablauf unseres Agents. In dieser Anleitung benötigen Sie einen Webhook, um die alphanumerischen Sequenzen zu validieren, die im Intent „Sequence“ erfasst wurden. Der Webhook wiederholt diesen Intent in einer Schleife, um eine lange Sequenz in besser verwaltbaren Iterationen zu erfassen.

Webhook mit dem Inline-Editor erstellen

Dialogflow hat in der Konsole einen Inline-Editor, mit dem Sie NodeJS-Code schreiben können, der dann als Webhook in Cloud Functions bereitgestellt werden kann.

So erstellen Sie einen Webhook mit dem Inline-Editor von Dialogflow:

  1. Klicken Sie auf den Tab Fulfillment in der Navigationsleiste, um zur Seite für Auftragsausführungen zu gelangen.
  2. Schalten Sie nun den Schieberegler des Inline-Editors auf ENABLED.
  3. Löschen Sie den vorhandenen Inhalt im Tab package.json des Inline-Editors.
  4. Kopieren Sie dann den folgenden JSON-Code und fügen Sie ihn in den Tab package.json des Inline-Editors ein:

    {
      "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. Löschen Sie den vorhandenen Code im Tab index.js des Inline-Editors.

  6. Kopieren Sie anschließend den folgenden Code und fügen Sie ihn in den Tab index.js des Inline-Editors ein:

    /**
     * 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. Klicken Sie auf DEPLOY (Bereitstellen).

Sie sollten nun in der Lage sein, die Integration zu testen, indem Sie den Agent aufrufen. Falls noch nicht geschehen, empfiehlt es sich, eine der Telefonieintegrationen mit einem Klick von unseren Partnern einzurichten oder das Dialogflow-Telefon einzurichten. Gateway zum Testen des Agents über das Telefon.

Code verstehen

Als Einstiegspunkt für den Webhook wird die Funktion dialogflowFirebaseFulfillment bei jedem Auslösen des Webhooks aufgerufen. Mit jeder Anfrage sendet Dialogflow den Aktionsnamen, den Sie in der Dialogflow-Konsole für einen Intent angegeben haben. Der Code verwendet diesen Aktionsnamen, um zu bestimmen, welche Webhook-Funktion (handleSequence oder validateSequence) aufgerufen wird.

Handle-Sequenz

handleSequence ist die Kernfunktion dieser Anleitung. Sie ist für alle Aspekte der Sequenz-Slot-Füllung zuständig, darunter:

  • Die ersten Anweisungen aussprechen, wenn eine Sitzung zum ersten Mal auf den Intent trifft.
  • Die Sequenz vor der Eingabeaufforderung für den nächsten Satz wiederholen.
  • Endnutzern mitteilen, wie der Bot korrigiert werden kann.
  • Wissen, wann genügend Ziffern für eine gültige Sequenz vorhanden sind, und dem Endnutzer mitteilen, wie die Eingabe abgeschlossen werden kann (siehe „MIN_SEQUENCE_LENGTH“ im Code).
  • Die Slot-Füllung in eine Schleife setzen, um mehrere Teilsequenzen zu erfassen
  • Teilsequenzen zu einer langen Sequenz verketten.

Validate-Sequenz

In validateSequence möchten Sie Ihrem Datenspeicher eine Verbindung hinzufügen, um die endgültige Sequenz zu validieren und eine benutzerdefinierte Nachricht an den Nutzer anhand dieser Daten zurückzugeben. Wenn Sie beispielsweise einen Agent für die Bestellsuche erstellen, können Sie die Antwort hier anpassen:

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

Dabei ist lookup ein Objekt das Sie für diese Bestellung in Ihrem Datenspeicher gefunden haben.

Hilfsfunktionen

In diesem Beispiel werden keine Dialogflow-spezifischen Abhängigkeiten verwendet. Verwenden Sie stattdessen die WebhookRequest-Referenz für das, was in request.body zu erwarten ist, und die WebhookResponse-Referenz, um zu antworten, was zu antworten ist. mit response.json({...}).

Der Code enthält zwei Hilfsfunktionen, die folgende Aufgaben vereinfachen:

  • Die richtige Antwort-JSON für die aktuelle Plattform senden, indem Sie einen String an sendSSML übergeben.
  • In der Anfrage nach einem aktiven Dialogflow-Kontext suchen und dazu den Kontextnamen an getOutputContext übergeben.

Weitere Verbesserung

Sie sollten nun wissen, wie Sie Webhook für erweiterte Anwendungsfälle verwenden. Sie haben einen Agent entwickelt, der eine Sequenzaufforderung in eine Schleife setzen kann, während ein Endnutzer seine Sequenz spricht und nebenbei bestätigt, dass er vom virtuellen Agent richtig verstanden wird.

Hier einige Vorschläge zur weiteren Verbesserung dieser Funktion:

  • Ändern Sie einige der Webhook-Antworten so, dass sie Ihrer Marke entsprechen. Statt der allgemeinen Aufforderung „Wie lautet Ihre Sequenz?“ können Sie den Code beispielsweise zu „Wie lautet Ihre Bestellnummer?“ bearbeiten. Sie finden sie stattdessen auf ...“.
  • Erwägen Sie die Hinzufügung eines weiteren Ausgabekontextes zum Intent „Sequenz – Fertig“. Erstellen Sie dann unter diesem Eingabekontext neue Intents, damit Nutzer Folgefragen zu ihrer Bestellung stellen können.
  • Wenn Sie diesen Anwendungsfall genauer ansehen möchten, finden Sie im TODO: CHALLENGE im Beispielcode weitere Informationen dazu, wie Sie diese für Ihre Nutzer noch weiter verbessern können.