From 359af749ac360ca2b50e824dc5571ebfd7c3c3f8 Mon Sep 17 00:00:00 2001 From: liu Date: Thu, 6 Jun 2024 18:28:36 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=97=AE=E7=AD=94?= =?UTF-8?q?=E6=B5=81=E7=A8=8B,=E5=AF=B9=E9=97=AE=E7=AD=94=E8=BF=87?= =?UTF-8?q?=E7=A8=8B=E4=B8=AD=E7=9A=84=E6=B6=88=E6=81=AF=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/v3.0.0/step1.sql | 62 ++++++ .../config/VectorSimilarityConfiguration.java | 4 +- .../domain/QaSimilarityQuestionAnswer.java | 7 +- .../com/supervision/util/SimilarityUtil.java | 2 +- .../mapper/AskCirculationDetailMapper.java | 18 ++ .../model/AskCirculationDetail.java | 112 ++++++++++ .../supervision/model/DiagnosisQaRecord.java | 21 +- .../service/AskCirculationDetailService.java | 13 ++ .../impl/AskCirculationDetailServiceImpl.java | 22 ++ .../mapper/AskCirculationDetailMapper.xml | 43 ++++ .../impl/AskDiagnosisResultServiceImpl.java | 73 +++---- .../service/impl/AskServiceImpl.java | 199 +++++++++++------- 12 files changed, 441 insertions(+), 135 deletions(-) create mode 100644 docker/v3.0.0/step1.sql create mode 100644 virtual-patient-model/src/main/java/com/supervision/mapper/AskCirculationDetailMapper.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/model/AskCirculationDetail.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/service/AskCirculationDetailService.java create mode 100644 virtual-patient-model/src/main/java/com/supervision/service/impl/AskCirculationDetailServiceImpl.java create mode 100644 virtual-patient-model/src/main/resources/mapper/AskCirculationDetailMapper.xml diff --git a/docker/v3.0.0/step1.sql b/docker/v3.0.0/step1.sql new file mode 100644 index 00000000..81fe1e80 --- /dev/null +++ b/docker/v3.0.0/step1.sql @@ -0,0 +1,62 @@ +-- 创建详细问表 + +create table vp_ask_template_question_similarity +( + id varchar(64) not null comment '主键' + primary key, + library_id varchar(64) not null comment '标准问题ID', + similarity_question varchar(255) not null comment '相似问题', + create_user_id varchar(64) null comment '创建人ID', + create_time datetime default CURRENT_TIMESTAMP null comment '创建时间', + update_user_id varchar(64) null comment '更新人ID', + update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间' +) comment '相似问问题库'; + +-- 修改标准问表,移除默认回复 +alter table vp_ask_template_question_library drop column default_answer_code; + +alter table vp_ask_template_question_library drop column default_answer; + +alter table vp_ask_template_question_library drop column default_answer_resource_id; + +-- 修改问题记录表结构 +alter table vp_diagnosis_qa_record + add match_type int not null default 1 comment '匹配类型 1相似度匹配 2大模型回答' after process_id; + +alter table vp_diagnosis_qa_record modify question_library_id varchar (64) null comment '问题ID(如果走了相似问,则记录匹配到的问题ID)'; + +alter table vp_diagnosis_qa_record drop column question_wav_id; + +alter table vp_diagnosis_qa_record drop column answer_type; + +alter table vp_diagnosis_qa_record drop column answer_id; + +alter table vp_diagnosis_qa_record drop column answer_wav_id; + +create table vp_ask_circulation_detail +( + id varchar(64) not null comment '主键' + primary key, + process_id varchar(64) null comment '诊断进程ID', + circulation_id varchar(64) null comment '对话流转ID', + circulation_no int null comment '流转序号,消息每经过一次处理,就+1', + question varchar(255) null comment '问题内容', + answer varchar(255) null comment '回答内容', + success_info varchar(255) null comment '成功的记录信息', + success_type int null comment '成功回答的类型 1相似度 2大模型', + fail_info text null comment '失败记录信息', + similarity_info text null comment '相似度匹配信息的JSON', + match_item varchar(64) null comment '匹配到的类目名称', + match_question varchar(255) null comment '相似度匹配 匹配到的问题', + match_score double null comment '如走相似度匹配,记录得分', + match_library_id varchar(64) null comment '如相似度匹配的是标准问ID,则保存', + match_similarity_id varchar(64) null comment '如果匹配到详细问ID则记录', + ai_medical_context text null comment '走大模型时,大模型的病例内容', + remark varchar(255) null comment '备注', + create_user_id varchar(64) null comment '创建人ID', + create_time datetime default CURRENT_TIMESTAMP null comment '创建时间', + update_user_id varchar(64) null comment '更新人ID', + update_time datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新人时间' +) comment '问诊流程详细流转记录表'; + + diff --git a/virtual-patient-common/src/main/java/com/supervision/config/VectorSimilarityConfiguration.java b/virtual-patient-common/src/main/java/com/supervision/config/VectorSimilarityConfiguration.java index beffc151..1e3463ce 100644 --- a/virtual-patient-common/src/main/java/com/supervision/config/VectorSimilarityConfiguration.java +++ b/virtual-patient-common/src/main/java/com/supervision/config/VectorSimilarityConfiguration.java @@ -29,11 +29,11 @@ public class VectorSimilarityConfiguration { // 定义搜索过滤器使用的元数据字段(!!!!!!!!千万重要,数据类型一定要用字符串,否则会导致查询不到!!!!!!!!) .withMetadataFields( // 问题的ID - RedisVectorStore.MetadataField.tag("questionId"), + RedisVectorStore.MetadataField.tag("matchQuestionId"), //关联字典ID RedisVectorStore.MetadataField.tag("dictId"), // 标准问ID - RedisVectorStore.MetadataField.tag("standardQuestionId"), + RedisVectorStore.MetadataField.tag("libraryQuestionId"), // 类型 1标准问 2相似问 3自定义 RedisVectorStore.MetadataField.tag("type")) .build(); diff --git a/virtual-patient-common/src/main/java/com/supervision/domain/QaSimilarityQuestionAnswer.java b/virtual-patient-common/src/main/java/com/supervision/domain/QaSimilarityQuestionAnswer.java index b1592634..c6651ee1 100644 --- a/virtual-patient-common/src/main/java/com/supervision/domain/QaSimilarityQuestionAnswer.java +++ b/virtual-patient-common/src/main/java/com/supervision/domain/QaSimilarityQuestionAnswer.java @@ -13,7 +13,12 @@ public class QaSimilarityQuestionAnswer { /** * 匹配到的问题ID */ - private String matchQuestionCode; + private String matchQuestionId; + + /** + * 对应的标准问ID + */ + private String libraryQuestionId; /** * 关联的字典ID diff --git a/virtual-patient-common/src/main/java/com/supervision/util/SimilarityUtil.java b/virtual-patient-common/src/main/java/com/supervision/util/SimilarityUtil.java index d9515107..41107161 100644 --- a/virtual-patient-common/src/main/java/com/supervision/util/SimilarityUtil.java +++ b/virtual-patient-common/src/main/java/com/supervision/util/SimilarityUtil.java @@ -44,7 +44,7 @@ public class SimilarityUtil { QaSimilarityQuestionAnswer qaSimilarityQuestionAnswer = new QaSimilarityQuestionAnswer(); qaSimilarityQuestionAnswer.setMatchQuestion(document.getContent()); qaSimilarityQuestionAnswer.setDictId(String.valueOf(document.getMetadata().get("dictId"))); - qaSimilarityQuestionAnswer.setMatchQuestionCode(String.valueOf(document.getMetadata().get("standardQuestionId"))); + qaSimilarityQuestionAnswer.setLibraryQuestionId(String.valueOf(document.getMetadata().get("standardQuestionId"))); // 1- 可以使数据进行排序,相似度越高,数值越大(redis相似度给的数据是越小相似度越高) // -0.25目的是使数据趋近于中间,相似度不要太大(太大也不好调整),以使我们数据和张总之前提供的方法相似度差异稍小一点,但是不能小于0,如果小于0,取一个较大的值 double score = Math.max(0, 1 - Double.parseDouble(String.valueOf(document.getMetadata().get("vector_score"))) - 0.25); diff --git a/virtual-patient-model/src/main/java/com/supervision/mapper/AskCirculationDetailMapper.java b/virtual-patient-model/src/main/java/com/supervision/mapper/AskCirculationDetailMapper.java new file mode 100644 index 00000000..9404571c --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/mapper/AskCirculationDetailMapper.java @@ -0,0 +1,18 @@ +package com.supervision.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.supervision.model.AskCirculationDetail; + +/** + * @author flevance + * @description 针对表【vp_ask_circulation_detail(问诊流程详细流转记录表)】的数据库操作Mapper + * @createDate 2024-06-06 17:08:59 + * @Entity com.supervision.model.AskCirculationDetail + */ +public interface AskCirculationDetailMapper extends BaseMapper { + +} + + + + diff --git a/virtual-patient-model/src/main/java/com/supervision/model/AskCirculationDetail.java b/virtual-patient-model/src/main/java/com/supervision/model/AskCirculationDetail.java new file mode 100644 index 00000000..7ba00e75 --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/model/AskCirculationDetail.java @@ -0,0 +1,112 @@ +package com.supervision.model; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 问诊流程详细流转记录表 + * + * @TableName vp_ask_circulation_detail + */ +@Builder +@TableName(value = "vp_ask_circulation_detail") +@Data +public class AskCirculationDetail implements Serializable { + @TableField(exist = false) + private static final long serialVersionUID = 1L; + /** + * 主键 + */ + @TableId + private String id; + /** + * 诊断进程ID + */ + private String processId; + /** + * 病历ID + */ + private String medicalId; + /** + * 问话记录ID + */ + private String recordId; + /** + * 流转序号,消息在后端每经过一次判断,就+1 + */ + private Integer circulationNo; + /** + * 问题内容 + */ + private String question; + /** + * 回答内容 + */ + private String answer; + /** + * 成功的记录信息 + */ + private String successInfo; + /** + * 成功回答的类型 1相似度 2大模型 + */ + private Integer successType; + /** + * 失败记录信息 + */ + private String failInfo; + /** + * 相似度匹配信息的JSON + */ + private String similarityInfo; + /** + * 匹配到的类目名称 + */ + private String matchItem; + /** + * 相似度匹配 匹配到的问题 + */ + private String matchQuestion; + /** + * 如走相似度匹配,记录得分 + */ + private Double matchScore; + /** + * 如匹配的是标准问,则保存标准问ID + */ + private String matchLibraryId; + /** + * 如果匹配的是相似问,则记录相似问ID + */ + private String matchSimilarityId; + /** + * 走大模型时,大模型的病例内容 + */ + private String aiMedicalContext; + /** + * 备注 + */ + private String remark; + /** + * 创建人ID + */ + private String createUserId; + /** + * 创建时间 + */ + private LocalDateTime createTime; + /** + * 更新人ID + */ + private String updateUserId; + /** + * 更新人时间 + */ + private LocalDateTime updateTime; +} \ No newline at end of file diff --git a/virtual-patient-model/src/main/java/com/supervision/model/DiagnosisQaRecord.java b/virtual-patient-model/src/main/java/com/supervision/model/DiagnosisQaRecord.java index 72055581..b1c36fac 100644 --- a/virtual-patient-model/src/main/java/com/supervision/model/DiagnosisQaRecord.java +++ b/virtual-patient-model/src/main/java/com/supervision/model/DiagnosisQaRecord.java @@ -33,14 +33,8 @@ public class DiagnosisQaRecord extends Model implements Seria @Schema(description = "诊断进程ID") private String processId; - @Schema(description = "问答类型:default,patient,如果patient,说明是病历配置的回答,打勾") - private String answerType; - - /** - * 问题答案表ID - */ - @Schema(description = "问题答案表ID,default:vp_ask_template_question_library的主键,patient:vp_ask_patient_answer主键") - private String answerId; + @Schema(description = "匹配类型 1相似度匹配 2大模型回答") + private Integer matchType; /** * 问题 @@ -54,23 +48,12 @@ public class DiagnosisQaRecord extends Model implements Seria @Schema(description = "问题ID") private String questionLibraryId; - /** - * 问题语音文件ID - */ - @Schema(description = "问题语音文件ID") - private String questionWavId; - /** * 回答 */ @Schema(description = "回答") private String answer; - /** - * 回答语音文件ID - */ - @Schema(description = "回答语音文件ID") - private String answerWavId; @Schema(description = "是否是证实诊断依据(0否1是)") private Integer basisConfirmFlag; diff --git a/virtual-patient-model/src/main/java/com/supervision/service/AskCirculationDetailService.java b/virtual-patient-model/src/main/java/com/supervision/service/AskCirculationDetailService.java new file mode 100644 index 00000000..33b0f419 --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/service/AskCirculationDetailService.java @@ -0,0 +1,13 @@ +package com.supervision.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.supervision.model.AskCirculationDetail; + +/** + * @author flevance + * @description 针对表【vp_ask_circulation_detail(问诊流程详细流转记录表)】的数据库操作Service + * @createDate 2024-06-06 17:08:59 + */ +public interface AskCirculationDetailService extends IService { + +} diff --git a/virtual-patient-model/src/main/java/com/supervision/service/impl/AskCirculationDetailServiceImpl.java b/virtual-patient-model/src/main/java/com/supervision/service/impl/AskCirculationDetailServiceImpl.java new file mode 100644 index 00000000..6dd918e0 --- /dev/null +++ b/virtual-patient-model/src/main/java/com/supervision/service/impl/AskCirculationDetailServiceImpl.java @@ -0,0 +1,22 @@ +package com.supervision.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.mapper.AskCirculationDetailMapper; +import com.supervision.model.AskCirculationDetail; +import com.supervision.service.AskCirculationDetailService; +import org.springframework.stereotype.Service; + +/** + * @author flevance + * @description 针对表【vp_ask_circulation_detail(问诊流程详细流转记录表)】的数据库操作Service实现 + * @createDate 2024-06-06 17:08:59 + */ +@Service +public class AskCirculationDetailServiceImpl extends ServiceImpl + implements AskCirculationDetailService { + +} + + + + diff --git a/virtual-patient-model/src/main/resources/mapper/AskCirculationDetailMapper.xml b/virtual-patient-model/src/main/resources/mapper/AskCirculationDetailMapper.xml new file mode 100644 index 00000000..8311c43e --- /dev/null +++ b/virtual-patient-model/src/main/resources/mapper/AskCirculationDetailMapper.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id + ,process_id,medical_id, + record_id,circulation_no,question, + answer,success_info,success_type, + fail_info,similarity_info,match_item, + match_question,match_score,match_library_id, + match_similarity_id,ai_medical_context,remark, + create_user_id,create_time,update_user_id, + update_time + + diff --git a/virtual-patient-web/src/main/java/com/supervision/service/impl/AskDiagnosisResultServiceImpl.java b/virtual-patient-web/src/main/java/com/supervision/service/impl/AskDiagnosisResultServiceImpl.java index 08c5108c..e06d4db6 100644 --- a/virtual-patient-web/src/main/java/com/supervision/service/impl/AskDiagnosisResultServiceImpl.java +++ b/virtual-patient-web/src/main/java/com/supervision/service/impl/AskDiagnosisResultServiceImpl.java @@ -187,69 +187,70 @@ public class AskDiagnosisResultServiceImpl implements AskDiagnosisResultService List ancillaryRecordList = diagnosisAncillaryRecordService.queryAncillaryResultByProcessId(process.getId()); List diseaseAncillaryResVos = diseaseAncillaryService.queryListByDiseaseId(medicalRec.getDiseaseId()); - basisDiagnosisNodeResVOS.addAll(ancillaryRecordMergeListsBasisPrimary(ancillaryRecordList,diseaseAncillaryResVos)); + basisDiagnosisNodeResVOS.addAll(ancillaryRecordMergeListsBasisPrimary(ancillaryRecordList, diseaseAncillaryResVos)); basisPrimaryResultResVO.setNodeList(basisDiagnosisNodeResVOS); return basisPrimaryResultResVO; } - private List qaRecordMergeListsBasisPrimary(List qaRecordList, List list){ + private List qaRecordMergeListsBasisPrimary(List qaRecordList, List list) { - BiFunction biFunction = (qaRecord, qa) -> qa.getLibraryQuestionId().equals(qaRecord.getQuestionLibraryId()); + BiFunction biFunction = (qaRecord, qa) -> qa.getLibraryQuestionId().equals(qaRecord.getQuestionLibraryId()); list = list.stream().filter(item -> !Integer.valueOf("0").equals(item.getAnswerType())).collect(Collectors.toList()); Set requireCheckIdSet = list.stream().map(AskPatientAnswer::getId).collect(Collectors.toSet()); - Function t1 = r -> new BasisDiagnosisNodeResVO(0,r.getQuestion(),r.getId(), + Function t1 = r -> new BasisDiagnosisNodeResVO(0, r.getQuestion(), r.getId(), requireCheckIdSet.contains(r.getAnswerId()) ? 1 : 0); - Function t2 = item -> new BasisDiagnosisNodeResVO(0, item.getQuestion(), item.getId(), 0); + Function t2 = item -> new BasisDiagnosisNodeResVO(0, item.getQuestion(), item.getId(), 0); return mergeListsBasedOnCondition(qaRecordList, list, biFunction, t1, t2); } private List physicalRecordMergeLists(List physicalRecordList, - List diseasePhysicalResVos, - Function functionCorrect){ + List diseasePhysicalResVos, + Function functionCorrect) { physicalRecordList = physicalRecordList.stream() .filter(distinctPredicateNotNull(PhysicalRecordByResultDTO::getToolLocationName)).collect(Collectors.toList()); // 工具id相等且 位置为空或者位置id相等 - BiFunction biFunction = (r, diseasePhysical) -> StrUtil.equals(diseasePhysical.getToolId(),r.getToolId()) - && ((StrUtil.isEmpty(diseasePhysical.getLocationId()) && StrUtil.isEmpty(diseasePhysical.getLocationId())) - || StrUtil.equals(diseasePhysical.getLocationId(),(r.getLocationId()))); + BiFunction biFunction = (r, diseasePhysical) -> StrUtil.equals(diseasePhysical.getToolId(), r.getToolId()) + && ((StrUtil.isEmpty(diseasePhysical.getLocationId()) && StrUtil.isEmpty(diseasePhysical.getLocationId())) + || StrUtil.equals(diseasePhysical.getLocationId(), (r.getLocationId()))); - Function t1 = r -> new BasisDiagnosisNodeResVO(1, r.getToolLocationName(), + Function t1 = r -> new BasisDiagnosisNodeResVO(1, r.getToolLocationName(), r.getRecordId(), functionCorrect.apply(r)); // 这里的recordId只为了展示用diseasePhysical.getId()代替。只是为展示使用correct的值固定为不正确 - Function t2 = diseasePhysical -> new BasisDiagnosisNodeResVO(1, - diseasePhysical.getToolName() + (StrUtil.isEmpty(diseasePhysical.getLocationName())? "" : "|"+ diseasePhysical.getLocationName()), + Function t2 = diseasePhysical -> new BasisDiagnosisNodeResVO(1, + diseasePhysical.getToolName() + (StrUtil.isEmpty(diseasePhysical.getLocationName()) ? "" : "|" + diseasePhysical.getLocationName()), diseasePhysical.getId(), 0); - return mergeListsBasedOnCondition(physicalRecordList, diseasePhysicalResVos,biFunction, t1, t2); + return mergeListsBasedOnCondition(physicalRecordList, diseasePhysicalResVos, biFunction, t1, t2); } private List ancillaryRecordMergeLists(List ancillaryRecordList, List diseaseAncillaryResVos, - Function functionCorrect){ + Function functionCorrect) { ancillaryRecordList = ancillaryRecordList.stream() .filter(distinctPredicateNotNull(AncillaryRecordByResultDTO::getItemName)).collect(Collectors.toList()); - BiFunction biFunction =(r, diseaseAncillary) -> StrUtil.equals(diseaseAncillary.getItemId(),r.getItemId()); + BiFunction biFunction = (r, diseaseAncillary) -> StrUtil.equals(diseaseAncillary.getItemId(), r.getItemId()); - Function t1 = record -> new BasisDiagnosisNodeResVO(2, record.getItemName(), record.getRecordId(), + Function t1 = record -> new BasisDiagnosisNodeResVO(2, record.getItemName(), record.getRecordId(), functionCorrect.apply(record)); // 这里的recordId只为了展示用diseaseAncillary.getId()代替。只是为展示使用correct的值固定为不正确 - Function t2 = diseaseAncillary -> new BasisDiagnosisNodeResVO(2, diseaseAncillary.getItemName(), + Function t2 = diseaseAncillary -> new BasisDiagnosisNodeResVO(2, diseaseAncillary.getItemName(), diseaseAncillary.getId(), 0); - return mergeListsBasedOnCondition(ancillaryRecordList, diseaseAncillaryResVos,biFunction, t1, t2); + return mergeListsBasedOnCondition(ancillaryRecordList, diseaseAncillaryResVos, biFunction, t1, t2); } + private List physicalRecordMergeListsBasisPrimary(List physicalRecordList, - List diseasePhysicalResVos){ + List diseasePhysicalResVos) { diseasePhysicalResVos = diseasePhysicalResVos.stream() .filter(diseasePhysicalResVo -> Integer.valueOf(1).equals(diseasePhysicalResVo.getPrimarilyDiagnosisCriteriaFlag())).collect(Collectors.toList()); @@ -257,9 +258,9 @@ public class AskDiagnosisResultServiceImpl implements AskDiagnosisResultService } private List physicalRecordMergeListsConfirm(List physicalRecordList, - List diseasePhysicalResVos){ + List diseasePhysicalResVos) { - Function functionCorrect = record -> (ObjectUtil.isNotNull(record.getBasisConfirm()) && 1 == record.getBasisConfirm() + Function functionCorrect = record -> (ObjectUtil.isNotNull(record.getBasisConfirm()) && 1 == record.getBasisConfirm() && Objects.equals(record.getBasisConfirm(), record.getRecordBasisConfirmFlag())) ? 1 : 0; diseasePhysicalResVos = diseasePhysicalResVos.stream() @@ -268,9 +269,9 @@ public class AskDiagnosisResultServiceImpl implements AskDiagnosisResultService } private List physicalRecordMergeListsIdentification(List physicalRecordList, - List diseasePhysicalResVos){ + List diseasePhysicalResVos) { - Function functionCorrect = record -> (Integer.valueOf(1).equals(record.getBasisIdentification()) + Function functionCorrect = record -> (Integer.valueOf(1).equals(record.getBasisIdentification()) && Objects.equals(record.getBasisIdentification(), record.getRecordBasisIdentificationFlag())) ? 1 : 0; diseasePhysicalResVos = diseasePhysicalResVos.stream() @@ -279,7 +280,7 @@ public class AskDiagnosisResultServiceImpl implements AskDiagnosisResultService } private List ancillaryRecordMergeListsBasisPrimary(List ancillaryRecordList, - List diseaseAncillaryResVos){ + List diseaseAncillaryResVos) { diseaseAncillaryResVos = diseaseAncillaryResVos.stream() .filter(diseaseAncillaryResVo -> Integer.valueOf(1).equals(diseaseAncillaryResVo.getPrimarilyDiagnosisCriteriaFlag())).collect(Collectors.toList()); @@ -290,35 +291,35 @@ public class AskDiagnosisResultServiceImpl implements AskDiagnosisResultService private List ancillaryRecordMergeListsConfirm(List ancillaryRecordList, - List diseaseAncillaryResVos){ + List diseaseAncillaryResVos) { - Function functionCorrect = record -> ObjectUtil.isNotNull(record.getBasisConfirm()) && 1 == record.getBasisConfirm() - && Objects.equals(record.getBasisConfirm(), record.getRecordBasisConfirmFlag()) ? 1 : 0 ; + Function functionCorrect = record -> ObjectUtil.isNotNull(record.getBasisConfirm()) && 1 == record.getBasisConfirm() + && Objects.equals(record.getBasisConfirm(), record.getRecordBasisConfirmFlag()) ? 1 : 0; diseaseAncillaryResVos = diseaseAncillaryResVos.stream() .filter(diseaseAncillaryResVo -> Integer.valueOf(1).equals(diseaseAncillaryResVo.getBasisConfirmFlag())).collect(Collectors.toList()); - return ancillaryRecordMergeLists(ancillaryRecordList, diseaseAncillaryResVos,functionCorrect); + return ancillaryRecordMergeLists(ancillaryRecordList, diseaseAncillaryResVos, functionCorrect); } private List ancillaryRecordMergeListsIdentification(List ancillaryRecordList, - List diseaseAncillaryResVos){ + List diseaseAncillaryResVos) { - Function functionCorrect = record -> (Integer.valueOf(1).equals(record.getBasisIdentification()) + Function functionCorrect = record -> (Integer.valueOf(1).equals(record.getBasisIdentification()) && Objects.equals(record.getBasisIdentification(), record.getRecordBasisIdentificationFlag())) ? 1 : 0; diseaseAncillaryResVos = diseaseAncillaryResVos.stream() .filter(diseaseAncillaryResVo -> Integer.valueOf(1).equals(diseaseAncillaryResVo.getBasisIdentificationFlag())).collect(Collectors.toList()); - return ancillaryRecordMergeLists(ancillaryRecordList, diseaseAncillaryResVos,functionCorrect); + return ancillaryRecordMergeLists(ancillaryRecordList, diseaseAncillaryResVos, functionCorrect); } /** * 根据指定条件合并两个列表。 * * @param simpleList 要处理的简单列表。 - * @param poolList 用于比较的池列表。 + * @param poolList 用于比较的池列表。 * @param biFunction 定义合并元素条件。 - * @param t1 用于处理简单列表中元素的函数。 - * @param t2 用于处理池列表中元素的函数。 + * @param t1 用于处理简单列表中元素的函数。 + * @param t2 用于处理池列表中元素的函数。 * @return 包含根据条件合并的元素的列表。 */ private List mergeListsBasedOnCondition(List simpleList, List poolList, @@ -346,7 +347,7 @@ public class AskDiagnosisResultServiceImpl implements AskDiagnosisResultService break; } } - if (!find){ + if (!find) { rs.add(t2.apply(m)); } } 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 2ce52d8a..422a1c69 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 @@ -2,6 +2,7 @@ package com.supervision.service.impl; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.supervision.domain.QaSimilarityQuestionAnswer; import com.supervision.exception.BusinessException; import com.supervision.model.Process; @@ -15,12 +16,14 @@ import com.supervision.util.TtsUtil; import com.supervision.util.UserUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.ai.vectorstore.RedisVectorStore; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; @Slf4j @Service @@ -37,9 +40,10 @@ public class AskServiceImpl implements AskService { private final MedicalRecService medicalRecService; - private final DiagnosisAiRecordService diagnosisAiRecordService; + private final AskCirculationDetailService askCirculationDetailService; + + private final CommonDicService commonDicService; - private final RedisVectorStore redisVectorStore; @Value("${threshold:0.7}") private String threshold; @@ -61,21 +65,6 @@ public class AskServiceImpl implements AskService { return text; } - - private void saveQaRecord(String processId, String answerType, String answerId, String question, AskTemplateQuestionLibrary library, String resText) { - DiagnosisQaRecord record = new DiagnosisQaRecord(); - record.setProcessId(processId); - record.setAnswerType(answerType); - record.setAnswerId(answerId); - if (ObjectUtil.isNotEmpty(library)) { - record.setQuestionLibraryId(library.getId()); - } - record.setQuestion(question); - record.setAnswer(resText); - record.setCreateUserId(UserUtil.getUser().getId()); - record.insert(); - } - /** * 使用无声视频+语音转文字的形式来做 * @@ -84,69 +73,127 @@ public class AskServiceImpl implements AskService { */ @Override public TalkVideoTtsResultResVO talkByVideoAndTts(TalkVideoReqVO talkReqVO) { + String answer = talkByVideoAndTts(talkReqVO.getProcessId(), talkReqVO.getText()); + TalkVideoTtsResultResVO talkVideoTtsResultResVO = new TalkVideoTtsResultResVO(); + talkVideoTtsResultResVO.setVoiceBase64(TtsUtil.ttsTransform(answer)); + talkVideoTtsResultResVO.setAnswerMessage(answer); + return talkVideoTtsResultResVO; + } + + private String talkByVideoAndTts(String processId, String question) { + // 流转记录表 + List circulationList = new ArrayList<>(); + // 根据processId找到对应的病人 - Process process = Optional.ofNullable(processService.getById(talkReqVO.getProcessId())).orElseThrow(() -> new BusinessException("未找到诊疗进程")); + Process process = Optional.ofNullable(processService.getById(processId)).orElseThrow(() -> new BusinessException("未找到诊疗进程")); MedicalRec medicalRec = medicalRecService.getById(process.getMedicalRecId()); - Optional qaSimilarityQuestionAnswerOptional = SimilarityUtil.talkRedisVectorWithScoreByFirst(talkReqVO.getText()); - TalkVideoTtsResultResVO talkVideoTtsResultResVO = new TalkVideoTtsResultResVO(); - // 如果匹配度没有匹配到任何数据,则走大模型 - if (qaSimilarityQuestionAnswerOptional.isEmpty()) { - String talk = aiService.talk(talkReqVO.getText(), medicalRec.getMedicalRecordAi()); - talkVideoTtsResultResVO.setAnswerMessage(talk); - saveAiRecord(process.getId(), talkReqVO.getText(), talkVideoTtsResultResVO.getAnswerMessage()); - } else { - QaSimilarityQuestionAnswer qaSimilarityQuestionAnswer = qaSimilarityQuestionAnswerOptional.get(); - // 如果阈值过低,也走大模型 - double thresholdValue = Double.parseDouble(threshold); - if (qaSimilarityQuestionAnswer.getMatchScore() < thresholdValue) { - log.info("{}:匹配到的结果阈值过低,走大模型回答", qaSimilarityQuestionAnswer); - String talk = aiService.talk(talkReqVO.getText(), medicalRec.getMedicalRecordAi()); - talkVideoTtsResultResVO.setAnswerMessage(talk); - saveAiRecord(process.getId(), talkReqVO.getText(), talkVideoTtsResultResVO.getAnswerMessage()); - } else { - // 如果查到的问题不在问题库中,走大模型回答 - AskTemplateQuestionLibrary library = askTemplateQuestionLibraryService.getById(qaSimilarityQuestionAnswer.getMatchQuestionCode()); - if (ObjectUtil.isEmpty(library)) { - log.info("{}:未从问题库中找到,走大模型回答", qaSimilarityQuestionAnswer); - String talk = aiService.talk(talkReqVO.getText(), medicalRec.getMedicalRecordAi()); - talkVideoTtsResultResVO.setAnswerMessage(talk); - saveAiRecord(process.getId(), talkReqVO.getText(), talkVideoTtsResultResVO.getAnswerMessage()); - } else { - // 根据问题找这个病历配置的答案 - AskPatientAnswer askPatientAnswer = askPatientAnswerService.lambdaQuery().eq(AskPatientAnswer::getMedicalId, process.getMedicalRecId()) - .eq(AskPatientAnswer::getLibraryQuestionId, library.getId()).last("limit 1").one(); - // 如果找到了,就走病历配置的内容回答 - if (ObjectUtil.isNotEmpty(askPatientAnswer)) { - String resText = askPatientAnswer.getAnswer(); - log.info("{}:找到了病历配置的回答语句:{},回答内容:{},走病历回答", qaSimilarityQuestionAnswer.getMatchQuestionCode(), askPatientAnswer.getId(), resText); - talkVideoTtsResultResVO.setAnswerMessage(resText); - // 保存记录到问答记录表 - saveQaRecord(talkReqVO.getProcessId(), "patient", askPatientAnswer.getId(), talkReqVO.getText(), library, resText); - } else { - // 如果问题的答案没有配置,还是走大模型的回答 - log.info("{}:病历配置,从AskPatientAnswer中未找到回答结果,走大模型", qaSimilarityQuestionAnswer.getMatchQuestionCode()); - String talk = aiService.talk(talkReqVO.getText(), medicalRec.getMedicalRecordAi()); - talkVideoTtsResultResVO.setAnswerMessage(talk); - saveAiRecord(process.getId(), talkReqVO.getText(), talkVideoTtsResultResVO.getAnswerMessage()); - } - } - } + // 进行相似度匹配 + List similarityAnswerList = SimilarityUtil.talkRedisVectorWithScore(question); + Optional first = similarityAnswerList.stream().findFirst(); + // 如果匹配度没有匹配到任何数据 + if (first.isEmpty()) { + // 记录流转信息 + circulationList.add(AskCirculationDetail.builder().failInfo("相似度返回内容为空,走大模型").build()); + // 如果没有匹配到,就走大模型 + String answer = aiService.talk(question, medicalRec.getMedicalRecordAi()); + // 记录大模型的流转记录 + buildAiCirculationDetail(circulationList, answer, medicalRec); + // 保存消息到记录表 + saveQaRecord(process.getId(), 2, question, null, answer, circulationList); + return answer; } - talkVideoTtsResultResVO.setVoiceBase64(TtsUtil.ttsTransform(talkVideoTtsResultResVO.getAnswerMessage())); - return talkVideoTtsResultResVO; + QaSimilarityQuestionAnswer similarityResult = first.get(); + // 如果阈值过低,也走大模型 + double thresholdValue = Double.parseDouble(threshold); + if (similarityResult.getMatchScore() < thresholdValue) { + log.info("{}:匹配到的结果阈值过低,走大模型回答", similarityResult); + circulationList.add(AskCirculationDetail.builder() + .failInfo("相似度为:" + similarityResult.getMatchScore() + ",低于配置的阈值:" + threshold + ",走大模型") + .similarityInfo(JSONUtil.toJsonStr(similarityAnswerList)) + .build()); + + String answer = aiService.talk(question, medicalRec.getMedicalRecordAi()); + // 记录流转记录 + buildAiCirculationDetail(circulationList, answer, medicalRec); + saveQaRecord(process.getId(), 2, question, null, answer, circulationList); + return answer; + } + // 根据对应的标准问题,从标准问题表中找到标准问题 + AskTemplateQuestionLibrary library = askTemplateQuestionLibraryService.getById(similarityResult.getLibraryQuestionId()); + if (ObjectUtil.isEmpty(library)) { + log.info("{}:未从问题库中找到答案,走大模型回答", similarityResult); + circulationList.add(AskCirculationDetail.builder() + // 问题库中未找到该问题 + .failInfo("问题库中匹配到的问题为:" + similarityResult.getMatchQuestion() + ",但未找到该标准问题:" + similarityResult.getLibraryQuestionId() + ",走大模型") + .similarityInfo(JSONUtil.toJsonStr(similarityAnswerList)) + .build()); + String answer = aiService.talk(question, medicalRec.getMedicalRecordAi()); + // 记录流转记录 + buildAiCirculationDetail(circulationList, answer, medicalRec); + saveQaRecord(process.getId(), 2, question, null, answer, circulationList); + return answer; + } + // 根据问题找这个病历配置的答案 + AskPatientAnswer askPatientAnswer = askPatientAnswerService.lambdaQuery().eq(AskPatientAnswer::getMedicalId, process.getMedicalRecId()) + .eq(AskPatientAnswer::getLibraryQuestionId, library.getId()).last("limit 1").one(); + // 如果问题的答案没有配置,还是走大模型的回答 + if (ObjectUtil.isEmpty(askPatientAnswer)) { + circulationList.add(AskCirculationDetail.builder() + .failInfo("相似问匹配到了,但未找到对应的病历配置的回答,匹配到的标准问ID为:" + similarityResult.getLibraryQuestionId()) + .similarityInfo(JSONUtil.toJsonStr(similarityAnswerList)) + .build()); + log.info("{}:病历配置,从AskPatientAnswer中未找到回答结果,走大模型", similarityResult.getLibraryQuestionId()); + String answer = aiService.talk(question, medicalRec.getMedicalRecordAi()); + // 记录流转记录 + buildAiCirculationDetail(circulationList, answer, medicalRec); + saveQaRecord(process.getId(), 2, question, null, answer, circulationList); + return answer; + } + // 如果找到了,就走病历配置的内容回答 + String patientAnswer = askPatientAnswer.getAnswer(); + log.info("{}:找到了病历配置的回答语句:{},回答内容:{},走病历回答", similarityResult.getMatchQuestion(), askPatientAnswer.getId(), patientAnswer); + + circulationList.add(AskCirculationDetail.builder() + .answer(patientAnswer) + // 问题库中未找到该问题 + .successInfo("相似问匹配到了,走相似问") + .similarityInfo(JSONUtil.toJsonStr(similarityAnswerList)) + .matchScore(similarityResult.getMatchScore()) + .matchItem(Optional.ofNullable(commonDicService.getById(Long.parseLong(similarityResult.getDictId()))).orElse(new CommonDic()).getNameZhPath()) + .matchSimilarityId(similarityResult.getMatchQuestionId()) + .matchLibraryId(similarityResult.getLibraryQuestionId()) + .matchQuestion(similarityResult.getMatchQuestion()) + .successType(1) + .build()); + saveQaRecord(process.getId(), 1, question, similarityResult.getLibraryQuestionId(), patientAnswer, circulationList); + return patientAnswer; + } + + + private void buildAiCirculationDetail(List detailList, String answer, MedicalRec medicalRec) { + AskCirculationDetail newDetail = AskCirculationDetail.builder().answer(answer) + .successInfo("走大模型进行匹配,获得答案,回复用户").aiMedicalContext(medicalRec.getMedicalRecordAi()) + .successType(2).build(); + detailList.add(newDetail); } - /** - * 保存到AI对话记录表中,方便后期对AI对话记录再进行分类 - */ - private void saveAiRecord(String processId, String question, String answer) { - DiagnosisAiRecord diagnosisAiRecord = new DiagnosisAiRecord(); - diagnosisAiRecord.setProcessId(processId); - diagnosisAiRecord.setQuestion(question); - diagnosisAiRecord.setAnswer(answer); - diagnosisAiRecord.setCreateUserId(UserUtil.getUser().getId()); - diagnosisAiRecord.setUpdateUserId(UserUtil.getUser().getId()); - diagnosisAiRecordService.save(diagnosisAiRecord); + private void saveQaRecord(String processId, Integer matchType, String question, String libraryId, String answer, List circulationList) { + DiagnosisQaRecord record = new DiagnosisQaRecord(); + record.setProcessId(processId); + record.setMatchType(matchType); + record.setQuestionLibraryId(libraryId); + record.setQuestion(question); + record.setAnswer(answer); + record.setCreateUserId(UserUtil.getUser().getId()); + record.insert(); + AtomicInteger atomicInteger = new AtomicInteger(0); + circulationList.forEach(e -> { + e.setRecordId(record.getId()); + e.setCirculationNo(atomicInteger.incrementAndGet()); + e.setQuestion(question); + } + ); + askCirculationDetailService.saveBatch(circulationList); } } From 2b3dd19b8d81a9ad60615b3c9fb139cd2ae963ce Mon Sep 17 00:00:00 2001 From: liu Date: Thu, 6 Jun 2024 18:34:23 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=97=AE=E7=AD=94?= =?UTF-8?q?=E6=B5=81=E7=A8=8B,=E5=AF=B9=E9=97=AE=E7=AD=94=E8=BF=87?= =?UTF-8?q?=E7=A8=8B=E4=B8=AD=E7=9A=84=E6=B6=88=E6=81=AF=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/v3.0.0/step1.sql | 2 -- .../src/main/java/com/supervision/model/DiagnosisQaRecord.java | 3 +++ .../service/impl/AskDiagnosisResultServiceImpl.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docker/v3.0.0/step1.sql b/docker/v3.0.0/step1.sql index 81fe1e80..98213da1 100644 --- a/docker/v3.0.0/step1.sql +++ b/docker/v3.0.0/step1.sql @@ -29,8 +29,6 @@ alter table vp_diagnosis_qa_record drop column question_wav_id; alter table vp_diagnosis_qa_record drop column answer_type; -alter table vp_diagnosis_qa_record drop column answer_id; - alter table vp_diagnosis_qa_record drop column answer_wav_id; create table vp_ask_circulation_detail diff --git a/virtual-patient-model/src/main/java/com/supervision/model/DiagnosisQaRecord.java b/virtual-patient-model/src/main/java/com/supervision/model/DiagnosisQaRecord.java index b1c36fac..2ad67019 100644 --- a/virtual-patient-model/src/main/java/com/supervision/model/DiagnosisQaRecord.java +++ b/virtual-patient-model/src/main/java/com/supervision/model/DiagnosisQaRecord.java @@ -54,6 +54,9 @@ public class DiagnosisQaRecord extends Model implements Seria @Schema(description = "回答") private String answer; + @Schema(description = "回答来源的ID") + private String answerId; + @Schema(description = "是否是证实诊断依据(0否1是)") private Integer basisConfirmFlag; diff --git a/virtual-patient-web/src/main/java/com/supervision/service/impl/AskDiagnosisResultServiceImpl.java b/virtual-patient-web/src/main/java/com/supervision/service/impl/AskDiagnosisResultServiceImpl.java index e06d4db6..eeec68c4 100644 --- a/virtual-patient-web/src/main/java/com/supervision/service/impl/AskDiagnosisResultServiceImpl.java +++ b/virtual-patient-web/src/main/java/com/supervision/service/impl/AskDiagnosisResultServiceImpl.java @@ -170,7 +170,7 @@ public class AskDiagnosisResultServiceImpl implements AskDiagnosisResultService // 初步诊断依据 vp_medical_rec的primarily_diagnosis_criteria basisPrimaryResultResVO.setPreliminaryDiagnosis(medicalRec.getPrimarilyDiagnosisCriteria()); // 根据record记录寻找符合初步诊断依据的项目 - // 2.1 首先获取对话 + // 2.1 首先获取对话记录 List qaRecordList = diagnosisQaRecordService.lambdaQuery().eq(DiagnosisQaRecord::getProcessId, process.getId()).list(); List list = askPatientAnswerService.lambdaQuery() .eq(AskPatientAnswer::getMedicalId, medicalRec.getId()).list();