O agente pré-criado que você criou na última etapa não pode fornecer dados dinâmicos, como saldos de contas, porque tudo está fixado no código no agente. Nesta etapa do tutorial, você criará um webhook que pode fornecer dados dinâmicos ao agente. As funções do Cloud são usadas para hospedar o webhook neste tutorial devido à simplicidade dele, mas há muitas outras maneiras de hospedar um serviço de webhook. No exemplo, também usamos a linguagem de programação Go, mas é possível usar qualquer linguagem compatível com o Cloud Functions.
Criar a função
O Cloud Functions pode ser criado 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 e a função do Dialogflow estejam no mesmo projeto. Essa é a maneira mais fácil de o 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 do Cloud Functions.
Clique em Criar função e defina os seguintes campos:
- Ambiente: 1a geração
- Nome da função: tutorial-banking-webhook
- Região: se você especificou uma região para seu agente, use a mesma região.
- Tipo de acionador HTTP: HTTP
- URL: clique no botão de cópia aqui e salve o valor. Você precisará desse URL ao configurar o webhook.
- Autenticação: exige autenticação.
- Exigir HTTPS: marcada
Clique em Save.
Clique em Next. Você não precisa de configurações especiais de ambiente de execução, build, conexões ou 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 pelo seguinte:
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 com sucesso. Enquanto espera, examine o código que você acabou de implantar.
Configurar o webhook do agente
Agora que o webhook existe como um serviço, você precisa associá-lo ao seu agente. Isso é feito por meio do fulfillment. Para ativar e gerenciar o fulfillment do seu 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 ativá-lo 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 Save.
Testar o agente
Seu agente está pronto para tentar. Clique no botão Testar agente para abrir o simulador. Tente ter a seguinte conversa com o agente:
Turno da conversa | Você | Agente |
---|---|---|
1 | Olá | Olá, agradecemos por escolher o ACME Bank. |
2 | Quero saber o saldo da minha conta | Você quer saldo para qual conta: poupança ou corrente? |
3 | Verificando | Aqui está seu saldo mais recente: $0,00 |
Na terceira rodada de conversas, você forneceu "checking" como tipo de conta. A intent account.balance.check tem um parâmetro chamado account. Esse parâmetro está definido como "checking" 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, verá que essa ação aciona uma função nomeada semelhante a ser chamada. A função determina o saldo da conta. A função verifica se 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 forem definidas, a função usará um saldo de conta fixado no código. Nas próximas etapas, você vai alterar o ambiente da função para que ela recupere dados de um banco de dados.
Solução de problemas
O código do webhook inclui instruções de geração de registros. Se você tiver problemas, tente visualizar os registros da sua função.
Mais informações
Para mais informações sobre as etapas acima, consulte: