CustomerFacingMessageSplitter.java 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package com.fs.company.service.workflow.deepdialogue;
  2. import java.util.ArrayList;
  3. import java.util.LinkedHashSet;
  4. import java.util.List;
  5. import java.util.regex.Pattern;
  6. /**
  7. * Split customer-facing text into: main answer first, then each question as its own message.
  8. */
  9. public final class CustomerFacingMessageSplitter {
  10. private static final Pattern SENTENCE_SPLIT = Pattern.compile("[。!??!;\\n]+");
  11. private CustomerFacingMessageSplitter() {
  12. }
  13. /**
  14. * @return non-empty list: index 0 = answer body; rest = follow-up / rhetorical questions
  15. */
  16. public static List<String> split(String reply, String plannedProbe, boolean shouldProbe) {
  17. List<String> out = new ArrayList<>();
  18. if (reply == null || reply.isBlank()) {
  19. appendUnique(out, plannedProbe, shouldProbe);
  20. return out;
  21. }
  22. String body = UniversalDeepDialogueService.stripProbeFromReply(reply, plannedProbe);
  23. List<String> answerParts = new ArrayList<>();
  24. List<String> questionParts = new ArrayList<>();
  25. for (String sentence : SENTENCE_SPLIT.split(body)) {
  26. String part = sentence != null ? sentence.trim() : "";
  27. if (part.isEmpty()) {
  28. continue;
  29. }
  30. if (isQuestionSentence(part)) {
  31. questionParts.add(normalizeQuestion(part));
  32. } else {
  33. answerParts.add(part);
  34. }
  35. }
  36. String answer = String.join("。", answerParts).trim();
  37. if (!answer.isEmpty() && !answer.endsWith("。") && !answer.endsWith("!")
  38. && !answer.endsWith("!") && !answer.endsWith("~") && !answer.endsWith("…")) {
  39. answer = answer + "。";
  40. }
  41. appendUnique(out, answer.isEmpty() ? null : answer.replaceAll("。+$", "。"), false);
  42. LinkedHashSet<String> seen = new LinkedHashSet<>();
  43. if (!out.isEmpty() && out.get(0) != null) {
  44. seen.add(out.get(0));
  45. }
  46. for (String q : questionParts) {
  47. appendUnique(out, q, false, seen);
  48. }
  49. appendUnique(out, plannedProbe, shouldProbe, seen);
  50. return out;
  51. }
  52. /** Keep declarative sentences only; questions are sent as separate follow-up messages. */
  53. public static String extractAnswerOnly(String reply) {
  54. if (reply == null || reply.isBlank()) {
  55. return reply;
  56. }
  57. List<String> parts = split(reply, null, false);
  58. if (parts.isEmpty()) {
  59. return reply.trim();
  60. }
  61. String answer = parts.get(0);
  62. return answer != null && !answer.isBlank() ? answer.trim() : reply.trim();
  63. }
  64. public static List<String> followUps(String reply, String plannedProbe, boolean shouldProbe) {
  65. List<String> all = split(reply, plannedProbe, shouldProbe);
  66. if (all.size() <= 1) {
  67. return List.of();
  68. }
  69. return new ArrayList<>(all.subList(1, all.size()));
  70. }
  71. private static void appendUnique(List<String> out, String text, boolean required) {
  72. appendUnique(out, text, required, null);
  73. }
  74. private static void appendUnique(List<String> out, String text, boolean required, LinkedHashSet<String> seen) {
  75. if (!required && (text == null || text.isBlank())) {
  76. return;
  77. }
  78. if (text == null || text.isBlank()) {
  79. return;
  80. }
  81. String normalized = text.trim();
  82. if (seen != null) {
  83. String key = normalized.length() > 12 ? normalized.substring(0, 12) : normalized;
  84. if (seen.contains(key) || seen.stream().anyMatch(s -> s != null && s.contains(key))) {
  85. return;
  86. }
  87. seen.add(key);
  88. }
  89. out.add(normalized);
  90. }
  91. static boolean isQuestionSentence(String sentence) {
  92. if (sentence == null || sentence.isBlank()) {
  93. return false;
  94. }
  95. String t = sentence.trim();
  96. if (t.endsWith("?") || t.endsWith("?")) {
  97. return true;
  98. }
  99. return t.matches(".*(吗|呢|吗?|呢?|么|啊|吧|嘛|是不是|能不能|可不可以|要不要|有没有|多少|几|哪|谁)[??]?$");
  100. }
  101. private static String normalizeQuestion(String sentence) {
  102. String t = sentence.trim();
  103. if (!t.endsWith("?") && !t.endsWith("?")) {
  104. t = t + "?";
  105. }
  106. return t;
  107. }
  108. }