סוכנים ב-Dialogflow CX מספקים כלים ואפשרויות שליטה מתקדמים יותר בשיחות מאשר סוכנים ב-Dialogflow ES. אם הנציג שלכם ב-Dialogflow ES מטפל בשיחות מורכבות, כדאי לשקול מעבר ל-Dialogflow CX.
במדריך הזה מוסבר איך להעביר סוכן מ-Dialogflow ES ל-Dialogflow CX. יש הרבה הבדלים מהותיים בין שני סוגי הסוכנים האלה, ולכן אין דרך פשוטה לבצע את ההעברה הזו.
אם אתם משתמשים במדריך הזה להעברה, אתם מוזמנים לשלוח משוב חיובי או שלילי בלחיצה על הלחצן שליחת משוב שלמעלה. המשוב הזה יעזור לנו לשפר את המדריך הזה לאורך זמן.
באופן כללי, התהליך המומלץ הוא תהליך היברידי אוטומטי/ידני. תשתמשו בכלי שקורא חלק מהנתונים של נציג Dialogflow ES, כותב את הנתונים האלה לנציג Dialogflow CX ויוצר רשימת מטלות. לאחר מכן, יוצרים מחדש את סוכן Dialogflow CX המלא באמצעות שיטות מומלצות, רשימת המשימות ונתונים שהועברו על ידי הכלי.
הסבר על Dialogflow CX
לפני שמנסים לבצע את ההעברה הזו, חשוב להבין היטב איך פועלים התהליכים ב-Dialogflow CX. אפשר להתחיל כאן:
מומלץ גם לקרוא מסמכי קונספט נוספים שכוללים תכונות שסביר שתצטרכו בסוכן החדש. כדאי להתמקד בנקודות הבאות:
הסבר על ההבדלים בין Dialogflow ES לבין Dialogflow CX
בסעיף הזה מפורטים ההבדלים החשובים ביותר בין Dialogflow ES לבין Dialogflow CX. כשמבצעים שלבים ידניים של העברת נתונים מאוחר יותר, כדאי לעיין בקטע הזה כדי לקבל הנחיות.
שליטה במבנה ובנתיב השיחה
Dialogflow ES מספק את האפשרויות הבאות לשליטה במבנה ובנתיב השיחה:
- כוונות משמשות כאבני הבניין של הסוכן. בכל שלב בשיחה, מתבצעת התאמה של כוונת המשתמש, ובמובן מסוים, כל כוונת משתמש היא צומת בשיחה.
- ההקשר משמש לשליטה בשיחה. ההקשר משמש כדי לקבוע אילו כוונות יכולות להתאים בכל זמן נתון. ההקשר פג אחרי מספר מסוים של תפניות בשיחה, לכן סוג הבקרה הזה עשוי להיות לא מדויק בשיחות ארוכות.
Dialogflow CX מספק היררכיה של משאבי מבנה ושליטה מדויקת יותר בנתיב השיחה:
- דפים הם צמתי גרף של השיחה. שיחות ב-Dialogflow CX דומות למכונות מצב. בכל נקודה בשיחה, דף אחד פעיל. על סמך קלט או אירועים של משתמשי קצה, יכול להיות שהשיחה תעבור לדף אחר. בדרך כלל, דף נשאר פעיל למשך כמה תורות בשיחה.
- תהליכים הם קבוצות של דפים קשורים. כל זרימת שיחה צריכה לטפל בנושא שיחה ברמה גבוהה.
- רכיבי handler של מצב
משמשים לשליטה במעברים ובתגובות.
יש שלושה סוגים של מטפלי מצב:
- נתיב כוונות: מכיל כוונה שצריך להתאים לה, תגובות אופציונליות ומעבר דף אופציונלי.
- מסלול עם תנאי: מכיל תנאי שצריך להתקיים, תשובות אופציונליות ומעבר אופציונלי לדף.
- Event handler: מכיל שם של אירוע שצריך להפעיל, תגובות אופציונליות ומעבר דף אופציונלי.
- ההיקף משמש כדי לקבוע אם אפשר לקרוא ל-state handler. רוב אמצעי הטיפול משויכים לדף או לזרימה שלמה. אם הדף או התהליך המשויכים פעילים, אז ה-handler נמצא בהיקף, ואפשר לקרוא לו. מסלול ליעד של כוונת Dialogflow CX בהיקף דומה לכוונת Dialogflow ES עם הקשר קלט פעיל.
כשמעצבים את התהליכים והדפים של הסוכן, חשוב להבין את ההמלצות שבקטע התהליכים במדריך לעיצוב סוכנים.
מילוי טופס
ב-Dialogflow ES נעשה שימוש במילוי משבצות כדי לאסוף פרמטרים נדרשים ממשתמש הקצה:
- הפרמטרים האלה הם פרמטרים של כוונות שמסומנים כפרמטרים נדרשים.
- ההתאמה לכוונת המשתמש נמשכת עד שכל הפרמטרים הנדרשים נאספים.
- אתם יכולים להגדיר הנחיה שבה תבקשו ממשתמש הקצה לספק ערך.
Dialogflow CX משתמש במילוי טפסים כדי לאסוף מהמשתמשים את הפרמטרים הנדרשים:
- הפרמטרים האלה משויכים לדף והם נאספים בזמן שהדף פעיל.
- אתם משתמשים בנתיבי תנאים לדפים כדי לקבוע שמילוי הטופס הושלם. בדרך כלל, מסלולי התנאים האלה עוברים לדף אחר.
- אתם יכולים להגדיר הנחיה וגם אמצעים לטיפול בהנחיות חוזרות כדי לטפל בצורה חלקה בכמה ניסיונות לאיסוף ערך.
מעברים
מערכת Dialogflow ES עוברת באופן אוטומטי מכוונה אחת לכוונה הבאה כשקלט של משתמש קצה תואם לכוונה. ההתאמה הזו יכולה להתרחש רק לגבי כוונות שאין להן הקשר קלט או כוונות שיש להן הקשר קלט פעיל.
מערכת Dialogflow CX עוברת מדף אחד לדף הבא כשמטפל במצב בהיקף מסוים עומד בדרישות שלו ומספק יעד מעבר. באמצעות המעברים האלה, תוכלו להנחות את משתמשי הקצה בשיחות בצורה מהימנה. יש כמה דרכים לשלוט במעברים האלה:
- התאמה של כוונות יכולה להפעיל מסלול כוונה.
- אם מתקיים תנאי מסוים, המערכת יכולה להפעיל מסלול תנאי.
- הפעלת אירוע יכולה להפעיל גורם מטפל באירועים.
- אמצעי טיפול בהנחיות חוזרות יכולים לגרום למעבר אם משתמש הקצה לא מצליח לספק ערך אחרי כמה ניסיונות.
- אפשר להשתמש ביעדי מעבר סמליים כיעדי מעבר.
תשובות של סוכנים
תשובות של נציג Dialogflow ES נשלחות למשתמש הקצה כשמתבצעת התאמה ליעד:
- הסוכן יכול לבחור הודעה אחת לתשובה מתוך רשימה של תשובות אפשריות.
- התשובות יכולות להיות ספציפיות לפלטפורמה, ואפשר להשתמש בהן בפורמטים עשירים של תשובות.
- אפשר להשתמש ב-webhook כדי להעביר את התשובות.
התשובות של נציג Dialogflow CX נשלחות למשתמש הקצה כשמתבצעת קריאה ל-fulfillment. בניגוד ל-fulfillment ב-Dialogflow ES, שתמיד כולל webhook, יכול להיות ש-fulfillment ב-Dialogflow CX יכלול קריאה ל-webhook ויכול להיות שלא, בהתאם לשאלה אם הוגדר webhook במשאב ה-fulfillment. התשובות הסטטיות והדינמיות מבוססות על תשובות של webhook ונשלטות על ידי fulfillment. יש כמה דרכים ליצור תשובות של סוכנים:
- אפשר לספק מילוי הזמנה לכל סוג של handler של מצב.
- אפשר לשרשר כמה תגובות במהלך תור לשיחה באמצעות תור התגובות. במקרים מסוימים, התכונה הזו יכולה לפשט את עיצוב הסוכן.
- Dialogflow CX לא תומך בתשובות מובנות שספציפיות לפלטפורמה. עם זאת, הוא מספק סוגי תגובות שונים, כולל מטען ייעודי (payload) מותאם אישית שאפשר להשתמש בו לתגובות ספציפיות לפלטפורמה.
פרמטרים
הפרמטרים של Dialogflow ES כוללים את המאפיינים הבאים:
- מוגדר רק בכוונה.
- ההגדרה מתבצעת על ידי קלט של משתמש קצה, אירועים, webhooks וקריאות API.
- הפרמטר הזה מוזכר בתגובות, בהנחיות לפרמטרים, בקוד של ה-webhook ובערכי הפרמטרים:
- הפורמט הבסיסי של הפניה הוא
$parameter-name. - התמיכה במקורות כוללת את תחביר הסיומות
.original,.partialו-.recent. - אפשר לציין את ההקשר הפעיל בקובצי העזר:
#context-name.parameter-name. - אפשר לציין פרמטרים של אירועים בהפניות:
#event-name.parameter-name.
- הפורמט הבסיסי של הפניה הוא
הפרמטרים של Dialogflow CX כוללים את המאפיינים הבאים:
- מוגדרים בכוונות ובטפסים של דפים.
- פרמטרים של כוונת רכישה ופרמטרים של טופס מועברים לפרמטרים של סשן, שבהם הם זמינים להפניה למשך הסשן.
- ההגדרה מתבצעת באמצעות קלט של משתמש קצה, וווב-הוקים, הגדרה מראש של פרמטרים של ביצוע וקריאות ל-API.
- ההפניה מופיעה בתגובות, בהנחיות לפרמטרים, ב-handlers של הנחיות חוזרות, בהגדרות קבועות מראש של פרמטרים ובקוד של webhook:
- פורמט ההפניה הוא
$session.params.parameter-idלפרמטרים של סשן ו-$intent.params.parameter-idלפרמטרים של Intent. - הפניות לפרמטרים של Intent תומכות בתחביר של הסיומות
.originalו-.resolved. פרמטרים של סשן לא תומכים בתחביר הזה.
- פורמט ההפניה הוא
ישויות מערכת
Dialogflow ES תומך בהרבה ישויות מערכת.
Dialogflow CX תומך בהרבה ישויות מערכת דומות, אבל יש כמה הבדלים. כשמבצעים מיגרציה, צריך לוודא שיש תמיכה בישויות המערכת שבהן משתמשים ב-Dialogflow ES גם ב-Dialogflow CX, באותה שפה. אם לא, צריך ליצור ישויות מותאמות אישית בשבילן.
אירועים
אירועים ב-Dialogflow ES כוללים את המאפיינים הבאים:
- אפשר להפעיל אותו מקריאות ל-API או מ-webhook כדי להתאים כוונה.
- אפשר להגדיר פרמטרים.
- מספר קטן של אירועים מופעלים על ידי פלטפורמות שילוב.
לאירועים ב-Dialogflow CX יש את המאפיינים הבאים:
- אפשר להפעיל אותו משיחות API או מ-webhook כדי לקרוא ל-גורם מטפל באירועים.
- אי אפשר להגדיר פרמטרים.
- אפשר להשתמש בהרבה אירועים מובנים כדי לטפל במקרים הבאים: חוסר קלט ממשתמש הקצה, קלט ממשתמש הקצה שלא מזוהה, פרמטרים שבוטלו על ידי webhook ושגיאות ב-webhook.
- אפשר לשלוט בהפעלות באמצעות אותם כללי היקף כמו במטפלי מצב אחרים.
כוונות מובנות
Dialogflow ES תומך בכוונה המובנית הבאה:
בהמשך מפורטת התמיכה של Dialogflow CX בכוונה מובנית:
- יש תמיכה בכוונות של הודעות פתיחה.
- לא צוינו כוונות חלופיות. במקום זאת, צריך להשתמש באירועי no-match במטפלי אירועים.
- לדוגמאות שליליות, משתמשים בכוונת ברירת המחדל השלילית.
- לא מסופקים אובייקטים מוגדרים מראש של Intent להמשך. צריך ליצור את הכוונות האלה לפי הדרישות של הסוכן. לדוגמה, סביר להניח שתצטרכו ליצור כוונה לטיפול בתשובות שליליות לשאלה של נציג ("לא", "לא תודה", "לא, לא רוצה" וכו'). אפשר להשתמש שוב בכוונה של Dialogflow CX בנציג, ולכן צריך להגדיר אותה רק פעם אחת. שימוש בנתיבי כוונות שונים עבור הכוונות הנפוצות האלה, בהיקפים שונים, מאפשר לכם לשלוט בשיחה בצורה טובה יותר.
תגובות לפעולה מאתר אחר (webhook)
מאפייני ה-webhook של Dialogflow ES:
- אפשר להגדיר שירות webhook אחד לסוכן.
- אפשר לסמן כל כוונה ככזו שמשתמשת ב-webhook.
- אין תמיכה מובנית בטיפול בשגיאות של webhook.
- שמות של פעולות או Intent משמשים ב-Webhook כדי לקבוע מאיפה בסוכן בוצעה הקריאה.
- במסוף יש כלי לעריכה בשורה.
מאפייני ה-webhook של Dialogflow CX:
- אפשר להגדיר כמה שירותי webhook לסוכן.
- אפשר לציין לכל מילוי הזמנה קריאה ל-webhook.
- יש תמיכה מובנית בטיפול בשגיאות של webhook.
- Webhook של מילוי בקשה ב-Dialogflow CX מכיל תג. התג הזה דומה לפעולה ב-Dialogflow ES, אבל הוא משמש רק כשקוראים ל-webhook. שירות ה-webhook יכול להשתמש בתגים האלה כדי לקבוע מאיפה בסוכן הוא נקרא.
- אין בעורך הקוד המובנה של ה-webhook במסוף. אפשר להשתמש בפונקציות Cloud Run, אבל יש עוד הרבה אפשרויות.
כשעוברים ל-Dialogflow CX, צריך לשנות את קוד ה-webhook, כי מאפייני הבקשה והתגובה שונים.
שילובים
השילובים של Dialogflow ES והשילובים של Dialogflow CX תומכים בפלטפורמות שונות. יכול להיות שיהיו הבדלים בהגדרות של פלטפורמות שנתמכות על ידי שני סוגי הסוכנים.
אם השילוב עם Dialogflow ES שבו השתמשתם לא נתמך ב-Dialogflow CX, יכול להיות שתצטרכו לעבור לפלטפורמה אחרת או להטמיע את השילוב בעצמכם.
תכונות נוספות שזמינות רק ב-Dialogflow CX
יש עוד הרבה תכונות שזמינות רק ב-Dialogflow CX. מומלץ להשתמש בתכונות האלה במהלך ההעברה. לדוגמה:
- Advanced NLU
- הגדרות דיבור מתקדמות (רגישות לסיום הדיבור, זמן קצוב לתפוגה כשלא אומרים כלום וכו')
- היסטוריית שינויים
- לוגיקה של משפט תנאי
- קלט DTMF לשילובים של טלפוניה
- ווּבּהוּקים ספציפיים לסביבה
- ניסויים
- קבוצות של מסלולים
- נתונים של סוכן חיפוש
- הגדרות אבטחה (הסתרת מידע ושמירת נתונים)
- פונקציות מערכת לתשובות ולתנאים מתקדמים
- תרחישי בדיקה
- אימות נתוני הסוכן
שיטות מומלצות
לפני המיגרציה, מומלץ לעיין בשיטות המומלצות לעיצוב סוכנים ב-Dialogflow CX. רבות מהשיטות המומלצות האלה ל-Dialogflow CX דומות לשיטות המומלצות ל-Dialogflow ES, אבל חלקן ייחודיות ל-Dialogflow CX.
מידע על כלי ההעברה
כלי ההעברה מעתיק את רוב הנתונים של Dialogflow ES לסוכן Dialogflow CX שלכם, והוא כותב לקובץ TODO עם רשימה של פריטים שצריך להעביר באופן ידני. הכלי מעתיק רק סוגים של ישויות בהתאמה אישית ומשפטים לאימון של Intent. כדאי להתאים אישית את הכלי הזה לצרכים הספציפיים שלכם.
קוד של כלי ההעברה
זה הקוד של הכלי. כדאי לבדוק את הקוד של הכלי הזה כדי להבין מה הוא עושה. יכול להיות שתרצו לשנות את הקוד הזה כדי לטפל במצבים ספציפיים בנציג שלכם. בשלבים הבאים תפעילו את הכלי הזה.
// 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 וסוגי הישויות ב-Dialogflow CX דומים מאוד, ולכן הם סוג הנתונים הכי קל להעברה. הכלי פשוט מעתיק את סוגי הישויות כמו שהם.
העברה של כוונות בכלי
ה-Intents ב-Dialogflow ES וה-Intents ב-Dialogflow CX שונים מאוד.
ה-Intents ב-Dialogflow ES משמשים כאבני הבניין של הסוכן; הם מכילים ביטויי אימון, תגובות, הקשר לשליטה בשיחה, הגדרות של webhook, אירועים, פעולות ופרמטרים של מילוי משבצות.
רוב הנתונים האלה הועברו ב-Dialogflow CX למשאבים אחרים. לכוונות ב-Dialogflow CX יש רק פרמטרים וביטויי אימון, מה שהופך את הכוונות לניתנות לשימוש חוזר בסוכן. הכלי מעתיק רק את שני סוגי נתוני הכוונות האלה לכוונות שלכם ב-Dialogflow CX.
מגבלות של כלי ההעברה
כלי ההעברה לא תומך בפעולות הבאות:
- סוכנים ראשיים: הכלי לא יכול לקרוא מכמה סוכנים משניים, אבל אפשר להפעיל את הכלי כמה פעמים מול כל סוכן משני.
- סוכנים רב-לשוניים: צריך לשנות את הכלי כדי ליצור ביטויים לאימון וערכי ישויות בכמה שפות.
- אימות של ישויות מערכת בשפות שאינן אנגלית: הכלי יוצר פריטים לביצוע כשהוא מוצא ישויות מערכת שלא נתמכות על ידי Dialogflow CX, בהנחה שאנגלית היא שפת ברירת המחדל, ושנעשה שימוש באזור ארה"ב. התמיכה בישויות מערכת משתנה בהתאם לשפה ולאזור. בשפות ובאזורים אחרים, צריך לשנות את הכלי כדי לבצע את הבדיקה הזו.
השלבים החשובים בהעברה
בקטעי המשנה הבאים מפורטים השלבים שצריך לבצע כדי להעביר את הנתונים. לא צריך לבצע את השלבים האלה לפי הסדר, ואולי אפילו תצטרכו לבצע אותם בו-זמנית או בסדר שונה. מומלץ לקרוא את השלבים ולהתחיל לתכנן את השינויים לפני שמבצעים אותם בפועל.
אחרי שמריצים את כלי ההעברה, אפשר לבנות מחדש את הסוכן שלכם ב-Dialogflow CX. עדיין תצטרכו לבצע לא מעט עבודת העברה, אבל רוב הנתונים שהוזנו ידנית יהיו בסוכן שלכם ב-Dialogflow CX ובקובץ TODO.
יצירת סוכן Dialogflow CX
אם עדיין לא עשיתם זאת, צרו סוכן Dialogflow CX. חשוב להשתמש באותה שפת ברירת מחדל כמו הסוכן שלכם ב-Dialogflow ES.
הפעלת כלי ההעברה
כדי להפעיל את הכלי:
- אם עוד לא עשיתם זאת, מתקינים את Go במחשב.
- יוצרים ספרייה לקוד הכלי בשם
migrate. - מעתיקים את קוד הכלי שלמעלה
לקובץ בספרייה הזו שנקרא
main.go. - אם צריך, משנים את הקוד בהתאם למקרה שלכם.
יוצרים מודול Go בספרייה הזו. לדוגמה:
go mod init migrateמתקינים את ספריות הלקוח של Dialogflow ES V2 ו-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 שבה חלק מביטוי לאימון מזכיר פרמטר שלא הוגדר ל-Intent. | זה יכול לקרות אם השתמשתם בעבר ב-Dialogflow ES API כדי ליצור פרמטרים של כוונות באופן שלא תאם לביטויי ההדרכה. כדי לפתור את הבעיה, משנים את השם של הפרמטר ב-Dialogflow ES במסוף, מוודאים שהביטויים לאימון משתמשים בפרמטר בצורה תקינה ולוחצים על 'שמירה'. זה יכול לקרות גם אם הביטויים לאימון מפנים לפרמטרים שלא קיימים. |
אחרי שתתקנו את השגיאות, תצטרכו לנקות את הסוכן של Dialogflow CX מ-Intents ומ-Entities לפני שתריצו שוב את כלי ההעברה.
העברת נתוני כוונות מ-Dialogflow ES ל-Dialogflow CX
הכלי מעביר את הביטויים והפרמטרים לאימון כוונות לכוונות של Dialogflow CX, אבל יש עוד הרבה שדות של כוונות ב-Dialogflow ES שצריך להעביר באופן ידני.
יכול להיות שיהיה צורך בדף Dialogflow CX תואם, בכוונה תואמת של Dialogflow CX או בשניהם.
אם נעשה שימוש בהתאמה של כוונת Dialogflow ES כדי להעביר את השיחה מצומת שיחה מסוים לצומת אחר, צריכים להיות שני דפים בסוכן שקשורים לכוונת הזו:
- הדף המקורי שמכיל את נתיב ההפניה של הכוונה, שיוביל לדף הבא: נתיב ההפניה של הכוונה בדף המקורי עשוי להכיל הודעות ביצוע של Dialogflow CX שדומות לתגובות של כוונות ב-Dialogflow ES. יכול להיות שבדף הזה יהיו הרבה מסלולי כוונות. בזמן שהדף המקורי פעיל, מסלולי הכוונות האלה יכולים להעביר את השיחה למסלולים אפשריים רבים. הרבה כוונות ב-Dialogflow ES ישתפו את אותו דף מקורי תואם ב-Dialogflow CX.
- הדף הבא, שהוא יעד המעבר של נתיב ההפניה של הכוונה בדף המקורי: יכול להיות שיהיו בדף הבא הודעות מילוי של Dialogflow CX, בדומה לתגובות של כוונות ב-Dialogflow ES.
אם יש בכוונה של Dialogflow ES פרמטרים נדרשים, צריך ליצור דף תואם ב-Dialogflow CX עם אותם פרמטרים בטופס.
בדרך כלל, כוונה בדף Dialogflow CX ודף Dialogflow CX חולקים את אותה רשימת פרמטרים, מה שאומר שלכוונה אחת ב-Dialogflow ES יש דף תואם ב-Dialogflow CX וכוונה תואמת ב-Dialogflow CX. כשמתבצעת התאמה של כוונת משתמש ב-Dialogflow CX עם פרמטרים בנתיב של כוונת משתמש, השיחה עוברת בדרך כלל לדף עם אותם פרמטרים. הפרמטרים שחולצו מהתאמה לכוונת המשתמש מועברים לפרמטרים של הסשן, שזמינים למילוי חלקי או מלא של פרמטרים של טופס בדף.
אין ב-Dialogflow CX אובייקטים של Intent חלופיים ואובייקטים של Intent מוגדרים מראש למעקב. מידע נוסף על כוונות מובנות
בטבלה הבאה מוסבר איך למפות נתונים ספציפיים של כוונות מ-Dialogflow ES למשאבים של Dialogflow CX:
| נתוני כוונות ב-Dialogflow ES | נתונים תואמים של Dialogflow CX | נדרשת פעולה |
|---|---|---|
| ביטויים לאימון | ביטויים לאימון כוונות | הועבר באמצעות כלי. הכלי בודק אם יש תמיכה בישויות מערכת, ויוצר פריטים לביצוע (TODO) עבור ישויות מערכת שלא נתמכות. |
| תשובות של סוכנים | הודעות תגובה לביצוע פעולות | תשובות של נציגים |
| הקשר לשליטה בשיחה | ללא | איך שולטים במבנה ובנתיב השיחה |
| הגדרת webhook | הגדרת webhook של אספקה | מידע נוסף על webhooks |
| אירועים | מטפלים באירועים ברמת הזרימה או ברמת הדף | מידע נוסף זמין במאמר בנושא אירועים. |
| פעולות | תגי webhook של הכנה להפצה | מידע נוסף על webhooks |
| פרמטרים | פרמטרים של כוונות ו/או פרמטרים של טופס בדף | הועברו לפרמטרים של כוונת המשתמש לפי כלי. אם הפרמטרים נדרשים, הכלי יוצר פריטים ברשימת המשימות לביצוע, שאפשר להעביר לדף. פרמטרים |
| הנחיות לפרמטרים | הנחיות לפרמטרים של טופס בדף | איך ממלאים טפסים |
יצירת רצפי פעולות
יוצרים תהליך עבודה לכל נושא שיחה ברמה גבוהה. הנושאים בכל תהליך צריכים להיות שונים, כדי שהשיחה לא תעבור כל הזמן בין התהליכים.
אם השתמשתם במגה סוכן, כל סוכן משנה צריך להפוך לזרימת שיחה אחת או יותר.
מתחילים עם נתיבי שיחה בסיסיים
מומלץ לבדוק את הנציג באמצעות הסימולטור בזמן שחוזרים על שינויים. לכן, בשלב מוקדם של השיחה, כדאי להתמקד בנתיבי השיחה הבסיסיים ולבדוק את השינויים שאתם מבצעים. אחרי שתפעילו את אלה, תעברו לנתיבי שיחה מפורטים יותר.
מטפלי מצב ברמת הזרימה לעומת מטפלי מצב ברמת הדף
כשיוצרים רכיבי handler של מצב, כדאי לחשוב אם צריך להחיל אותם ברמת התהליך או ברמת הדף. ה-handler ברמת ה-flow נמצא בהיקף בכל פעם שה-flow (ולכן כל דף ב-flow) פעיל. ההיקף של handler ברמת הדף הוא רק כשהדף הספציפי פעיל. רכיבי handler ברמת התהליך דומים לכוונות ב-Dialogflow ES ללא הקשר קלט. רכיבי handler ברמת הדף דומים לכוונות של Dialogflow ES עם הקשר קלט.
קוד webhook
המאפיינים של בקשת ה-webhook והתגובה שונים ב-Dialogflow CX. מידע נוסף זמין בקטע בנושא webhooks.
מחברי ידע
Dialogflow CX עדיין לא תומך במחברים של מקורות ידע. תצטרכו להטמיע אותן ככוונות רגילות או לחכות עד ש-Dialogflow CX יתמוך במחברים של מאגרי ידע.
הגדרות הסוכן
בודקים את ההגדרות של סוכן Dialogflow ES ומשנים את ההגדרות של סוכן Dialogflow CX לפי הצורך.
שימוש בקובץ TODO
כלי ההעברה יוצר קובץ CSV. הפריטים ברשימה הזו מתמקדים בחלקים ספציפיים של נתונים שעשויים לדרוש התייחסות. מייבאים את הקובץ לגיליון אלקטרוני. לפתור כל פריט בגיליון האלקטרוני, ולהשתמש בעמודה לסימון השלמה.
מיגרציה של השימוש ב-API
אם המערכת שלכם משתמשת ב-Dialogflow ES API לקריאות בזמן ריצה או בזמן עיצוב, תצטרכו לעדכן את הקוד הזה כדי להשתמש ב-Dialogflow CX API. אם אתם משתמשים רק בקריאות לזיהוי כוונות בזמן ריצה, העדכון הזה אמור להיות פשוט למדי.
שילובים
אם הסוכן שלכם משתמש בשילובים, כדאי לעיין בקטע בנושא שילובים ולבצע שינויים לפי הצורך.
פעולות מומלצות להעברה
בקטעי המשנה הבאים מפורטים השלבים המומלצים להעברה.
אימות
כדאי להשתמש באימות נציג כדי לוודא שהנציג פועל לפי השיטות המומלצות.
בדיקה
במהלך ביצוע השלבים שלמעלה להעברה ידנית, מומלץ לבדוק את הסוכן באמצעות הסימולטור. אחרי שמוודאים שהסוכן פועל, צריך להשוות בין שיחות של סוכני Dialogflow ES ו-Dialogflow CX, ולוודא שההתנהגות דומה או משופרת.
במהלך הבדיקה של השיחות האלה באמצעות הסימולטור, כדאי ליצור תרחישי בדיקה כדי למנוע רגרסיות בעתיד.
סביבות
בודקים את סביבות Dialogflow ES ומעדכנים את סביבות Dialogflow CX לפי הצורך.