|
@@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
|
|
import cn.iocoder.yudao.framework.ai.core.util.AiUtils;
|
|
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
|
|
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
|
|
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
|
|
@@ -19,6 +18,7 @@ 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.AiModelDO;
|
|
|
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
|
|
|
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
|
|
|
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
|
|
|
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
|
|
@@ -27,6 +27,7 @@ import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchR
|
|
|
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
|
|
|
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
|
|
|
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
|
|
|
+import cn.iocoder.yudao.module.ai.service.model.AiToolService;
|
|
|
import jakarta.annotation.Resource;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.ai.chat.messages.Message;
|
|
@@ -50,6 +51,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
|
|
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
|
|
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
|
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
|
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
|
|
|
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;
|
|
|
|
|
@@ -82,6 +84,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
private AiKnowledgeSegmentService knowledgeSegmentService;
|
|
|
@Resource
|
|
|
private AiKnowledgeDocumentService knowledgeDocumentService;
|
|
|
+ @Resource
|
|
|
+ private AiToolService toolService;
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
|
|
@@ -118,11 +122,13 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
String newContent = chatResponse.getResult().getOutput().getText();
|
|
|
chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
|
|
|
// 3.4 响应结果
|
|
|
- List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class,
|
|
|
+ List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments,
|
|
|
+ AiChatMessageRespVO.KnowledgeSegment.class,
|
|
|
segment -> {
|
|
|
- AiKnowledgeDocumentDO document = knowledgeDocumentService.getKnowledgeDocument(segment.getDocumentId());
|
|
|
- segment.setDocumentName(document != null ? document.getName() : null);
|
|
|
- });
|
|
|
+ AiKnowledgeDocumentDO document = knowledgeDocumentService
|
|
|
+ .getKnowledgeDocument(segment.getDocumentId());
|
|
|
+ segment.setDocumentName(document != null ? document.getName() : null);
|
|
|
+ });
|
|
|
return new AiChatMessageSendRespVO()
|
|
|
.setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
|
|
|
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
|
|
@@ -130,7 +136,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
- public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId) {
|
|
|
+ public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO,
|
|
|
+ Long userId) {
|
|
|
// 1.1 校验对话存在
|
|
|
AiChatConversationDO conversation = chatConversationService
|
|
|
.validateChatConversationExists(sendReqVO.getConversationId());
|
|
@@ -143,7 +150,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
StreamingChatModel chatModel = modalService.getChatModel(model.getId());
|
|
|
|
|
|
// 2. 知识库找回
|
|
|
- List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(), conversation);
|
|
|
+ List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(sendReqVO.getContent(),
|
|
|
+ conversation);
|
|
|
|
|
|
// 3. 插入 user 发送消息
|
|
|
AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
|
|
@@ -167,7 +175,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
if (StrUtil.isEmpty(contentBuffer)) {
|
|
|
segments = BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class,
|
|
|
segment -> TenantUtils.executeIgnore(() -> {
|
|
|
- AiKnowledgeDocumentDO document = knowledgeDocumentService.getKnowledgeDocument(segment.getDocumentId());
|
|
|
+ AiKnowledgeDocumentDO document = knowledgeDocumentService
|
|
|
+ .getKnowledgeDocument(segment.getDocumentId());
|
|
|
segment.setDocumentName(document != null ? document.getName() : null);
|
|
|
}));
|
|
|
}
|
|
@@ -192,7 +201,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
}
|
|
|
|
|
|
private List<AiKnowledgeSegmentSearchRespBO> recallKnowledgeSegment(String content,
|
|
|
- AiChatConversationDO conversation) {
|
|
|
+ AiChatConversationDO conversation) {
|
|
|
// 1. 查询聊天角色
|
|
|
if (conversation == null || conversation.getRoleId() == null) {
|
|
|
return Collections.emptyList();
|
|
@@ -212,8 +221,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
}
|
|
|
|
|
|
private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
|
|
|
- List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
|
|
|
- AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
|
|
|
+ List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
|
|
|
+ AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
|
|
|
List<Message> chatMessages = new ArrayList<>();
|
|
|
// 1.1 System Context 角色设定
|
|
|
if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
|
|
@@ -236,11 +245,18 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference)));
|
|
|
}
|
|
|
|
|
|
- // 2. 构建 ChatOptions 对象
|
|
|
+ // 2.1 查询 tool 工具
|
|
|
+ Set<String> toolNames = null;
|
|
|
+ if (conversation.getRoleId() != null) {
|
|
|
+ AiChatRoleDO chatRole = chatRoleService.getChatRole(conversation.getRoleId());
|
|
|
+ if (chatRole != null && CollUtil.isNotEmpty(chatRole.getToolIds())) {
|
|
|
+ toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 2.2 构建 ChatOptions 对象
|
|
|
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
|
|
|
ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),
|
|
|
- conversation.getTemperature(), conversation.getMaxTokens(),
|
|
|
- SetUtils.asSet("directory_list", "weather_query"));
|
|
|
+ conversation.getTemperature(), conversation.getMaxTokens(), toolNames);
|
|
|
return new Prompt(chatMessages, chatOptions);
|
|
|
}
|
|
|
|
|
@@ -255,8 +271,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
* @return 消息上下文
|
|
|
*/
|
|
|
private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages,
|
|
|
- AiChatConversationDO conversation,
|
|
|
- AiChatMessageSendReqVO sendReqVO) {
|
|
|
+ AiChatConversationDO conversation,
|
|
|
+ AiChatMessageSendReqVO sendReqVO) {
|
|
|
if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) {
|
|
|
return Collections.emptyList();
|
|
|
}
|
|
@@ -285,9 +301,9 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
|
|
|
}
|
|
|
|
|
|
private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
|
|
|
- AiModelDO model, Long userId, Long roleId,
|
|
|
- MessageType messageType, String content, Boolean useContext,
|
|
|
- List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments) {
|
|
|
+ AiModelDO model, Long userId, Long roleId,
|
|
|
+ MessageType messageType, String content, Boolean useContext,
|
|
|
+ List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments) {
|
|
|
AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
|
|
|
.setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
|
|
|
.setType(messageType.getValue()).setContent(content).setUseContext(useContext)
|