O agente predefinido que você criou na última etapa não pode fornecer dados dinâmicos, como saldos de conta, porque tudo está codificado no agente. Nesta etapa do tutorial, você vai criar um webhook que pode fornecer dados dinâmicos ao agente. As funções do Cloud Run são usadas para hospedar o webhook neste tutorial devido à simplicidade, mas há muitas outras maneiras de hospedar um serviço de webhook. O exemplo também usa a linguagem de programação Go, mas é possível usar qualquer linguagem compatível com as funções do Cloud Run.
Criar a função
As funções do Cloud Run podem ser criadas com o console do Google Cloud (acesse a documentação e abra o console). Para criar uma função para este tutorial:
É importante que o agente do Dialogflow e a função estejam no mesmo projeto. Essa é a maneira mais fácil do Dialogflow ter acesso seguro à função. Antes de criar a função, selecione seu projeto no console do Google Cloud.
Abra a página de visão geral das funções do Cloud Run.
Clique em Criar função e defina os seguintes campos:
- Ambiente: 1ª geração
- Nome da função: tutorial-banking-webhook
- Região: se você especificou uma região para o agente, use a mesma.
- Tipo de gatilho HTTP: HTTP
- URL: clique no botão de cópia aqui e salve o valor. Você vai precisar desse URL ao configurar o webhook.
- Autenticação: exija autenticação.
- Requerer HTTPS: marcada
Clique em Salvar.
Clique em Next (você não precisa de recursos especiais de ambiente de execução, build ou configurações de segurança).
Defina os seguintes campos:
- Ambiente de execução: selecione o ambiente de execução mais recente do Go.
- Código-fonte: editor in-line
- Ponto de entrada: HandleWebhookRequest
Substitua o código por este:
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 } }
Clique em Implantar.
Aguarde até que o indicador de status mostre que a função foi implantada. Enquanto aguarda, examine o código que você acabou de implantar.
Configurar o webhook para seu agente
Agora que o webhook existe como um serviço, é necessário associá-lo ao seu agente. Isso é feito por meio do fulfillment. Para ativar e gerenciar o fulfillment do agente:
- Acesse o console do Dialogflow ES.
- Selecione o agente pré-criado que você acabou de criar.
- Selecione Fulfillment no menu da barra lateral esquerda.
- Alterne o campo Webhook para Ativado.
- Informe o URL que você copiou acima. Não preencha os outros campos.
- Clique em Salvar, na parte inferior da página.
Agora que o fulfillment está ativado para o agente, é necessário ativar o fulfillment para uma intent:
- Selecione Intents no menu da barra lateral à esquerda.
- Selecione a intent account.balance.check.
- Role para baixo até a seção Fulfillment.
- Alternar Ativar chamada de webhook para este intent para ativada.
- Clique em Salvar.
Testar o agente
Seu agente já pode fazer um teste. Clique no botão Testar agente para abrir o simulador. Tente ter a seguinte conversa com o agente:
Turno de conversa | Você | Agente |
---|---|---|
1 | Olá | Olá, agradecemos por escolher o ACME Bank. |
2 | Quero saber o saldo da minha conta | Qual conta você quer consultar o saldo: poupança ou corrente? |
3 | Verificando | Aqui está seu saldo mais recente: US$ 0,00 |
Na terceira vez que você interagiu com o bot, você informou "checking" como o tipo de conta. A intent account.balance.check tem um parâmetro chamado account. Esse parâmetro é definido como "em verificação", nesta conversa. A intent também tem um valor de ação "account.balance.check". O serviço de webhook é chamado e recebe os valores de parâmetro e ação.
Se você examinar o código do webhook acima, vai notar que essa ação aciona uma função com nome semelhante para ser chamada. A função determina o saldo da conta. A função verifica se as variáveis de ambiente específicas estão definidas com informações para se conectar ao banco de dados. Se essas variáveis de ambiente não estiverem definidas, a função usa um saldo de conta fixado no código. Nas próximas etapas, você vai alterar o ambiente da função para recuperar dados de um banco de dados.
Solução de problemas
O código do webhook inclui instruções de registro. Se você tiver problemas, tente visualizar os registros da função.
Mais informações
Para mais informações sobre as etapas acima, consulte: