טיפים להטמעה (Dialogflow)

הטיפים הבאים יעזרו לכם להטמיע שיטות עבודה טובות לעיצוב שיחה בתוך הפעולה.

צפויים וריאציות

טיפול בכך בקטע "המשתמש אומר" קלט ב-Dialogflow. כמו כן, צריך להשתמש ביותר מאישור אחד כוונת רכישה שיכולה למפות לאותה פעולה, שבה ניתן להפעיל כל Intent באמצעות קבוצות שונות של "המשתמש אומר" וביטויים.

לשלוח תגובות מועילות ולהיכשל בחינוּת

לפעמים הפעולה לא יכולה להתקדם כי היא לא קיבלה קלט (נקרא גם 'ללא קלט') או לא הבין את הקלט של המשתמש (שנקרא 'ללא התאמה'). במקרים כאלה, Assistant תנסה קודם לבדוק אם המשתמש רוצה כדי להפעיל פעולה אחרת. אם Assistant לא תואמת לקלט של המשתמש לפעולה אחרת, המשתמש ממשיך בהקשר של הפעולה. תרחיש זה יכול לקרות בכל שלב, ולכן השיטה המומלצת היא לטפל באופן ייחודי מצבים של אי-קלט וחוסר התאמה בכל תור בשיחה עם חלופה. שימוש בחלופות יכול לעזור למשתמשים לחזור לעניינים.

כדי לעשות זאת, צריך לאתחל משתנה fallbackCount באובייקט conv.data, ומגדירים את הערך 0. הכינו מערך של שתי הנחיות חלופיות (שיובילו לטיפול ברמה גבוהה יותר), והצעה חלופית אחרונה לסיום השיחה.

לאחר מכן צריך ליצור Intent חלופי (רצוי אחד לכל כוונה לפעולה סוכן). ב-Intent ב-handler, מאחזרים את מספר החלופות מה-conv.data. של האובייקט, להגדיל אותו, ואם הוא קטן מ-3, לשלוף את ההנחיה מהמערך מתוך 3. אם הספירה היא 4 או יותר, סוגרים את השיחה באמצעות . בכל הכוונות שהן לא חלופות, צריך לאפס את ספירת החלופות ל-0. באופן אידיאלי, כדאי ליצור תבניות של החלופות האלה לכוונות ספציפיות להיות ספציפיות אליהן.

Node.js

const GENERAL_FALLBACK = [
   'Sorry, what was that?',
   'I didn\'t quite get that. I can help you find good local restaurants, what do you want to know about?',
];

const LIST_FALLBACK = [
   'Sorry, what was that?',
   'I didn\'t catch that. Could you tell me which one you prefer?',
];

const FINAL_FALLBACK = 'I\'m sorry I\'m having trouble here. Let\'s talk again later.';

const handleFallback = (conv, promptFetch, callback) => {
 conv.data.fallbackCount = parseInt(conv.data.fallbackCount, 10);
 conv.data.fallbackCount++;
 if (conv.data.fallbackCount > 2) {
   conv.close(promptFetch.getFinalFallbackPrompt());
 } else {
   callback();
 }
}
// Intent handlers below
const generalFallback = (conv) => {
  handleFallback = (conv, promptFetch, () => {
    conv.ask(GENERAL_FALLBACK[conv.data.fallbackCount],
      getGeneralNoInputPrompts());
 });
}

const listFallback = (conv) => {
  handleFallback = (conv, promptFetch, () => {
   conv.ask(LIST_FALLBACK[conv.data.fallbackCount],
       getGeneralNoInputPrompts());
 });
}

const nonFallback = (conv) => {
  conv.data.fallbackCount = 0;
  conv.ask('A non-fallback message here');
}

Java

private static final List<String> GENERAL_FALLBACK =
    Arrays.asList(
        "Sorry, what was that?",
        "I didn\'t quite get that. I can tell you all about IO, like date or location, or about the sessions. What do you want to know about?");
private static final List<String> LIST_FALLBACK =
    Arrays.asList(
        "Sorry, what was that?",
        "I didn\'t catch that. Could you tell me which one you liked?");
private static final List<String> FINAL_FALLBACK =
    Arrays.asList("I\'m sorry I\'m having trouble here. Maybe we should try this again later.");

@ForIntent("General Fallback")
public ActionResponse generalFallback(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  int fallbackCount = (Integer) request.getConversationData().get("fallbackCount");
  fallbackCount++;
  request.getConversationData().put("fallbackCount", fallbackCount);
  if (fallbackCount > 2) {
    responseBuilder.add(getRandomPromptFromList(FINAL_FALLBACK)).endConversation();
  } else {
    responseBuilder.add(getRandomPromptFromList(GENERAL_FALLBACK));
  }
  return responseBuilder.build();
}

private String getRandomPromptFromList(List<String> prompts) {
  Random rand = new Random();
  int i = rand.nextInt(prompts.size());
  return prompts.get(i);
}

@ForIntent("List Fallback")
public ActionResponse listFallback(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  int fallbackCount = (Integer) request.getConversationData().get("fallbackCount");
  fallbackCount++;
  request.getConversationData().put("fallbackCount", fallbackCount);
  if (fallbackCount > 2) {
    responseBuilder.add(getRandomPromptFromList(FINAL_FALLBACK)).endConversation();
  } else {
    responseBuilder.add(getRandomPromptFromList(LIST_FALLBACK));
  }
  return responseBuilder.build();
}

@ForIntent("Non Fallback")
public ActionResponse nonFallback(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  request.getConversationData().put("fallbackCount", 0);
  responseBuilder.add("Non Fallback message");
  return responseBuilder.build();
}

תמיד אפשר לעזור

ליצור כוונה ש מקשיבה לביטויי עזרה כמו "מה אפשר לעשות?", 'מה אפשר לומר לי' או 'עזרה'. בכוונה הזו, יש להציע חלק (מתחלפות) תשובה שמציגה סקירה כללית של מה שהנציג יכול לעשות ומפנה את המשתמשים אל הפעולה האפשרית. באופן אידיאלי, כדאי גם להשתמש בכוונות עזרה נוספות ב-Dialogflow כדי ליצור תרחישי עזרה שונים לכוונות פעולה שונות.

Node.js

const HELP_PROMPTS = [
   'There\'s a lot you might want to know about the local restaurants, and I can tell you all about it, like where it is and what kind of food they have. What do you want to know?',
   'I\'m here to help, so let me know if you need any help figuring out where or what to eat. What do you want to know?',
];

// Intent handler
const help = (conv) => {
 reply(conv, promptFetch.getHelpPrompt(), // fetches random entry from HELP_PROMPTS
     promptFetch.getGeneralNoInputPrompts());
}

Java

private static final List<String> HELP_PROMPTS =
    Arrays.asList(
        "There's a lot you might want to know about IO, and I can tell you all about it, like where it is and what the sessions are. What do you want to know?",
        "IO can be a little overwhelming, so I\'m here to help. Let me know if you need any help figuring out the event, like when it is, or what the sessions are. What do you want to know?");

@ForIntent("Help")
public ActionResponse help(ActionRequest request) {
  return getResponseBuilder(request).add(getRandomPromptFromList(HELP_PROMPTS)).build();
}

המשתמשים יכולים להפעיל מחדש את המידע

כוללים את כל השיטות של app.ask(output) באמצעות פונקציית proxy שמוסיפה את הפלט אל conv.data.lastPrompt. יצירה של אובייקט Intent שחוזר על עצמו, שמאזינים הודעות שהמשתמש צריך לחזור עליהן, כמו "מה?", "תגיד שוב" או "האם אתה יכול לחזור על זה?". יצירת מערך של תחיליות חוזרות שניתן להשתמש בהן כדי מאשרים שהמשתמש ביקש שמשהו יחזרו על עצמו. חוזרים handler של Intent, מפעילים את ask() עם מחרוזת משורשרת של הקידומת של החזרה הערך של conv.data.lastPrompt. חשוב לזכור שתצטרכו לשנות תגים פותחים SSML, אם נעשה בהם שימוש בהנחיה האחרונה.

Node.js

const REPEAT_PREFIX = [
    'Sorry, I said ',
    'Let me repeat that. ',
];

const reply = (conv, inputPrompt, noInputPrompts) => {
  conv.data.lastPrompt = inputPrompt;
  conv.data.lastNoInputPrompts = noInputPrompts;
  conv.ask(inputPrompt, noInputPrompts);
}
// Intent handlers
const normalIntent = (conv) => {
  reply(conv, 'Hey this is a question', SOME_NO_INPUT_PROMPTS);
}

const repeat = (conv) => {
  let repeatPrefix = promptFetch.getRepeatPrefix(); // randomly chooses from REPEAT_PREFIX
  // Move SSML start tags over
  if (conv.data.lastPrompt.startsWith(promptFetch.getSSMLPrefix())) {
    conv.data.lastPrompt =
        conv.data.lastPrompt.slice(promptFetch.getSSMLPrefix().length);
    repeatPrefix = promptFetch.getSSMLPrefix() + repeatPrefix;
  }
  conv.ask(repeatPrefix + conv.data.lastPrompt,
      conv.data.lastNoInputPrompts);
}

Java

private final List<String> REPEAT_PREFIX = Arrays.asList("Sorry, I said ", "Let me repeat that.");

private final String SsmlPrefix = "<speak>";

@ForIntent("Normal Intent")
public ActionResponse normalIntent(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  responseBuilder.getConversationData().put("lastPrompt", "Hey this is a question");
  return responseBuilder.build();
}

@ForIntent("repeat")
public ActionResponse repeat(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  String repeatPrefix = getRandomPromptFromList(REPEAT_PREFIX);
  // Move SSML start tags over
  String lastPrompt = (String) responseBuilder.getConversationData().get("lastPrompt");
  if (lastPrompt.startsWith(SsmlPrefix)) {
    String newLastPrompt = lastPrompt.substring(SsmlPrefix.length());
    responseBuilder.getConversationData().put("lastPrompt", newLastPrompt);
    repeatPrefix = SsmlPrefix + repeatPrefix;
  }
  responseBuilder.add(repeatPrefix + lastPrompt);
  return responseBuilder.build();
}

להתאים אישית את השיחה לפי העדפות המשתמש

הפעולה יכולה לבקש מהמשתמשים את ההעדפות שלהם ולזכור אותן לשימוש מאוחר יותר, כדי לאפשר לך להתאים אישית שיחות עתידיות עם אותו משתמש.

הפעולה לדוגמה הזו מספקת למשתמשים דיווח על מזג האוויר באזור המיקוד. הבאים הקוד לדוגמה שואל את המשתמש אם הוא רוצה שהפעולה תזכור את המיקוד שלו לשיחות מאוחרות יותר.

Node.js

app.intent('weather_report', (conv) => {
  let zip = conv.arguments.get('zipcode');
  conv.data.zip = zip;
  conv.ask(getWeatherReport(zip));
  conv.ask(new Confirmation(`Should I remember ${zip} for next time?`));
});

app.intent('remember_zip', (conv, params, confirmation) => {
  if (confirmation) {
    conv.user.storage.zip = conv.data.zip;
    conv.close('Great! See you next time.');
  } else conv.close('Ok, no problem.');
});

Java

@ForIntent("weather_report")
public ActionResponse weatherReport(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  String zip = (String) request.getArgument("location").getStructuredValue().get("zipCode");
  responseBuilder.getConversationData().put("zip", zip);
  responseBuilder.add(getWeatherReport(zip));
  responseBuilder.add(
      new Confirmation().setConfirmationText("Should I remember " + zip + " for next time?"));
  return responseBuilder.build();
}

@ForIntent("remember_zip")
public ActionResponse rememberZip(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (request.getUserConfirmation()) {
    responseBuilder.getUserStorage().put("zip", responseBuilder.getConversationData().get("zip"));
    responseBuilder.add("Great! See you next time.").endConversation();
  } else {
    responseBuilder.add("Ok, no problem.").endConversation();
  }
  return responseBuilder.build();
}

אחרי ששאלת את המשתמש באיזה מיקוד הוא נמצא בתיבת הדו-שיח הראשונה, הם יכולים לדלג על ההנחיה הבאה בהפעלה הבאה שלהם ולהשתמש באותו מיקוד. עדיין צריך לספק נתיב בריחה (כמו צ'יפ הצעה שמאפשר זאת) לבחור מיקוד אחר), אבל על ידי צמצום תור השיחה במקרה כזה, יוצרים חוויה חלקה יותר.

Node.js

app.intent('weather_report', (conv) => {
  let zip = conv.arguments.get('zipcode');
  if (zip) {
    conv.close(getWeatherReport(zip));
  } else if (conv.user.storage.zip) {
    conv.ask(new SimpleResponse(getWeatherReport(conv.user.storage.zip)));
    conv.ask(new Suggestions('Try another zipcode'));
  } else {
    conv.ask('What\'s your zip code?');
  }
});

app.intent('provide_zip_df', (conv) => {
  conv.user.storage.zip = conv.arguments.get('zipcode');
  conv.close(getWeatherReport(conv.user.storage.zip));
});

Java

public ActionResponse weatherReport2(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  String zip = (String) request.getArgument("location").getStructuredValue().get("zipCode");
  if (zip != null) {
    responseBuilder.add(getWeatherReport(zip)).endConversation();
  } else if ((zip = (String) responseBuilder.getUserStorage().get("zip")) != null) {
    responseBuilder.add(new SimpleResponse().setTextToSpeech(getWeatherReport(zip)));
    responseBuilder.add(new Suggestion().setTitle("Try another zipcode"));
  } else {
    responseBuilder.add("What's your zip code?");
  }
  return responseBuilder.build();
}

התאמה אישית למשתמשים חוזרים

שמירה על מצב כלשהו בין השיחות מבטיחה שהשיחה תהיה טבעית יותר למשתמשים חוזרים. השלב הראשון ביצירת החוויה הזו הוא לברך משתמשים חוזרים באופן שונה. לדוגמה, אפשר להקיש על הודעת הפתיחה או להציג מידע שימושי על סמך שיחות קודמות. כדי לעשות את זה משתמשים נכס AppRequest.User lastSeen נכנס כדי לקבוע אם המשתמש קיים אינטראקציה עם הפעולה שלך בעבר. אם המאפיין lastSeen כלול במטען הייעודי (payload) של הבקשה, אפשר להשתמש בפתיח שונה מהרגיל.

הקוד שבהמשך משתמש בספריית הלקוח של Node.js כדי לאחזר את הערך של last.seen

Node.js

// This function is used to handle the welcome intent
// In Dialogflow, the Default Welcome Intent ('input.welcome' action)
// In Actions SDK, the 'actions.intent.MAIN' intent
const welcome = (conv) => {
  if (conv.user.last.seen) {
    conv.ask(`Hey you're back...`);
  } else {
    conv.ask('Welcome to World Cities Trivia!...');
  }
}

Java

// This function is used to handle the welcome intent
// In Dialogflow, the Default Welcome Intent ('input.welcome' action)
// In Actions SDK, the 'actions.intent.MAIN' intent
public ActionResponse welcome(ActionRequest request) {
  ResponseBuilder responseBuilder = getResponseBuilder(request);
  if (request.getUser().getLastSeen() != null) {
    responseBuilder.add("Hey you're back...");
  } else {
    responseBuilder.add("Welcome to Number Genie!...");
  }
  return responseBuilder.build();
}

אפשר לשפר עוד יותר את הפתיח הזה על ידי התאמת התשובה של lastSeen. למשל, משתמשים שהאינטראקציה האחרונה שלהם התרחשה חודשים לפני שהאינטראקציה הנוכחית עשויה לקבל פתיח שונה מזה של אנשים שהשתמשו בפעולה יום קודם לכן.

שליטה בעוצמת הקול במהלך שיחה

במכשירים נתמכים, Assistant מאפשרת למשתמשים לשלוט בעוצמת הקול של המכשיר מתוך כדי לבצע פעולות בשיחה, אפשר לומר דברים כמו "Turn the volume up" או "set the עוצמת קול ל-50 אחוזים". אם יש לכם כוונות שמטפלות בביטויים דומים של אימון, לכוונות שלכם יש עדיפות. מומלץ לאפשר ל-Assistant לטפל בבעיות האלה משתמשים מבקשים, אלא אם לפעולה יש סיבה ספציפית לכך.