相較於 Dialogflow ES 代理程式,Conversational Agents (Dialogflow CX) 代理程式提供更強大的對話控制選項和工具。如果您的 Dialogflow ES 服務專員處理複雜的對話,建議您考慮遷移至 Conversational Agents (Dialogflow CX)。
本指南說明如何將代理程式從 Dialogflow Dialogflow ES 遷移至 Conversational Agents (Dialogflow CX)。這兩種代理程式類型有許多基本差異,因此沒有簡單的方法可執行這項遷移作業。
如果您使用本指南進行遷移,請按一下上方的「提供意見」按鈕,提供正面或負面意見。我們會根據這項意見回饋,持續改善本指南。
大致上來說,建議的程序是自動/手動混合式程序。您將使用一項工具,讀取部分 Dialogflow ES 服務專員資料、將資料寫入 Conversational Agents (Dialogflow CX) 服務專員,並擷取待辦事項清單。接著,您可以使用最佳做法、待辦事項清單和工具遷移的資料,重新建立完整的 Conversational Agents (Dialogflow CX) 服務專員。
瞭解 Conversational Agents (Dialogflow CX)
嘗試進行遷移前,請先確實瞭解 Conversational Agents (Dialogflow CX) 流程的運作方式。您可以從以下連結開始:
您也應詳閱其他概念文件,瞭解新服務可能需要的功能。著重於以下事項:
瞭解 Dialogflow ES/Conversational Agents (Dialogflow CX) 的差異
本節列出 Dialogflow ES 和 Conversational Agents (Dialogflow CX) 之間最重要的差異。日後執行手動遷移步驟時,請參考本節的說明。
結構和對話路徑控制
Dialogflow ES 提供以下結構和對話路徑控制選項:
- 意圖可用來當做機器人的構成要素。在對話的任何時間點,系統都會比對意圖,而從某種意義上來說,每個意圖都是對話的節點。
- Context 用於控管對話。背景資訊可用於控制在任何時間點可相符的意圖。背景資訊會在一定次數的對話輪次後過期,因此這類控制項可能無法正確處理長篇對話。
Conversational Agents (Dialogflow CX) 提供結構資源階層,並可更精確地控制對話路徑:
- 頁面是對話的圖表節點。Conversational Agents (Dialogflow CX) 對話與狀態機器人類似。在對話的任何時間點,都只有一個頁面處於活動狀態。根據使用者輸入內容或事件,對話可能會轉移到其他頁面。頁面通常會在多個對話回合中保持啟用狀態。
- 流程是指一組相關頁面。每個對話流程都應處理高層級的對話主題。
- 狀態處理常式可用於控制轉換和回應。狀態處理常式分為三種類型:
- 意圖路徑:包含必須比對的意圖、選用回應和選用頁面轉場。
- 條件路徑:包含必須符合的條件、選用的回應和選用的頁面轉換。
- 事件處理常式:包含必須叫用的事件名稱、選用的回應和選用的頁面轉場。
- Scope 可用來控制是否可以呼叫狀態處理常式。大多數的處理程序都與網頁或整個流程相關聯。如果相關聯的頁面或流程處於活動狀態,則處理常式就會在範圍內,且可呼叫。範圍內的 Conversational Agents (Dialogflow CX) 意圖路徑,類似於含有啟用輸入背景資訊的 Dialogflow ES 意圖。
設計服務機器人的流程和頁面時,請務必參閱服務機器人設計指南的「流程」一節中的建議。
填寫表單
Dialogflow ES 會使用運算單元填充功能,向使用者收集必要參數:
- 這些參數是標示為必要的意圖參數。
- 系統會持續比對意圖,直到收集到所有必要參數為止。
- 您可以定義提示,要求使用者提供值。
Conversational Agents (Dialogflow CX) 會使用表單填寫功能,向使用者收集必要參數:
- 這些參數與網頁相關,並在網頁處於活動狀態時收集。
- 您可以使用頁面條件路徑,判斷表單填寫作業是否完成。這些條件路徑通常會轉換至其他頁面。
- 您可以定義提示,以及重複提示處理常式,以便妥善處理多次收集值的嘗試。
轉場
當使用者輸入內容與意圖相符時,Dialogflow ES 會自動從一個意圖轉換到下一個意圖。這種比對結果只會出現在沒有輸入背景資訊的意圖,或是含有有效輸入背景資訊的意圖。
當範圍內的狀態處理常式符合其需求並提供轉換目標時,Conversational Agents (Dialogflow CX) 就會從一個頁面轉換到下一個頁面。透過這些轉場效果,您可以穩定地引導使用者進行對話。您可以透過多種方式控制這些轉場效果:
- 意圖比對可觸發意圖路徑。
- 符合條件時,系統會觸發條件路徑。
- 事件的叫用作業可觸發事件處理常式。
- 如果使用者在多次嘗試後仍無法提供值,重複提示處理常式就會導致轉換。
- 您可以使用符號轉場目標做為轉場目標。
服務專員回應
當意圖比對成功時,Dialogflow ES 代理程式會將回應傳送給使用者:
- 代理人可以從可能的回覆清單中選取一則訊息做為回覆。
- 回應可以是平台專屬,可使用複合式回應格式。
- 回應可由 webhook 觸發。
呼叫執行服務時,Conversational Agents (Dialogflow CX) 服務專員回應會傳送給使用者。與 Dialogflow ES 執行要求不同,後者一律會涉及 webhook,而對話式服務專員 (Dialogflow CX) 執行要求則可能會或不會涉及呼叫 webhook,這取決於執行要求資源是否已設定 webhook。以 Webhook 回應為基礎的靜態回應和動態回應都由執行要求控制。您可以透過多種方式建立對話方塊回應:
- 執行要求可提供給任何類型的狀態處理常式。
- 在對話輪次中,您可以透過回應佇列串聯多個回應。在某些情況下,這項功能可簡化您的代理程式設計。
- Conversational Agents (Dialogflow CX) 不支援內建的平台專屬回應。不過,它提供多種回應類型,包括可用於特定平台回應的自訂酬載。
參數
Dialogflow ES 參數具有下列特性:
- 僅在意圖中定義。
- 由使用者輸入內容、事件、Webhook 和 API 呼叫設定。
- 在回應、參數提示、webhook 程式碼和參數值中參照:
- 基本參考格式為
$parameter-name
。 - 參照項目支援
.original
、.partial
和.recent
後置詞語法。 - 參照可指定有效的結構定義:
#context-name.parameter-name
。 - 參照可指定事件參數:
#event-name.parameter-name
。
- 基本參考格式為
Conversational Agents (Dialogflow CX) 參數具有下列特性:
- 在意圖和頁面表單中定義。
- 意圖和表單參數會傳播至工作階段參數,在工作階段期間可供參考。
- 由使用者輸入內容、webhook、訂單處理參數預設值和 API 呼叫設定。
- 在回應、參數提示、重新提示處理常式、參數預設值和 webhook 程式碼中參照:
- 參照格式為工作階段參數的
$session.params.parameter-id
,意圖參數的$intent.params.parameter-id
。 - 意圖參數參照支援
.original
和.resolved
後置詞語法。工作階段參數不支援這項語法。
- 參照格式為工作階段參數的
系統實體
Dialogflow ES 支援許多系統實體。
Conversational Agents (Dialogflow CX) 支援許多相同的系統實體,但仍有些差異。遷移時,請確認在 Dialogflow ES 中使用的系統實體也支援相同語言的 Conversational Agents (Dialogflow CX)。如果沒有,請為這些項目建立自訂實體。
活動
Dialogflow ES 事件具有下列特性:
- 可透過 API 呼叫或 webhook 叫用,以便與意圖相符。
- 可設定參數。
- 整合平台會叫用少量事件。
Conversational Agents (Dialogflow CX) 事件具有下列特徵:
- 可透過 API 呼叫或 Webhook 叫用,以便呼叫事件處理常式。
- 無法設定參數。
- 許多內建事件可用於處理缺少使用者輸入內容、無法辨識的使用者輸入內容、由 webhook 使參數失效,以及 webhook 錯誤。
- 喚出作業可透過與其他狀態處理常式相同的範圍規則加以控制。
內建意圖
Dialogflow ES 支援下列內建意圖:
以下說明 Conversational Agents (Dialogflow CX) 支援內建意圖的方式:
- 支援歡迎意圖。
- 系統不會提供備用意圖。請改為在事件處理常式中使用不相符事件。
- 如要提供負面範例,請使用預設負面意圖。
- 系統不會提供預先定義的後續追蹤意圖。您必須根據代理程式需求建立這些意圖。舉例來說,您可能需要建立意圖,以便處理代理程式問題的否定答案 (例如「不」、「謝謝不用」、「不,我不要」等)。Conversational Agents (Dialogflow CX) 意圖可在代理程式中重複使用,因此您只需定義一次。在不同範圍內為這些常見意圖使用不同的意圖路徑,可讓您更有效地控管對話。
Webhook
Dialogflow ES webhook 具有下列特性:
- 您可以為代理程式設定一個 Webhook 服務。
- 每個意圖都可以標示為使用 webhook。
- 沒有內建的 webhook 錯誤處理機制。
- 意圖動作或意圖名稱可讓 webhook 判斷意圖在介面中的位置。
- 控制台提供內嵌編輯器。
Conversational Agents (Dialogflow CX) webhook 具有下列特性:
- 您可以為代理程式設定多個 Webhook 服務。
- 每個執行階段都可以選擇指定 webhook 呼叫。
- 內建webhook 錯誤處理支援功能。
- Conversational Agents (Dialogflow CX) 執行 webhook 包含標記。這個標記類似於 Dialogflow ES 動作,但只會在呼叫 webhook 時使用。Webhook 服務可使用這些標記,判斷呼叫來自介面的哪個位置。
- 主控台沒有內建的 Webhook 程式碼編輯器。一般來說,您可以使用 Cloud Run 函式,但還有許多其他選項。
遷移至 Conversational Agents (Dialogflow CX) 時,您需要變更 webhook 程式碼,因為要求和回應的屬性不同。
整合
Dialogflow ES 整合和 Conversational Agents (Dialogflow CX) 整合支援不同的平台。對於兩種代理程式類型皆支援的平台,設定可能會有所不同。
如果 Conversational Agents (Dialogflow CX) 不支援您使用的 Dialogflow ES 整合,您可能需要切換平台,或自行實作整合。
更多 Conversational Agents (Dialogflow CX) 專屬功能
還有許多其他功能僅限 Conversational Agents (Dialogflow CX) 提供。您應考慮在遷移期間使用這些功能。例如:
- 進階 NLU
- 進階語音設定 (語音感測結束處、不設語音逾時時間等等)
- 變更記錄
- 條件式邏輯
- 電話整合功能的 DTMF 輸入內容
- 環境專屬 webhook
- 實驗
- 路由群組
- 搜尋代理程式資料
- 安全性設定 (遮蓋和資料保留)
- 用於進階回應和條件的系統函式
- 測試案例
- 驗證代理程式資料
最佳做法
遷移前,請先熟悉 Conversational Agents (Dialogflow CX) 代理程式設計最佳做法。許多 Conversational Agents (Dialogflow CX) 最佳做法與 Dialogflow ES 的最佳做法相似,但有些則是 Conversational Agents (Dialogflow CX) 專屬。
關於遷移工具
遷移工具會將大部分的 Dialogflow ES 資料複製到 Conversational Agents (Dialogflow CX) 代理程式,並將必須手動遷移的項目清單寫入待辦檔案。這項工具只會複製自訂實體類型和意圖訓練字詞。建議您根據自身需求自訂這項工具。
遷移工具代碼
以下是工具的程式碼。您應該查看這項工具的程式碼,瞭解其運作方式。您可能需要變更此程式碼,以便在您的服務中處理特定情況。您將在下列步驟中執行這項工具。
// 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() }
實體類型的工具遷移
Dialogflow ES 實體類型和 Conversational Agents (Dialogflow CX) 實體類型非常相似,因此是最容易遷移的資料類型。這項工具只會直接複製實體類型。
意圖遷移工具
Dialogflow ES 意圖和 Conversational Agents (Dialogflow CX) 意圖有很大的不同。
Dialogflow ES 意圖可用於建構代理程式,其中包含訓練字詞、回應、對話控制的背景資訊、Webhook 設定、事件、動作和運算單元填充參數。
Conversational Agents (Dialogflow CX) 已將這類資料的大部分內容移至其他資源。Conversational Agents (Dialogflow CX) 意圖只包含訓練詞組和參數,因此意圖可在代理程式中重複使用。這項工具只會將這兩種意圖資料類型複製到 Conversational Agents (Dialogflow CX) 意圖。
遷移工具限制
遷移工具不支援下列項目:
- Mega agent:此工具無法從多個子代理程式讀取資料,但您可以針對每個子代理程式多次呼叫此工具。
- 多語言代理程式:您應修改工具,以建立多語言訓練詞組和實體項目。
- 非英文語言的系統實體驗證:當工具發現系統實體不受Conversational Agents (Dialogflow CX) 支援時,會建立待辦事項項目,並假設英文為預設語言,且使用美國地區。系統實體支援功能因語言和地區而異。如要針對其他語言和地區執行這項檢查,您必須修改工具。
必要的遷移步驟
以下各小節將概略說明遷移步驟。您不必按照順序執行這些手動步驟,甚至可能需要同時執行這些步驟,或以不同的順序執行。請詳閱這些步驟,並在實際進行變更前開始規劃變更。
執行遷移工具後,您可以重新建構 Conversational Agents (Dialogflow CX) 代理程式。您仍須進行相當程度的遷移作業,但手動輸入的大部分資料會顯示在 Conversational Agents (Dialogflow CX) 服務專員和待辦檔案中。
建立 Conversational Agents (Dialogflow CX) 服務專員
如果您尚未建立 Conversational Agents (Dialogflow CX) 服務專員,請先完成這項作業。請務必使用與 Dialogflow ES 服務專員相同的預設語言。
執行遷移工具
請按照下列步驟執行工具:
- 如果您尚未在電腦上安裝 Go,請在電腦上安裝 Go。
- 為名為
migrate
的工具程式碼建立目錄。 - 將上述工具程式碼複製到這個目錄中名為
main.go
的檔案。 - 視需要修改程式碼。
在這個目錄中建立 Go 模組。例如:
go mod init migrate
安裝 Dialogflow ES V2 和 Conversational Agents (Dialogflow CX) V3 Go 用戶端程式庫:
go get cloud.google.com/go/dialogflow/apiv2 go get cloud.google.com/go/dialogflow/cx/apiv3
請確認您已設定用戶端程式庫驗證。
執行工具,並將輸出內容儲存至檔案:
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
遷移工具疑難排解
如果在執行工具時發生錯誤,請檢查下列項目:
錯誤 | 解決方法 |
---|---|
RPC 錯誤:訓練詞組部分提及的意圖未定義的參數。 | 如果您先前使用 Dialogflow ES API 建立意圖參數時,所採用的方式與訓練字詞不一致,就可能會發生這種情況。如要修正這個問題,請在控制台重新命名 Dialogflow ES 參數,確認訓練詞組是否正確使用參數,然後按一下「儲存」。如果訓練詞組參照不存在的參數,也會發生這種情況。 |
修正錯誤後,您必須先清除 Conversational Agents (Dialogflow CX) 代理程式中的意圖和實體,再重新執行遷移工具。
將 Dialogflow ES 意圖資料移至 Conversational Agents (Dialogflow CX)
這項工具會將意圖訓練詞組和參數遷移至 Conversational Agents (Dialogflow CX) 意圖,但仍有許多其他 Dialogflow ES 意圖欄位需要手動遷移。
Dialogflow ES 意圖可能需要對應的 Conversational Agents (Dialogflow CX) 頁面、對應的 Conversational Agents (Dialogflow CX) 意圖,或兩者皆需。
如果使用 Dialogflow ES 意圖比對功能,將對話從特定對話節點轉移至另一個節點,代理程式中應有與此意圖相關的兩個頁面:
- 原始頁面包含意圖路徑,可轉換至下一頁:原始頁面中的意圖路徑可能包含與 Dialogflow ES 意圖回應類似的 Conversational Agents (Dialogflow CX) 執行要求訊息。這個頁面可能包含多個意圖路徑。當原始網頁處於啟用狀態時,這些意圖路徑可將對話轉換至許多可能的路徑。許多 Dialogflow ES 意圖都會與相同的對應 Conversational Agents (Dialogflow CX) 原始頁面共用。
- 下一個網頁,也就是原始網頁中意圖路徑的轉換目標:下一個網頁的 Conversational Agents (Dialogflow CX) 輸入執行要求可能會有類似於 Dialogflow ES 意圖回應的 Conversational Agents (Dialogflow CX) 執行要求訊息。
如果 Dialogflow ES 意圖含有必要參數,您應在表單中使用相同的參數,建立對應的 Conversational Agents (Dialogflow CX) 頁面。
通常,Conversational Agents (Dialogflow CX) 意圖和 Conversational Agents (Dialogflow CX) 頁面會共用相同的參數清單,也就是說,單一 Dialogflow ES 意圖會對應至一個 Conversational Agents (Dialogflow CX) 頁面,以及一個 Conversational Agents (Dialogflow CX) 意圖。當意圖路徑中含有參數的 Conversational Agents (Dialogflow CX) 意圖相符時,對話通常會轉換至含有相同參數的網頁。從意圖比對中擷取的參數會傳播至工作階段參數,可用於部分或完全填入網頁表單參數。
備用意圖和預先定義的後續意圖不會出現在 Conversational Agents (Dialogflow CX) 中。請參閱內建意圖。
下表說明如何將特定意圖資料從 Dialogflow ES 對應至對話式服務專員 (Dialogflow CX) 資源:
Dialogflow ES 意圖資料 | 對應的 Conversational Agents (Dialogflow CX) 資料 | 需採取行動 |
---|---|---|
訓練詞組 | 意圖訓練詞組 | 使用工具進行遷移。工具會檢查系統實體支援,並為不支援的系統實體建立待辦事項項目。 |
服務專員回應 | 執行要求回應訊息 | 請參閱代理程式回應。 |
對話控制項的背景資訊 | 無 | 請參閱「結構和對話路徑控管」。 |
Webhook 設定 | 執行要求 Webhook 設定 | 請參閱「webhook」。 |
活動 | 流程層級或網頁層級事件處理常式 | 請參閱「事件」。 |
動作 | 執行要求 Webhook 代碼 | 請參閱「webhook」。 |
參數 | 意圖參數和/或網頁表單參數 | 已由工具遷移至意圖參數。如果需要參數,工具會建立待辦事項項目,以便遷移至頁面。請參閱「參數」。 |
參數提示 | 網頁表單參數提示 | 請參閱「表單填寫」一文。 |
建立流程
為每個高層級對話主題建立流程。每個對話流程中的主題應有所區隔,以免對話經常在不同對話流程之間來回跳躍。
如果您使用的是超級代理程式,則每個子代理程式都應成為一個或多個流程。
從基本對話路徑開始
建議您在變更迭代時,使用模擬工具測試代理程式。因此,您一開始應專注於對話初期的基本對話路徑,並在進行變更時進行測試。完成這些設定後,請繼續進行更詳細的對話路徑。
流程層級與網頁層級狀態處理常式
建立狀態處理常式時,請考量是否應在流程層級或頁面層級套用這些處理常式。只要流程 (以及流程中的任何頁面) 處於作用中狀態,流程層級處理常式就會在範圍內。只有在特定網頁處於活動狀態時,網頁層級的處理常式才會納入範圍。流程層級處理程序與沒有輸入背景資訊的 Dialogflow ES 意圖相似。網頁層級處理程序與含有輸入背景資訊的 Dialogflow ES 意圖相似。
Webhook code
在 Conversational Agents (Dialogflow CX) 中,webhook 要求和回應屬性有所不同。請參閱Webhooks 專區。
知識連接器
Conversational Agents (Dialogflow CX) 目前尚不支援知識連接器。您必須將這些意圖設為一般意圖,或等到 Conversational Agents (Dialogflow CX) 支援知識連接器。
代理程式設定
查看 Dialogflow ES 服務專員設定,並視需要調整 Conversational Agents (Dialogflow CX) 服務專員設定。
使用 TODO 檔案
遷移工具會輸出 CSV 檔案。這份清單中的項目著重於可能需要處理的特定資料。將這個檔案匯入試算表。請解決試算表中的每個項目,並使用一欄標示完成情況。
API 用途遷移
如果系統使用 Dialogflow ES API 進行執行階段或設計階段的呼叫,您必須更新這段程式碼,才能使用 Conversational Agents (Dialogflow CX) API。如果您只在執行階段使用偵測意圖呼叫,這項更新應該會相當簡單。
整合
如果您的代理程式使用整合功能,請參閱整合功能專區,並視需要進行變更。
建議的遷移步驟
以下各小節將概略說明建議的遷移步驟。
驗證
使用代理程式驗證功能,確認代理程式是否遵循最佳做法。
測試
執行上述手動遷移步驟時,請使用模擬工具測試代理程式。一旦服務專員似乎運作正常,您應該比較 Dialogflow ES 和 Conversational Agents (Dialogflow CX) 服務專員之間的對話,並確認行為是否相似或有所改善。
使用模擬器測試這些對話時,請建立測試案例,以免日後發生回歸。
環境
檢查您的 Dialogflow ES 環境,並視需要更新 Conversational Agents (Dialogflow CX) 環境。