Mẹo triển khai (Dialogflow)

Hãy xem các mẹo sau đây để triển khai các phương pháp thiết kế cuộc trò chuyện hiệu quả vào Hành động của bạn.

Dự kiến có sự thay đổi

Hãy xử lý yêu cầu này trong mục nhập "Người dùng nói" trong Dialogflow. Ngoài ra, hãy sử dụng nhiều ý định có thể ánh xạ đến cùng một hành động, trong đó mỗi ý định có thể được kích hoạt bằng các nhóm cụm từ "Người dùng nói".

Cung cấp các lời nhắc lại hữu ích và tránh trường hợp không thành công

Đôi khi, Hành động của bạn không thể tiếp tục vì không nhận được dữ liệu đầu vào (còn gọi là dữ liệu không khớp) hoặc không hiểu dữ liệu đầu vào của người dùng (còn gọi là dữ liệu không khớp). Khi điều này xảy ra, trước tiên, Trợ lý sẽ cố gắng xác định xem người dùng có muốn kích hoạt một Hành động khác hay không. Nếu Trợ lý không khớp đầu vào của người dùng với một Hành động khác, thì người dùng sẽ tiếp tục trong ngữ cảnh của Hành động đó. Trường hợp này có thể xảy ra bất cứ lúc nào. Vì vậy, phương pháp hay nhất là xử lý riêng các tình huống không có dữ liệu đầu vào và không khớp ở mỗi lượt trong một cuộc trò chuyện có phương án dự phòng. Khi sử dụng tính năng dự phòng, bạn có thể giúp người dùng trở lại đúng hướng.

Để thực hiện việc này, hãy khởi chạy biến fallbackCount trong đối tượng conv.data và đặt biến đó thành 0. Chuẩn bị một mảng gồm 2 lời nhắc dự phòng (rõ ràng hơn theo từng bước) và một lời nhắc dự phòng cuối cùng để kết thúc cuộc trò chuyện.

Sau đó, hãy tạo một ý định dự phòng (tốt nhất là một ý định cho từng ý định có thể hành động trong tác nhân). Trong trình xử lý ý định, hãy lấy số lượng dự phòng từ đối tượng conv.data rồi tăng số lượng đó lên và nếu nhỏ hơn 3, hãy lấy lời nhắc từ mảng 3. Nếu số lượng từ 4 trở lên, hãy đóng cuộc trò chuyện bằng lời nhắc cuối cùng. Trong mọi ý định không phải là ý định dự phòng, hãy đặt lại số lượng dự phòng về 0. Tốt nhất là bạn nên chuẩn hoá các ý định dự phòng cho những ý định cụ thể tương ứng với các ý định đó.

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

Sẵn sàng trợ giúp bất cứ lúc nào

Tạo một ý định để lắng nghe các cụm từ trợ giúp như "Tôi có thể làm gì?", "bạn có thể cho tôi biết điều gì" hoặc "trợ giúp". Trong ý định này, hãy đưa ra một số phản hồi (xoay vòng) để cung cấp thông tin tổng quan về những việc tác nhân có thể làm và hướng người dùng đến một hành động có thể thực hiện. Tốt nhất là hãy sử dụng các ý định trợ giúp tiếp theo trong Dialogflow để tạo các tình huống trợ giúp khác nhau cho các ý định có thể thực hiện khác nhau.

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

Cho phép người dùng phát lại thông tin

Gói tất cả phương thức app.ask(output) bằng một hàm proxy sẽ thêm đầu ra vào conv.data.lastPrompt. Tạo một ý định lặp lại để lắng nghe lời nhắc lặp lại của người dùng, chẳng hạn như "cái gì?", "nói lại lần nữa" hoặc "bạn có thể nhắc lại điều đó không?". Tạo một mảng tiền tố lặp lại có thể dùng để xác nhận rằng người dùng đã yêu cầu lặp lại nội dung nào đó. Trong trình xử lý ý định lặp lại, hãy gọi ask() bằng một chuỗi nối của tiền tố lặp lại và giá trị conv.data.lastPrompt. Xin lưu ý rằng bạn phải thay đổi mọi thẻ mở SSML nếu được sử dụng trong lời nhắc cuối cùng.

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

Cá nhân hoá cuộc trò chuyện bằng lựa chọn ưu tiên của người dùng

Hành động của bạn có thể yêu cầu người dùng cung cấp các lựa chọn ưu tiên của họ và ghi nhớ các lựa chọn đó để sử dụng sau này, cho phép bạn cá nhân hoá các cuộc trò chuyện trong tương lai với người dùng đó.

Ví dụ về Hành động này cung cấp cho người dùng báo cáo thời tiết cho một mã bưu chính. Mã ví dụ sau đây hỏi người dùng xem họ có muốn Hành động ghi nhớ mã bưu chính cho các cuộc trò chuyện sau này hay không.

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

Sau khi hỏi người dùng xem họ đang dùng mã bưu chính nào trong hộp thoại đầu tiên, bạn có thể bỏ qua lời nhắc đó trong lần gọi tiếp theo và sử dụng cùng một mã bưu chính. Bạn vẫn nên cung cấp một lối thoát (như khối đề xuất cho phép họ chọn một mã bưu chính khác), nhưng bằng cách giảm lượt trò chuyện trong trường hợp phổ biến, bạn sẽ tạo ra được trải nghiệm liền mạch hơn nhiều.

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

Tuỳ chỉnh cho người dùng cũ

Việc duy trì một số trạng thái giữa các cuộc trò chuyện giúp đảm bảo trải nghiệm tự nhiên hơn nhiều cho người dùng cũ. Bước đầu tiên để tạo trải nghiệm này là chào đón người dùng cũ theo cách khác. Ví dụ: bạn có thể giảm bớt lời chào hoặc hiện thông tin hữu ích dựa trên các cuộc trò chuyện trước đây. Để thực hiện việc này, hãy sử dụng thuộc tính AppRequest.User lastSeen sắp đến để xác định xem người dùng có từng tương tác với Hành động của bạn hay không. Nếu thuộc tính lastSeen có trong tải trọng yêu cầu, bạn có thể sử dụng một lời chào khác với bình thường.

Đoạn mã bên dưới sử dụng thư viện ứng dụng Node.js để tìm nạp giá trị của 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();
}

Bạn có thể cải thiện thêm lời chào này bằng cách điều chỉnh phản hồi cho phù hợp với giá trị thực tế của lastSeen. Ví dụ: những người dùng có lượt tương tác gần đây nhất xảy ra nhiều tháng trước khi tương tác hiện tại có thể nhận được lời chào khác với những người dùng đã sử dụng Hành động vào ngày trước đó.

Điều khiển âm lượng trong cuộc trò chuyện

Trên các thiết bị được hỗ trợ, Trợ lý cho phép người dùng điều khiển âm lượng thiết bị trong Hành động trò chuyện bằng cách nói những câu như "tăng âm lượng" hoặc "đặt âm lượng ở mức 50 phần trăm". Nếu bạn có các ý định xử lý các cụm từ huấn luyện tương tự, thì ý định sẽ được ưu tiên. Bạn nên cho phép Trợ lý xử lý các yêu cầu này của người dùng, trừ phi Hành động của bạn có lý do cụ thể.