Servicio webhook

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

Requisitos del servicio webhook

Tu servicio de webhook debe cumplir los siguientes requisitos:

  • Debe gestionar las solicitudes HTTPS. No se admite HTTP. Si alojas tu servicio de webhook en Google Cloud Platform mediante una solución de Compute o de computación sin servidor, consulta la documentación del producto para usar HTTPS. Para ver otras opciones de alojamiento, consulta Obtener un certificado SSL para tu dominio.
  • Su URL para las solicitudes debe ser de acceso público.
  • Debe gestionar las solicitudes POST con un cuerpo JSON WebhookRequest.
  • Debe responder a las solicitudes WebhookRequest con un cuerpo JSON. WebhookResponse

Autenticación

Es importante proteger tu servicio de webhook para que solo tú o tu agente de Dialogflow tengáis autorización para enviar solicitudes. Dialogflow admite los siguientes mecanismos de autenticación:

Término Definición
Nombre de usuario y contraseña de inicio de sesión En la configuración de webhook, puedes especificar valores opcionales de nombre de usuario y contraseña para iniciar sesión. Si se proporciona, Dialogflow añade 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 de webhook, puede especificar pares clave-valor de encabezado HTTP opcionales. Si se proporcionan, Dialogflow añade estos encabezados HTTP a las solicitudes de webhook. Es habitual proporcionar un solo par con una clave de authorization.
Autenticación integrada de Cloud Functions Puedes usar la autenticación integrada cuando uses Cloud Functions. Para usar este tipo de autenticación, no proporciones el nombre de usuario ni la contraseña de inicio de sesión, ni los encabezados de autorización. Si proporcionas alguno de estos campos, se usarán para la autenticación en lugar de la autenticación integrada.
Tokens de identidad de servicio Puedes usar tokens de identidad de servicio para la autenticación. Si no proporcionas el nombre de usuario, la contraseña o una cabecera con la clave authorization, Dialogflow asumirá automáticamente que se deben usar tokens de identidad de servicio y añadirá una cabecera HTTP de autorización a las solicitudes de webhook. Este encabezado tiene el siguiente formato: "authorization: Bearer <identity token>".
Autenticación TLS mutuo Consulta la documentación sobre la autenticación TLS mutua.

Solicitud de webhook

Cuando se detecta una intención configurada para la respuesta, Dialogflow envía una solicitud webhook HTTPS POST a tu servicio webhook. El cuerpo de esta solicitud es un objeto JSON que contiene información sobre la intención 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 para identificar de forma única al usuario. Se puede acceder a esta información a través del campo originalDetectIntentRequest de la solicitud webhook, que contendrá la información enviada desde la plataforma de integración.

Consulta la documentación de referencia de WebhookRequest para obtener más información.

Aquí tienes 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

Una vez que tu webhook reciba una solicitud de webhook, deberá enviar una respuesta de webhook. El cuerpo de esta respuesta es un objeto JSON con la siguiente información:

Se aplican las siguientes limitaciones a tu respuesta:

  • La respuesta debe producirse en un plazo de 10 segundos para las aplicaciones del Asistente de Google o de 5 segundos para el resto de las aplicaciones. De lo contrario, la solicitud se agotará.
  • El tamaño de la respuesta no puede ser superior a 64 KiB.

Consulta la documentación de referencia de WebhookResponse para obtener más información.

Respuesta de texto rápida

Ejemplo de respuesta de texto:

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

Respuesta de la tarjeta

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

Ejemplo de una respuesta del Asistente de Google:

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

Contexto

Ejemplo que define 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

Ejemplo que invoca un evento personalizado:

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

Entidad de sesión

Ejemplo que define 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

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
      }
    }
  ]
}

Habilitar y gestionar el estado de los pedidos

Para habilitar y gestionar la respuesta de tu agente con la consola, sigue estos pasos:

  1. Ve a la consola de Dialogflow ES.
  2. Selecciona un agente.
  3. Selecciona Cumplimiento en el menú de la barra lateral de la izquierda.
  4. Cambia el campo Webhook a Habilitado.
  5. Proporciona los detalles de tu servicio de webhook en el formulario. Si tu webhook no requiere autenticación, deja los campos de autenticación en blanco.
  6. En la parte inferior de la página, haga clic en Guardar.

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

Para habilitar y gestionar el cumplimiento de tu agente con la API, consulta la referencia del agente. Los métodos getFulfillment y updateFulfillment se pueden usar para gestionar la configuración de los cumplimientos.

Para habilitar la función de completar pedidos en una intención con la consola, sigue estos pasos:

  1. Selecciona Intenciones en el menú de la barra lateral de la izquierda.
  2. Selecciona una intención.
  3. Desplázate hacia abajo hasta la sección Cumplimiento.
  4. Activa Habilitar llamada de webhook para esta intención.
  5. Haz clic en Guardar.

Para habilitar la respuesta de una intención con la API, consulta la referencia de intenciones. Asigna el valor WEBHOOK_STATE_ENABLED al campo webhookState.

Errores de webhook

Si tu servicio de webhook detecta un error, debe devolver uno de los siguientes códigos de estado HTTP:

  • 400: solicitud incorrecta
  • 401 Sin autorización
  • 403 Forbidden
  • 404 no encontrado
  • 500 Error del servidor
  • 503 Servicio no disponible

En cualquiera de las siguientes situaciones de error, Dialogflow responde al usuario final con la respuesta integrada configurada para la intención que se ha detectado:

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

Además, si la coincidencia de la intención se ha activado mediante una llamada a la API de detección de intenciones, el campo status de la respuesta de detección de intenciones 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 vuelven a intentar automáticamente ciertas llamadas a webhooks para mejorar la solidez. Solo se reintentan los errores no terminales (por ejemplo, los errores de tiempo de espera o de conexión).

Para reducir la probabilidad de que se dupliquen las llamadas, sigue estos pasos:

  • Define umbrales de tiempo de espera de webhook más largos.
  • Admite la idempotencia en la lógica de webhook o elimina los duplicados.

Usar Cloud Functions

Hay varias formas de usar Cloud Functions para completar las solicitudes. El editor en línea de Dialogflow se integra con Cloud Functions. Cuando usas el editor insertado para crear y editar el código de tu webhook, Dialogflow establece una conexión segura con tu función de Cloud.

También puedes usar una función de Cloud que no se haya creado con el editor insertado (por ejemplo, si quieres usar un lenguaje distinto de Node.js). Si la función de Cloud reside en el mismo proyecto que tu agente, este podrá llamar a tu webhook sin necesidad de ninguna configuración especial.

Sin embargo, hay dos situaciones en las que debes configurar manualmente esta integración:

  1. Tu proyecto de agente debe tener la cuenta de servicio del agente de servicio de Dialogflow con la siguiente dirección:
    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 de un proyecto. Si tu agente se creó antes del 10 de mayo del 2021, es posible que tengas que activar la creación de esta cuenta de servicio especial con lo siguiente:
    1. Crea un agente para el proyecto.
    2. Ejecuta el comando siguiente:
      gcloud beta services identity create --service=dialogflow.googleapis.com --project=agent-project-id
  2. Si tu función de webhook se encuentra en un proyecto diferente al del agente, debes proporcionar el rol de gestión de identidades y accesos Cloud Functions Invoker a la cuenta de servicio Dialogflow Service Agent del proyecto de tu función.

Tokens de identidad de servicio

Cuando Dialogflow llama a un webhook, proporciona un token de identidad de Google con la solicitud. Cualquier webhook puede validar el token de forma opcional con bibliotecas de cliente de Google o 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

Ejemplos

En los siguientes ejemplos se muestra cómo recibir un WebhookRequest y enviar un WebhookResponse. Estas muestras hacen referencia a las intenciones creadas en la guía de inicio rápido.

Go

Para autenticarte en Dialogflow, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en 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, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en 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, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en 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, configura las credenciales predeterminadas de la aplicación. Para obtener más información, consulta el artículo Configurar la autenticación en 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