Servicio de webhooks

Para usar la entrega en un sistema de producción, debes implementar un servicio de webhook. Para controlar la entrega, tu servicio de webhook debe aceptar solicitudes JSON y devolver respuestas JSON como se especifica en esta guía. El flujo de procesamiento detallado para la entrega y los webhooks se describe en el documento de descripción general de la entrega.

Requisitos del servicio de webhook

Tu servicio de webhook debe cumplir con los siguientes requisitos:

  • Controlar solicitudes HTTPS HTTP no es compatible. Si alojas tu servicio de webhook en Google Cloud con una solución de Compute o computación sin servidores, consulta la documentación del producto para entregar con HTTPS. Para conocer otras opciones de hosting, consulta Obtén un certificado SSL para tu dominio.
  • Asegúrate de que la URL del servicio de webhook sea de acceso público.
  • Controla las solicitudes POST con un cuerpo JSON WebhookRequest.
  • Responde a las solicitudes WebhookRequest con un cuerpo JSON WebhookResponse.

Autenticación

X Elemento
Nombre de usuario y contraseña de acceso En la configuración del webhook, puedes especificar valores opcionales de nombre de usuario y contraseña de acceso. Si se proporciona, Dialogflow agrega un encabezado HTTP de autorización a las solicitudes de webhook. Este encabezado tiene el siguiente formato: "authorization: Basic <base 64 encoding of the string username:password>".
Encabezados de autenticación En la configuración del webhook, puedes especificar pares clave-valor opcionales del encabezado HTTP. Si se proporcionan, Dialogflow agrega estos encabezados HTTP a las solicitudes de webhook. Es común proporcionar un solo par con una clave de authorization.
Autenticación integrada de Cloud Run Functions Puedes usar la autenticación integrada cuando usas Cloud Run Functions. Para usar este tipo de autenticación, no proporciones encabezados de nombre de usuario, contraseña ni autorización. Si proporcionas alguno de estos campos, no se usarán para la autenticación integrada.
Tokens de identidad del servicio Puedes usar tokens de identidad de servicio para la autenticación. Si no proporcionas un nombre de usuario de acceso, una contraseña de acceso o un encabezado con una clave de authorization, Dialogflow asume automáticamente que se deben usar tokens de identidad de servicio y agrega un encabezado HTTP de autorización a las solicitudes de webhook. Este encabezado tiene el siguiente formato: "authorization: Bearer <identity token>".
Autenticación TLS mutua Consulta la documentación de Autenticación TLS mutua.

Solicitud de webhook

Cuando se detecta una coincidencia con un intent configurado para la entrega, Dialogflow envía una solicitud de webhook HTTPS POST a tu servicio de webhook. El cuerpo de esta solicitud es un objeto JSON con información sobre el intent coincidente.

Además de la consulta del usuario final, muchas integraciones también envían información sobre el usuario final. Por ejemplo, un ID que identifica de forma inequívoca al usuario. Se puede acceder a esta información con el campo originalDetectIntentRequest en la solicitud de webhook, que contiene la información enviada desde la plataforma de integración.

Para obtener más información, consulta la documentación de referencia de WebhookRequest.

La siguiente es una solicitud de ejemplo:

{
  "responseId": "response-id",
  "session": "projects/project-id/agent/sessions/session-id",
  "queryResult": {
    "queryText": "End-user expression",
    "parameters": {
      "param-name": "param-value"
    },
    "allRequiredParamsPresent": true,
    "fulfillmentText": "Response configured for matched intent",
    "fulfillmentMessages": [
      {
        "text": {
          "text": [
            "Response configured for matched intent"
          ]
        }
      }
    ],
    "outputContexts": [
      {
        "name": "projects/project-id/agent/sessions/session-id/contexts/context-name",
        "lifespanCount": 5,
        "parameters": {
          "param-name": "param-value"
        }
      }
    ],
    "intent": {
      "name": "projects/project-id/agent/intents/intent-id",
      "displayName": "matched-intent-name"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": "en"
  },
  "originalDetectIntentRequest": {}
}

Respuesta de webhook

Después de que tu webhook reciba una solicitud, debe enviar una respuesta. El cuerpo de esta respuesta es un objeto JSON que contiene la siguiente información:

Se aplican las siguientes limitaciones a tu respuesta:

  • Responde en un plazo de 10 segundos para las aplicaciones de Asistente de Google o de 5 segundos para otras aplicaciones. De lo contrario, se agotará el tiempo de espera de la solicitud.
  • Mantén el tamaño de la respuesta en 64 KiB o menos.

Para obtener más información, consulta la documentación de referencia de WebhookResponse.

Respuesta de texto

A continuación, se muestra un ejemplo de una respuesta de texto:

{
  "fulfillmentMessages": [
    {
      "text": {
        "text": [
          "Text response from webhook"
        ]
      }
    }
  ]
}

Respuesta de tarjeta

A continuación, se muestra un ejemplo de una respuesta de tarjeta:

{
  "fulfillmentMessages": [
    {
      "card": {
        "title": "card title",
        "subtitle": "card text",
        "imageUri": "https://example.com/images/example.png",
        "buttons": [
          {
            "text": "button text",
            "postback": "https://example.com/path/for/end-user/to/follow"
          }
        ]
      }
    }
  ]
}

Respuesta del Asistente de Google

A continuación, se muestra un ejemplo de una respuesta de Asistente de Google:

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "this is a Google Assistant response"
            }
          }
        ]
      }
    }
  }
}

Contexto

A continuación, se muestra un ejemplo que establece el contexto de salida:

{
  "fulfillmentMessages": [
    {
      "text": {
        "text": [
          "Text response from webhook"
        ]
      }
    }
  ],
  "outputContexts": [
    {
      "name": "projects/project-id/agent/sessions/session-id/contexts/context-name",
      "lifespanCount": 5,
      "parameters": {
        "param-name": "param-value"
      }
    }
  ]
}

Evento

En el siguiente ejemplo, se invoca un evento personalizado:

{
  "followupEventInput": {
    "name": "event-name",
    "languageCode": "en-US",
    "parameters": {
      "param-name": "param-value"
    }
  }
}

Entidad de sesión

A continuación, se muestra un ejemplo en el que se establece una entidad de sesión:

{
  "fulfillmentMessages": [
    {
      "text": {
        "text": [
          "Choose apple or orange"
        ]
      }
    }
  ],
  "sessionEntityTypes":[
    {
      "name":"projects/project-id/agent/sessions/session-id/entityTypes/fruit",
      "entities":[
        {
          "value":"APPLE_KEY",
          "synonyms":[
            "apple",
            "green apple",
            "crabapple"
          ]
        },
        {
          "value":"ORANGE_KEY",
          "synonyms":[
            "orange"
          ]
        }
      ],
      "entityOverrideMode":"ENTITY_OVERRIDE_MODE_OVERRIDE"
    }
  ]
}

Carga útil personalizada

A continuación, se muestra un ejemplo que proporciona una carga útil personalizada:

{
  "fulfillmentMessages": [
    {
      "payload": {
        "facebook": { // for Facebook Messenger integration
          "attachment": {
            "type": "",
            "payload": {}
          }
        },
        "slack": { // for Slack integration
          "text": "",
          "attachments": []
        },
        "richContent": [ // for Dialogflow Messenger integration
          [
            {
              "type": "image",
              "rawUrl": "https://example.com/images/logo.png",
              "accessibilityText": "Example logo"
            }
          ]
        ],
        // custom integration payload here
      }
    }
  ]
}

Habilita y administra la entrega

Si deseas habilitar y administrar la entrega para el agente con la consola, sigue estos pasos:

  1. Ve a la consola de Dialogflow ES.
  2. Selecciona un agente.
  3. Selecciona Fulfillment en el menú de la barra lateral.
  4. Con el botón para activar o desactivar, selecciona Enabled (Habilitado) en el campo Webhook.
  5. Proporciona los detalles del servicio de webhook en el formulario. Si tu webhook no requiere autenticación, deja los campos de autenticación en blanco.
  6. Haz clic en Guardar.

Captura de pantalla de la habilitación de la entrega.

Para habilitar y administrar la entrega de tu agente con la API, consulta la referencia del agente. Los métodos getFulfillment y updateFulfillment te permiten administrar la configuración de entrega.

Si deseas habilitar la entrega para un intent con la consola, haz lo siguiente:

  1. En el menú de la barra lateral izquierda, selecciona Intents.
  2. Selecciona un intent.
  3. Ve a la sección Fulfillment.
  4. Activa Enable webhook call for this intent.
  5. Haz clic en Guardar.

Para habilitar la entrega de un intent con la API, consulta la referencia de intents y establece el campo webhookState en WEBHOOK_STATE_ENABLED.

Errores de webhook

Si tu servicio de webhook encuentra un error, debería mostrar uno de los siguientes códigos de estado HTTP:

  • 400: Solicitud incorrecta
  • 401: Sin autorización
  • 403: Acción prohibida
  • 404: No se encontró
  • 500: Error interno del servidor
  • 503: El servicio no está disponible

En cualquiera de las siguientes situaciones de error, Dialogflow responde al usuario final con la respuesta integrada configurada para el intent coincidente:

  • Se superó el tiempo de espera de la respuesta.
  • Se recibió un código de estado de error.
  • La respuesta no es válida.
  • El servicio de webhook no está disponible.

Además, si una llamada a la API de detección de intents activa la coincidencia de intents, el campo status de la respuesta de detección de intents contiene la información del error del webhook. Por ejemplo:

"status": {
    "code": 206,
    "message": "Webhook call failed. <details of the error...>"
}

Reintentos automáticos

Dialogflow ES incluye mecanismos internos que reintentan automáticamente en ciertos errores de webhook para mejorar la solidez. Solo se reintentan los errores no terminales, como los de tiempo de espera o de conexión.

Para reducir la probabilidad de que se dupliquen las llamadas, haz lo siguiente:

  • Establece umbrales de tiempo de espera de webhook más largos.
  • Admite la idempotencia en la lógica de tu webhook o anula la duplicación de las solicitudes.

Usa Cloud Run Functions

Puedes usar Cloud Run Functions para la entrega de varias maneras. El editor directo de Dialogflow se integra en Cloud Run Functions. Cuando usas el editor intercalado para crear y editar el código de tu webhook, Dialogflow establece una conexión segura con tu Cloud Function.

También puedes usar una Cloud Function que no se haya creado con el editor directo. Si la Cloud Function reside en el mismo proyecto que tu agente, este puede llamar al webhook sin necesidad de realizar ninguna configuración especial.

Sin embargo, debes configurar esta integración de forma manual en las siguientes dos situaciones:

  1. Debe existir la cuenta de servicio del Agente de servicio de Dialogflow con la siguiente dirección para tu proyecto de agente:
    service-agent-project-number@gcp-sa-dialogflow.iam.gserviceaccount.com
    Esta cuenta de servicio especial y la clave asociada se crean automáticamente cuando creas el primer agente para un proyecto. Si tu agente se creó antes del 10 de mayo de 2021, es posible que debas activar la creación de esta cuenta de servicio especial con lo siguiente:
    1. Crea un agente nuevo para el proyecto.
    2. Ejecuta el siguiente comando:
      gcloud beta services identity create --service=dialogflow.googleapis.com --project=agent-project-id
  2. Si tu función de webhook reside en un proyecto diferente al del agente, debes proporcionar el rol de IAM de Invocador de Cloud Functions a la cuenta de servicio del agente de servicio de Dialogflow en el proyecto de tu función.

Tokens de identidad del servicio

Cuando Dialogflow llama a un webhook, proporciona un token de identidad de Google con la solicitud. De manera opcional, cualquier webhook puede validar el token con las bibliotecas cliente de Google o las bibliotecas de código abierto, como github.com/googleapis/google-auth-library-nodejs. Por ejemplo, puedes verificar el email del token de ID de la siguiente manera:

service-agent-project-number@gcp-sa-dialogflow.iam.gserviceaccount.com

Muestras

En los siguientes ejemplos, se muestra cómo recibir un WebhookRequest y enviar un WebhookResponse. En estas muestras, se usan intents creados en la guía de inicio rápido.

Go

Para autenticarte en Dialogflow CX, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type intent struct {
	DisplayName string `json:"displayName"`
}

type queryResult struct {
	Intent intent `json:"intent"`
}

type text struct {
	Text []string `json:"text"`
}

type message struct {
	Text text `json:"text"`
}

// webhookRequest is used to unmarshal a WebhookRequest JSON object. Note that
// not all members need to be defined--just those that you need to process.
// As an alternative, you could use the types provided by
// the Dialogflow protocol buffers:
// https://godoc.org/google.golang.org/genproto/googleapis/cloud/dialogflow/v2#WebhookRequest
type webhookRequest struct {
	Session     string      `json:"session"`
	ResponseID  string      `json:"responseId"`
	QueryResult queryResult `json:"queryResult"`
}

// webhookResponse is used to marshal a WebhookResponse JSON object. Note that
// not all members need to be defined--just those that you need to process.
// As an alternative, you could use the types provided by
// the Dialogflow protocol buffers:
// https://godoc.org/google.golang.org/genproto/googleapis/cloud/dialogflow/v2#WebhookResponse
type webhookResponse struct {
	FulfillmentMessages []message `json:"fulfillmentMessages"`
}

// welcome creates a response for the welcome intent.
func welcome(request webhookRequest) (webhookResponse, error) {
	response := webhookResponse{
		FulfillmentMessages: []message{
			{
				Text: text{
					Text: []string{"Welcome from Dialogflow Go Webhook"},
				},
			},
		},
	}
	return response, nil
}

// getAgentName creates a response for the get-agent-name intent.
func getAgentName(request webhookRequest) (webhookResponse, error) {
	response := webhookResponse{
		FulfillmentMessages: []message{
			{
				Text: text{
					Text: []string{"My name is Dialogflow Go Webhook"},
				},
			},
		},
	}
	return response, nil
}

// handleError handles internal errors.
func handleError(w http.ResponseWriter, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprintf(w, "ERROR: %v", err)
}

// HandleWebhookRequest handles WebhookRequest and sends the WebhookResponse.
func HandleWebhookRequest(w http.ResponseWriter, r *http.Request) {
	var request webhookRequest
	var response webhookResponse
	var err error

	// Read input JSON
	if err = json.NewDecoder(r.Body).Decode(&request); err != nil {
		handleError(w, err)
		return
	}
	log.Printf("Request: %+v", request)

	// Call intent handler
	switch intent := request.QueryResult.Intent.DisplayName; intent {
	case "Default Welcome Intent":
		response, err = welcome(request)
	case "get-agent-name":
		response, err = getAgentName(request)
	default:
		err = fmt.Errorf("Unknown intent: %s", intent)
	}
	if err != nil {
		handleError(w, err)
		return
	}
	log.Printf("Response: %+v", response)

	// Send response
	if err = json.NewEncoder(w).Encode(&response); err != nil {
		handleError(w, err)
		return
	}
}

Java

Para autenticarte en Dialogflow CX, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.


// TODO: add GSON dependency to Pom file
// (https://mvnrepository.com/artifact/com.google.code.gson/gson/2.8.5)
// TODO: Uncomment the line bellow before running cloud function
// package com.example;

import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.BufferedWriter;

public class Example implements HttpFunction {

  public void service(HttpRequest request, HttpResponse response) throws Exception {
    JsonParser parser = new JsonParser();
    Gson gson = new GsonBuilder().create();

    JsonObject job = gson.fromJson(request.getReader(), JsonObject.class);
    String str =
        job.getAsJsonObject("queryResult")
            .getAsJsonObject("intent")
            .getAsJsonPrimitive("displayName")
            .toString();
    JsonObject o = null;
    String a = '"' + "Default Welcome Intent" + '"';
    String b = '"' + "get-agent-name" + '"';
    String responseText = "";

    if (str.equals(a)) {
      responseText = '"' + "Hello from a Java GCF Webhook" + '"';
    } else if (str.equals(b)) {
      responseText = '"' + "My name is Flowhook" + '"';
    } else {
      responseText = '"' + "Sorry I didn't get that" + '"';
    }

    o =
        parser
            .parse(
                "{\"fulfillmentMessages\": [ { \"text\": { \"text\": [ "
                    + responseText
                    + " ] } } ] }")
            .getAsJsonObject();

    BufferedWriter writer = response.getWriter();
    writer.write(o.toString());
  }
}

Node.js

Para autenticarte en Dialogflow CX, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

const functions = require('@google-cloud/functions-framework');

// TODO: Add handleWebhook to 'Entry point' in the Google Cloud Function
functions.http('handleWebhook', (request, response) => {
  const tag = request.body.queryResult.intent.displayName;

  let jsonResponse = {};
  if (tag === 'Default Welcome Intent') {
    //fulfillment response to be sent to the agent if the request tag is equal to "welcome tag"
    jsonResponse = {
      fulfillment_messages: [
        {
          text: {
            //fulfillment text response to be sent to the agent
            text: ['Hello from a GCF Webhook'],
          },
        },
      ],
    };
  } else if (tag === 'get-name') {
    //fulfillment response to be sent to the agent if the request tag is equal to "welcome tag"
    jsonResponse = {
      fulfillment_messages: [
        {
          text: {
            //fulfillment text response to be sent to the agent
            text: ['My name is Flowhook'],
          },
        },
      ],
    };
  } else {
    jsonResponse = {
      //fulfillment text response to be sent to the agent if there are no defined responses for the specified tag
      fulfillment_messages: [
        {
          text: {
            ////fulfillment text response to be sent to the agent
            text: [
              `There are no fulfillment responses defined for "${tag}"" tag`,
            ],
          },
        },
      ],
    };
  }
  response.send(jsonResponse);
});

Python

Para autenticarte en Dialogflow CX, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta Configura la autenticación para un entorno de desarrollo local.

# TODO: change the default Entry Point text to handleWebhook
import functions_framework


@functions_framework.http
def handleWebhook(request):
    req = request.get_json()

    responseText = ""
    intent = req["queryResult"]["intent"]["displayName"]

    if intent == "Default Welcome Intent":
        responseText = "Hello from a GCF Webhook"
    elif intent == "get-agent-name":
        responseText = "My name is Flowhook"
    else:
        responseText = f"There are no fulfillment responses defined for Intent {intent}"

    # You can also use the google.cloud.dialogflowcx_v3.types.WebhookRequest protos instead of manually writing the json object
    res = {"fulfillmentMessages": [{"text": {"text": [responseText]}}]}

    return res