From 8edb5cab16bd71231c9d3b5284c5dd367a2e68d7 Mon Sep 17 00:00:00 2001 From: liu Date: Wed, 25 Oct 2023 18:00:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E7=94=9F=E6=88=90rasa?= =?UTF-8?q?=E7=9A=84yml=E6=96=87=E4=BB=B6=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- domain.yml | 33 +++ nlu.yml | 28 +++ pom.xml | 2 +- rules.yml | 28 +++ .../handler/StringListTypeHandler.java | 27 +++ .../com/supervision/model/AskQuestion.java | 5 - .../model/ConfigAncillaryItem.java | 10 +- .../supervision/model/ConfigPhysicalTool.java | 11 +- .../resources/mapper/AskQuestionMapper.xml | 3 +- .../mapper/ConfigAncillaryItemMapper.xml | 1 + .../mapper/ConfigPhysicalToolMapper.xml | 1 + virtual-patient-web/pom.xml | 6 + .../supervision/controller/AskController.java | 18 +- .../controller/RasaController.java | 22 ++ .../controller/TestController.java | 13 + .../com/supervision/pojo/rasa/RasaReqDTO.java | 13 + .../com/supervision/pojo/rasa/RasaResDTO.java | 11 + .../rasa/train/intent/DomainYmlTemplate.java | 28 +++ .../pojo/rasa/train/intent/Intent.java | 17 ++ .../pojo/rasa/train/intent/NluTemplate.java | 11 + .../rasa/train/intent/NluYmlTemplate.java | 23 ++ .../rasa/train/intent/RuleYmlTemplate.java | 44 ++++ .../com/supervision/service/AskService.java | 3 + .../com/supervision/service/RasaService.java | 6 + .../service/impl/AskServiceImpl.java | 19 ++ .../service/impl/RasaServiceImpl.java | 228 ++++++++++++++++++ .../src/main/resources/application.yml | 4 +- .../src/main/resources/templates/domain.ftl | 23 ++ .../src/main/resources/templates/nlu.ftl | 10 + .../src/main/resources/templates/rules.ftl | 12 + 30 files changed, 640 insertions(+), 20 deletions(-) create mode 100644 domain.yml create mode 100644 nlu.yml create mode 100644 rules.yml create mode 100644 virtual-patient-model/src/main/java/com/supervision/handler/StringListTypeHandler.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/controller/RasaController.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/pojo/rasa/RasaReqDTO.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/pojo/rasa/RasaResDTO.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/DomainYmlTemplate.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/Intent.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/NluTemplate.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/NluYmlTemplate.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/RuleYmlTemplate.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/service/RasaService.java create mode 100644 virtual-patient-web/src/main/java/com/supervision/service/impl/RasaServiceImpl.java create mode 100644 virtual-patient-web/src/main/resources/templates/domain.ftl create mode 100644 virtual-patient-web/src/main/resources/templates/nlu.ftl create mode 100644 virtual-patient-web/src/main/resources/templates/rules.ftl diff --git a/domain.yml b/domain.yml new file mode 100644 index 00000000..7ca48fdc --- /dev/null +++ b/domain.yml @@ -0,0 +1,33 @@ +version: "3.1" + +intents: + - self_introduction + - goodbye + - greet + - ask_bushufu + +responses: + utter_self_introduction: + - text: "再见" + utter_goodbye: + - text: "你好医生" + utter_greet: + - text: "你好" + utter_ask_bushufu: + - text: "我最近感觉心跳特别快,喘不上气。" + utter_tool_tool_shizhen: + - text: "---tool---1" + utter_tool_tool_huxi: + - text: "---tool---10" + +actions: + - utter_self_introduction + - utter_goodbye + - utter_greet + - utter_ask_bushufu + - utter_tool_tool_shizhen + - utter_tool_tool_huxi + +session_config: + session_expiration_time: 60 + carry_over_slots_to_new_session: true diff --git a/nlu.yml b/nlu.yml new file mode 100644 index 00000000..fc043472 --- /dev/null +++ b/nlu.yml @@ -0,0 +1,28 @@ +version: "3.1" + +nlu: + - intent: greet + examples: | + - 你好 + - 你好啊 + - 你好你好 + - intent: goodbye + examples: | + - 再见 + - 拜拜 + - intent: self_introduction + examples: | + - 我是张医生 + - intent: ask_bushufu + examples: | + - 今天您有什么不舒服?哪里不舒服? + - intent: tool_tool_shizhen + examples: | + - 1 + - 22 + - 333 + - intent: tool_tool_huxi + examples: | + - 1 + - 22 + - 333 diff --git a/pom.xml b/pom.xml index 99788073..2d86683f 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ 2.3.5.RELEASE 1.2.76 3.3.1 - 1.1.10 + 1.1.22 5.8.16 3.0.3 diff --git a/rules.yml b/rules.yml new file mode 100644 index 00000000..fb074ea2 --- /dev/null +++ b/rules.yml @@ -0,0 +1,28 @@ +version: "3.1" + +rules: + + - rule: 自我介绍 + steps: + - intent: self_introduction + - action: utter_self_introduction + - rule: 再见 + steps: + - intent: goodbye + - action: utter_goodbye + - rule: 问候 + steps: + - intent: greet + - action: utter_greet + - rule: 问不舒服 + steps: + - intent: ask_bushufu + - action: utter_ask_bushufu + - rule: 视诊 + steps: + - intent: tool_tool_shizhen + - action: utter_tool_tool_shizhen + - rule: 呼吸 + steps: + - intent: tool_tool_huxi + - action: utter_tool_tool_huxi diff --git a/virtual-patient-model/src/main/java/com/supervision/handler/StringListTypeHandler.java b/virtual-patient-model/src/main/java/com/supervision/handler/StringListTypeHandler.java new file mode 100644 index 00000000..d5f8c28a --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/handler/StringListTypeHandler.java @@ -0,0 +1,27 @@ +package com.supervision.handler; + +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.util.List; + +public class StringListTypeHandler extends JacksonTypeHandler { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + + public StringListTypeHandler(Class type) { + super(type); + } + + @Override + protected Object parse(String json) { + try { + return objectMapper.readValue(json, new TypeReference>() {}); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/virtual-patient-model/src/main/java/com/supervision/model/AskQuestion.java b/virtual-patient-model/src/main/java/com/supervision/model/AskQuestion.java index d5dae4ad..3177a7bb 100644 --- a/virtual-patient-model/src/main/java/com/supervision/model/AskQuestion.java +++ b/virtual-patient-model/src/main/java/com/supervision/model/AskQuestion.java @@ -26,11 +26,6 @@ public class AskQuestion implements Serializable { */ private String intentId; - /** - * 编码 - */ - private String code; - /** * 问题 */ diff --git a/virtual-patient-model/src/main/java/com/supervision/model/ConfigAncillaryItem.java b/virtual-patient-model/src/main/java/com/supervision/model/ConfigAncillaryItem.java index d0b6c31a..bbb36886 100644 --- a/virtual-patient-model/src/main/java/com/supervision/model/ConfigAncillaryItem.java +++ b/virtual-patient-model/src/main/java/com/supervision/model/ConfigAncillaryItem.java @@ -4,15 +4,20 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; + import java.io.Serializable; import java.time.LocalDateTime; +import java.util.List; + +import com.supervision.handler.StringListTypeHandler; import lombok.Data; /** * 辅助检查项目配置表 + * * @TableName vp_config_ancillary_item */ -@TableName(value ="vp_config_ancillary_item") +@TableName(value = "vp_config_ancillary_item", autoResultMap = true) @Data public class ConfigAncillaryItem implements Serializable { /** @@ -36,6 +41,9 @@ public class ConfigAncillaryItem implements Serializable { */ private String info; + @TableField(typeHandler = StringListTypeHandler.class) + private List callOutQuestion; + /** * 创建人ID */ diff --git a/virtual-patient-model/src/main/java/com/supervision/model/ConfigPhysicalTool.java b/virtual-patient-model/src/main/java/com/supervision/model/ConfigPhysicalTool.java index 7873b563..109cee99 100644 --- a/virtual-patient-model/src/main/java/com/supervision/model/ConfigPhysicalTool.java +++ b/virtual-patient-model/src/main/java/com/supervision/model/ConfigPhysicalTool.java @@ -1,18 +1,20 @@ package com.supervision.model; -import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; +import com.supervision.handler.StringListTypeHandler; +import lombok.Data; + import java.io.Serializable; import java.time.LocalDateTime; -import lombok.Data; +import java.util.List; /** * 体格检查工具配置表 * @TableName vp_config_physical_tool */ -@TableName(value ="vp_config_physical_tool") +@TableName(value ="vp_config_physical_tool",autoResultMap = true) @Data public class ConfigPhysicalTool implements Serializable { /** @@ -41,6 +43,9 @@ public class ConfigPhysicalTool implements Serializable { */ private Integer requireLocation; + @TableField(typeHandler = StringListTypeHandler.class) + private List callOutQuestion; + /** * 创建人ID */ diff --git a/virtual-patient-model/src/main/resources/mapper/AskQuestionMapper.xml b/virtual-patient-model/src/main/resources/mapper/AskQuestionMapper.xml index 9a226c1b..380f365a 100644 --- a/virtual-patient-model/src/main/resources/mapper/AskQuestionMapper.xml +++ b/virtual-patient-model/src/main/resources/mapper/AskQuestionMapper.xml @@ -7,7 +7,6 @@ - @@ -16,7 +15,7 @@ - id,intent_id,code, + id,intent_id question,create_user_id,create_time, update_user_id,update_time diff --git a/virtual-patient-model/src/main/resources/mapper/ConfigAncillaryItemMapper.xml b/virtual-patient-model/src/main/resources/mapper/ConfigAncillaryItemMapper.xml index 49953d48..d7b56462 100644 --- a/virtual-patient-model/src/main/resources/mapper/ConfigAncillaryItemMapper.xml +++ b/virtual-patient-model/src/main/resources/mapper/ConfigAncillaryItemMapper.xml @@ -9,6 +9,7 @@ + diff --git a/virtual-patient-model/src/main/resources/mapper/ConfigPhysicalToolMapper.xml b/virtual-patient-model/src/main/resources/mapper/ConfigPhysicalToolMapper.xml index 6a4617da..290923c8 100644 --- a/virtual-patient-model/src/main/resources/mapper/ConfigPhysicalToolMapper.xml +++ b/virtual-patient-model/src/main/resources/mapper/ConfigPhysicalToolMapper.xml @@ -10,6 +10,7 @@ + diff --git a/virtual-patient-web/pom.xml b/virtual-patient-web/pom.xml index c6b9d5d9..a11baa56 100644 --- a/virtual-patient-web/pom.xml +++ b/virtual-patient-web/pom.xml @@ -34,6 +34,12 @@ org.springframework.boot spring-boot-starter-websocket + + + org.freemarker + freemarker + 2.3.31 + diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/AskController.java b/virtual-patient-web/src/main/java/com/supervision/controller/AskController.java index 26bfb095..93b4ea89 100644 --- a/virtual-patient-web/src/main/java/com/supervision/controller/AskController.java +++ b/virtual-patient-web/src/main/java/com/supervision/controller/AskController.java @@ -13,6 +13,7 @@ import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import java.io.IOException; +import java.util.List; @Api(tags = "问诊") @RestController @@ -38,17 +39,20 @@ public class AskController { @ApiOperation("回复语音及文字消息") @GetMapping("replyVoice") - public ReplyVoiceResVO replyVoice(){ - return askService.replyVoice(); - } - - @ApiOperation("查询对话历史") - public void queryAskHistory(String processId){ - + public ReplyVoiceResVO replyVoice() { + return askService.replyVoice(); } + @ApiOperation("进行对话") + @GetMapping("conversation") + public List conversation(String question) { + return askService.conversation(question); + } + @ApiOperation("查询对话历史") + public void queryAskHistory(String processId) { + } } diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/RasaController.java b/virtual-patient-web/src/main/java/com/supervision/controller/RasaController.java new file mode 100644 index 00000000..5c63ec87 --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/controller/RasaController.java @@ -0,0 +1,22 @@ +package com.supervision.controller; + +import com.supervision.service.RasaService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("rasa") +@RequiredArgsConstructor +public class RasaController { + + private final RasaService rasaService; + + @GetMapping("generateRasaYml") + public void generateRasaYml(String diseaseId){ + rasaService.generateRasaYml(diseaseId); + } + + +} diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/TestController.java b/virtual-patient-web/src/main/java/com/supervision/controller/TestController.java index 94dbab9b..dcfc118f 100644 --- a/virtual-patient-web/src/main/java/com/supervision/controller/TestController.java +++ b/virtual-patient-web/src/main/java/com/supervision/controller/TestController.java @@ -1,15 +1,28 @@ package com.supervision.controller; +import com.supervision.model.ConfigPhysicalTool; +import com.supervision.service.ConfigPhysicalToolService; +import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.List; + @RestController @RequestMapping("test") +@RequiredArgsConstructor public class TestController { + private final ConfigPhysicalToolService configPhysicalToolService; + @GetMapping("testExpireTime") public String testExpireTime() { return "OK"; } + + @GetMapping("testQueryJSON") + public List testQueryJSON(){ + return configPhysicalToolService.list(); + } } diff --git a/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/RasaReqDTO.java b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/RasaReqDTO.java new file mode 100644 index 00000000..0634dce5 --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/RasaReqDTO.java @@ -0,0 +1,13 @@ +package com.supervision.pojo.rasa; + +import lombok.Data; + +@Data +public class RasaReqDTO { + + private String sender; + + private String message; + + +} diff --git a/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/RasaResDTO.java b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/RasaResDTO.java new file mode 100644 index 00000000..5b73187e --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/RasaResDTO.java @@ -0,0 +1,11 @@ +package com.supervision.pojo.rasa; + +import lombok.Data; + +@Data +public class RasaResDTO { + + private String recipient_id; + + private String text; +} diff --git a/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/DomainYmlTemplate.java b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/DomainYmlTemplate.java new file mode 100644 index 00000000..2c1f381c --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/DomainYmlTemplate.java @@ -0,0 +1,28 @@ +package com.supervision.pojo.rasa.train.intent; + +import lombok.Data; + +import java.util.LinkedHashMap; +import java.util.List; + +@Data +public class DomainYmlTemplate { + + private List intents; + + private LinkedHashMap> responses; + + private List actions; + + private SessionConfig session_config = new SessionConfig(); + + + @Data + public static class SessionConfig{ + + private final int session_expiration_time = 60; + + private final Boolean carry_over_slots_to_new_session = true; + } + +} diff --git a/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/Intent.java b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/Intent.java new file mode 100644 index 00000000..eb7bd9a8 --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/Intent.java @@ -0,0 +1,17 @@ +package com.supervision.pojo.rasa.train.intent; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Intent { + + private String id; + + private String code; + + private String desc; +} diff --git a/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/NluTemplate.java b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/NluTemplate.java new file mode 100644 index 00000000..c0e45a5d --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/NluTemplate.java @@ -0,0 +1,11 @@ +package com.supervision.pojo.rasa.train.intent; + +import lombok.Data; + +import java.util.List; + +@Data +public class NluTemplate { + + +} diff --git a/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/NluYmlTemplate.java b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/NluYmlTemplate.java new file mode 100644 index 00000000..e4eb283c --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/NluYmlTemplate.java @@ -0,0 +1,23 @@ +package com.supervision.pojo.rasa.train.intent; + +import lombok.Data; +import org.yaml.snakeyaml.nodes.Tag; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +@Data +public class NluYmlTemplate { + + private List nlu; + + @Data + public static class Nlu{ + private String intent; + + private List examples; + } + + +} diff --git a/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/RuleYmlTemplate.java b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/RuleYmlTemplate.java new file mode 100644 index 00000000..47572cb2 --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/pojo/rasa/train/intent/RuleYmlTemplate.java @@ -0,0 +1,44 @@ +package com.supervision.pojo.rasa.train.intent; + +import cn.hutool.core.map.MapUtil; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.*; + +@Data +public class RuleYmlTemplate { + + private List rules; + + + @Data + public static class Rule { + private String rule; + + private List steps; + + public Rule() { + } + + public Rule(String rule, String intent, String action) { + this.rule = rule; + steps = new ArrayList<>(); + Step step = new Step(intent, action); + steps.add(step); + } + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Step { + private String intent; + + private String action; + + + } + +} diff --git a/virtual-patient-web/src/main/java/com/supervision/service/AskService.java b/virtual-patient-web/src/main/java/com/supervision/service/AskService.java index 8ec5ccb3..b975aae3 100644 --- a/virtual-patient-web/src/main/java/com/supervision/service/AskService.java +++ b/virtual-patient-web/src/main/java/com/supervision/service/AskService.java @@ -6,6 +6,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.List; public interface AskService { @@ -13,5 +14,7 @@ public interface AskService { ReplyVoiceResVO replyVoice() ; + List conversation(String question); + } diff --git a/virtual-patient-web/src/main/java/com/supervision/service/RasaService.java b/virtual-patient-web/src/main/java/com/supervision/service/RasaService.java new file mode 100644 index 00000000..3396a3be --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/service/RasaService.java @@ -0,0 +1,6 @@ +package com.supervision.service; + +public interface RasaService { + + void generateRasaYml(String diseaseId); +} diff --git a/virtual-patient-web/src/main/java/com/supervision/service/impl/AskServiceImpl.java b/virtual-patient-web/src/main/java/com/supervision/service/impl/AskServiceImpl.java index e6d9ec84..8ad72e22 100644 --- a/virtual-patient-web/src/main/java/com/supervision/service/impl/AskServiceImpl.java +++ b/virtual-patient-web/src/main/java/com/supervision/service/impl/AskServiceImpl.java @@ -13,6 +13,8 @@ import com.supervision.pojo.paddlespeech.req.TtsReqDTO; import com.supervision.pojo.paddlespeech.res.AsrResultDTO; import com.supervision.pojo.paddlespeech.res.PaddleSpeechResDTO; import com.supervision.pojo.paddlespeech.res.TtsResultDTO; +import com.supervision.pojo.rasa.RasaReqDTO; +import com.supervision.pojo.rasa.RasaResDTO; import com.supervision.pojo.vo.ReplyVoiceResVO; import com.supervision.service.AskService; import lombok.RequiredArgsConstructor; @@ -21,6 +23,9 @@ import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -33,6 +38,9 @@ public class AskServiceImpl implements AskService { @Value("${paddle-speech.asr}") private String asr; + @Value("${rasa.url}") + private String rasa; + private final ObjectMapper objectMapper = new ObjectMapper(); @Override @@ -82,4 +90,15 @@ public class AskServiceImpl implements AskService { } } + + @Override + public List conversation(String question) { + RasaReqDTO rasaReqDTO = new RasaReqDTO(); + rasaReqDTO.setSender("tester1"); + rasaReqDTO.setMessage(question); + + String post = HttpUtil.post(rasa, JSONUtil.toJsonStr(rasaReqDTO)); + List list = JSONUtil.toList(post, RasaResDTO.class); + return list.stream().map(RasaResDTO::getText).collect(Collectors.toList()); + } } diff --git a/virtual-patient-web/src/main/java/com/supervision/service/impl/RasaServiceImpl.java b/virtual-patient-web/src/main/java/com/supervision/service/impl/RasaServiceImpl.java new file mode 100644 index 00000000..2204b9e1 --- /dev/null +++ b/virtual-patient-web/src/main/java/com/supervision/service/impl/RasaServiceImpl.java @@ -0,0 +1,228 @@ +package com.supervision.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; +import com.supervision.model.*; +import com.supervision.pojo.rasa.train.intent.*; +import com.supervision.service.*; +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class RasaServiceImpl implements RasaService { + + private final AskIntentService askIntentService; + + private final AskQuestionService askQuestionService; + + private final AskDefaultIntentService askDefaultIntentService; + + private final AskDefaultQuestionService askDefaultQuestionService; + + private final AskDefaultAnswerService askDefaultAnswerService; + + private final AskAnswerService askAnswerService; + + private final ConfigPhysicalToolService configPhysicalToolService; + + @Override + public void generateRasaYml(String diseaseId) { + Map defaultIntentCodeAndIdMap = new HashMap<>(); + Map questionCodeAndIdMap = new HashMap<>(); + Map toolCodeIdMap = new HashMap<>(); + List ruleList = new ArrayList<>(); + try { + generateNlu(diseaseId, defaultIntentCodeAndIdMap, questionCodeAndIdMap, toolCodeIdMap); + } catch (Exception e) { + e.printStackTrace(); + } + generateDomain(defaultIntentCodeAndIdMap, questionCodeAndIdMap, toolCodeIdMap, ruleList); + generateRule(ruleList); + + } + + private void generateNlu(String diseaseId, Map defaultIntentCodeAndIdMap, + Map intentCodeAndIdMap, + Map toolCodeIdMap) throws IOException, TemplateException { + // 首先生成根据意图查找到nlu文件 + List nluList = new ArrayList<>(); + // 默认意图 + List defaultIntentList = askDefaultIntentService.list(); + // 根据默认意图找到所有的问题 + if (CollUtil.isNotEmpty(defaultIntentList)) { + Set defaultIntentIdSet = defaultIntentList.stream().map(AskDefaultIntent::getId).collect(Collectors.toSet()); + // 去默认问题表找问题 + List questionList = askDefaultQuestionService.lambdaQuery().in(AskDefaultQuestion::getDefaultIntentId, defaultIntentIdSet).list(); + Map> defaultQuestionByDefaultIntentId = questionList.stream().collect(Collectors.groupingBy(AskDefaultQuestion::getDefaultIntentId)); + // 生成nlu的节点 + for (AskDefaultIntent askDefaultIntent : defaultIntentList) { + List questions = defaultQuestionByDefaultIntentId.get(askDefaultIntent.getId()); + if (CollUtil.isNotEmpty(questions)) { + // 开始生成 + NluYmlTemplate.Nlu nlu = new NluYmlTemplate.Nlu(); + nlu.setIntent(askDefaultIntent.getCode()); + // 注意,这里的格式应该是 "- 你好\n- 你好啊\n- 你好你好" 这种的格式,所以我们要拼接 + nlu.setExamples(questions.stream().map(AskDefaultQuestion::getQuestion).collect(Collectors.toList())); + nluList.add(nlu); + // 添加到map中,key为意图编码,value为意图ID + defaultIntentCodeAndIdMap.put(askDefaultIntent.getCode(), new Intent(askDefaultIntent.getId(), askDefaultIntent.getCode(), askDefaultIntent.getDescription())); + } + } + } + // 然后处理该疾病对应的意图 + List askIntentList = askIntentService.lambdaQuery().eq(AskIntent::getDiseaseId, diseaseId).list(); + // 根据默认意图找到所有的问题 + if (CollUtil.isNotEmpty(askIntentList)) { + Set askIntentListSet = askIntentList.stream().map(AskIntent::getId).collect(Collectors.toSet()); + // 去默认问题表找问题 + List questionList = askQuestionService.lambdaQuery().in(AskQuestion::getIntentId, askIntentListSet).list(); + Map> questionByDefaultIntentId = questionList.stream().collect(Collectors.groupingBy(AskQuestion::getIntentId)); + // 生成nlu的节点 + for (AskIntent askIntent : askIntentList) { + List questions = questionByDefaultIntentId.get(askIntent.getId()); + if (CollUtil.isNotEmpty(questions)) { + // 开始生成 + NluYmlTemplate.Nlu nlu = new NluYmlTemplate.Nlu(); + nlu.setIntent(askIntent.getCode()); + nlu.setExamples(questions.stream().map(AskQuestion::getQuestion).collect(Collectors.toList())); + nluList.add(nlu); + // 添加到map中,key为意图编码,value为意图ID + intentCodeAndIdMap.put(askIntent.getCode(), new Intent(askIntent.getId(), askIntent.getCode(), askIntent.getDescription())); + } + } + } + // 这里处理呼出的问题(code和问题不能为空) + List physicalToolList = configPhysicalToolService.lambdaQuery() + .isNotNull(ConfigPhysicalTool::getCode) + .isNotNull(ConfigPhysicalTool::getCallOutQuestion).list(); + + for (ConfigPhysicalTool tool : physicalToolList) { + // 把呼出的问题全部加进去 + NluYmlTemplate.Nlu nlu = new NluYmlTemplate.Nlu(); + String toolIntent = "tool_" + tool.getCode(); + nlu.setIntent(toolIntent); + nlu.setExamples(tool.getCallOutQuestion()); + nluList.add(nlu); + // 生成tool的map,key是code,value是工具对应的ID + toolCodeIdMap.put(toolIntent, tool); + } + // 生成后生成yml对象 + +// createYmlFile(nluYml, "nlu.yml"); + // 加载模板配置 + NluYmlTemplate nluYmlTemplate = new NluYmlTemplate(); + nluYmlTemplate.setNlu(nluList); + createYmlFile(NluYmlTemplate.class, "nlu.ftl", nluYmlTemplate, "nlu.yml"); + + } + + + public void generateDomain(Map defaultQuestionCodeAndIdMap, + Map questionCodeAndIdMap, Map toolCodeIdMap, + List ruleList) { + LinkedHashMap> responses = new LinkedHashMap<>(); + // 首先根据默认意图找到所有的意图ID + Collection defaultIntentIdColl = defaultQuestionCodeAndIdMap.values(); + // 找到默认意图对应的回复 + if (CollUtil.isNotEmpty(defaultIntentIdColl)) { + Set defaultIntentIdSet = defaultIntentIdColl.stream().map(Intent::getId).collect(Collectors.toSet()); + List defaultAnswerList = askDefaultAnswerService.lambdaQuery().in(AskDefaultAnswer::getDefaultIntentId, defaultIntentIdSet).list(); + Map answerMap = defaultAnswerList.stream().collect(Collectors.toMap(AskDefaultAnswer::getId, AskDefaultAnswer::getAnswer, (k1, k2) -> k1)); + for (Map.Entry entry : defaultQuestionCodeAndIdMap.entrySet()) { + String defaultIntentCode = entry.getKey(); + Intent defaultIntent = entry.getValue(); + String answer = answerMap.get(defaultIntent.getId()); + String utter = "utter_" + defaultIntentCode; + responses.put(utter, CollUtil.newArrayList(answer)); + ruleList.add(new RuleYmlTemplate.Rule(defaultIntent.getDesc(), defaultIntent.getCode(), utter)); + } + } + // 然后疾病意图对应的回复 + Collection intentIdColl = questionCodeAndIdMap.values(); + if (CollUtil.isNotEmpty(intentIdColl)) { + Set intentIdSet = intentIdColl.stream().map(Intent::getId).collect(Collectors.toSet()); + List answerList = askAnswerService.lambdaQuery().in(AskAnswer::getIntentId, intentIdSet).list(); + Map answerMap = answerList.stream().collect(Collectors.toMap(AskAnswer::getId, AskAnswer::getAnswer, (k1, k2) -> k1)); + for (Map.Entry entry : questionCodeAndIdMap.entrySet()) { + String intentCode = entry.getKey(); + Intent intent = entry.getValue(); + String answer = answerMap.get(intent.getId()); + String utter = "utter_" + intentCode; + responses.put(utter, CollUtil.newArrayList(answer)); + ruleList.add(new RuleYmlTemplate.Rule(intent.getDesc(), intent.getCode(), utter)); + } + } + // 生成呼出tool对应的回复 + for (Map.Entry entry : toolCodeIdMap.entrySet()) { + String intentCode = entry.getKey(); + ConfigPhysicalTool tool = entry.getValue(); + String utter = "utter_" + intentCode; + String answer = "---tool---" + tool.getId() ; + responses.put(utter, CollUtil.newArrayList(JSONUtil.toJsonStr(answer))); + ruleList.add(new RuleYmlTemplate.Rule(tool.getToolName(), intentCode, utter)); + } + + + DomainYmlTemplate domainYmlTemplate = new DomainYmlTemplate(); + // 意图 + List intentList = new ArrayList<>(); + intentList.addAll(defaultQuestionCodeAndIdMap.keySet()); + intentList.addAll(questionCodeAndIdMap.keySet()); + domainYmlTemplate.setIntents(intentList); + // 回复 + domainYmlTemplate.setResponses(responses); + // action + List actionList = new ArrayList<>(responses.keySet()); + domainYmlTemplate.setActions(actionList); + // 生成yml文件 + createYmlFile(DomainYmlTemplate.class, "domain.ftl", domainYmlTemplate, "domain.yml"); + } + + /** + * 生成rule + */ + public void generateRule(List ruleList) { + RuleYmlTemplate ruleYmlTemplate = new RuleYmlTemplate(); + ruleYmlTemplate.setRules(ruleList); + // 生成yml文件 + createYmlFile(RuleYmlTemplate.class, "rules.ftl", ruleYmlTemplate, "rules.yml"); + } + + private void createYmlFile(Class clazz, String ftlName, Object data, String ymlName) { + try { + // 这个版本和maven依赖的版本一致 + Configuration configuration = new Configuration(Configuration.VERSION_2_3_31); + configuration.setClassForTemplateLoading(clazz, "/templates"); // 模板文件的所在目录 + // 获取模板 + Template template = configuration.getTemplate(ftlName); + // 创建输出文件 + PrintWriter out = new PrintWriter(ymlName); + // 填充并生成输出 + template.process(data, out); + + // 关闭资源 + out.close(); + } catch (Exception e) { + log.error("导出模板失败", e); + } + + + } +} diff --git a/virtual-patient-web/src/main/resources/application.yml b/virtual-patient-web/src/main/resources/application.yml index 68737445..15f554ae 100644 --- a/virtual-patient-web/src/main/resources/application.yml +++ b/virtual-patient-web/src/main/resources/application.yml @@ -60,4 +60,6 @@ mybatis-plus: paddle-speech: # https://github.com/PaddlePaddle/PaddleSpeech/wiki/PaddleSpeech-Server-RESTful-API tts: http://192.168.10.137:8090/paddlespeech/tts - asr: http://192.168.10.137:8090/paddlespeech/asr \ No newline at end of file + asr: http://192.168.10.137:8090/paddlespeech/asr +rasa: + url: 192.168.10.137:5005/webhooks/rest/webhook \ No newline at end of file diff --git a/virtual-patient-web/src/main/resources/templates/domain.ftl b/virtual-patient-web/src/main/resources/templates/domain.ftl new file mode 100644 index 00000000..8ee679bb --- /dev/null +++ b/virtual-patient-web/src/main/resources/templates/domain.ftl @@ -0,0 +1,23 @@ +version: "3.1" + +intents: +<#list intents as intent> + - ${intent} + + +responses: +<#list responses?keys as response> + ${response}: + <#list responses[response] as item> + - text: "${item}" + + + +actions: +<#list actions as action> + - ${action} + + +session_config: + session_expiration_time: 60 + carry_over_slots_to_new_session: true diff --git a/virtual-patient-web/src/main/resources/templates/nlu.ftl b/virtual-patient-web/src/main/resources/templates/nlu.ftl new file mode 100644 index 00000000..70097b65 --- /dev/null +++ b/virtual-patient-web/src/main/resources/templates/nlu.ftl @@ -0,0 +1,10 @@ +version: "3.1" + +nlu: +<#list nlu as item> + - intent: ${item.intent} + examples: | + <#list item.examples as example> + - ${example} + + diff --git a/virtual-patient-web/src/main/resources/templates/rules.ftl b/virtual-patient-web/src/main/resources/templates/rules.ftl new file mode 100644 index 00000000..d2e2c0fd --- /dev/null +++ b/virtual-patient-web/src/main/resources/templates/rules.ftl @@ -0,0 +1,12 @@ +version: "3.1" + +rules: + +<#list rules as item> + - rule: ${item.rule} + steps: + <#list item.steps as ss> + - intent: ${ss.intent} + - action: ${ss.action} + + \ No newline at end of file