【功能新增】AI:聊天记录返回时,增加 segments
This commit is contained in:
parent
b709af11a1
commit
32e1ef4da8
|
@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - AI 聊天消息发送 Response VO")
|
@Schema(description = "管理后台 - AI 聊天消息发送 Response VO")
|
||||||
@Data
|
@Data
|
||||||
|
@ -28,6 +29,12 @@ public class AiChatMessageSendRespVO {
|
||||||
@Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
|
@Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
|
@Schema(description = "知识库段落编号数组", example = "[1,2,3]")
|
||||||
|
private List<Long> segmentIds;
|
||||||
|
|
||||||
|
@Schema(description = "知识库段落数组")
|
||||||
|
private List<AiChatMessageRespVO.KnowledgeSegment> segments;
|
||||||
|
|
||||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
private LocalDateTime createTime;
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,17 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
|
||||||
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
|
||||||
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
|
||||||
|
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
|
||||||
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
|
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
|
||||||
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
||||||
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
|
||||||
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
|
||||||
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
|
||||||
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
|
||||||
|
@ -76,6 +79,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||||
private AiModelService modalService;
|
private AiModelService modalService;
|
||||||
@Resource
|
@Resource
|
||||||
private AiKnowledgeSegmentService knowledgeSegmentService;
|
private AiKnowledgeSegmentService knowledgeSegmentService;
|
||||||
|
@Resource
|
||||||
|
private AiKnowledgeDocumentService knowledgeDocumentService;
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
|
public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
|
||||||
|
@ -108,18 +113,23 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||||
Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
|
Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, model, sendReqVO);
|
||||||
ChatResponse chatResponse = chatModel.call(prompt);
|
ChatResponse chatResponse = chatModel.call(prompt);
|
||||||
|
|
||||||
// 3.3 段式返回
|
// 3.3 更新响应内容
|
||||||
String newContent = chatResponse.getResult().getOutput().getText();
|
String newContent = chatResponse.getResult().getOutput().getText();
|
||||||
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
|
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
|
||||||
|
// 3.4 响应结果
|
||||||
|
List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class,
|
||||||
|
segment -> {
|
||||||
|
AiKnowledgeDocumentDO document = knowledgeDocumentService.getKnowledgeDocument(segment.getDocumentId());
|
||||||
|
segment.setDocumentName(document != null ? document.getName() : null);
|
||||||
|
});
|
||||||
return new AiChatMessageSendRespVO()
|
return new AiChatMessageSendRespVO()
|
||||||
.setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
.setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
||||||
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
|
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
|
||||||
.setContent(newContent));
|
.setContent(newContent).setSegments(segments));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO,
|
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) {
|
||||||
Long userId) {
|
|
||||||
// 1.1 校验对话存在
|
// 1.1 校验对话存在
|
||||||
AiChatConversationDO conversation = chatConversationService
|
AiChatConversationDO conversation = chatConversationService
|
||||||
.validateChatConversationExists(sendReqVO.getConversationId());
|
.validateChatConversationExists(sendReqVO.getConversationId());
|
||||||
|
@ -132,8 +142,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||||
StreamingChatModel chatModel = modalService.getChatModel(model.getId());
|
StreamingChatModel chatModel = modalService.getChatModel(model.getId());
|
||||||
|
|
||||||
// 2. 知识库找回
|
// 2. 知识库找回
|
||||||
List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
|
List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), conversation);
|
||||||
conversation);
|
|
||||||
|
|
||||||
// 3. 插入 user 发送消息
|
// 3. 插入 user 发送消息
|
||||||
AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
|
AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
|
||||||
|
@ -152,14 +161,23 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
||||||
// 4.3 流式返回
|
// 4.3 流式返回
|
||||||
StringBuffer contentBuffer = new StringBuffer();
|
StringBuffer contentBuffer = new StringBuffer();
|
||||||
return streamResponse.map(chunk -> {
|
return streamResponse.map(chunk -> {
|
||||||
|
// 处理知识库的返回,只有首次才有
|
||||||
|
List<AiChatMessageRespVO.KnowledgeSegment> segments = null;
|
||||||
|
if (StrUtil.isEmpty(contentBuffer)) {
|
||||||
|
segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class,
|
||||||
|
segment -> TenantUtils.executeIgnore(() -> {
|
||||||
|
AiKnowledgeDocumentDO document = knowledgeDocumentService.getKnowledgeDocument(segment.getDocumentId());
|
||||||
|
segment.setDocumentName(document != null ? document.getName() : null);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// 响应结果
|
||||||
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
|
String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
|
||||||
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
newContent = StrUtil.nullToDefault(newContent, ""); // 避免 null 的 情况
|
||||||
contentBuffer.append(newContent);
|
contentBuffer.append(newContent);
|
||||||
// 响应结果
|
|
||||||
return success(new AiChatMessageSendRespVO()
|
return success(new AiChatMessageSendRespVO()
|
||||||
.setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
.setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
||||||
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
|
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
|
||||||
.setContent(newContent)));
|
.setContent(newContent).setSegments(segments)));
|
||||||
}).doOnComplete(() -> {
|
}).doOnComplete(() -> {
|
||||||
// 忽略租户,因为 Flux 异步无法透传租户
|
// 忽略租户,因为 Flux 异步无法透传租户
|
||||||
TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
|
TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
|
||||||
|
|
Loading…
Reference in New Issue