Migrer de Dialogflow ES vers Dialogflow CX

Les agents Dialogflow CX vous offrent des outils et des contrôles de conversation plus puissants que les agents Dialogflow ES. Si votre agent Dialogflow ES gère des conversations complexes, vous devriez envisager de migrer vers Dialogflow CX.

Ce guide explique comment migrer un agent de Dialogflow ES vers Dialogflow CX. Ces deux types d'agents présentent de nombreuses différences fondamentales. Il n'existe donc pas de moyen simple d'effectuer cette migration.

Si vous utilisez ce guide pour une migration, veuillez nous faire part de vos commentaires positifs ou négatifs en cliquant sur le bouton Envoyer des commentaires ci-dessus. Nous utiliserons ces commentaires pour améliorer ce guide au fil du temps.

De manière générale, le processus recommandé est un processus hybride automatisé/manuel. Vous utiliserez un outil qui lit certaines données de votre agent Dialogflow ES, les écrit dans votre agent Dialogflow CX et capture une liste de tâches. Vous recréez ensuite votre agent Dialogflow CX complet en suivant les bonnes pratiques, la liste des tâches et les données migrées par l'outil.

Comprendre Dialogflow CX

Avant de tenter cette migration, vous devez bien comprendre le fonctionnement des flux Dialogflow CX. Pour commencer :

Vous devez également lire les documents conceptuels supplémentaires qui contiennent les fonctionnalités dont vous aurez probablement besoin dans votre nouvel agent. Concentrez-vous sur les points suivants :

Comprendre les différences entre Dialogflow ES et Dialogflow CX

Cette section liste les principales différences entre Dialogflow ES et Dialogflow CX. Lorsque vous effectuerez les étapes de migration manuelles ultérieures, vous devrez vous reporter à cette section pour obtenir des conseils.

Structure et contrôle du fil de la conversation

Dialogflow ES fournit les éléments suivants pour la structure et le contrôle du chemin de conversation :

  • Les intents sont utilisés comme éléments de base de l'agent. À tout moment de la conversation, une intention est mise en correspondance. En quelque sorte, chaque intention est un nœud de la conversation.
  • Le contexte permet de contrôler la conversation. Le contexte permet de contrôler les intents qui peuvent être mis en correspondance à tout moment. Le contexte expire après un certain nombre de tours de conversation. Ce type de contrôle peut donc être inexact pour les longues conversations.

Dialogflow CX fournit une hiérarchie de ressources de structure et des contrôles plus précis sur le chemin de conversation :

  • Les pages sont des nœuds de graphe pour la conversation. Les conversations Dialogflow CX sont semblables à des machines à états. À tout moment de la conversation, une page est active. En fonction des données saisies par l'utilisateur final ou des événements, la conversation peut passer à une autre page. Il est courant qu'une page reste active pendant plusieurs tours de conversation.
  • Les flux sont des groupes de pages associées. Chaque flux doit traiter un thème de conversation de haut niveau.
  • Les gestionnaires d'état permettent de contrôler les transitions et les réponses. Il existe trois types de gestionnaires d'état :
    • Route d'intent : contient un intent qui doit correspondre, des réponses facultatives et une transition de page facultative.
    • Route de condition : contient une condition à respecter, des réponses facultatives et une transition de page facultative.
    • Gestionnaire d'événements : contient un nom d'événement qui doit être appelé, des réponses facultatives et une transition de page facultative.
  • La portée permet de contrôler si un gestionnaire d'état peut être appelé. La plupart des gestionnaires sont associés à une page ou à un flux entier. Si la page ou le flux associés sont actifs, le gestionnaire est dans le champ d'application et peut être appelé. Une route d'intent Dialogflow CX dans le champ d'application est semblable à un intent Dialogflow ES avec un contexte d'entrée actif.

Lorsque vous concevez les flux et les pages de votre agent, assurez-vous de comprendre les conseils de la section sur les flux du guide de conception des agents.

Remplissage de formulaire

Dialogflow ES utilise le remplissage d'emplacement pour collecter les paramètres requis auprès de l'utilisateur final :

  • Il s'agit de paramètres d'intent marqués comme obligatoires.
  • L'intent continue d'être mis en correspondance jusqu'à ce que tous les paramètres requis soient collectés.
  • Vous pouvez définir une invite demandant à l'utilisateur final de fournir une valeur.

Dialogflow CX utilise le remplissage de formulaire pour collecter les paramètres requis auprès de l'utilisateur final :

  • Ces paramètres sont associés à une page et sont collectés lorsque la page est active.
  • Vous utilisez des routes de condition pour les pages afin de déterminer si le remplissage du formulaire est terminé. Ces routes de condition redirigent généralement vers une autre page.
  • Vous pouvez définir une invite, ainsi que des gestionnaires de réinvite pour gérer correctement les tentatives multiples de collecte d'une valeur.

Transitions

Dialogflow ES passe automatiquement d'un intent à l'autre lorsque l'entrée de l'utilisateur final correspond à un intent. Cette mise en correspondance ne peut se produire que pour les intents sans contexte d'entrée ou pour les intents avec un contexte d'entrée actif.

Dialogflow CX passe d'une page à l'autre lorsqu'un gestionnaire d'état dans le champ d'application répond à ses exigences et fournit une cible de transition. Grâce à ces transitions, vous pouvez guider les utilisateurs finaux de manière fiable tout au long des conversations. Vous pouvez contrôler ces transitions de plusieurs façons :

  • La correspondance des intents peut déclencher une route d'intent.
  • Le respect d'une condition peut déclencher une route de condition.
  • L'invocation d'un événement peut déclencher un gestionnaire d'événements.
  • Les gestionnaires de nouvelles invites peuvent entraîner une transition lorsque l'utilisateur final ne fournit pas de valeur après plusieurs tentatives.
  • Vous pouvez utiliser des cibles de transition symboliques pour les cibles de transition.

Réponses de l'agent

Les réponses de l'agent Dialogflow ES sont envoyées à l'utilisateur final lorsqu'un intent correspond :

  • L'agent peut sélectionner un message pour la réponse dans une liste de réponses possibles.
  • Les réponses peuvent être spécifiques à une plate-forme et utiliser des formats de réponses enrichies.
  • Les réponses peuvent être déclenchées par des webhooks.

Les réponses de l'agent Dialogflow CX sont envoyées à l'utilisateur final lorsque l'exécution est appelée. Contrairement au fulfillment Dialogflow ES, qui implique toujours un webhook, le fulfillment Dialogflow CX peut ou non impliquer l'appel d'un webhook, selon que la ressource de fulfillment a un webhook configuré. Les réponses statiques et dynamiques basées sur les réponses de webhook sont contrôlées par le fulfillment. Il existe plusieurs façons de créer des réponses d'agent :

  • Le fulfillment peut être fourni à n'importe quel type de gestionnaire d'état.
  • Plusieurs réponses peuvent être concaténées au cours d'un tour de conversation via la file d'attente de réponses. Cette fonctionnalité peut simplifier la conception de votre agent dans certains cas.
  • Dialogflow CX n'est pas compatible avec les réponses intégrées spécifiques à une plate-forme. Toutefois, il fournit plusieurs types de réponses, y compris une charge utile personnalisée qui peut être utilisée pour les réponses spécifiques à la plate-forme.

Paramètres

Les paramètres Dialogflow ES présentent les caractéristiques suivantes :

  • Défini uniquement dans les intents.
  • Définie par les saisies de l'utilisateur final, les événements, les webhooks et les appels d'API.
  • Référencé dans les réponses, les invites de paramètres, le code du webhook et les valeurs de paramètre :
    • Le format de référence de base est $parameter-name.
    • Les références sont compatibles avec la syntaxe des suffixes .original, .partial et .recent.
    • Les références peuvent spécifier le contexte actif : #context-name.parameter-name.
    • Les références peuvent spécifier des paramètres d'événement : #event-name.parameter-name.

Les paramètres Dialogflow CX présentent les caractéristiques suivantes :

  • Définis dans les intents et les formulaires de page.
  • Les paramètres d'intent et de formulaire sont propagés aux paramètres de session, où ils peuvent être référencés pendant toute la durée de la session.
  • Définis par l'entrée de l'utilisateur final, les Webhooks, le préréglage de paramètres de fulfillment et les appels d'API.
  • Référencé dans les réponses, les invites de paramètres, les gestionnaires de nouvelles invites, les préréglages de paramètres et le code du webhook :
    • Le format de référence est $session.params.parameter-id pour les paramètres de session et $intent.params.parameter-id pour les paramètres d'intention.
    • Les références aux paramètres d'intent sont compatibles avec la syntaxe des suffixes .original et .resolved. Les paramètres de session ne sont pas compatibles avec cette syntaxe.

Entités système

Dialogflow ES est compatible avec de nombreuses entités système.

Dialogflow CX accepte de nombreuses entités système, mais il existe quelques différences. Lors de la migration, vérifiez que les entités système que vous utilisez dans Dialogflow ES sont également compatibles avec Dialogflow CX pour la même langue. Si ce n'est pas le cas, vous devez créer des entités personnalisées pour ces éléments.

Événements

Les événements Dialogflow ES présentent les caractéristiques suivantes :

  • Peut être invoqué à partir d'appels d'API ou de webhooks pour faire correspondre une intention.
  • Vous pouvez définir des paramètres.
  • Un petit nombre d'événements sont appelés par les plates-formes d'intégration.

Les événements Dialogflow CX présentent les caractéristiques suivantes :

  • Peut être appelé à partir d'appels d'API ou de webhooks pour appeler un gestionnaire d'événements.
  • Impossible de définir les paramètres.
  • De nombreux événements intégrés peuvent être utilisés pour gérer l'absence d'entrée de l'utilisateur final, les entrées de l'utilisateur final non reconnues, les paramètres invalidés par un webhook et les erreurs de webhook.
  • Les invocations peuvent être contrôlées par les mêmes règles de portée que les autres gestionnaires d'état.

Intents intégrés

Dialogflow ES est compatible avec les intentions intégrées suivantes :

Voici la description de la prise en charge des intentions intégrées par Dialogflow CX :

  • Les intents de bienvenue sont acceptés.
  • Les intents de remplacement ne sont pas fournis. Utilisez plutôt les événements no-match dans les gestionnaires d'événements.
  • Pour les exemples négatifs, utilisez l'intention négative par défaut.
  • Les intents de suivi prédéfinis ne sont pas fournis. Vous devez créer ces intents selon les besoins de votre agent. Par exemple, vous devrez probablement créer une intention pour gérer les réponses négatives à une question de l'agent ("non", "non merci", "non, je n'en ai pas", etc.). Les intents Dialogflow CX sont réutilisables dans votre agent. Vous n'avez donc besoin de les définir qu'une seule fois. L'utilisation de différentes routes d'intention pour ces intentions courantes, dans différentes portées, vous permet de mieux contrôler la conversation.

Webhooks

Les Webhooks Dialogflow ES présentent les caractéristiques suivantes :

  • Vous pouvez configurer un service de webhook pour l'agent.
  • Chaque intention peut être marquée comme utilisant le webhook.
  • Il n'existe aucune fonctionnalité intégrée permettant de gérer les erreurs de webhook.
  • Les actions ou les noms d'intent sont utilisés par les Webhooks pour déterminer l'emplacement de l'agent à partir duquel ils ont été appelés.
  • La console fournit l'éditeur intégré.

Les Webhooks Dialogflow CX présentent les caractéristiques suivantes :

  • Vous pouvez configurer plusieurs services de webhook pour l'agent.
  • Chaque fulfillment peut éventuellement spécifier un appel de webhook.
  • La gestion des erreurs de webhook est intégrée.
  • Un webhook de fulfillment Dialogflow CX contient un tag. Ce tag est semblable à une action Dialogflow ES, mais il n'est utilisé que lors de l'appel de webhooks. Le service de webhook peut utiliser ces balises pour déterminer l'emplacement de l'appel dans l'agent.
  • La console ne dispose pas d'éditeur de code de webhook intégré. Il est courant d'utiliser Cloud Run Functions, mais il existe de nombreuses options.

Lorsque vous migrez vers Dialogflow CX, vous devez modifier le code de votre webhook, car les propriétés de requête et de réponse sont différentes.

Intégrations

Les intégrations Dialogflow ES et les intégrations Dialogflow CX sont compatibles avec différentes plates-formes. Pour les plates-formes compatibles avec les deux types d'agents, il peut exister des différences de configuration.

Si l'intégration Dialogflow ES que vous utilisiez n'est pas compatible avec Dialogflow CX, vous devrez peut-être changer de plate-forme ou implémenter l'intégration vous-même.

Autres fonctionnalités exclusives à Dialogflow CX

Dialogflow CX propose de nombreuses autres fonctionnalités. Nous vous recommandons d'utiliser ces fonctionnalités lors de la migration. Exemple :

Bonnes pratiques

Avant de migrer, familiarisez-vous avec les bonnes pratiques de conception des agents Dialogflow CX. Bon nombre de ces bonnes pratiques Dialogflow CX sont semblables à celles de Dialogflow ES, mais certaines sont propres à Dialogflow CX.

À propos de l'outil de migration

L'outil de migration copie la majeure partie des données Dialogflow ES dans votre agent Dialogflow CX. Il écrit également dans un fichier TODO une liste d'éléments qui doivent être migrés manuellement. L'outil ne copie que les types d'entités personnalisés et les expressions d'entraînement des intentions. Vous devriez envisager de personnaliser cet outil en fonction de vos besoins spécifiques.

Code de l'outil de migration

Voici le code de l'outil. Vous devez examiner le code de cet outil pour comprendre ce qu'il fait. Vous pouvez modifier ce code pour gérer des situations spécifiques dans votre agent. Vous exécuterez cet outil dans les étapes ci-dessous.

// Package main implements the ES to CX migration tool.
package main

import (
	"context"
	"encoding/csv"
	"flag"
	"fmt"
	"os"
	"strings"
	"time"

	v2 "cloud.google.com/go/dialogflow/apiv2"
	proto2 "cloud.google.com/go/dialogflow/apiv2/dialogflowpb"
	v3 "cloud.google.com/go/dialogflow/cx/apiv3"
	proto3 "cloud.google.com/go/dialogflow/cx/apiv3/cxpb"
	"google.golang.org/api/iterator"
	"google.golang.org/api/option"
)

// Commandline flags
var v2Project *string = flag.String("es-project-id", "", "ES project")
var v3Project *string = flag.String("cx-project-id", "", "CX project")
var v2Region *string = flag.String("es-region-id", "", "ES region")
var v3Region *string = flag.String("cx-region-id", "", "CX region")
var v3Agent *string = flag.String("cx-agent-id", "", "CX region")
var outFile *string = flag.String("out-file", "", "Output file for CSV TODO items")
var dryRun *bool = flag.Bool("dry-run", false, "Set true to skip CX agent writes")

// Map from entity type display name to fully qualified name.
var entityTypeShortToLong = map[string]string{}

// Map from ES system entity to CX system entity
var convertSystemEntity = map[string]string{
	"sys.address":         "sys.address",
	"sys.any":             "sys.any",
	"sys.cardinal":        "sys.cardinal",
	"sys.color":           "sys.color",
	"sys.currency-name":   "sys.currency-name",
	"sys.date":            "sys.date",
	"sys.date-period":     "sys.date-period",
	"sys.date-time":       "sys.date-time",
	"sys.duration":        "sys.duration",
	"sys.email":           "sys.email",
	"sys.flight-number":   "sys.flight-number",
	"sys.geo-city-gb":     "sys.geo-city",
	"sys.geo-city-us":     "sys.geo-city",
	"sys.geo-city":        "sys.geo-city",
	"sys.geo-country":     "sys.geo-country",
	"sys.geo-state":       "sys.geo-state",
	"sys.geo-state-us":    "sys.geo-state",
	"sys.geo-state-gb":    "sys.geo-state",
	"sys.given-name":      "sys.given-name",
	"sys.language":        "sys.language",
	"sys.last-name":       "sys.last-name",
	"sys.street-address":  "sys.location",
	"sys.location":        "sys.location",
	"sys.number":          "sys.number",
	"sys.number-integer":  "sys.number-integer",
	"sys.number-sequence": "sys.number-sequence",
	"sys.ordinal":         "sys.ordinal",
	"sys.percentage":      "sys.percentage",
	"sys.person":          "sys.person",
	"sys.phone-number":    "sys.phone-number",
	"sys.temperature":     "sys.temperature",
	"sys.time":            "sys.time",
	"sys.time-period":     "sys.time-period",
	"sys.unit-currency":   "sys.unit-currency",
	"sys.url":             "sys.url",
	"sys.zip-code":        "sys.zip-code",
}

// Issues found for the CSV output
var issues = [][]string{
	{"Field", "Issue"},
}

// logIssue logs an issue for the CSV output
func logIssue(field string, issue string) {
	issues = append(issues, []string{field, issue})
}

// convertEntityType converts an ES entity type to CX
func convertEntityType(et2 *proto2.EntityType) *proto3.EntityType {
	var kind3 proto3.EntityType_Kind
	switch kind2 := et2.Kind; kind2 {
	case proto2.EntityType_KIND_MAP:
		kind3 = proto3.EntityType_KIND_MAP
	case proto2.EntityType_KIND_LIST:
		kind3 = proto3.EntityType_KIND_LIST
	case proto2.EntityType_KIND_REGEXP:
		kind3 = proto3.EntityType_KIND_REGEXP
	default:
		kind3 = proto3.EntityType_KIND_UNSPECIFIED
	}
	var expansion3 proto3.EntityType_AutoExpansionMode
	switch expansion2 := et2.AutoExpansionMode; expansion2 {
	case proto2.EntityType_AUTO_EXPANSION_MODE_DEFAULT:
		expansion3 = proto3.EntityType_AUTO_EXPANSION_MODE_DEFAULT
	default:
		expansion3 = proto3.EntityType_AUTO_EXPANSION_MODE_UNSPECIFIED
	}
	et3 := &proto3.EntityType{
		DisplayName:           et2.DisplayName,
		Kind:                  kind3,
		AutoExpansionMode:     expansion3,
		EnableFuzzyExtraction: et2.EnableFuzzyExtraction,
	}
	for _, e2 := range et2.Entities {
		et3.Entities = append(et3.Entities, &proto3.EntityType_Entity{
			Value:    e2.Value,
			Synonyms: e2.Synonyms,
		})
	}
	return et3
}

// convertParameterEntityType converts a entity type found in parameters
func convertParameterEntityType(intent string, parameter string, t2 string) string {
	if len(t2) == 0 {
		return ""
	}
	t2 = t2[1:] // remove @
	if strings.HasPrefix(t2, "sys.") {
		if val, ok := convertSystemEntity[t2]; ok {
			t2 = val
		} else {
			t2 = "sys.any"
			logIssue("Intent<"+intent+">.Parameter<"+parameter+">",
				"This intent parameter uses a system entity not supported by CX English agents. See the migration guide for advice. System entity: "+t2)
		}
		return fmt.Sprintf("projects/-/locations/-/agents/-/entityTypes/%s", t2)
	}
	return entityTypeShortToLong[t2]
}

// convertIntent converts an ES intent to CX
func convertIntent(intent2 *proto2.Intent) *proto3.Intent {
	if intent2.DisplayName == "Default Fallback Intent" ||
		intent2.DisplayName == "Default Welcome Intent" {
		return nil
	}

	intent3 := &proto3.Intent{
		DisplayName: intent2.DisplayName,
	}

	// WebhookState
	if intent2.WebhookState != proto2.Intent_WEBHOOK_STATE_UNSPECIFIED {
		logIssue("Intent<"+intent2.DisplayName+">.WebhookState",
			"This intent has webhook enabled. You must configure this in your CX agent.")
	}

	// IsFallback
	if intent2.IsFallback {
		logIssue("Intent<"+intent2.DisplayName+">.IsFallback",
			"This intent is a fallback intent. CX does not support this. Use no-match events instead.")
	}

	// MlDisabled
	if intent2.MlDisabled {
		logIssue("Intent<"+intent2.DisplayName+">.MlDisabled",
			"This intent has ML disabled. CX does not support this.")
	}

	// LiveAgentHandoff
	if intent2.LiveAgentHandoff {
		logIssue("Intent<"+intent2.DisplayName+">.LiveAgentHandoff",
			"This intent uses live agent handoff. You must configure this in a fulfillment.")
	}

	// EndInteraction
	if intent2.EndInteraction {
		logIssue("Intent<"+intent2.DisplayName+">.EndInteraction",
			"This intent uses end interaction. CX does not support this.")
	}

	// InputContextNames
	if len(intent2.InputContextNames) > 0 {
		logIssue("Intent<"+intent2.DisplayName+">.InputContextNames",
			"This intent uses context. See the migration guide for alternatives.")
	}

	// Events
	if len(intent2.Events) > 0 {
		logIssue("Intent<"+intent2.DisplayName+">.Events",
			"This intent uses events. Use event handlers instead.")
	}

	// TrainingPhrases
	var trainingPhrases3 []*proto3.Intent_TrainingPhrase
	for _, tp2 := range intent2.TrainingPhrases {
		if tp2.Type == proto2.Intent_TrainingPhrase_TEMPLATE {
			logIssue("Intent<"+intent2.DisplayName+">.TrainingPhrases",
				"This intent has a training phrase that uses a template (@...) training phrase type. CX does not support this.")
		}
		var parts3 []*proto3.Intent_TrainingPhrase_Part
		for _, part2 := range tp2.Parts {
			parts3 = append(parts3, &proto3.Intent_TrainingPhrase_Part{
				Text:        part2.Text,
				ParameterId: part2.Alias,
			})
		}
		trainingPhrases3 = append(trainingPhrases3, &proto3.Intent_TrainingPhrase{
			Parts:       parts3,
			RepeatCount: 1,
		})
	}
	intent3.TrainingPhrases = trainingPhrases3

	// Action
	if len(intent2.Action) > 0 {
		logIssue("Intent<"+intent2.DisplayName+">.Action",
			"This intent sets the action field. Use a fulfillment webhook tag instead.")
	}

	// OutputContexts
	if len(intent2.OutputContexts) > 0 {
		logIssue("Intent<"+intent2.DisplayName+">.OutputContexts",
			"This intent uses context. See the migration guide for alternatives.")
	}

	// ResetContexts
	if intent2.ResetContexts {
		logIssue("Intent<"+intent2.DisplayName+">.ResetContexts",
			"This intent uses context. See the migration guide for alternatives.")
	}

	// Parameters
	var parameters3 []*proto3.Intent_Parameter
	for _, p2 := range intent2.Parameters {
		if len(p2.Value) > 0 && p2.Value != "$"+p2.DisplayName {
			logIssue("Intent<"+intent2.DisplayName+">.Parameters<"+p2.DisplayName+">.Value",
				"This field is not set to $parameter-name. This feature is not supported by CX. See: https://cloud.google.com/dialogflow/es/docs/intents-actions-parameters#valfield.")
		}
		if len(p2.DefaultValue) > 0 {
			logIssue("Intent<"+intent2.DisplayName+">.Parameters<"+p2.DisplayName+">.DefaultValue",
				"This intent parameter is using a default value. CX intent parameters do not support default values, but CX page form parameters do. This parameter should probably become a form parameter.")
		}
		if p2.Mandatory {
			logIssue("Intent<"+intent2.DisplayName+">.Parameters<"+p2.DisplayName+">.Mandatory",
				"This intent parameter is marked as mandatory. CX intent parameters do not support mandatory parameters, but CX page form parameters do. This parameter should probably become a form parameter.")
		}
		for _, prompt := range p2.Prompts {
			logIssue("Intent<"+intent2.DisplayName+">.Parameters<"+p2.DisplayName+">.Prompts",
				"This intent parameter has a prompt. Use page form parameter prompts instead. Prompt: "+prompt)
		}
		if len(p2.EntityTypeDisplayName) == 0 {
			p2.EntityTypeDisplayName = "@sys.any"
			logIssue("Intent<"+intent2.DisplayName+">.Parameters<"+p2.DisplayName+">.EntityTypeDisplayName",
				"This intent parameter does not have an entity type. CX requires an entity type for all parameters..")
		}
		parameters3 = append(parameters3, &proto3.Intent_Parameter{
			Id:         p2.DisplayName,
			EntityType: convertParameterEntityType(intent2.DisplayName, p2.DisplayName, p2.EntityTypeDisplayName),
			IsList:     p2.IsList,
		})
		//fmt.Printf("Converted parameter: %+v\n", parameters3[len(parameters3)-1])
	}
	intent3.Parameters = parameters3

	// Messages
	for _, message := range intent2.Messages {
		m, ok := message.Message.(*proto2.Intent_Message_Text_)
		if ok {
			for _, t := range m.Text.Text {
				warnings := ""
				if strings.Contains(t, "#") {
					warnings += " This message may contain a context parameter reference, but CX does not support this."
				}
				if strings.Contains(t, ".original") {
					warnings += " This message may contain a parameter reference suffix of '.original', But CX only supports this for intent parameters (not session parameters)."
				}
				if strings.Contains(t, ".recent") {
					warnings += " This message may contain a parameter reference suffix of '.recent', but CX does not support this."
				}
				if strings.Contains(t, ".partial") {
					warnings += " This message may contain a parameter reference suffix of '.partial', but CX does not support this."
				}
				logIssue("Intent<"+intent2.DisplayName+">.Messages",
					"This intent has a response message. Use fulfillment instead."+warnings+" Message: "+t)
			}
		} else {
			logIssue("Intent<"+intent2.DisplayName+">.Messages",
				"This intent has a non-text response message. See the rich response message information in the migration guide.")
		}
		if message.Platform != proto2.Intent_Message_PLATFORM_UNSPECIFIED {
			logIssue("Intent<"+intent2.DisplayName+">.Platform",
				"This intent has a message with a non-default platform. See the migration guide for advice.")
		}
	}

	return intent3
}

// migrateEntities migrates ES entities to your CX agent
func migrateEntities(ctx context.Context) error {
	var err error

	// Create ES client
	var client2 *v2.EntityTypesClient
	options2 := []option.ClientOption{}
	if len(*v2Region) > 0 {
		options2 = append(options2,
			option.WithEndpoint(*v2Region+"-dialogflow.googleapis.com:443"))
	}
	client2, err = v2.NewEntityTypesClient(ctx, options2...)
	if err != nil {
		return err
	}
	defer client2.Close()
	var parent2 string
	if len(*v2Region) == 0 {
		parent2 = fmt.Sprintf("projects/%s/agent", *v2Project)
	} else {
		parent2 = fmt.Sprintf("projects/%s/locations/%s/agent", *v2Project, *v2Region)
	}

	// Create CX client
	var client3 *v3.EntityTypesClient
	options3 := []option.ClientOption{}
	if len(*v3Region) > 0 {
		options3 = append(options3,
			option.WithEndpoint(*v3Region+"-dialogflow.googleapis.com:443"))
	}
	client3, err = v3.NewEntityTypesClient(ctx, options3...)
	if err != nil {
		return err
	}
	defer client3.Close()
	parent3 := fmt.Sprintf("projects/%s/locations/%s/agents/%s", *v3Project, *v3Region, *v3Agent)

	// Read each V2 entity type, convert, and write to V3
	request2 := &proto2.ListEntityTypesRequest{
		Parent: parent2,
	}
	it2 := client2.ListEntityTypes(ctx, request2)
	for {
		var et2 *proto2.EntityType
		et2, err = it2.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		fmt.Printf("Entity Type: %s\n", et2.DisplayName)

		if *dryRun {
			convertEntityType(et2)
			continue
		}

		request3 := &proto3.CreateEntityTypeRequest{
			Parent:     parent3,
			EntityType: convertEntityType(et2),
		}
		et3, err := client3.CreateEntityType(ctx, request3)
		entityTypeShortToLong[et3.DisplayName] = et3.Name
		if err != nil {
			return err
		}

		// ES and CX each have a quota limit of 60 design-time requests per minute
		time.Sleep(2 * time.Second)
	}
	return nil
}

// migrateIntents migrates intents to your CX agent
func migrateIntents(ctx context.Context) error {
	var err error

	// Create ES client
	var client2 *v2.IntentsClient
	options2 := []option.ClientOption{}
	if len(*v2Region) > 0 {
		options2 = append(options2,
			option.WithEndpoint(*v2Region+"-dialogflow.googleapis.com:443"))
	}
	client2, err = v2.NewIntentsClient(ctx, options2...)
	if err != nil {
		return err
	}
	defer client2.Close()
	var parent2 string
	if len(*v2Region) == 0 {
		parent2 = fmt.Sprintf("projects/%s/agent", *v2Project)
	} else {
		parent2 = fmt.Sprintf("projects/%s/locations/%s/agent", *v2Project, *v2Region)
	}

	// Create CX client
	var client3 *v3.IntentsClient
	options3 := []option.ClientOption{}
	if len(*v3Region) > 0 {
		options3 = append(options3,
			option.WithEndpoint(*v3Region+"-dialogflow.googleapis.com:443"))
	}
	client3, err = v3.NewIntentsClient(ctx, options3...)
	if err != nil {
		return err
	}
	defer client3.Close()
	parent3 := fmt.Sprintf("projects/%s/locations/%s/agents/%s", *v3Project, *v3Region, *v3Agent)

	// Read each V2 entity type, convert, and write to V3
	request2 := &proto2.ListIntentsRequest{
		Parent:     parent2,
		IntentView: proto2.IntentView_INTENT_VIEW_FULL,
	}
	it2 := client2.ListIntents(ctx, request2)
	for {
		var intent2 *proto2.Intent
		intent2, err = it2.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		fmt.Printf("Intent: %s\n", intent2.DisplayName)
		intent3 := convertIntent(intent2)
		if intent3 == nil {
			continue
		}

		if *dryRun {
			continue
		}

		request3 := &proto3.CreateIntentRequest{
			Parent: parent3,
			Intent: intent3,
		}
		_, err := client3.CreateIntent(ctx, request3)
		if err != nil {
			return err
		}

		// ES and CX each have a quota limit of 60 design-time requests per minute
		time.Sleep(2 * time.Second)
	}
	return nil
}

// checkFlags checks commandline flags
func checkFlags() error {
	flag.Parse()
	if len(*v2Project) == 0 {
		return fmt.Errorf("Need to supply es-project-id flag")
	}
	if len(*v3Project) == 0 {
		return fmt.Errorf("Need to supply cx-project-id flag")
	}
	if len(*v2Region) == 0 {
		fmt.Printf("No region supplied for ES, using default\n")
	}
	if len(*v3Region) == 0 {
		return fmt.Errorf("Need to supply cx-region-id flag")
	}
	if len(*v3Agent) == 0 {
		return fmt.Errorf("Need to supply cx-agent-id flag")
	}
	if len(*outFile) == 0 {
		return fmt.Errorf("Need to supply out-file flag")
	}
	return nil
}

// closeFile is used as a convenience for defer
func closeFile(f *os.File) {
	err := f.Close()
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR closing CSV file: %v\n", err)
		os.Exit(1)
	}
}

func main() {
	if err := checkFlags(); err != nil {
		fmt.Fprintf(os.Stderr, "ERROR checking flags: %v\n", err)
		os.Exit(1)
	}
	ctx := context.Background()
	if err := migrateEntities(ctx); err != nil {
		fmt.Fprintf(os.Stderr, "ERROR migrating entities: %v\n", err)
		os.Exit(1)
	}
	if err := migrateIntents(ctx); err != nil {
		fmt.Fprintf(os.Stderr, "ERROR migrating intents: %v\n", err)
		os.Exit(1)
	}
	csvFile, err := os.Create(*outFile)
	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR opening output file: %v", err)
		os.Exit(1)
	}
	defer closeFile(csvFile)
	csvWriter := csv.NewWriter(csvFile)
	if err := csvWriter.WriteAll(issues); err != nil {
		fmt.Fprintf(os.Stderr, "ERROR writing CSV output file: %v", err)
		os.Exit(1)
	}
	csvWriter.Flush()
}

Migration des types d'entités à l'aide de l'outil

Les types d'entités Dialogflow ES et les types d'entités Dialogflow CX sont très semblables. Ils constituent donc le type de données le plus facile à migrer. L'outil copie simplement les types d'entités tels quels.

Migration des intentions à l'aide d'un outil

Les intentions Dialogflow ES et les intentions Dialogflow CX sont très différentes.

Les intents Dialogflow ES sont utilisés comme éléments de base de l'agent. Ils contiennent des expressions d'entraînement, des réponses, un contexte pour le contrôle des conversations, des configurations de webhook, des événements, des actions et des paramètres de remplissage de cases.

Dialogflow CX a déplacé la plupart de ces données vers d'autres ressources. Les intents Dialogflow CX ne comportent que des phrases d'entraînement et des paramètres, ce qui les rend réutilisables dans l'ensemble de l'agent. L'outil ne copie que ces deux types de données d'intent dans vos intents Dialogflow CX.

Limites de l'outil de migration

L'outil de migration n'est pas compatible avec les éléments suivants :

  • Méga-agents : l'outil ne peut pas lire plusieurs sous-agents, mais vous pouvez l'appeler plusieurs fois pour chaque sous-agent.
  • Agents multilingues : vous devez modifier l'outil pour créer des expressions d'entraînement et des entrées d'entité multilingues.
  • Validation des entités système pour les langues autres que l'anglais : l'outil crée des éléments TODO lorsqu'il trouve des entités système qui ne sont pas prises en charge par Dialogflow CX, en partant du principe que l'anglais est la langue par défaut et qu'il utilise une région américaine. La compatibilité des entités système varie selon la langue et la région. Pour les autres langues et régions, vous devez modifier l'outil pour effectuer cette vérification.

Étapes essentielles de la migration

Les sous-sections suivantes décrivent les étapes de migration à suivre. Vous n'avez pas besoin de suivre ces étapes manuelles dans l'ordre. Vous devrez peut-être même les effectuer simultanément ou dans un ordre différent. Lisez les étapes et commencez à planifier vos modifications avant de les appliquer.

Après avoir exécuté l'outil de migration, vous pouvez recréer votre agent Dialogflow CX. Vous aurez encore pas mal de travail de migration à effectuer, mais la majeure partie des données saisies manuellement sera présente dans votre agent Dialogflow CX et dans le fichier TODO.

Créer votre agent Dialogflow CX

Si vous ne l'avez pas déjà fait, créez votre agent Dialogflow CX. Veillez à utiliser la même langue par défaut que votre agent Dialogflow ES.

Exécuter l'outil de migration

Pour exécuter l'outil, procédez comme suit :

  1. Si ce n'est pas déjà fait, installez Go sur votre machine.
  2. Créez un répertoire pour le code de l'outil appelé migrate.
  3. Copiez le code de l'outil ci-dessus dans un fichier de ce répertoire appelé main.go.
  4. Modifiez le code si nécessaire pour votre cas.
  5. Créez un module Go dans ce répertoire. Exemple :

    go mod init migrate
    
  6. Installez les bibliothèques clientes Go Dialogflow ES V2 et Dialogflow CX V3 :

    go get cloud.google.com/go/dialogflow/apiv2
    go get cloud.google.com/go/dialogflow/cx/apiv3
    
  7. Assurez-vous d'avoir configuré l'authentification de la bibliothèque cliente.

  8. Exécutez l'outil et enregistrez le résultat dans un fichier :

    go run main.go -es-project-id=<ES_PROJECT_ID> -cx-project-id=<CX_PROJECT_ID> \
    -cx-region-id=<CX_REGION_ID> -cx-agent-id=<CX_AGENT_ID> -out-file=out.csv
    

Résolution des problèmes liés à l'outil de migration

Si vous rencontrez des erreurs lors de l'exécution de l'outil, vérifiez les points suivants :

Erreur Solution
Erreur RPC indiquant qu'une partie d'une expression d'entraînement mentionne un paramètre non défini pour l'intention. Cela peut se produire si vous avez déjà utilisé l'API Dialogflow ES pour créer des paramètres d'intent de manière incohérente avec les expressions d'entraînement. Pour résoudre ce problème, renommez le paramètre Dialogflow ES dans la console, vérifiez que vos expressions d'entraînement utilisent correctement le paramètre, puis cliquez sur "Enregistrer". Cela peut également se produire si vos expressions d'entraînement font référence à des paramètres inexistants.

Après avoir corrigé les erreurs, vous devrez supprimer les intents et les entités de l'agent Dialogflow CX avant d'exécuter à nouveau l'outil de migration.

Transférer les données d'intent Dialogflow ES vers Dialogflow CX

L'outil migre les phrases d'entraînement et les paramètres d'intent vers les intents Dialogflow CX, mais de nombreux autres champs d'intent Dialogflow ES doivent être migrés manuellement.

Une intention Dialogflow ES peut nécessiter une page Dialogflow CX correspondante, une intention Dialogflow CX correspondante ou les deux.

Si une correspondance d'intent Dialogflow ES est utilisée pour faire passer la conversation d'un nœud de conversation particulier à un autre, vous devez avoir deux pages dans votre agent liées à cet intent :

  • Page d'origine contenant la route d'intent, qui sera redirigée vers la page suivante : la route d'intent de la page d'origine peut contenir des messages de fulfillment Dialogflow CX semblables aux réponses d'intent Dialogflow ES. Cette page peut contenir de nombreuses routes d'intention. Lorsque la page d'origine est active, ces routes d'intent peuvent faire passer la conversation par de nombreux chemins possibles. De nombreux intents Dialogflow ES partageront la même page d'origine Dialogflow CX correspondante.
  • La page suivante, qui est la cible de transition pour la route d'intent de la page d'origine : Le fulfillment d'entrée Dialogflow CX pour la page suivante peut contenir des messages de fulfillment Dialogflow CX semblables aux réponses d'intent Dialogflow ES.

Si un intent Dialogflow ES contient des paramètres obligatoires, vous devez créer une page Dialogflow CX correspondante avec les mêmes paramètres dans un formulaire.

Il est courant qu'une intention Dialogflow CX et une page Dialogflow CX partagent la même liste de paramètres. Cela signifie qu'une seule intention Dialogflow ES a une page Dialogflow CX et une intention Dialogflow CX correspondantes. Lorsqu'un intent Dialogflow CX avec des paramètres dans une route d'intent est mis en correspondance, la conversation passe souvent à une page avec les mêmes paramètres. Les paramètres extraits de la correspondance d'intent sont propagés aux paramètres de session, qui sont disponibles pour remplir partiellement ou entièrement les paramètres du formulaire de la page.

Les intents de remplacement et les intents de suivi prédéfinis n'existent pas dans Dialogflow CX. Consultez la section Intents intégrés.

Le tableau suivant décrit comment mapper des données d'intent spécifiques de Dialogflow ES à des ressources Dialogflow CX :

Données d'intent Dialogflow ES Données Dialogflow CX correspondantes Action requise
Phrases d'entraînement Phrases d'entraînement d'intent Migré par l'outil. L'outil vérifie la prise en charge des entités système et crée des éléments TODO pour les entités système non prises en charge.
Réponses de l'agent Messages de réponse du fulfillment Consultez Réponses de l'agent.
Contexte pour le contrôle de la conversation Aucun Consultez Structure et contrôle du chemin de conversation.
Paramètre de webhook Configuration du webhook de fulfillment Voir Webhooks.
Événements Gestionnaires d'événements au niveau du flux ou de la page Voir Événements.
Actions Tags de webhook de fulfillment Voir Webhooks.
Paramètres Paramètres d'intent et/ou paramètres de formulaire de page Migré vers les paramètres d'intent par outil. Si les paramètres sont obligatoires, l'outil crée des éléments TODO pour une éventuelle migration vers une page. Consultez les paramètres.
Requêtes de paramètres Invites de paramètres de formulaire de page Consultez Remplissage de formulaires.

Créer des flux

Créez un flux pour chaque thème de conversation de haut niveau. Les thèmes de chaque flux doivent être distincts, afin que la conversation ne passe pas fréquemment d'un flux à l'autre.

Si vous utilisiez un méga-agent, chaque sous-agent doit devenir un ou plusieurs flux.

Commencer par des chemins de conversation de base

Il est préférable de tester votre agent avec le simulateur lorsque vous apportez des modifications. Vous devez donc vous concentrer sur les chemins de conversation de base au début de la conversation et effectuer des tests à mesure que vous apportez des modifications. Une fois que vous avez réussi à les faire fonctionner, passez à des chemins de conversation plus détaillés.

Gestionnaires d'état au niveau du flux et au niveau de la page

Lorsque vous créez des gestionnaires d'état, déterminez s'ils doivent être appliqués au niveau du flux ou de la page. Un gestionnaire au niveau du flux est dans le champ d'application chaque fois que le flux (et donc n'importe quelle page du flux) est actif. Un gestionnaire au niveau de la page n'est dans le champ d'application que lorsque la page concernée est active. Les gestionnaires au niveau du flux sont semblables aux intents Dialogflow ES sans contexte d'entrée. Les gestionnaires au niveau de la page sont semblables aux intents Dialogflow ES avec contexte d'entrée.

Code de webhook

Les propriétés de requête et de réponse du webhook sont différentes pour Dialogflow CX. Consultez la section sur les Webhooks.

Connecteurs de connaissances

Dialogflow CX n'est pas encore compatible avec les connecteurs de connaissances. Vous devrez les implémenter en tant qu'intentions normales ou attendre que Dialogflow CX soit compatible avec les connecteurs de connaissances.

Paramètres de l'agent

Vérifiez les paramètres de votre agent Dialogflow ES et ajustez les paramètres de votre agent Dialogflow CX si nécessaire.

Utiliser le fichier TODO

L'outil de migration génère un fichier CSV. Les éléments de cette liste sont axés sur des données spécifiques qui peuvent nécessiter votre attention. Importez ce fichier dans une feuille de calcul. Résolvez chaque élément de la feuille de calcul en utilisant une colonne pour marquer l'état d'avancement.

Migration de l'utilisation de l'API

Si votre système utilise l'API Dialogflow ES pour les appels au moment de l'exécution ou de la conception, vous devrez mettre à jour ce code pour utiliser l'API Dialogflow CX. Si vous n'utilisez que les appels de détection d'intention au moment de l'exécution, cette mise à jour devrait être assez simple.

Intégrations

Si votre agent utilise des intégrations, consultez la section sur les intégrations et apportez les modifications nécessaires.

Les sous-sections suivantes décrivent les étapes de migration recommandées.

Validation

Utilisez la validation de l'agent pour vérifier que votre agent respecte les bonnes pratiques.

Tests

Lorsque vous effectuez les étapes de migration manuelle ci-dessus, vous devez tester votre agent avec le simulateur. Une fois que votre agent semble fonctionner, vous devez comparer les conversations entre vos agents Dialogflow ES et Dialogflow CX, et vérifier que le comportement est similaire ou amélioré.

Lorsque vous testez ces conversations avec le simulateur, vous devez créer des scénarios de test pour éviter toute régression future.

Environnements

Passez en revue vos environnements Dialogflow ES et mettez à jour vos environnements Dialogflow CX si nécessaire.