Membuat fulfillment menggunakan webhook

Fulfillment Webhook di Dialogflow memberi kita banyak kontrol atas alur agen kita. Dalam tutorial ini, Anda memerlukan webhook untuk memvalidasi urutan alfanumerik yang dikumpulkan dalam intent "Urutan". Webhook akan melakukan loop atas intent tersebut berulang kali untuk mengumpulkan urutan panjang dalam iterasi yang lebih mudah dikelola.

Membuat webhook dengan editor inline

Dialogflow memiliki editor inline di konsol yang memungkinkan Anda menulis langsung kode NodeJS, yang kemudian dapat di-deploy untuk dijalankan sebagai webhook di Cloud Functions.

Untuk membuat webhook menggunakan editor inline Dialogflow, ikuti langkah-langkah berikut:

  1. Klik tab Fulfillment di menu navigasi untuk membuka halaman fulfillment.
  2. Alihkan tombol editor langsung ke DIAKTIFKAN.
  3. Hapus konten yang ada di tab package.json pada editor inline.
  4. Salin dan tempel konten JSON di bawah ke tab package.json di editor langsung:

    {
      "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. Hapus kode yang ada di tab index.js pada editor inline.

  6. Salin dan tempel kode di bawah ke tab index.js di editor langsung:

    /**
     * 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. Klik DEPLOY.

Sekarang Anda dapat menguji integrasi dengan memanggil agen. Jika Anda belum melakukannya, sekarang adalah saat yang tepat untuk menyiapkan salah satu integrasi telepon sekali klik dari partner kami, atau siapkan Dialogflow Phone Gateway untuk menguji agen Anda melalui telepon.

Memahami kode

Sebagai titik entri ke webhook, fungsi dialogflowFirebaseFulfillment di sini dipanggil setiap kali webhook dipicu. Dengan setiap permintaan, Dialogflow akan mengirimkan nama "tindakan" yang Anda tentukan di konsol Dialogflow untuk suatu intent. Kode menggunakan nama tindakan ini untuk menentukan fungsi webhook yang akan dipanggil, handleSequence atau validateSequence.

Menangani Urutan

handleSequence adalah fungsi inti untuk tutorial ini. Ini bertanggung jawab atas semua aspek pengisian slot urutan, termasuk:

  • Mengucapkan petunjuk awal saat sesi pertama kali memasuki intent.
  • Mengulangi urutan sebelum meminta set berikutnya.
  • Memberi tahu pengguna akhir cara memperbaiki bot.
  • Mengenali saat ada cukup digit untuk urutan yang valid dan memberi tahu pengguna akhir cara menyelesaikan input (lihat `MIN_SEQUENCE_LENGTH' dalam kode).
  • Mengulang pengisian slot untuk mengumpulkan beberapa urutan parsial.
  • Menggabungkan urutan parsial menjadi satu urutan panjang.

Validasi Urutan

validateSequence adalah tempat Anda sebaiknya menambahkan koneksi ke penyimpanan data untuk memvalidasi urutan akhir dan menampilkan pesan kustom kepada pengguna berdasarkan data tersebut. Misalnya, jika Anda membuat agen pencarian pesanan, Anda dapat menyesuaikan respons di sini untuk menyatakan:

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

Dengan lookup adalah beberapa objek yang Anda temukan di penyimpanan data untuk pesanan ini.

Fungsi bantuan

Contoh ini tidak menggunakan dependensi khusus Dialogflow. Sebagai gantinya, ikuti referensi WebhookRequest untuk mengetahui apa yang akan terjadi di request.body, dan referensi WebhookResponse untuk mengetahui apa yang akan direspons dengan response.json({...}).

Kode ini menyertakan dua fungsi bantuan untuk mempermudah:

  • Kirim JSON respons yang tepat untuk platform saat ini dengan meneruskan string ke sendSSML.
  • Cari konteks Dialogflow yang aktif dari permintaan dengan meneruskan nama konteks ke getOutputContext.

Peningkatan lebih lanjut

Ini akan membantu Anda mulai menggunakan webhook untuk kasus penggunaan lanjutan. Anda merancang agen yang dapat mengulang perintah urutan saat pengguna akhir mengucapkan urutannya, agar agen virtual dapat mendengarkannya dengan benar.

Berikut beberapa ide untuk meningkatkan kualitas pengalaman pengguna:

  • Ubah beberapa respons webhook agar sesuai dengan brand Anda. Misalnya, daripada perintah "Apa urutan Anda?..." yang umum, Anda dapat mengedit kode untuk mengatakan "Berapa nomor pesanan Anda? Anda dapat menemukannya di ...".
  • Pertimbangkan untuk menambahkan konteks output lain ke intent "Urutan - Selesai", lalu buat beberapa intent baru dalam konteks input tersebut untuk memungkinkan pengguna mengajukan pertanyaan lanjutan tentang urutan mereka.
  • Jika Anda ingin mempelajari kasus penggunaan ini lebih lanjut, lihat TODO: CHALLENGE pada kode contoh di atas untuk mengetahui cara meningkatkan pengalaman ini lebih jauh bagi pengguna Anda.