From 2de5a9dea2838abaf31ce7376c6288b152442133 Mon Sep 17 00:00:00 2001 From: liu Date: Fri, 26 Apr 2024 14:13:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../supervision/controller/AskController.java | 2 +- .../handler/gpt/AnswerQuestionHandler.java | 4 +- .../handler/gpt/ConditionJudgeHandler.java | 6 +-- .../service/impl/AskServiceImpl.java | 42 ++++++++++++++----- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/kbqa-graph/src/main/java/com/supervision/controller/AskController.java b/kbqa-graph/src/main/java/com/supervision/controller/AskController.java index 441537a..26023e0 100644 --- a/kbqa-graph/src/main/java/com/supervision/controller/AskController.java +++ b/kbqa-graph/src/main/java/com/supervision/controller/AskController.java @@ -41,7 +41,7 @@ public class AskController { // 如果多轮对话过程中出现了异常,则说明多轮对话走不通,那么就根据sessionId获取Redis中的原始问题,直接走单轮对话 Object cache = redisTemplate.opsForValue().get(SESSION_PARAM + roundTalkReqVO.getSessionId()); SessionParamDTO sessionParamDTO = BeanUtil.toBean(cache, SessionParamDTO.class); - SingleTalkResVO singleTalkResVO = askService.singleTalk(SingleTalkReqVO.builder().userTalk(sessionParamDTO.getOriginalQuestion()).build()); + SingleTalkResVO singleTalkResVO = askService.singleTalk(SingleTalkReqVO.builder().userTalk(roundTalkReqVO.getUserTalk()).build()); return RoundTalkResVO.builder().sessionId(roundTalkReqVO.getSessionId()) .answerText(singleTalkResVO.getAnswerText()).build(); } diff --git a/kbqa-graph/src/main/java/com/supervision/handler/gpt/AnswerQuestionHandler.java b/kbqa-graph/src/main/java/com/supervision/handler/gpt/AnswerQuestionHandler.java index 5a2d253..e3e4dcf 100644 --- a/kbqa-graph/src/main/java/com/supervision/handler/gpt/AnswerQuestionHandler.java +++ b/kbqa-graph/src/main/java/com/supervision/handler/gpt/AnswerQuestionHandler.java @@ -21,7 +21,7 @@ public class AnswerQuestionHandler { "\"\"\"{}\"\"\"\n" + "同时,有一个和用户的问答过程的记录供你参考以提供更精确的回答,内容是:" + "\"\"\"{}\"\"\"\n" + - "请用不超过 100 个词的长度准确和详细的回答用户的问题。请直接给我最终答案,不要反问"; + "请用不超过 100 个词的长度准确和详细的回答用户的问题。回答一定要忠于原文,简洁但不丢信息,不要胡乱编造。请直接给我最终答案,不要反问"; String askRecordTemplate = "问:{}\n 用户答:{};\n"; StringBuilder askRecord = new StringBuilder(); for (Map.Entry entry : talkRecord.entrySet()) { @@ -42,7 +42,7 @@ public class AnswerQuestionHandler { public String answerSingleQuestionNew(String question, Map> detailMap) { String template = "作为AI助手,你的任务是帮助用户查找和理解特定政务的政策内容并给于用户答案。在这个场景中,你将使用政策内容来回答用户问题:{}。请根据政策内容\n" + "\"\"\"{}\"\"\"\n" + - "并用不超过 100 个词的长度准确和详细的回答用户的问题。请直接给我最终答案,不要反问"; + "并用不超过 100 个词的长度准确和详细的回答用户的问题。回答一定要忠于原文,简洁但不丢信息,不要胡乱编造。请直接给我最终答案,不要反问"; List messageList = new ArrayList<>(); messageList.add(new MessageDTO("user", StrUtil.format(template, question, JSONUtil.toJsonStr(detailMap)))); diff --git a/kbqa-graph/src/main/java/com/supervision/handler/gpt/ConditionJudgeHandler.java b/kbqa-graph/src/main/java/com/supervision/handler/gpt/ConditionJudgeHandler.java index dd4c6ae..1d8a4f5 100644 --- a/kbqa-graph/src/main/java/com/supervision/handler/gpt/ConditionJudgeHandler.java +++ b/kbqa-graph/src/main/java/com/supervision/handler/gpt/ConditionJudgeHandler.java @@ -33,10 +33,10 @@ public class ConditionJudgeHandler { public Set conditionJudgeAll(String question, Collection candidateAnswerList, String userAnswer) { List messageList = new ArrayList<>(); String template = "当我问用户:{},用户给我的回答是:[{}],\n" + - "基于用户的回答,请依次判断以下列表:[{}]中的哪些项满足用户的回答?\n" + - "请从给定列表中找出所有满足的项,并以json数组的格式返回,格式示例[\"xxx\",\"xxx\"]。直接说答案,不需要推理过程。如果列表中项全部不满足,则返回:未找到"; + "基于用户的回答,请依次判断用户的回答满足以下列表:{}中的哪些项?\n" + + "以json数组的格式返回,格式示例[\"xxx\",\"xxx\"]。回答一定要来自于列表原文。不要胡乱编造。不需要推理过程。如果列表中项全部不满足,则返回:未找到"; - String judgeAsk = StrUtil.format(template, question, userAnswer, CollUtil.join(candidateAnswerList, ";")); + String judgeAsk = StrUtil.format(template, question, userAnswer, JSONUtil.toJsonStr(candidateAnswerList)); log.info("conditionJudge问题是:{}", judgeAsk); messageList.add(new MessageDTO("user", judgeAsk)); String judgeResult = AiUtil.chatByMessage(messageList); diff --git a/kbqa-graph/src/main/java/com/supervision/service/impl/AskServiceImpl.java b/kbqa-graph/src/main/java/com/supervision/service/impl/AskServiceImpl.java index 93cdd2a..ec6ef6e 100644 --- a/kbqa-graph/src/main/java/com/supervision/service/impl/AskServiceImpl.java +++ b/kbqa-graph/src/main/java/com/supervision/service/impl/AskServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Pair; import cn.hutool.core.lang.UUID; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -88,19 +89,30 @@ public class AskServiceImpl implements AskService { } else { sessionParamDTO = BeanUtil.toBean(cache, SessionParamDTO.class); } + // 如果前端传了角色设置ID,就更新缓存 if (StrUtil.isNotBlank(roundTalkReqVO.getRoleSetId())) { sessionParamDTO.setRoleSetId(roundTalkReqVO.getRoleSetId()); redisTemplate.opsForValue().set(SESSION_PARAM + sessionId, sessionParamDTO); } + // 判断,如果已经有确定的叶子节点了,这时,就可以直接根据意图来问了,就不要要提取实体了 + if (ObjectUtil.isNotEmpty(sessionParamDTO.getMatchItemLeaf())) { + sessionParamDTO.setOriginalQuestion(roundTalkReqVO.getUserTalk()); + String intent = identifyIntentHandler.identifyIntentExample(roundTalkReqVO.getUserTalk()); + sessionParamDTO.setIntent(intent); + redisTemplate.opsForValue().set(SESSION_PARAM + sessionId, sessionParamDTO); + return afterMatchReturnAnswer(sessionParamDTO); + } // 判断意图是否为空,如果意图为空,进行识别意图 if (StrUtil.isBlank(sessionParamDTO.getIntent())) { String intent = identifyIntentHandler.identifyIntentExample(roundTalkReqVO.getUserTalk()); sessionParamDTO.setIntent(intent); + log.info("对问题:{}进行意图识别,识别的意图为:{}", roundTalkReqVO.getUserTalk(), intent); redisTemplate.opsForValue().set(SESSION_PARAM + sessionId, sessionParamDTO); } - // 识别出来意图之后,再去判断是否识别过实体 - if (CollUtil.isEmpty(sessionParamDTO.getEntityValueByExtract())) { + + // 识别出来意图之后,再去判断是否识别过实体(如果最终叶子节点已经确定了,就不需要再判断实体了) + if (CollUtil.isEmpty(sessionParamDTO.getEntityValueByExtract()) && ObjectUtil.isEmpty(sessionParamDTO.getMatchItemLeaf())) { // 识别实体(先从图中获取所有的节点名称,然后识别) List allItemNode = findItemNodeHandler.findAllItemNode(); List extractValue = itemExtractHandler.itemExtractByPossibleItemWithExample(sessionParamDTO.getOriginalQuestion(), allItemNode); @@ -150,17 +162,19 @@ public class AskServiceImpl implements AskService { } } // 多轮问答支持设置角色,如果设置角色了,在这里应该先根据角色来排除一遍路径 - filterUserRoleSet(sessionParamDTO); + if (ObjectUtil.isEmpty(sessionParamDTO.getMatchItemLeaf())) { + filterUserRoleSet(sessionParamDTO); + } + // 如果没有确定节点,且没有路径可供匹配,抛出异常 + if (CollUtil.isEmpty(sessionParamDTO.getConditionPathMap())) { + throw new BusinessException("未找到条件判断路径"); + } // 如果判断过实体,这时就要判断是否已经确认了节点,如果没有确认,在这里进行确认 match: if (ObjectUtil.isEmpty(sessionParamDTO.getMatchItemLeaf())) { - // 如果没有确定节点,且没有路径可供匹配,抛出异常 - if (CollUtil.isEmpty(sessionParamDTO.getConditionPathMap())) { - throw new BusinessException("未找到条件判断路径"); - } + // 判断待匹配的节点是不是只有一个了,如果有多个,就从路径中选择一个问题问前端 if (sessionParamDTO.getWaitMatchItemLeafMap().size() != 1) { - if (ObjectUtil.isNotEmpty(sessionParamDTO.getCurrentEntity())) { // 如果当前对话实体不为空,说明当前问答就是上一个问题的回复,这个时候,就去大模型中进行匹配并排除路径 filterPath(sessionParamDTO, sessionParamDTO.getCurrentEntity().getCurrentEntityType(), sessionParamDTO.getCurrentEntity().getCurrentQuestion(), roundTalkReqVO.getUserTalk()); @@ -173,6 +187,10 @@ public class AskServiceImpl implements AskService { } // 首先获取出现次数最多的实体类型 Map newCountMap = countCondition(sessionParamDTO); + if (MapUtil.isEmpty(newCountMap)) { + log.info("为空了"); + } + // 如果计数不为空,就继续进行筛选 String mostFrequentType = newCountMap.entrySet().stream() .filter(entry -> !sessionParamDTO.getAlreadyMatchEntitySet().contains(entry.getKey())) .max(Map.Entry.comparingByValue(Integer::compareTo)) @@ -232,6 +250,10 @@ public class AskServiceImpl implements AskService { private void filterPath(SessionParamDTO sessionParamDTO, String currentEntityType, String currentQuestion, String userTalk) { // 遍历所有的path,找到回答 Set possibleAnswerSet = findPossibleAnswerSet(sessionParamDTO, currentEntityType); + // 如果可能的回答为空,就不进行过滤,直接跳过 + if (CollUtil.isEmpty(possibleAnswerSet)) { + return; + } Set judgeResultSet = conditionJudgeHandler.conditionJudgeAll(currentQuestion, possibleAnswerSet, userTalk); @@ -397,11 +419,9 @@ public class AskServiceImpl implements AskService { try { RetireRoleEnum retireRoleEnum = RetireRoleEnum.valueOf(roleSetNode.getItemEn()); if (ObjectUtils.isNotEmpty(retireRoleEnum)) { - Map entityCountMap = sessionParamDTO.getEntityCountMap(); - // 如果包含,就去尝试排除路径 - if (entityCountMap.containsKey(retireRoleEnum.getZhName()) && ObjectUtil.isNotEmpty(roleSetNode.getValueNum())) { + if (MapUtil.isNotEmpty(entityCountMap) && entityCountMap.containsKey(retireRoleEnum.getZhName()) && ObjectUtil.isNotEmpty(roleSetNode.getValueNum())) { List> answerList = retireRoleEnum.getAnswerList(); for (Pair pair : answerList) { // 如果枚举的key和用户填写的value相等,就排除