toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);
toolNames.forEach(toolName -> {
ToolCallback toolCallback = toolCallbackResolver.resolve(toolName);
if (toolCallback != null) {
toolCallbacks.add(toolCallback);
}
});
}
// 2. 通过 mcpClients
if (CollUtil.isNotEmpty(mcpClients) && CollUtil.isNotEmpty(chatRole.getMcpClientNames())) {
chatRole.getMcpClientNames().forEach(mcpClientName -> {
// 2.1 标准化名字,参考 McpClientAutoConfiguration 的 connectedClientName 方法
String finalMcpClientName = this.mcpClientName + " - " + mcpClientName;
// 2.2 匹配对应的 McpSyncClient
mcpClients.forEach(mcpClient -> {
if (ObjUtil.notEqual(mcpClient.getClientInfo().name(), finalMcpClientName)) {
return;
}
ToolCallback[] mcpToolCallBacks = SyncMcpToolCallbackProvider.builder()
.mcpClients(mcpClient)
.build()
.getToolCallbacks();
CollUtil.addAll(toolCallbacks, mcpToolCallBacks);
});
});
}
return toolCallbacks;
}
/**
* 从历史消息中,获得倒序的 n 组消息作为消息上下文
*
* n 组:指的是 user + assistant 形成一组
*
* @param messages 消息列表
* @param conversation 对话
* @param sendReqVO 发送请求
* @return 消息上下文
*/
private List filterContextMessages(List messages,
AiChatConversationDO conversation,
AiChatMessageSendReqVO sendReqVO) {
if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) {
return Collections.emptyList();
}
List contextMessages = new ArrayList<>(conversation.getMaxContexts() * 2);
for (int i = messages.size() - 1; i >= 0; i--) {
AiChatMessageDO assistantMessage = CollUtil.get(messages, i);
if (assistantMessage == null || assistantMessage.getReplyId() == null) {
continue;
}
AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
if (userMessage == null
|| ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
|| StrUtil.isEmpty(assistantMessage.getContent())) {
continue;
}
// 由于后续要 reverse 反转,所以先添加 assistantMessage
contextMessages.add(assistantMessage);
contextMessages.add(userMessage);
// 超过最大上下文,结束
if (contextMessages.size() >= conversation.getMaxContexts() * 2) {
break;
}
}
Collections.reverse(contextMessages);
return contextMessages;
}
private UserMessage buildAttachmentUserMessage(List attachmentUrls) {
if (CollUtil.isEmpty(attachmentUrls)) {
return null;
}
// 读取文件内容
Map attachmentContents = Maps.newLinkedHashMapWithExpectedSize(attachmentUrls.size());
for (String attachmentUrl : attachmentUrls) {
try {
String name = FileNameUtil.getName(attachmentUrl);
String mineType = FileTypeUtils.getMineType(name);
String content;
if (FileTypeUtils.isImage(mineType)) {
// 特殊:图片则转为 Base64
byte[] bytes = HttpUtil.downloadBytes(attachmentUrl);
content = Base64.encode(bytes);
} else {
content = knowledgeDocumentService.readUrl(attachmentUrl);
}
if (StrUtil.isNotEmpty(content)) {
attachmentContents.put(name, content);
}
} catch (Exception e) {
log.error("[buildAttachmentUserMessage][读取附件({}) 发生异常]", attachmentUrl, e);
}
}
if (CollUtil.isEmpty(attachmentContents)) {
return null;
}
// 拼接 UserMessage 消息
String attachment = attachmentContents.entrySet().stream()
.map(entry -> "" + entry.getValue() + "")
.collect(Collectors.joining("\n\n"));
return new UserMessage(String.format(Attachment_USER_MESSAGE_TEMPLATE, attachment));
}
private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
AiModelDO model, Long userId, Long roleId,
MessageType messageType, String content, Boolean useContext,
List knowledgeSegments,
List attachmentUrls,
AiWebSearchResponse webSearchResponse) {
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)
.setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId))
.setAttachmentUrls(attachmentUrls);
if (webSearchResponse != null) {
message.setWebSearchPages(webSearchResponse.getLists());
}
message.setCreateTime(LocalDateTime.now());
chatMessageMapper.insert(message);
return message;
}
@Override
public List getChatMessageListByConversationId(Long conversationId) {
return chatMessageMapper.selectListByConversationId(conversationId);
}
@Override
public void deleteChatMessage(Long id, Long userId) {
// 1. 校验消息存在
AiChatMessageDO message = chatMessageMapper.selectById(id);
if (message == null || ObjUtil.notEqual(message.getUserId(), userId)) {
throw exception(CHAT_MESSAGE_NOT_EXIST);
}
// 2. 执行删除
chatMessageMapper.deleteById(id);
}
@Override
public void deleteChatMessageByConversationId(Long conversationId, Long userId) {
// 1. 校验消息存在
List messages = chatMessageMapper.selectListByConversationId(conversationId);
if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.getFirst().getUserId(), userId)) {
throw exception(CHAT_MESSAGE_NOT_EXIST);
}
// 2. 执行删除
chatMessageMapper.deleteByIds(convertList(messages, AiChatMessageDO::getId));
}
@Override
public void deleteChatMessageByAdmin(Long id) {
// 1. 校验消息存在
AiChatMessageDO message = chatMessageMapper.selectById(id);
if (message == null) {
throw exception(CHAT_MESSAGE_NOT_EXIST);
}
// 2. 执行删除
chatMessageMapper.deleteById(id);
}
@Override
public Map getChatMessageCountMap(Collection conversationIds) {
return chatMessageMapper.selectCountMapByConversationId(conversationIds);
}
@Override
public PageResult getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {
return chatMessageMapper.selectPage(pageReqVO);
}
}