نصائح التنفيذ (Dialogflow)

مراجعة النصائح التالية لتنفيذ ممارسات تصميم المحادثات الجيدة إلى الإجراء الخاص بك.

توقع اختلافات

التعامل مع هذا الإجراء في حقل "يقول المستخدم" البيانات في Dialogflow. كذلك، استخدم أكثر من عنصر النية التي يمكن ربطها بالإجراء نفسه، حيث يمكن تشغيل كل نية باستخدام مجموعات مختلفة من "يقول المستخدم" عبارات.

تقديم ردود مطالبة مفيدة والإخفاق بشكل رشيق

في بعض الأحيان، يتعذر على الإجراء التقدم للأمام لأنه لم يتلقَ إدخال (يُعرَف باسم no-input) أو لم يفهم إدخال المستخدم (يُعرف باسم "عدم التطابق") وعند حدوث ذلك، يحاول "مساعد Google" أولاً تحديد ما إذا كان المستخدم يريد لتشغيل إجراء مختلف. في حال لم يطابق "مساعد Google" البيانات التي أدخلها المستخدم إلى إجراء آخر، يستمر المستخدم في سياق الإجراء الخاص بك. يمكن أن يحدث هذا السيناريو في أي وقت، لذا فإن أفضل الممارسات هي التعامل مع المواقف التي لا يمكن فيها الإدخال وغير المطابقة عند كل منعطف في المحادثة مع عنصر احتياطي. باستخدام الإجراءات الاحتياطية، يمكنك مساعدة المستخدمين على العودة إلى المسار الصحيح.

ولإجراء ذلك، عليك إعداد متغيّر fallbackCount في الكائن conv.data، وتعيينه على 0. إعداد صفيف من مطالبتين احتياطيين (تصعيد الوضوح) ومطالبة احتياطية أخيرة تُنهي المحادثة.

بعد ذلك، أنشئ هدفًا احتياطياً (من الأفضل أن يكون واحدًا لكل هدف قابل للتنفيذ في وكيل المستخدم). في معالج intent، اسحب العدد الاحتياطي من 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) باستخدام دالة خادم وكيل تضيف إلى conv.data.lastPrompt. أنشئ نية متكررة يستمع إلى الطلبات بتكرارها من المستخدم مثل "ماذا؟"، "قول ذلك مرة أخرى"، أو "هل أكرر ذلك؟". إنشاء مصفوفة من البادئات المتكررة التي يمكن استخدامها الإقرار بأن المستخدم طلب تكرار شيء ما. في التكرار معالج 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 في حمولة الطلب، يمكنك استخدام رسالة ترحيب مختلفة عن المعتاد.

يستخدم الكود أدناه مكتبة برامج 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. على سبيل المثال، المستخدمون الذين حدث تفاعلهم الأخير عدة مرات قبل أشهر من قد يتلقى التفاعل الحالي تحية مختلفة عن لأولئك الذين استخدموا المهمّة في اليوم السابق.

التحكّم في مستوى الصوت أثناء المحادثة

على الأجهزة المتوافقة، يتيح "مساعد Google" للمستخدمين التحكّم في مستوى صوت الجهاز ضمن إجراء تحاوري من خلال قول عبارات مثل "رفع مستوى الصوت" أو "ضبط إلى 50 في المئة". إذا كانت لديك أغراض تتعلق بعبارات تدريبية مماثلة، فإن نواياك تكون لها الأولوية. ننصح بالسماح لخدمة "مساعد Google" بالتعامل مع هذه الأمور. طلبات المستخدمين، ما لم يكن للإجراء الخاص بك سبب محدّد لذلك.