El agente prediseñado que has creado en el paso anterior no puede proporcionar datos dinámicos, como saldos de cuentas, porque todo está codificado en el agente. En este paso del tutorial, crearás un webhook que pueda proporcionar datos dinámicos al agente. En este tutorial, se usan funciones de Cloud Run para alojar el webhook debido a su sencillez, pero hay muchas otras formas de alojar un servicio de webhook. En el ejemplo también se usa el lenguaje de programación Go, pero puedes usar cualquier lenguaje compatible con Cloud Run Functions.
Crear la función
Las funciones de Cloud Run se pueden crear con la consola de Google Cloud (consulta la documentación o abre la consola). Para crear una función en este tutorial, sigue estos pasos:
Es importante que tu agente de Dialogflow y la función estén en el mismo proyecto. Es la forma más sencilla de que Dialogflow tenga acceso seguro a tu función. Antes de crear la función, selecciona tu proyecto en la Google Cloud consola.
Abre la página de información general de Cloud Run Functions.
Haz clic en Crear función y define los siguientes campos:
- Entorno: 1.ª gen.
- Nombre de la función: tutorial-banking-webhook
- Región: si has especificado una región para tu agente, usa la misma.
- Tipo de activador HTTP: HTTP
- URL: haz clic en el botón de copiar y guarda el valor. Necesitarás esta URL al configurar el webhook.
- Autenticación: requiere autenticación
- Requerir HTTPS: marcada
Haz clic en Guardar.
Haz clic en Siguiente (no necesitas ajustes especiales de tiempo de ejecución, compilación, conexiones ni seguridad).
Configura los siguientes campos:
- Tiempo de ejecución: selecciona el tiempo de ejecución de Go más reciente.
- Código fuente: editor insertado
- Punto de entrada: HandleWebhookRequest
Sustituye el código por el siguiente:
package estwh import ( "context" "encoding/json" "fmt" "log" "net/http" "os" "strings" "cloud.google.com/go/spanner" "google.golang.org/grpc/codes" ) // client is a Spanner client, created only once to avoid creation // for every request. // See: https://cloud.google.com/functions/docs/concepts/go-runtime#one-time_initialization var client *spanner.Client func init() { // If using a database, these environment variables will be set. pid := os.Getenv("PROJECT_ID") iid := os.Getenv("SPANNER_INSTANCE_ID") did := os.Getenv("SPANNER_DATABASE_ID") if pid != "" && iid != "" && did != "" { db := fmt.Sprintf("projects/%s/instances/%s/databases/%s", pid, iid, did) log.Printf("Creating Spanner client for %s", db) var err error // Use the background context when creating the client, // but use the request context for calls to the client. // See: https://cloud.google.com/functions/docs/concepts/go-runtime#contextcontext client, err = spanner.NewClient(context.Background(), db) if err != nil { log.Fatalf("spanner.NewClient: %v", err) } } } type queryResult struct { Action string `json:"action"` Parameters map[string]interface{} `json:"parameters"` } 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"` } // accountBalanceCheck handles the similar named action func accountBalanceCheck(ctx context.Context, request webhookRequest) ( webhookResponse, error) { account := request.QueryResult.Parameters["account"].(string) account = strings.ToLower(account) var table string if account == "savings account" { table = "Savings" } else { table = "Checking" } s := "Your balance is $0" if client != nil { // A Spanner client exists, so access the database. // See: https://pkg.go.dev/cloud.google.com/go/spanner#ReadOnlyTransaction.ReadRow row, err := client.Single().ReadRow(ctx, table, spanner.Key{1}, // The account ID []string{"Balance"}) if err != nil { if spanner.ErrCode(err) == codes.NotFound { log.Printf("Account %d not found", 1) } else { return webhookResponse{}, err } } else { // A row was returned, so check the value var balance int64 err := row.Column(0, &balance) if err != nil { return webhookResponse{}, err } s = fmt.Sprintf("Your balance is $%.2f", float64(balance)/100.0) } } response := webhookResponse{ FulfillmentMessages: []message{ { Text: text{ Text: []string{s}, }, }, }, } return response, nil } // Define a type for handler functions. type handlerFn func(ctx context.Context, request webhookRequest) ( webhookResponse, error) // Create a map from action to handler function. var handlers map[string]handlerFn = map[string]handlerFn{ "account.balance.check": accountBalanceCheck, } // handleError handles internal errors. func handleError(w http.ResponseWriter, err error) { log.Printf("ERROR: %v", err) http.Error(w, fmt.Sprintf("ERROR: %v", err), http.StatusInternalServerError) } // 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) // Get the action from the request, and call the corresponding // function that handles that action. action := request.QueryResult.Action if fn, ok := handlers[action]; ok { response, err = fn(r.Context(), request) } else { err = fmt.Errorf("Unknown action: %s", action) } 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 } }
Haz clic en Desplegar.
Espera hasta que el indicador de estado muestre que la función se ha implementado correctamente. Mientras esperas, examina el código que acabas de implementar.
Configurar el webhook de tu agente
Ahora que el webhook existe como servicio, debes asociarlo a tu agente. Esto se hace mediante la respuesta. Para habilitar y gestionar la respuesta de tu agente, sigue estos pasos:
- Ve a la consola de Dialogflow ES.
- Selecciona el agente prediseñado que acabas de crear.
- Selecciona Cumplimiento en el menú de la barra lateral de la izquierda.
- Cambia el campo Webhook a Habilitado.
- Proporciona la URL que has copiado anteriormente. Deja el resto de los campos en blanco.
- En la parte inferior de la página, haga clic en Guardar.
Ahora que la función de respuesta se ha habilitado en el agente, debes habilitarla en una intención:
- Selecciona Intenciones en el menú de la barra lateral de la izquierda.
- Selecciona la intención account.balance.check.
- Desplázate hacia abajo hasta la sección Cumplimiento.
- Activa Habilitar llamada de webhook para esta intención.
- Haz clic en Guardar.
Probar el agente
Tu agente ya está listo para probarse. Haz clic en el botón Agente de prueba para abrir el simulador. Intenta mantener la siguiente conversación con el agente:
Turno de conversación | Tú | Agente |
---|---|---|
1 | Hola | Hola, gracias por elegir ACME Bank. |
2 | Quiero saber el saldo de mi cuenta | ¿De qué cuenta quieres saber el saldo: de ahorro o corriente? |
3 | Corriente | Este es tu saldo más reciente: 0,00 € |
En la conversación #3, has indicado "checking" como tipo de cuenta. El intent account.balance.check tiene un parámetro llamado account. Este parámetro se ha definido como "checking" en esta conversación. El intent también tiene el valor de acción "account.balance.check". Se llama al servicio de webhook y se le transfieren los valores de los parámetros y de las acciones.
Si examinas el código del webhook anterior, verás que esta acción activa una función con un nombre similar. La función determina el saldo de la cuenta. La función comprueba si se han definido variables de entorno específicas con información para conectarse a la base de datos. Si estas variables de entorno no están definidas, la función usa un saldo de cuenta codificado. En los próximos pasos, modificarás el entorno de la función para que recupere datos de una base de datos.
Solución de problemas
El código del webhook incluye instrucciones de registro. Si tienes problemas, prueba a ver los registros de tu función.
Más información
Para obtener más información sobre los pasos anteriores, consulta los siguientes artículos: