Bermigrasi dari Dialogflow ES ke Dialogflow CX

Agen Dialogflow CX memberi Anda kontrol dan alat percakapan yang lebih canggih daripada agen Dialogflow ES. Jika agen Dialogflow ES Anda menangani percakapan yang kompleks, Anda harus mempertimbangkan untuk bermigrasi ke Dialogflow CX.

Panduan ini menjelaskan cara memigrasikan agen dari Dialogflow ES ke Dialogflow CX. Kedua jenis agen ini memiliki banyak perbedaan mendasar, sehingga tidak ada cara mudah untuk melakukan migrasi ini.

Jika Anda menggunakan panduan ini untuk migrasi, berikan masukan positif atau negatif dengan mengklik tombol Kirim masukan di atas. Kami akan menggunakan masukan ini untuk meningkatkan kualitas panduan ini dari waktu ke waktu.

Secara umum, proses yang direkomendasikan adalah proses hybrid otomatis/manual. Anda akan menggunakan alat yang membaca beberapa data agen Dialogflow ES, menulis data tersebut ke agen Dialogflow CX, dan mencatat daftar tugas. Kemudian, Anda membuat ulang agen Dialogflow CX lengkap menggunakan praktik terbaik, daftar tugas, dan data yang dimigrasikan oleh alat.

Memahami Dialogflow CX

Sebelum mencoba migrasi ini, Anda harus memiliki pemahaman yang kuat tentang cara kerja alur Dialogflow CX. Anda dapat memulai di sini:

Anda juga harus membaca dokumen konsep tambahan yang memiliki fitur yang kemungkinan akan Anda butuhkan di agen baru Anda. Fokus pada hal-hal berikut:

Memahami perbedaan Dialogflow ES/Dialogflow CX

Bagian ini mencantumkan perbedaan paling penting antara Dialogflow ES dan Dialogflow CX. Saat melakukan langkah-langkah migrasi manual nanti, Anda harus membaca bagian ini untuk mendapatkan panduan.

Struktur dan kontrol jalur percakapan

Dialogflow ES menyediakan hal berikut untuk kontrol struktur dan jalur percakapan:

  • Maksud (Intent) digunakan sebagai elemen penyusun agen. Pada titik mana pun dalam percakapan, maksud dicocokkan, dan dalam arti tertentu, setiap maksud adalah node untuk percakapan.
  • Konteks digunakan untuk mengontrol percakapan. Konteks digunakan untuk mengontrol maksud yang dapat dicocokkan pada waktu tertentu. Konteks berakhir setelah sejumlah giliran percakapan tertentu, sehingga jenis kontrol ini dapat menjadi tidak akurat untuk percakapan yang panjang.

Dialogflow CX menyediakan hierarki resource struktur dan kontrol yang lebih akurat atas jalur percakapan:

  • Halaman adalah node grafik untuk percakapan. Percakapan Dialogflow CX mirip dengan mesin status. Pada titik tertentu dalam percakapan, satu halaman aktif. Berdasarkan input atau peristiwa pengguna akhir, percakapan dapat bertransisi ke halaman lain. Halaman biasanya tetap aktif untuk beberapa giliran percakapan.
  • Alur adalah grup halaman terkait. Setiap alur harus menangani topik percakapan tingkat tinggi.
  • Handler status digunakan untuk mengontrol transisi dan respons. Ada tiga jenis pengendali status:
    • Rute intent: berisi intent yang harus cocok, respons opsional, dan transisi halaman opsional.
    • Rute kondisi: berisi kondisi yang harus dipenuhi, respons opsional, dan transisi halaman opsional.
    • Pengendali peristiwa: berisi nama peristiwa yang harus dipanggil, respons opsional, dan transisi halaman opsional.
  • Cakupan digunakan untuk mengontrol apakah pemroses status dapat dipanggil. Sebagian besar handler dikaitkan dengan halaman atau seluruh alur. Jika halaman atau alur terkait aktif, maka pengendali berada dalam cakupan, dan dapat dipanggil. Rute intent Dialogflow CX dalam cakupan mirip dengan intent Dialogflow ES dengan konteks input yang aktif.

Saat mendesain alur dan halaman agen, pastikan Anda memahami saran di bagian alur panduan desain agen.

Pengisian formulir

Dialogflow ES menggunakan pengisian slot untuk mengumpulkan parameter yang diperlukan dari pengguna akhir:

  • Parameter ini adalah parameter maksud yang ditandai sebagai wajib diisi.
  • Maksud terus dicocokkan hingga semua parameter yang diperlukan dikumpulkan.
  • Anda dapat menentukan perintah yang meminta pengguna akhir untuk memberikan nilai.

Dialogflow CX menggunakan pengisian formulir untuk mengumpulkan parameter yang diperlukan dari pengguna akhir:

  • Parameter ini terkait dengan halaman dan dikumpulkan saat halaman aktif.
  • Anda menggunakan rute kondisi untuk halaman guna menentukan bahwa pengisian formulir telah selesai. Rute kondisi ini biasanya bertransisi ke halaman lain.
  • Anda dapat menentukan perintah, serta handler perintah ulang untuk menangani beberapa upaya pengumpulan nilai dengan baik.

Transisi

Dialogflow ES otomatis bertransisi dari satu maksud ke maksud berikutnya saat input pengguna akhir cocok dengan maksud. Pencocokan ini hanya dapat terjadi untuk intent yang tidak memiliki konteks input atau intent yang memiliki konteks input aktif.

Dialogflow CX bertransisi dari satu halaman ke halaman berikutnya saat pengendali status dalam cakupan memenuhi persyaratannya dan menyediakan target transisi. Dengan menggunakan transisi ini, Anda dapat memandu pengguna akhir melalui percakapan dengan andal. Ada beberapa cara untuk mengontrol transisi ini:

  • Pencocokan intent dapat memicu rute intent.
  • Memenuhi kondisi dapat memicu rute kondisi.
  • Pemanggilan peristiwa dapat memicu pengendali peristiwa.
  • Handler permintaan ulang dapat menyebabkan transisi saat pengguna akhir gagal memberikan nilai setelah beberapa kali mencoba.
  • Anda dapat menggunakan target transisi simbolis untuk target transisi.

Respons agen

Respons agen Dialogflow ES dikirim ke pengguna akhir saat intent cocok:

  • Agen dapat memilih satu pesan untuk respons dari daftar kemungkinan respons.
  • Respons dapat bersifat spesifik per platform, yang dapat menggunakan format respons multimedia.
  • Respons dapat didorong oleh webhook.

Respons agen Dialogflow CX dikirim ke pengguna akhir saat pemenuhan dipanggil. Tidak seperti pemenuhan Dialogflow ES, yang selalu melibatkan webhook, pemenuhan Dialogflow CX dapat atau tidak melibatkan panggilan webhook, bergantung pada apakah resource pemenuhan telah mengonfigurasi webhook. Respons statis dan dinamis berdasarkan respons webhook dikontrol oleh pemenuhan. Ada beberapa cara untuk membuat respons agen:

  • Pemenuhan pesanan dapat diberikan ke semua jenis pengendali status.
  • Beberapa respons dapat digabungkan selama giliran percakapan melalui antrean respons. Fitur ini dapat menyederhanakan desain agen Anda dalam beberapa kasus.
  • Dialogflow CX tidak mendukung respons bawaan khusus platform. Namun, API ini menyediakan beberapa jenis respons, termasuk payload kustom yang dapat digunakan untuk respons khusus platform.

Parameter

Parameter Dialogflow ES memiliki karakteristik berikut:

  • Hanya ditentukan dalam maksud.
  • Ditetapkan oleh input pengguna akhir, peristiwa, webhook, dan panggilan API.
  • Dirujuk dalam respons, perintah parameter, kode webhook, dan nilai parameter:
    • Format referensi dasar adalah $parameter-name.
    • Referensi mendukung sintaksis akhiran .original, .partial, dan .recent.
    • Referensi dapat menentukan konteks aktif: #context-name.parameter-name.
    • Referensi dapat menentukan parameter peristiwa: #event-name.parameter-name.

Parameter Dialogflow CX memiliki karakteristik berikut:

  • Ditentukan dalam maksud dan formulir halaman.
  • Parameter niat dan formulir ditransmisikan ke parameter sesi, tempat parameter tersebut tersedia untuk dirujuk selama durasi sesi.
  • Ditetapkan oleh input pengguna akhir, webhook, preset parameter pemenuhan, dan panggilan API.
  • Direferensikan dalam respons, perintah parameter, handler perintah ulang, preset parameter, dan kode webhook:
    • Format referensi adalah $session.params.parameter-id untuk parameter sesi dan $intent.params.parameter-id untuk parameter intent.
    • Referensi parameter intent mendukung sintaksis akhiran .original dan .resolved. Parameter sesi tidak mendukung sintaksis ini.

Entity sistem

Dialogflow ES mendukung banyak entitas sistem.

Dialogflow CX mendukung banyak entitas sistem yang sama, tetapi ada beberapa perbedaan. Saat melakukan migrasi, verifikasi bahwa entity sistem yang Anda gunakan di Dialogflow ES juga didukung oleh Dialogflow CX untuk bahasa yang sama. Jika tidak, Anda harus membuat entitas kustom untuk ini.

Acara

Peristiwa Dialogflow ES memiliki karakteristik berikut:

  • Dapat dipanggil dari panggilan API atau webhook untuk mencocokkan maksud.
  • Dapat menetapkan parameter.
  • Sejumlah kecil peristiwa dipanggil oleh platform integrasi.

Peristiwa Dialogflow CX memiliki karakteristik berikut:

  • Dapat dipanggil dari panggilan API atau webhook untuk memanggil handler peristiwa.
  • Tidak dapat menyetel parameter.
  • Banyak peristiwa bawaan dapat digunakan untuk menangani kurangnya input pengguna akhir, input pengguna akhir yang tidak dikenali, parameter yang dibatalkan oleh webhook, dan error webhook.
  • Pemanggilan dapat dikontrol oleh aturan cakupan yang sama dengan pengendali status lainnya.

Intent bawaan

Dialogflow ES mendukung intent bawaan berikut:

Berikut ini penjelasan dukungan Dialogflow CX untuk intent bawaan:

  • Intent sambutan didukung.
  • Intent penggantian tidak disediakan. Gunakan peristiwa no-match di pengendali peristiwa.
  • Untuk contoh negatif, gunakan maksud negatif default.
  • Intent tindak lanjut yang telah ditetapkan tidak disediakan. Anda harus membuat intent ini sesuai kebutuhan agen Anda. Misalnya, Anda mungkin perlu membuat maksud untuk menangani jawaban negatif terhadap pertanyaan agen ("tidak", "tidak, terima kasih", "tidak", dan sebagainya). Niat Dialogflow CX dapat digunakan kembali di seluruh agen Anda, jadi Anda hanya perlu menentukannya sekali. Menggunakan rute maksud yang berbeda untuk maksud umum ini, dalam cakupan yang berbeda, memberi Anda kontrol yang jauh lebih baik atas percakapan.

Webhook

Webhook Dialogflow ES memiliki karakteristik berikut:

  • Anda dapat mengonfigurasi satu layanan webhook untuk agen.
  • Setiap maksud dapat ditandai sebagai menggunakan webhook.
  • Tidak ada dukungan bawaan untuk menangani error webhook.
  • Tindakan intent atau nama intent digunakan oleh webhook untuk menentukan dari mana di agen tersebut dipanggil.
  • Konsol menyediakan editor inline.

Webhook Dialogflow CX memiliki karakteristik berikut:

  • Anda dapat mengonfigurasi beberapa layanan webhook untuk agen.
  • Setiap pemenuhan dapat secara opsional menentukan panggilan webhook.
  • Ada dukungan bawaan untuk penanganan error webhook.
  • Webhook pemenuhan Dialogflow CX berisi tag. Tag ini mirip dengan tindakan Dialogflow ES, tetapi hanya digunakan saat memanggil webhook. Layanan webhook dapat menggunakan tag ini untuk menentukan tempat panggilan dari agen.
  • Konsol tidak memiliki editor kode webhook bawaan. Cloud Run Functions sering digunakan, tetapi ada banyak opsi.

Saat bermigrasi ke Dialogflow CX, Anda harus mengubah kode webhook, karena properti permintaan dan respons berbeda.

Integrasi

Integrasi Dialogflow ES dan integrasi Dialogflow CX mendukung platform yang berbeda. Untuk platform yang didukung oleh kedua jenis agen, mungkin ada perbedaan dalam konfigurasi.

Jika integrasi Dialogflow ES yang Anda gunakan tidak didukung oleh Dialogflow CX, Anda mungkin perlu beralih platform atau menerapkan integrasi sendiri.

Fitur khusus Dialogflow CX lainnya

Ada banyak fitur lain yang hanya disediakan oleh Dialogflow CX. Anda harus mempertimbangkan untuk menggunakan fitur ini selama migrasi. Contoh:

Praktik terbaik

Sebelum melakukan migrasi, pahami praktik terbaik desain agen Dialogflow CX. Banyak praktik terbaik Dialogflow CX ini serupa dengan praktik terbaik Dialogflow ES, tetapi beberapa di antaranya unik untuk Dialogflow CX.

Tentang alat migrasi

Alat migrasi menyalin sebagian besar data Dialogflow ES ke agen Dialogflow CX Anda, dan menulis ke file TODO dengan daftar item yang harus dimigrasikan secara manual. Alat ini hanya menyalin jenis entity kustom dan frasa pelatihan intent. Sebaiknya sesuaikan alat ini untuk kebutuhan spesifik Anda.

Kode alat migrasi

Berikut adalah kode untuk alat ini. Anda harus meninjau kode untuk alat ini, sehingga Anda memahami fungsinya. Anda mungkin ingin mengubah kode ini untuk menangani situasi tertentu di agen Anda. Pada langkah-langkah di bawah, Anda akan menjalankan alat ini.

// 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()
}

Migrasi alat jenis entity

Jenis entity Dialogflow ES dan jenis entity Dialogflow CX sangat mirip, sehingga merupakan jenis data yang paling mudah dimigrasikan. Alat ini hanya menyalin jenis entity apa adanya.

Migrasi alat maksud pengguna

Maksud Dialogflow ES dan maksud Dialogflow CX sangat berbeda.

Maksud Dialogflow ES digunakan sebagai elemen penyusun agen; dan maksud tersebut berisi frasa pelatihan, respons, konteks untuk kontrol percakapan, konfigurasi webhook, peristiwa, tindakan, dan parameter pengisian slot.

Dialogflow CX telah memindahkan sebagian besar data ini ke resource lain. Intent Dialogflow CX hanya memiliki frasa pelatihan dan parameter, yang membuat intent dapat digunakan kembali di seluruh agen. Alat ini hanya menyalin dua jenis data intent ini ke intent Dialogflow CX Anda.

Batasan alat migrasi

Alat migrasi tidak mendukung hal berikut:

  • Agen mega: Alat ini tidak dapat membaca dari beberapa sub-agen, tetapi Anda dapat memanggil alat ini beberapa kali untuk setiap sub-agen.
  • Agen multibahasa: Anda harus mengubah alat untuk membuat frasa pelatihan dan entri entitas multibahasa.
  • Verifikasi entity sistem untuk bahasa selain Inggris: Alat ini membuat item TODO saat menemukan entity sistem yang tidak didukung oleh Dialogflow CX, dengan asumsi bahwa bahasa Inggris adalah bahasa default, dan menggunakan wilayah AS. Dukungan entity sistem bervariasi menurut bahasa dan wilayah. Untuk bahasa dan wilayah lain, Anda harus mengubah alat untuk melakukan pemeriksaan ini.

Langkah-langkah migrasi penting

Subbagian berikut menguraikan langkah-langkah migrasi yang harus dilakukan. Anda tidak perlu mengikuti langkah-langkah manual ini secara berurutan, dan Anda bahkan mungkin perlu melakukan langkah-langkah ini secara bersamaan atau dalam urutan yang berbeda. Baca langkah-langkahnya dan mulailah merencanakan perubahan Anda sebelum benar-benar melakukan perubahan.

Setelah menjalankan alat migrasi, Anda dapat membangun kembali agen Dialogflow CX. Anda masih harus melakukan cukup banyak pekerjaan migrasi, tetapi sebagian besar data yang dimasukkan secara manual akan ada di agen Dialogflow CX dan file TODO.

Buat agen Dialogflow CX Anda

Jika belum melakukannya, buat agen Dialogflow CX Anda. Pastikan untuk menggunakan bahasa default yang sama dengan agen Dialogflow ES Anda.

Menjalankan alat migrasi

Lakukan langkah-langkah berikut untuk menjalankan alat:

  1. Instal Go di komputer Anda jika Anda belum melakukannya.
  2. Buat direktori untuk kode alat yang disebut migrate.
  3. Salin kode alat di atas ke file di direktori ini yang bernama main.go.
  4. Ubah kode jika diperlukan untuk kasus Anda.
  5. Buat modul Go di direktori ini. Contoh:

    go mod init migrate
    
  6. Instal library klien Go Dialogflow ES V2 dan Dialogflow CX V3:

    go get cloud.google.com/go/dialogflow/apiv2
    go get cloud.google.com/go/dialogflow/cx/apiv3
    
  7. Pastikan Anda telah menyiapkan autentikasi library klien.

  8. Jalankan alat, dan simpan output ke file:

    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
    

Pemecahan masalah alat migrasi

Jika Anda mengalami error saat menjalankan alat, periksa hal berikut:

Error Resolusi
Error RPC yang menunjukkan bahwa bagian frasa pelatihan menyebutkan parameter yang tidak ditentukan untuk intent. Hal ini dapat terjadi jika sebelumnya Anda menggunakan Dialogflow ES API untuk membuat parameter intent dengan cara yang tidak konsisten dengan frasa pelatihan. Untuk memperbaikinya, ganti nama parameter Dialogflow ES dari konsol, periksa apakah frasa pelatihan Anda menggunakan parameter dengan benar, lalu klik simpan. Hal ini juga dapat terjadi jika frasa pelatihan Anda mereferensikan parameter yang tidak ada.

Setelah memperbaiki error, Anda harus menghapus intent dan entitas agen Dialogflow CX sebelum menjalankan alat migrasi lagi.

Memindahkan data intent Dialogflow ES ke Dialogflow CX

Alat ini memigrasikan frasa dan parameter pelatihan intent ke intent Dialogflow CX, tetapi ada banyak kolom intent Dialogflow ES lainnya yang harus dimigrasikan secara manual.

Intent Dialogflow ES mungkin memerlukan halaman Dialogflow CX yang sesuai, intent Dialogflow CX yang sesuai, atau keduanya.

Jika kecocokan maksud Dialogflow ES digunakan untuk mentransisikan percakapan dari satu node percakapan tertentu ke node percakapan lain, Anda harus memiliki dua halaman di agen yang terkait dengan maksud ini:

  • Halaman asli yang berisi rute intent, yang akan bertransisi ke halaman berikutnya: Rute intent di halaman asli mungkin memiliki pesan pemenuhan Dialogflow CX yang mirip dengan respons intent Dialogflow ES. Anda mungkin memiliki banyak rute maksud di halaman ini. Saat halaman asli aktif, rute niat ini dapat mengalihkan percakapan ke berbagai kemungkinan jalur. Banyak intent Dialogflow ES akan berbagi halaman asli Dialogflow CX yang sama.
  • Halaman berikutnya, yang merupakan target transisi untuk rute intent di halaman asli: Pengisian entri Dialogflow CX untuk halaman berikutnya mungkin memiliki pesan pengisian Dialogflow CX yang mirip dengan respons intent Dialogflow ES.

Jika intent Dialogflow ES berisi parameter wajib, Anda harus membuat halaman Dialogflow CX yang sesuai dengan parameter yang sama dalam formulir.

Intent Dialogflow CX dan halaman Dialogflow CX biasanya berbagi daftar parameter yang sama, yang berarti bahwa satu intent Dialogflow ES memiliki halaman Dialogflow CX yang sesuai dan intent Dialogflow CX yang sesuai. Saat intent Dialogflow CX dengan parameter dalam rute intent cocok, percakapan sering kali beralih ke halaman dengan parameter yang sama. Parameter yang diekstrak dari kecocokan maksud disalurkan ke parameter sesi, yang tersedia untuk mengisi parameter formulir halaman sebagian atau sepenuhnya.

Intent penggantian dan intent tindak lanjut yang telah ditetapkan tidak ada di Dialogflow CX. Lihat intent bawaan.

Tabel berikut menjelaskan cara memetakan data intent tertentu dari Dialogflow ES ke resource Dialogflow CX:

Data Maksud (Intent) Dialogflow ES Data Dialogflow CX yang sesuai Tindakan diperlukan
Frasa latihan Frasa pelatihan maksud Dimigrasikan menurut alat. Alat ini memeriksa dukungan entitas sistem, dan membuat item TODO untuk entitas sistem yang tidak didukung.
Respons agen Pesan respons pemenuhan Lihat respons agen.
Konteks untuk kontrol percakapan Tidak ada Lihat Kontrol struktur dan jalur percakapan.
Setelan webhook Konfigurasi webhook pemenuhan Lihat webhook.
Acara Pengendali peristiwa tingkat Flow atau Tingkat Halaman Lihat acara.
Tindakan Tag webhook pemenuhan Lihat webhook.
Parameter Parameter intent dan/atau Parameter formulir halaman Dimigrasikan ke parameter intent menurut alat. Jika parameter diperlukan, alat akan membuat item TODO untuk kemungkinan migrasi ke halaman. Lihat parameter.
Perintah parameter Perintah parameter formulir halaman Lihat pengisian formulir.

Membuat alur

Buat alur untuk setiap topik percakapan tingkat tinggi. Topik dalam setiap alur harus berbeda, sehingga percakapan tidak sering berpindah-pindah antar-alur.

Jika Anda menggunakan agen mega, setiap sub-agen harus menjadi satu atau beberapa alur.

Mulai dengan jalur percakapan dasar

Sebaiknya uji agen Anda dengan simulator saat melakukan iterasi pada perubahan. Jadi, Anda harus memfokuskan diri pada jalur percakapan dasar di awal percakapan, dan melakukan pengujian saat membuat perubahan. Setelah Anda berhasil menggunakannya, lanjutkan ke jalur percakapan yang lebih mendetail.

Pengendali status tingkat halaman versus tingkat alur

Saat membuat pengendali status, pertimbangkan apakah pengendali tersebut harus diterapkan di tingkat alur atau tingkat halaman. Handler tingkat flow berada dalam cakupan setiap kali flow (dan oleh karena itu, halaman apa pun dalam flow) aktif. Handler tingkat halaman hanya dalam cakupan saat halaman tertentu aktif. Handler tingkat alur mirip dengan intent Dialogflow ES tanpa konteks input. Handler tingkat halaman mirip dengan maksud Dialogflow ES dengan konteks input.

Kode webhook

Properti permintaan dan respons webhook berbeda untuk Dialogflow CX. Lihat bagian webhook.

Konektor jawaban

Dialogflow CX belum mendukung penyambung pengetahuan. Anda harus menerapkan intent ini sebagai intent normal atau menunggu hingga Dialogflow CX mendukung konektor pengetahuan.

Setelan agen

Tinjau setelan agen Dialogflow ES, dan sesuaikan setelan agen Dialogflow CX sesuai kebutuhan.

Menggunakan file TODO

Alat migrasi akan menghasilkan file CSV. Item dalam daftar ini berfokus pada bagian data tertentu yang mungkin perlu diperhatikan. Impor file ini ke spreadsheet. Selesaikan setiap item dalam spreadsheet, menggunakan kolom untuk menandai penyelesaian.

Migrasi penggunaan API

Jika sistem Anda menggunakan Dialogflow ES API untuk panggilan runtime atau waktu desain, Anda harus mengupdate kode ini untuk menggunakan Dialogflow CX API. Jika Anda hanya menggunakan panggilan deteksi maksud saat runtime, pembaruan ini akan cukup mudah.

Integrasi

Jika agen Anda menggunakan integrasi, lihat bagian integrasi, dan lakukan perubahan yang diperlukan.

Subbagian berikut menguraikan langkah-langkah migrasi yang direkomendasikan.

Validasi

Gunakan validasi agen untuk memeriksa apakah agen Anda mengikuti praktik terbaik.

Pengujian

Saat melakukan langkah-langkah migrasi manual di atas, Anda harus menguji agen dengan simulator. Setelah agen Anda tampak berfungsi, Anda harus membandingkan percakapan antara agen Dialogflow ES dan Dialogflow CX, serta memverifikasi bahwa perilakunya serupa atau lebih baik.

Saat menguji percakapan ini dengan simulator, Anda harus membuat kasus pengujian untuk mencegah regresi di masa mendatang.

Lingkungan

Tinjau lingkungan Dialogflow ES dan perbarui lingkungan Dialogflow CX sesuai kebutuhan.