diff --git a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java index 1896ff11..c0f49754 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java +++ b/main/manager-api/src/main/java/xiaozhi/common/exception/ErrorCode.java @@ -228,4 +228,16 @@ public interface ErrorCode { // 智能体模板相关错误码(补充) int AGENT_TEMPLATE_NOT_FOUND = 10183; // 默认智能体未找到 + + // 知识库适配器相关错误码 + int RAG_ADAPTER_TYPE_NOT_SUPPORTED = 10184; // 不支持的适配器类型 + int RAG_CONFIG_VALIDATION_FAILED = 10185; // RAG配置验证失败 + int RAG_ADAPTER_CREATION_FAILED = 10186; // 适配器创建失败 + int RAG_ADAPTER_INIT_FAILED = 10187; // 适配器初始化失败 + int RAG_ADAPTER_CONNECTION_FAILED = 10188; // 适配器连接测试失败 + int RAG_ADAPTER_OPERATION_FAILED = 10189; // 适配器操作失败 + int RAG_ADAPTER_NOT_FOUND = 10190; // 适配器未找到 + int RAG_ADAPTER_CACHE_ERROR = 10191; // 适配器缓存错误 + int RAG_ADAPTER_TYPE_NOT_FOUND = 10192; // 适配器类型未找到 + } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/KnowledgeBaseConfig.java b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/KnowledgeBaseConfig.java new file mode 100644 index 00000000..3b34d348 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/config/KnowledgeBaseConfig.java @@ -0,0 +1,22 @@ +package xiaozhi.modules.knowledge.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapterFactory; + +/** + * 知识库配置类 + * 配置知识库相关的Bean + */ +@Configuration +public class KnowledgeBaseConfig { + + /** + * 提供KnowledgeBaseAdapterFactory的Bean实例 + * @return KnowledgeBaseAdapterFactory实例 + */ + @Bean + public KnowledgeBaseAdapterFactory knowledgeBaseAdapterFactory() { + return new KnowledgeBaseAdapterFactory(); + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeFilesDTO.java b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeFilesDTO.java index 84774bdd..ccf56447 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeFilesDTO.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/dto/KnowledgeFilesDTO.java @@ -77,7 +77,7 @@ public class KnowledgeFilesDTO implements Serializable { return STATUS_UNSTART; } - // 根据run字段的值直接映射到对应的状态码 + // RAGFlow根据run字段的值直接映射到对应的状态码 switch (run.toUpperCase()) { case "RUNNING": return STATUS_RUNNING; diff --git a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapter.java b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapter.java new file mode 100644 index 00000000..0c2d50ed --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapter.java @@ -0,0 +1,200 @@ +package xiaozhi.modules.knowledge.rag; + +import java.util.List; +import java.util.Map; + +import org.springframework.web.multipart.MultipartFile; + +import xiaozhi.common.page.PageData; +import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO; + +/** + * 知识库API适配器抽象基类 + * 定义通用的知识库操作接口,支持多种后端API实现 + */ +public abstract class KnowledgeBaseAdapter { + + /** + * 获取适配器类型标识 + * + * @return 适配器类型(如:ragflow, milvus, pinecone等) + */ + public abstract String getAdapterType(); + + /** + * 初始化适配器配置 + * + * @param config 配置参数 + */ + public abstract void initialize(Map config); + + /** + * 验证配置是否有效 + * + * @param config 配置参数 + * @return 验证结果 + */ + public abstract boolean validateConfig(Map config); + + /** + * 分页查询文档列表 + * + * @param datasetId 知识库ID + * @param queryParams 查询参数 + * @param page 页码 + * @param limit 每页数量 + * @return 分页数据 + */ + public abstract PageData getDocumentList(String datasetId, + Map queryParams, + Integer page, + Integer limit); + + /** + * 根据文档ID获取文档详情 + * + * @param datasetId 知识库ID + * @return 文档详情 + */ + public abstract KnowledgeFilesDTO getDocumentById(String datasetId, String documentId); + + /** + * 上传文档到知识库 + * + * @param datasetId 知识库ID + * @param file 上传的文件 + * @param name 文档名称 + * @param metaFields 元数据字段 + * @param chunkMethod 分块方法 + * @param parserConfig 解析器配置 + * @return 上传的文档信息 + */ + public abstract KnowledgeFilesDTO uploadDocument(String datasetId, + MultipartFile file, + String name, + Map metaFields, + String chunkMethod, + Map parserConfig); + + /** + * 根据状态分页查询文档列表 + * + * @param datasetId 知识库ID + * @param status 文档解析状态 + * @param page 页码 + * @param limit 每页数量 + * @return 分页数据 + */ + public abstract PageData getDocumentListByStatus(String datasetId, + Integer status, + Integer page, + Integer limit); + + /** + * 删除文档 + * + * @param datasetId 知识库ID + * @param documentId 文档ID + */ + public abstract void deleteDocument(String datasetId, String documentId); + + /** + * 解析文档(切块) + * + * @param datasetId 知识库ID + * @param documentIds 文档ID列表 + * @return 解析结果 + */ + public abstract boolean parseDocuments(String datasetId, List documentIds); + + /** + * 列出指定文档的切片 + * + * @param datasetId 知识库ID + * @param documentId 文档ID + * @param keywords 关键词过滤 + * @param page 页码 + * @param pageSize 每页数量 + * @param chunkId 切片ID + * @return 切片列表信息 + */ + public abstract Map listChunks(String datasetId, + String documentId, + String keywords, + Integer page, + Integer pageSize, + String chunkId); + + /** + * 召回测试 - 从知识库中检索相关切片 + * + * @param question 用户查询 + * @param datasetIds 数据集ID列表 + * @param documentIds 文档ID列表 + * @param retrievalParams 检索参数 + * @return 召回测试结果 + */ + public abstract Map retrievalTest(String question, + List datasetIds, + List documentIds, + Map retrievalParams); + + /** + * 测试连接 + * + * @return 连接测试结果 + */ + public abstract boolean testConnection(); + + /** + * 获取适配器状态信息 + * + * @return 状态信息 + */ + public abstract Map getStatus(); + + /** + * 获取支持的配置参数 + * + * @return 配置参数说明 + */ + public abstract Map getSupportedConfig(); + + /** + * 获取默认配置 + * + * @return 默认配置 + */ + public abstract Map getDefaultConfig(); + + /** + * 创建数据集 + * + * @param createParams 创建参数 + * @return 数据集ID + */ + public abstract String createDataset(Map createParams); + + /** + * 更新数据集 + * + * @param datasetId 数据集ID + * @param updateParams 更新参数 + */ + public abstract void updateDataset(String datasetId, Map updateParams); + + /** + * 删除数据集 + * + * @param datasetId 数据集ID + */ + public abstract void deleteDataset(String datasetId); + + /** + * 获取数据集的文档数量 + * + * @param datasetId 数据集ID + * @return 文档数量 + */ + public abstract Integer getDocumentCount(String datasetId); +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapterFactory.java b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapterFactory.java new file mode 100644 index 00000000..42fb4ca9 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/KnowledgeBaseAdapterFactory.java @@ -0,0 +1,197 @@ +package xiaozhi.modules.knowledge.rag; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import lombok.extern.slf4j.Slf4j; +import xiaozhi.common.exception.ErrorCode; +import xiaozhi.common.exception.RenException; + +/** + * 知识库适配器工厂类 + * 负责创建和管理不同类型的知识库API适配器 + */ +@Slf4j +public class KnowledgeBaseAdapterFactory { + + // 注册的适配器类型映射 + private static final Map> adapterRegistry = new HashMap<>(); + + // 适配器实例缓存 + private static final Map adapterCache = new ConcurrentHashMap<>(); + + static { + // 注册内置适配器类型 + registerAdapter("ragflow", xiaozhi.modules.knowledge.rag.impl.RAGFlowAdapter.class); + // 可以在这里注册更多适配器类型 + } + + /** + * 注册新的适配器类型 + * + * @param adapterType 适配器类型标识 + * @param adapterClass 适配器类 + */ + public static void registerAdapter(String adapterType, Class adapterClass) { + if (adapterRegistry.containsKey(adapterType)) { + log.warn("适配器类型 '{}' 已存在,将被覆盖", adapterType); + } + adapterRegistry.put(adapterType, adapterClass); + log.info("注册适配器类型: {} -> {}", adapterType, adapterClass.getSimpleName()); + } + + /** + * 获取适配器实例 + * + * @param adapterType 适配器类型 + * @param config 配置参数 + * @return 适配器实例 + */ + public static KnowledgeBaseAdapter getAdapter(String adapterType, Map config) { + String cacheKey = buildCacheKey(adapterType, config); + + // 检查缓存中是否已存在实例 + if (adapterCache.containsKey(cacheKey)) { + log.debug("从缓存获取适配器实例: {}", cacheKey); + return adapterCache.get(cacheKey); + } + + // 创建新的适配器实例 + KnowledgeBaseAdapter adapter = createAdapter(adapterType, config); + + // 缓存适配器实例 + adapterCache.put(cacheKey, adapter); + log.info("创建并缓存适配器实例: {}", cacheKey); + + return adapter; + } + + /** + * 获取适配器实例(无配置) + * + * @param adapterType 适配器类型 + * @return 适配器实例 + */ + public static KnowledgeBaseAdapter getAdapter(String adapterType) { + return getAdapter(adapterType, null); + } + + /** + * 获取所有已注册的适配器类型 + * + * @return 适配器类型集合 + */ + public static Set getRegisteredAdapterTypes() { + return adapterRegistry.keySet(); + } + + /** + * 检查适配器类型是否已注册 + * + * @param adapterType 适配器类型 + * @return 是否已注册 + */ + public static boolean isAdapterTypeRegistered(String adapterType) { + return adapterRegistry.containsKey(adapterType); + } + + /** + * 清除适配器缓存 + */ + public static void clearCache() { + int cacheSize = adapterCache.size(); + adapterCache.clear(); + log.info("清除适配器缓存,共清除 {} 个实例", cacheSize); + } + + /** + * 移除特定适配器类型的缓存 + * + * @param adapterType 适配器类型 + */ + public static void removeCacheByType(String adapterType) { + int removedCount = 0; + for (String cacheKey : adapterCache.keySet()) { + if (cacheKey.startsWith(adapterType + "@")) { + adapterCache.remove(cacheKey); + removedCount++; + } + } + log.info("移除适配器类型 '{}' 的缓存,共移除 {} 个实例", adapterType, removedCount); + } + + /** + * 获取适配器工厂状态信息 + * + * @return 状态信息 + */ + public static Map getFactoryStatus() { + Map status = new HashMap<>(); + status.put("registeredAdapterTypes", adapterRegistry.keySet()); + status.put("cachedAdapterCount", adapterCache.size()); + status.put("cacheKeys", adapterCache.keySet()); + return status; + } + + /** + * 创建适配器实例 + * + * @param adapterType 适配器类型 + * @param config 配置参数 + * @return 适配器实例 + */ + private static KnowledgeBaseAdapter createAdapter(String adapterType, Map config) { + if (!adapterRegistry.containsKey(adapterType)) { + throw new RenException(ErrorCode.RAG_ADAPTER_TYPE_NOT_SUPPORTED, + "不支持的适配器类型: " + adapterType); + } + + try { + Class adapterClass = adapterRegistry.get(adapterType); + KnowledgeBaseAdapter adapter = adapterClass.getDeclaredConstructor().newInstance(); + + // 初始化适配器 + if (config != null) { + adapter.initialize(config); + + // 验证配置 + if (!adapter.validateConfig(config)) { + throw new RenException(ErrorCode.RAG_CONFIG_VALIDATION_FAILED, + "适配器配置验证失败: " + adapterType); + } + } + + log.info("成功创建适配器实例: {}", adapterType); + return adapter; + + } catch (Exception e) { + log.error("创建适配器实例失败: {}", adapterType, e); + throw new RenException(ErrorCode.RAG_ADAPTER_CREATION_FAILED, + "创建适配器失败: " + adapterType + ", 错误: " + e.getMessage()); + } + } + + /** + * 构建缓存键 + * + * @param adapterType 适配器类型 + * @param config 配置参数 + * @return 缓存键 + */ + private static String buildCacheKey(String adapterType, Map config) { + if (config == null || config.isEmpty()) { + return adapterType + "@default"; + } + + // 基于配置参数生成缓存键 + StringBuilder keyBuilder = new StringBuilder(adapterType + "@"); + + // 使用配置的哈希值作为缓存键的一部分 + int configHash = config.hashCode(); + keyBuilder.append(configHash); + + return keyBuilder.toString(); + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/impl/RAGFlowAdapter.java b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/impl/RAGFlowAdapter.java new file mode 100644 index 00000000..2da8cc7e --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/rag/impl/RAGFlowAdapter.java @@ -0,0 +1,1155 @@ +package xiaozhi.modules.knowledge.rag.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.io.AbstractResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.util.UriComponentsBuilder; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.extern.slf4j.Slf4j; +import xiaozhi.common.exception.ErrorCode; +import xiaozhi.common.exception.RenException; +import xiaozhi.common.page.PageData; +import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO; +import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapter; + +/** + * RAGFlow知识库适配器实现 + */ +@Slf4j +public class RAGFlowAdapter extends KnowledgeBaseAdapter { + + private static final String ADAPTER_TYPE = "ragflow"; + + private RestTemplate restTemplate; + private ObjectMapper objectMapper; + private Map config; + + public RAGFlowAdapter() { + this.restTemplate = new RestTemplate(); + this.objectMapper = new ObjectMapper(); + } + + @Override + public String getAdapterType() { + return ADAPTER_TYPE; + } + + @Override + public void initialize(Map config) { + this.config = config; + log.info("RAGFlow适配器初始化完成,配置参数: {}", config.keySet()); + } + + @Override + public boolean validateConfig(Map config) { + if (config == null) { + throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND); + } + + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + if (StringUtils.isBlank(baseUrl)) { + throw new RenException(ErrorCode.RAG_API_ERROR_URL_NULL); + } + + if (StringUtils.isBlank(apiKey)) { + throw new RenException(ErrorCode.RAG_API_ERROR_API_KEY_NULL); + } + + if (apiKey.contains("你")) { + throw new RenException(ErrorCode.RAG_API_ERROR_API_KEY_INVALID); + } + + if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) { + throw new RenException(ErrorCode.RAG_API_ERROR_URL_INVALID); + } + + return true; + } + + @Override + public PageData getDocumentList(String datasetId, Map queryParams, Integer page, + Integer limit) { + try { + log.info("=== RAGFlow适配器开始获取文档列表 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents"; + + // 构建查询参数 + StringBuilder urlBuilder = new StringBuilder(url); + List params = new ArrayList<>(); + + // 基本分页参数 + if (page > 0) { + params.add("page=" + page); + } + if (limit > 0) { + params.add("page_size=" + limit); + } + + // 查询参数处理 + if (queryParams != null) { + // 关键词搜索 + if (queryParams.containsKey("name")) { + params.add("keywords=" + queryParams.get("name")); + } + + // 排序参数 + if (queryParams.containsKey("orderby")) { + String orderby = (String) queryParams.get("orderby"); + if ("create_time".equals(orderby) || "update_time".equals(orderby)) { + params.add("orderby=" + orderby); + } + } + + // 排序方向 + if (queryParams.containsKey("desc")) { + Boolean desc = (Boolean) queryParams.get("desc"); + params.add("desc=" + (desc != null ? desc : true)); + } + + // 文档ID过滤 + if (queryParams.containsKey("id")) { + params.add("id=" + queryParams.get("id")); + } + + // 文档名称过滤 + if (queryParams.containsKey("documentName")) { + params.add("name=" + queryParams.get("documentName")); + } + + // 创建时间范围过滤 + if (queryParams.containsKey("createTimeFrom")) { + Long createTimeFrom = (Long) queryParams.get("createTimeFrom"); + if (createTimeFrom != null && createTimeFrom > 0) { + params.add("create_time_from=" + createTimeFrom); + } + } + + if (queryParams.containsKey("createTimeTo")) { + Long createTimeTo = (Long) queryParams.get("createTimeTo"); + if (createTimeTo != null && createTimeTo > 0) { + params.add("create_time_to=" + createTimeTo); + } + } + + // 文件后缀过滤 + if (queryParams.containsKey("suffix")) { + Object suffixObj = queryParams.get("suffix"); + if (suffixObj instanceof String) { + params.add("suffix=" + suffixObj); + } else if (suffixObj instanceof List) { + @SuppressWarnings("unchecked") + List suffixes = (List) suffixObj; + if (!suffixes.isEmpty()) { + params.add("suffix=" + String.join(",", suffixes)); + } + } + } + + // 处理状态过滤 + if (queryParams.containsKey("run")) { + Object runObj = queryParams.get("run"); + if (runObj instanceof String) { + params.add("run=" + runObj); + } else if (runObj instanceof Integer) { + params.add("run=" + runObj); + } else if (runObj instanceof List) { + @SuppressWarnings("unchecked") + List runStatuses = (List) runObj; + if (!runStatuses.isEmpty()) { + List runParams = new ArrayList<>(); + for (Object status : runStatuses) { + if (status != null) { + runParams.add(status.toString()); + } + } + params.add("run=" + String.join(",", runParams)); + } + } + } + } + + if (!params.isEmpty()) { + urlBuilder.append("?").append(String.join("&", params)); + } + + url = urlBuilder.toString(); + log.debug("RAGFlow API请求URL: {}", url); + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + HttpEntity requestEntity = new HttpEntity<>(headers); + + // 发送GET请求 + log.info("发送GET请求到RAGFlow API获取文档列表..."); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + log.debug("RAGFlow API原始响应: {}", responseBody); + + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + if (code != null && code == 0) { + Object dataObj = responseMap.get("data"); + log.debug("RAGFlow API返回的data字段: {}", dataObj); + return parseDocumentListResponse(dataObj, page, limit); + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, errorDetail); + } + + } catch (Exception e) { + log.error("RAGFlow适配器获取文档列表失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器获取文档列表操作结束 ==="); + } + } + + @Override + public KnowledgeFilesDTO getDocumentById(String datasetId, String documentId) { + // 实现根据ID获取文档详情的逻辑 + // 这里需要调用RAGFlow API获取特定文档的详细信息 + throw new UnsupportedOperationException(); + } + + @Override + public KnowledgeFilesDTO uploadDocument(String datasetId, MultipartFile file, String name, + Map metaFields, String chunkMethod, + Map parserConfig) { + try { + log.info("=== RAGFlow适配器开始文档上传操作 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents"; + + // 构建多部分请求 + MultiValueMap body = new LinkedMultiValueMap<>(); + + // 添加文件 + body.add("file", new MultipartFileResource(file)); + + // 添加其他参数 + if (StringUtils.isNotBlank(name)) { + body.add("name", name); + } + + if (metaFields != null && !metaFields.isEmpty()) { + body.add("meta", objectMapper.writeValueAsString(metaFields)); + } + + if (StringUtils.isNotBlank(chunkMethod)) { + body.add("chunk_method", chunkMethod); + } + + if (parserConfig != null && !parserConfig.isEmpty()) { + body.add("parser_config", objectMapper.writeValueAsString(parserConfig)); + } + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + headers.set("Authorization", "Bearer " + apiKey); + + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + // 发送POST请求 + log.info("发送POST请求到RAGFlow API上传文档..."); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + log.debug("RAGFlow API获取文档数量原始响应: {}", responseBody); + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + log.debug("RAGFlow API获取文档数量响应码: {}, data字段: {}", code, responseMap.get("data")); + + if (code != null && code == 0) { + Object dataObj = responseMap.get("data"); + return parseUploadResponse(dataObj, datasetId, file); + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, errorDetail); + } + + } catch (Exception e) { + log.error("RAGFlow适配器文档上传失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器文档上传操作结束 ==="); + } + } + + @Override + public PageData getDocumentListByStatus(String datasetId, Integer status, Integer page, + Integer limit) { + try { + log.info("=== RAGFlow适配器开始根据状态获取文档列表 ==="); + log.info("datasetId: {}, status: {}, page: {}, limit: {}", datasetId, status, page, limit); + + // 构建查询参数,包含状态过滤 + Map queryParams = new HashMap<>(); + + // 将状态码转换为RAGFlow API支持的格式 + if (status != null) { + // 根据KnowledgeFilesDTO中的状态常量映射 + String runStatus; + switch (status) { + case 0: // STATUS_UNSTART + runStatus = "UNSTART"; + break; + case 1: // STATUS_RUNNING + runStatus = "RUNNING"; + break; + case 2: // STATUS_CANCEL + runStatus = "CANCEL"; + break; + case 3: // STATUS_DONE + runStatus = "DONE"; + break; + case 4: // STATUS_FAIL + runStatus = "FAIL"; + break; + default: + runStatus = status.toString(); // 使用数字格式 + } + queryParams.put("run", runStatus); + log.debug("状态过滤参数: run={}", runStatus); + } + + // 调用通用的文档列表获取方法 + return getDocumentList(datasetId, queryParams, page, limit); + + } catch (Exception e) { + log.error("RAGFlow适配器根据状态获取文档列表失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器根据状态获取文档列表操作结束 ==="); + } + } + + @Override + public void deleteDocument(String datasetId, String documentId) { + try { + log.info("=== RAGFlow适配器开始删除文档 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents"; + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + // 构建请求体 - 根据API文档,需要传递文档ID列表 + Map requestBody = new HashMap<>(); + List documentIds = new ArrayList<>(); + documentIds.add(documentId); + requestBody.put("ids", documentIds); + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // 发送DELETE请求 + log.info("发送DELETE请求到RAGFlow API删除文档..."); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, + String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + if (code != null && code == 0) { + log.info("文档删除成功: documentId={}, datasetId={}", documentId, datasetId); + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, errorDetail); + } + + } catch (Exception e) { + log.error("RAGFlow适配器删除文档失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器删除文档操作结束 ==="); + } + } + + @Override + public boolean parseDocuments(String datasetId, List documentIds) { + try { + log.info("=== RAGFlow适配器开始解析文档 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/datasets/" + datasetId + "/chunks"; + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + // 构建请求体 - 根据API文档,需要传递文档ID列表 + Map requestBody = new HashMap<>(); + requestBody.put("document_ids", documentIds); + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // 发送POST请求 + log.info("发送POST请求到RAGFlow API解析文档..."); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, + String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + if (code != null && code == 0) { + log.info("文档解析成功: datasetId={}, 文档数量={}", datasetId, documentIds.size()); + return true; + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, errorDetail); + } + + } catch (Exception e) { + log.error("RAGFlow适配器解析文档失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器解析文档操作结束 ==="); + } + } + + @Override + public Map listChunks(String datasetId, String documentId, String keywords, + Integer page, Integer pageSize, String chunkId) { + try { + log.info("=== RAGFlow适配器开始列出切片 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + // 构建URL和查询参数 + String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents/" + documentId + "/chunks"; + + // 构建查询参数 + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url); + if (StringUtils.isNotBlank(keywords)) { + builder.queryParam("keywords", keywords); + } + if (page != null) { + builder.queryParam("page", page); + } + if (pageSize != null) { + builder.queryParam("page_size", pageSize); + } + if (StringUtils.isNotBlank(chunkId)) { + builder.queryParam("id", chunkId); + } + + String finalUrl = builder.toUriString(); + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + apiKey); + + HttpEntity requestEntity = new HttpEntity<>(headers); + + // 发送GET请求 + log.info("发送GET请求到RAGFlow API列出切片..."); + ResponseEntity response = restTemplate.exchange(finalUrl, HttpMethod.GET, requestEntity, + String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + if (code != null && code == 0) { + Map data = (Map) responseMap.get("data"); + + // 解析切片数据 + List> chunks = (List>) data.get("chunks"); + Map doc = (Map) data.get("doc"); + Integer total = (Integer) data.get("total"); + + // 构建返回结果 + Map result = new HashMap<>(); + result.put("chunks", chunks); + result.put("document", doc); + result.put("total", total); + + log.info("切片列表获取成功: datasetId={}, documentId={}, 切片数量={}", datasetId, documentId, total); + return result; + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, errorDetail); + } + + } catch (Exception e) { + log.error("RAGFlow适配器列出切片失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器列出切片操作结束 ==="); + } + } + + @Override + public Map retrievalTest(String question, List datasetIds, List documentIds, + Map retrievalParams) { + try { + log.info("=== RAGFlow适配器开始召回测试 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/retrieval"; + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + // 构建请求体 + Map requestBody = new HashMap<>(); + + // 必需参数 + if (StringUtils.isBlank(question)) { + throw new RenException(ErrorCode.RAG_API_ERROR, "问题内容不能为空"); + } + requestBody.put("question", question); + + // 可选参数 + if (datasetIds != null && !datasetIds.isEmpty()) { + requestBody.put("dataset_ids", datasetIds); + } + if (documentIds != null && !documentIds.isEmpty()) { + requestBody.put("document_ids", documentIds); + } + + // 处理检索参数 + if (retrievalParams != null) { + if (retrievalParams.containsKey("page")) { + requestBody.put("page", retrievalParams.get("page")); + } + if (retrievalParams.containsKey("page_size")) { + requestBody.put("page_size", retrievalParams.get("page_size")); + } + if (retrievalParams.containsKey("similarity_threshold")) { + requestBody.put("similarity_threshold", retrievalParams.get("similarity_threshold")); + } + if (retrievalParams.containsKey("vector_similarity_weight")) { + requestBody.put("vector_similarity_weight", retrievalParams.get("vector_similarity_weight")); + } + if (retrievalParams.containsKey("top_k")) { + requestBody.put("top_k", retrievalParams.get("top_k")); + } + if (retrievalParams.containsKey("rerank_id")) { + requestBody.put("rerank_id", retrievalParams.get("rerank_id")); + } + if (retrievalParams.containsKey("keyword")) { + requestBody.put("keyword", retrievalParams.get("keyword")); + } + if (retrievalParams.containsKey("highlight")) { + requestBody.put("highlight", retrievalParams.get("highlight")); + } + if (retrievalParams.containsKey("cross_languages")) { + requestBody.put("cross_languages", retrievalParams.get("cross_languages")); + } + if (retrievalParams.containsKey("metadata_condition")) { + requestBody.put("metadata_condition", retrievalParams.get("metadata_condition")); + } + } + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // 发送POST请求 + log.info("发送POST请求到RAGFlow API进行召回测试..."); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + if (code != null && code == 0) { + Map data = (Map) responseMap.get("data"); + + // 解析召回结果 + List> chunks = (List>) data.get("chunks"); + List> docAggs = (List>) data.get("doc_aggs"); + Integer total = (Integer) data.get("total"); + + // 构建返回结果 + Map result = new HashMap<>(); + result.put("chunks", chunks); + result.put("doc_aggs", docAggs); + result.put("total", total); + + log.info("召回测试成功: 问题='{}', 召回切片数量={}", question, total); + return result; + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, errorDetail); + } + + } catch (Exception e) { + log.error("RAGFlow适配器召回测试失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器召回测试操作结束 ==="); + } + } + + @Override + public boolean testConnection() { + try { + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/health"; + + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + apiKey); + + HttpEntity requestEntity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); + + return response.getStatusCode().is2xxSuccessful(); + + } catch (Exception e) { + log.error("RAGFlow适配器连接测试失败: {}", e.getMessage()); + return false; + } + } + + @Override + public Map getStatus() { + Map status = new HashMap<>(); + status.put("adapterType", getAdapterType()); + status.put("configKeys", config != null ? config.keySet() : "未配置"); + status.put("connectionTest", testConnection()); + status.put("lastChecked", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); + return status; + } + + @Override + public Map getSupportedConfig() { + Map supportedConfig = new HashMap<>(); + supportedConfig.put("base_url", "RAGFlow API基础URL"); + supportedConfig.put("api_key", "RAGFlow API密钥"); + supportedConfig.put("timeout", "请求超时时间(毫秒)"); + return supportedConfig; + } + + @Override + public Map getDefaultConfig() { + Map defaultConfig = new HashMap<>(); + defaultConfig.put("timeout", 30000); + return defaultConfig; + } + + @Override + public String createDataset(Map createParams) { + try { + log.info("=== RAGFlow适配器开始创建数据集 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/datasets"; + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + // 构建请求体 + Map requestBody = new HashMap<>(); + if (createParams.containsKey("name")) { + requestBody.put("name", createParams.get("name")); + } + if (createParams.containsKey("description")) { + requestBody.put("description", createParams.get("description")); + } + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // 发送POST请求 + log.info("发送POST请求到RAGFlow API创建数据集..."); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + if (code != null && code == 0) { + Map data = (Map) responseMap.get("data"); + String datasetId = (String) data.get("id"); + log.info("数据集创建成功,datasetId: {}", datasetId); + return datasetId; + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); + } + + } catch (Exception e) { + log.error("RAGFlow适配器创建数据集失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器创建数据集操作结束 ==="); + } + } + + @Override + public void updateDataset(String datasetId, Map updateParams) { + try { + log.info("=== RAGFlow适配器开始更新数据集 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/datasets/" + datasetId; + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + // 构建请求体 + Map requestBody = new HashMap<>(); + if (updateParams.containsKey("name")) { + requestBody.put("name", updateParams.get("name")); + } + if (updateParams.containsKey("description")) { + requestBody.put("description", updateParams.get("description")); + } + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // 发送PUT请求 + log.info("发送PUT请求到RAGFlow API更新数据集..."); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + if (code != null && code == 0) { + log.info("数据集更新成功,datasetId: {}", datasetId); + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); + } + + } catch (Exception e) { + log.error("RAGFlow适配器更新数据集失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器更新数据集操作结束 ==="); + } + } + + @Override + public void deleteDataset(String datasetId) { + try { + log.info("=== RAGFlow适配器开始删除数据集 ==="); + + validateConfig(config); + String baseUrl = (String) config.get("base_url"); + String apiKey = (String) config.get("api_key"); + + String url = baseUrl + "/api/v1/datasets"; + + // 设置请求头 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + apiKey); + + // 构建请求体 - 根据API文档,需要传递数据集ID列表 + Map requestBody = new HashMap<>(); + List datasetIds = new ArrayList<>(); + datasetIds.add(datasetId); + requestBody.put("ids", datasetIds); + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // 发送DELETE请求 + log.info("发送DELETE请求到RAGFlow API删除数据集..."); + ResponseEntity response = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, + String.class); + + log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); + throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); + } + + String responseBody = response.getBody(); + Map responseMap = objectMapper.readValue(responseBody, Map.class); + Integer code = (Integer) responseMap.get("code"); + + if (code != null && code == 0) { + log.info("数据集删除成功,datasetId: {}", datasetId); + } else { + String apiMessage = (String) responseMap.get("message"); + String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; + log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", code, errorDetail); + throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); + } + + } catch (Exception e) { + log.error("RAGFlow适配器删除数据集失败: {}", e.getMessage(), e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } finally { + log.info("=== RAGFlow适配器删除数据集操作结束 ==="); + } + } + + /** + * 通过文档列表接口获取文档数量 + */ + @Override + public Integer getDocumentCount(String datasetId) { + try { + log.info("尝试使用文档列表接口获取文档数量作为备用方案"); + + // 构建查询参数,只获取第一页,页面大小为1,以减少网络开销 + Map queryParams = new HashMap<>(); + queryParams.put("page", 1); + queryParams.put("page_size", 1); + + // 调用文档列表方法 + PageData pageData = getDocumentList(datasetId, queryParams, 1, 1); + + if (pageData != null) { + log.info("通过文档列表接口获取文档数量成功,datasetId: {}, count: {}", datasetId, pageData.getTotal()); + return pageData.getTotal(); + } + + log.warn("通过文档列表接口获取文档数量失败,返回空结果"); + return 0; + + } catch (Exception e) { + log.error("通过文档列表接口获取文档数量也失败: {}", e.getMessage()); + return 0; + } + } + + // 辅助方法 + private PageData parseDocumentListResponse(Object dataObj, long curPage, long pageSize) { + try { + if (dataObj == null) { + log.warn("RAGFlow API返回的data字段为空"); + return new PageData(new ArrayList<>(), 0); + } + + log.debug("parseDocumentListResponse接收到的dataObj类型: {}", dataObj.getClass().getName()); + log.debug("parseDocumentListResponse接收到的dataObj内容: {}", dataObj); + + Map dataMap = (Map) dataObj; + + // 解析文档列表 - 根据RAGFlow API文档,字段名是"docs" + List> documents = (List>) dataMap.get("docs"); + if (documents == null || documents.isEmpty()) { + log.info("RAGFlow API返回的文档列表为空"); + return new PageData(new ArrayList<>(), 0); + } + + List knowledgeFilesList = new ArrayList<>(); + + for (Map doc : documents) { + KnowledgeFilesDTO knowledgeFile = new KnowledgeFilesDTO(); + + // 解析文档基本信息 - 根据RAGFlow API文档调整字段名 + if (doc.containsKey("id")) { + knowledgeFile.setId((String) doc.get("id")); + } + if (doc.containsKey("name")) { + knowledgeFile.setName((String) doc.get("name")); + } + if (doc.containsKey("size")) { + // 文件大小字段可能是字符串或数字类型 + Object sizeObj = doc.get("size"); + if (sizeObj instanceof Number) { + knowledgeFile.setFileSize(((Number) sizeObj).longValue()); + } else if (sizeObj instanceof String) { + try { + knowledgeFile.setFileSize(Long.parseLong((String) sizeObj)); + } catch (NumberFormatException e) { + log.warn("无法解析size字符串: {}", sizeObj); + knowledgeFile.setFileSize(0L); + } + } + } + if (doc.containsKey("status")) { + // 状态字段可能是字符串或数字类型 + Object statusObj = doc.get("status"); + if (statusObj instanceof Number) { + knowledgeFile.setStatus(((Number) statusObj).intValue()); + } else if (statusObj instanceof String) { + try { + knowledgeFile.setStatus(Integer.parseInt((String) statusObj)); + } catch (NumberFormatException e) { + log.warn("无法解析status字符串: {}", statusObj); + knowledgeFile.setStatus(0); + } + } + } + if (doc.containsKey("create_time")) { + // RAGFlow API返回的时间戳可能是字符串或数字类型 + Object createTimeObj = doc.get("create_time"); + Long createTime = null; + + if (createTimeObj instanceof Number) { + createTime = ((Number) createTimeObj).longValue(); + } else if (createTimeObj instanceof String) { + try { + createTime = Long.parseLong((String) createTimeObj); + } catch (NumberFormatException e) { + log.warn("无法解析create_time字符串: {}", createTimeObj); + } + } + + if (createTime != null && createTime > 0) { + knowledgeFile.setCreatedAt(new Date(createTime)); + } else { + knowledgeFile.setCreatedAt(new Date()); + } + } + if (doc.containsKey("update_time")) { + // RAGFlow API返回的时间戳可能是字符串或数字类型 + Object updateTimeObj = doc.get("update_time"); + Long updateTime = null; + + if (updateTimeObj instanceof Number) { + updateTime = ((Number) updateTimeObj).longValue(); + } else if (updateTimeObj instanceof String) { + try { + updateTime = Long.parseLong((String) updateTimeObj); + } catch (NumberFormatException e) { + log.warn("无法解析update_time字符串: {}", updateTimeObj); + } + } + + if (updateTime != null && updateTime > 0) { + knowledgeFile.setUpdatedAt(new Date(updateTime)); + } else { + knowledgeFile.setUpdatedAt(new Date()); + } + } + + // 处理文档解析状态字段 run + if (doc.containsKey("run")) { + Object runObj = doc.get("run"); + if (runObj != null) { + knowledgeFile.setRun(runObj.toString()); + log.debug("设置文档解析状态: documentId={}, run={}", knowledgeFile.getId(), runObj); + } + } + + knowledgeFilesList.add(knowledgeFile); + } + + // 解析总记录数 - 根据RAGFlow API响应,字段名是"total" + long total = 0; + if (dataMap.containsKey("total")) { + total = ((Number) dataMap.get("total")).longValue(); + } + + log.info("成功解析RAGFlow API响应,获取到{}个文档,总数: {}", knowledgeFilesList.size(), total); + return new PageData(knowledgeFilesList, total); + + } catch (Exception e) { + log.error("解析RAGFlow API文档列表响应失败: {}", e.getMessage(), e); + return new PageData(new ArrayList<>(), 0); + } + } + + private KnowledgeFilesDTO parseUploadResponse(Object dataObj, String datasetId, MultipartFile file) { + // 解析上传响应的逻辑 + // 这里需要实现从RAGFlow API响应中解析上传结果 + KnowledgeFilesDTO result = new KnowledgeFilesDTO(); + result.setDatasetId(datasetId); + result.setName(file.getOriginalFilename()); + result.setFileSize(file.getSize()); + result.setStatus(1); + return result; + } + + // MultipartFile资源包装类 + private static class MultipartFileResource extends AbstractResource { + private final MultipartFile multipartFile; + + public MultipartFileResource(MultipartFile multipartFile) { + this.multipartFile = multipartFile; + } + + @Override + public String getDescription() { + return "MultipartFile resource [" + multipartFile.getOriginalFilename() + "]"; + } + + @Override + public String getFilename() { + return multipartFile.getOriginalFilename(); + } + + @Override + public InputStream getInputStream() throws IOException { + return multipartFile.getInputStream(); + } + + @Override + public long contentLength() throws IOException { + return multipartFile.getSize(); + } + + @Override + public boolean exists() { + return !multipartFile.isEmpty(); + } + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeBaseServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeBaseServiceImpl.java index ddde7195..e3addd04 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeBaseServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeBaseServiceImpl.java @@ -35,6 +35,8 @@ import xiaozhi.common.utils.MessageUtils; import xiaozhi.modules.knowledge.dao.KnowledgeBaseDao; import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO; import xiaozhi.modules.knowledge.entity.KnowledgeBaseEntity; +import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapter; +import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapterFactory; import xiaozhi.modules.knowledge.service.KnowledgeBaseService; import xiaozhi.modules.model.dao.ModelConfigDao; import xiaozhi.modules.model.entity.ModelConfigEntity; @@ -106,7 +108,7 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl ragConfig = getValidatedRAGConfig(knowledgeBaseDTO.getRagModelId()); - datasetId = createDatasetInRAGFlow( + datasetId = createDatasetInRAG( knowledgeBaseDTO.getName(), knowledgeBaseDTO.getDescription(), ragConfig); @@ -162,13 +164,13 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl().eq("dataset_id", datasetId)); if (existingEntity != null) { - // 如果datasetId已存在,删除RAGFlow中的数据集并抛出异常 + // 如果datasetId已存在,删除RAG中的数据集并抛出异常 try { Map ragConfig = getValidatedRAGConfig(knowledgeBaseDTO.getRagModelId()); - deleteDatasetInRAGFlow(datasetId, ragConfig); + deleteDatasetInRAG(datasetId, ragConfig); } catch (Exception deleteException) { // 提供更详细的错误信息,包括异常类型和消息 - String errorMessage = "删除重复datasetId的RAGFlow数据集失败: " + deleteException.getClass().getSimpleName(); + String errorMessage = "删除重复datasetId的RAG数据集失败: " + deleteException.getClass().getSimpleName(); if (deleteException.getMessage() != null) { errorMessage += " - " + deleteException.getMessage(); } @@ -219,17 +221,17 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl ragConfig = getValidatedRAGConfig(knowledgeBaseDTO.getRagModelId()); - // 调用RAGFlow API更新数据集 - updateDatasetInRAGFlow( + // 调用RAG API更新数据集 + updateDatasetInRAG( knowledgeBaseDTO.getDatasetId(), knowledgeBaseDTO.getName(), knowledgeBaseDTO.getDescription(), ragConfig); - log.info("RAGFlow API更新成功,datasetId: {}", knowledgeBaseDTO.getDatasetId()); + log.info("RAG API更新成功,datasetId: {}", knowledgeBaseDTO.getDatasetId()); } catch (Exception e) { // 提供更详细的错误信息,包括异常类型和消息 - String errorMessage = "更新RAGFlow数据集失败: " + e.getClass().getSimpleName(); + String errorMessage = "更新RAG数据集失败: " + e.getClass().getSimpleName(); if (e.getMessage() != null) { errorMessage += " - " + e.getMessage(); } @@ -237,7 +239,7 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl ragConfig = getValidatedRAGConfig(entity.getRagModelId()); - deleteDatasetInRAGFlow(entity.getDatasetId(), ragConfig); - log.info("RAGFlow API删除调用完成"); + deleteDatasetInRAG(entity.getDatasetId(), ragConfig); + log.info("RAG API删除调用完成"); apiDeleteSuccess = true; } catch (Exception e) { // 提供更详细的错误信息,包括异常类型和消息 - String errorMessage = "删除RAGFlow数据集失败: " + e.getClass().getSimpleName(); + String errorMessage = "删除RAG数据集失败: " + e.getClass().getSimpleName(); if (e.getMessage() != null) { errorMessage += " - " + e.getMessage(); } @@ -308,7 +310,7 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl ragConfig) { - String datasetId = null; - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - - log.info("开始调用RAGFlow API创建数据集, name: {}", name); - log.debug("RAGFlow配置 - baseUrl: {}, apiKey: {}", baseUrl, StringUtils.isBlank(apiKey) ? "未配置" : "已配置"); - - // 构建请求URL - String url = baseUrl + "/api/v1/datasets"; - log.debug("请求URL: {}", url); - - // 构建请求体 - Map requestBody = new HashMap<>(); - String username = SecurityUser.getUser().getUsername(); - requestBody.put("name", username + "_" + name); - if (StringUtils.isNotBlank(description)) { - requestBody.put("description", description); - } - log.debug("请求体: {}", requestBody); - - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); - - HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); - - // 发送POST请求 - log.info("发送POST请求到RAGFlow API..."); - ResponseEntity response; - try { - response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); - } catch (Exception e) { - String errorMessage = url + e.getMessage(); - log.error(errorMessage, e); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); + private String extractAdapterType(Map config) { + if (config == null) { + throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND); } - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - log.debug("RAGFlow API响应内容: {}", response.getBody()); + // 从配置中提取适配器类型 + String adapterType = (String) config.get("type"); - if (!response.getStatusCode().is2xxSuccessful()) { - String errorMessage = response.getStatusCode() + ", 响应内容: " + response.getBody(); - log.error(errorMessage); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); + // 验证适配器类型是否存在且非空 + if (StringUtils.isBlank(adapterType)) { + throw new RenException(ErrorCode.RAG_ADAPTER_TYPE_NOT_FOUND); } - // 解析响应体,提取datasetId - String responseBody = response.getBody(); - if (StringUtils.isNotBlank(responseBody)) { - try { - // 解析RAGFlow API响应,支持多种可能的字段名 - ObjectMapper objectMapper = new ObjectMapper(); - Map responseMap = objectMapper.readValue(responseBody, Map.class); - - log.debug("RAGFlow API响应解析结果: {}", responseMap); - - // 首先检查响应码 - Integer code = (Integer) responseMap.get("code"); - String message = (String) responseMap.get("message"); - if (code != null && code == 0) { - // 响应码为0表示成功,从data字段中获取datasetId - Object dataObj = responseMap.get("data"); - if (dataObj instanceof Map) { - Map dataMap = (Map) dataObj; - datasetId = (String) dataMap.get("id"); - - if (StringUtils.isBlank(datasetId)) { - // 如果id字段为空,尝试其他可能的字段名 - datasetId = (String) dataMap.get("dataset_id"); - datasetId = (String) dataMap.get("datasetId"); - } - } - } else { - // 如果响应码不为0,说明API调用失败 - String errorMessage = code + (message != null ? message : "null"); - log.error(errorMessage); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } - - log.info("从RAGFlow API响应中解析出datasetId: {}", datasetId); - log.debug("完整响应内容: {}", responseBody); - } catch (IOException e) { - // 构建详细的错误信息,包含异常类型和消息 - String baseErrorMessage = e.getClass().getSimpleName(); - String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); - log.error(errorMessage, e); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } catch (Exception e) { - // 构建详细的错误信息,包含异常类型和消息 - String baseErrorMessage = e.getClass().getSimpleName(); - String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); - log.error(errorMessage, e); - // 如果解析失败,但响应体不为空,尝试直接使用响应体作为错误信息 - String finalErrorMessage = responseBody; - if (e.getMessage() != null) { - finalErrorMessage += errorMessage; - } - throw new RenException(ErrorCode.RAG_API_ERROR, finalErrorMessage); - } + // 验证适配器类型是否已注册 + if (!KnowledgeBaseAdapterFactory.isAdapterTypeRegistered(adapterType)) { + throw new RenException(ErrorCode.RAG_ADAPTER_TYPE_NOT_SUPPORTED, + "不支持的适配器类型: " + adapterType); } - if (StringUtils.isBlank(datasetId)) { - log.error("无法从RAGFlow API响应中获取datasetId,响应内容: {}", responseBody); - throw new RenException(ErrorCode.RAG_DATASET_ID_NOT_NULL); - } - log.info("RAGFlow数据集创建成功,datasetId: {}", datasetId); - - return datasetId; + return adapterType; } /** - * 调用RAGFlow API更新数据集 + * 使用适配器创建数据集 */ - private void updateDatasetInRAGFlow(String datasetId, String name, String description, + private String createDatasetInRAG(String name, String description, Map ragConfig) { + log.info("开始使用适配器创建数据集, name: {}", name); + + try { + // 从RAG配置中提取适配器类型 + String adapterType = extractAdapterType(ragConfig); + + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); + + // 构建数据集创建参数 + Map createParams = new HashMap<>(); + String username = SecurityUser.getUser().getUsername(); + createParams.put("name", username + "_" + name); + if (StringUtils.isNotBlank(description)) { + createParams.put("description", description); + } + + // 调用适配器的创建数据集方法 + String datasetId = adapter.createDataset(createParams); + + log.info("数据集创建成功,datasetId: {}", datasetId); + return datasetId; + + } catch (Exception e) { + // 直接传递底层适配器的详细错误信息 + log.error("创建数据集失败", e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); + } + } + + /** + * 使用适配器更新数据集 + */ + private void updateDatasetInRAG(String datasetId, String name, String description, Map ragConfig) { - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); + log.info("开始使用适配器更新数据集,datasetId: {}, name: {}", datasetId, name); - log.info("开始调用RAGFlow API更新数据集,datasetId: {}, name: {}", datasetId, name); - log.debug("RAGFlow配置 - baseUrl: {}, apiKey: {}", baseUrl, StringUtils.isBlank(apiKey) ? "未配置" : "已配置"); - - // 构建请求URL - String url = baseUrl + "/api/v1/datasets/" + datasetId; - log.debug("请求URL: {}", url); - // 构建请求体 - Map requestBody = new HashMap<>(); - requestBody.put("dataset_id", datasetId); - String username = SecurityUser.getUser().getUsername(); - requestBody.put("name", username + "_" + name); - if (StringUtils.isNotBlank(description)) { - requestBody.put("description", description); - } - log.debug("请求体: {}", requestBody); - - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); - - HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); - - // 发送PUT请求 - log.info("发送PUT请求到RAGFlow API..."); - ResponseEntity response; try { - response = restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class); - } catch (Exception e) { - String errorMessage = url + e.getMessage(); - log.error(errorMessage, e); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } + // 从RAG配置中提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - log.debug("RAGFlow API响应内容: {}", response.getBody()); + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); - if (!response.getStatusCode().is2xxSuccessful()) { - String errorMessage = response.getStatusCode() + ", 响应内容: " + response.getBody(); - log.error(errorMessage); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } - - // 解析响应体,验证操作是否真正成功 - String responseBody = response.getBody(); - if (responseBody != null) { - try { - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - String message = (String) responseMap.get("message"); - - if (code != null && code != 0) { - String errorMessage = code + (message != null ? message : "null"); - log.error(errorMessage); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } - } catch (IOException e) { - // 构建详细的错误信息,包含异常类型和消息 - String baseErrorMessage = e.getClass().getSimpleName(); - String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); - log.error(errorMessage, e); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } catch (Exception e) { - // 构建详细的错误信息,包含异常类型和消息 - String baseErrorMessage = e.getClass().getSimpleName(); - String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); - log.error(errorMessage, e); - // 如果解析失败,但响应体不为空,尝试直接使用响应体作为错误信息 - String finalErrorMessage = responseBody; - if (e.getMessage() != null) { - finalErrorMessage += errorMessage; - } - throw new RenException(ErrorCode.RAG_API_ERROR, finalErrorMessage); + // 构建数据集更新参数 + Map updateParams = new HashMap<>(); + String username = SecurityUser.getUser().getUsername(); + updateParams.put("name", username + "_" + name); + if (StringUtils.isNotBlank(description)) { + updateParams.put("description", description); } + + // 调用适配器的更新数据集方法 + adapter.updateDataset(datasetId, updateParams); + + log.info("数据集更新成功,datasetId: {}", datasetId); + + } catch (Exception e) { + // 直接传递底层适配器的详细错误信息 + log.error("更新数据集失败", e); + if (e instanceof RenException) { + throw (RenException) e; + } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); } - - log.info("RAGFlow数据集更新成功,datasetId: {}", datasetId); - } /** - * 调用RAGFlow API删除数据集 + * 使用适配器删除数据集 */ - private void deleteDatasetInRAGFlow(String datasetId, Map ragConfig) { - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); + private void deleteDatasetInRAG(String datasetId, Map ragConfig) { + log.info("开始使用适配器删除数据集,datasetId: {}", datasetId); - log.info("开始调用RAGFlow API删除数据集,datasetId: {}", datasetId); - log.debug("RAGFlow配置 - baseUrl: {}, apiKey: {}", baseUrl, StringUtils.isBlank(apiKey) ? "未配置" : "已配置"); - - // 构建请求URL - String url = baseUrl + "/api/v1/datasets"; - log.debug("请求URL: {}", url); - - // 构建请求体 - Map requestBody = new HashMap<>(); - requestBody.put("ids", List.of(datasetId)); - log.debug("请求体: {}", requestBody); - - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); - - HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); - - // 发送DELETE请求 - log.info("发送DELETE请求到RAGFlow API..."); - ResponseEntity response; try { - response = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, String.class); + // 从RAG配置中提取适配器类型 + String adapterType = extractAdapterType(ragConfig); + + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); + + // 调用适配器的删除数据集方法 + adapter.deleteDataset(datasetId); + + log.info("数据集删除成功,datasetId: {}", datasetId); + } catch (Exception e) { - String errorMessage = url + e.getMessage(); - log.error(errorMessage, e); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - log.debug("RAGFlow API响应内容: {}", response.getBody()); - - if (!response.getStatusCode().is2xxSuccessful()) { - String errorMessage = response.getStatusCode() + response.getBody(); - log.error(errorMessage); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } - - // 解析响应体,验证操作是否真正成功 - String responseBody = response.getBody(); - if (responseBody != null) { - try { - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - String message = (String) responseMap.get("message"); - - if (code != null && code != 0) { - String errorMessage = code + (message != null ? message : "null"); - log.error(errorMessage); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } - } catch (IOException e) { - // 构建详细的错误信息,包含异常类型和消息 - String baseErrorMessage = e.getClass().getSimpleName(); - String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); - log.error(errorMessage, e); - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); - } catch (Exception e) { - // 构建详细的错误信息,包含异常类型和消息 - String baseErrorMessage = e.getClass().getSimpleName(); - String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); - log.error(errorMessage, e); - // 如果解析失败,但响应体不为空,尝试直接使用响应体作为错误信息 - String finalErrorMessage = responseBody; - if (e.getMessage() != null) { - finalErrorMessage += errorMessage; - } - throw new RenException(ErrorCode.RAG_API_ERROR, finalErrorMessage); + // 直接传递底层适配器的详细错误信息 + log.error("删除数据集失败", e); + if (e instanceof RenException) { + throw (RenException) e; } + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); } - - log.info("RAGFlow数据集删除成功,datasetId: {}", datasetId); - } /** @@ -760,9 +606,9 @@ public class KnowledgeBaseServiceImpl extends BaseServiceImpl ragConfig = getValidatedRAGConfig(ragModelId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - - // 构建请求URL - 调用RAGFlow API获取文档列表,但不返回文档详情,只获取总数 - String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents?page=1&size=1"; - log.debug("请求URL: {}", url); - - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); - - HttpEntity requestEntity = new HttpEntity<>(headers); - - // 发送GET请求 - log.info("发送GET请求到RAGFlow API获取文档数量..."); - ResponseEntity response; try { - response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); + // 获取RAG配置 + Map ragConfig = getValidatedRAGConfig(ragModelId); + + // 从RAG配置中提取适配器类型 + String adapterType = extractAdapterType(ragConfig); + + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); + + // 调用适配器的获取文档数量方法 + Integer documentCount = adapter.getDocumentCount(datasetId); + + log.info("获取知识库 {} 的文档数量成功: {}", datasetId, documentCount); + return documentCount; + } catch (Exception e) { - String errorMessage = url + e.getMessage(); + // 构建详细的错误信息,包含异常类型和消息 + String baseErrorMessage = e.getClass().getSimpleName() + " - 获取知识库文档数量失败"; + String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); log.error(errorMessage, e); return 0; } - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - - if (!response.getStatusCode().is2xxSuccessful()) { - log.error("RAGFlow API调用失败,状态码: {}, 响应内容: {}", response.getStatusCode(), response.getBody()); - return 0; - } - - String responseBody = response.getBody(); - log.debug("RAGFlow API响应内容: {}", responseBody); - - // 解析响应 - try { - ObjectMapper objectMapper = new ObjectMapper(); - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - - if (code != null && code == 0) { - Object dataObj = responseMap.get("data"); - if (dataObj instanceof Map) { - Map dataMap = (Map) dataObj; - Object totalObj = dataMap.get("total"); - if (totalObj instanceof Integer) { - Integer documentCount = (Integer) totalObj; - log.info("获取知识库 {} 的文档数量成功: {}", datasetId, documentCount); - return documentCount; - } else if (totalObj instanceof Long) { - Long documentCount = (Long) totalObj; - log.info("获取知识库 {} 的文档数量成功: {}", datasetId, documentCount); - return documentCount.intValue(); - } - } - } else { - log.error("RAGFlow API调用失败,响应码: {}, 响应内容: {}", code, responseBody); - } - } catch (IOException e) { - // 构建详细的错误信息,包含异常类型和消息 - String baseErrorMessage = e.getClass().getSimpleName() + " - 解析RAGFlow API响应时发生IO异常"; - String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); - log.error(errorMessage, e); - } catch (Exception e) { - // 构建详细的错误信息,包含异常类型和消息 - String baseErrorMessage = e.getClass().getSimpleName() + " - 解析RAGFlow API响应失败"; - String errorMessage = baseErrorMessage + (e.getMessage() != null ? ": " + e.getMessage() : ""); - log.error(errorMessage, e); - } - return 0; } } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeFilesServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeFilesServiceImpl.java index 069d51cd..0ce841cf 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeFilesServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/knowledge/service/impl/KnowledgeFilesServiceImpl.java @@ -32,6 +32,8 @@ import xiaozhi.common.exception.ErrorCode; import xiaozhi.common.exception.RenException; import xiaozhi.common.page.PageData; import xiaozhi.modules.knowledge.dto.KnowledgeFilesDTO; +import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapter; +import xiaozhi.modules.knowledge.rag.KnowledgeBaseAdapterFactory; import xiaozhi.modules.knowledge.service.KnowledgeBaseService; import xiaozhi.modules.knowledge.service.KnowledgeFilesService; @@ -52,7 +54,6 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { @Override public PageData getPageList(KnowledgeFilesDTO knowledgeFilesDTO, Integer page, Integer limit) { try { - log.info("=== 开始获取文档列表 ==="); log.info("查询条件: datasetId={}, name={}, status={}, page={}, limit={}", knowledgeFilesDTO != null ? knowledgeFilesDTO.getDatasetId() : null, @@ -60,7 +61,7 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { knowledgeFilesDTO != null ? knowledgeFilesDTO.getStatus() : null, page, limit); - // 构建请求URL - 根据RAGFlow API文档,获取文档列表的接口 + // 获取数据集ID String datasetId = knowledgeFilesDTO != null ? knowledgeFilesDTO.getDatasetId() : null; if (StringUtils.isBlank(datasetId)) { throw new RenException(ErrorCode.RAG_DATASET_ID_NOT_NULL); @@ -68,84 +69,44 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { // 获取RAG配置 Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents"; + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - // 添加查询参数 - StringBuilder urlBuilder = new StringBuilder(url); - List params = new ArrayList<>(); + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); + // 构建查询参数 + Map queryParams = new HashMap<>(); if (knowledgeFilesDTO != null && StringUtils.isNotBlank(knowledgeFilesDTO.getName())) { - params.add("keywords=" + knowledgeFilesDTO.getName()); + queryParams.put("keywords", knowledgeFilesDTO.getName()); } if (page > 0) { - params.add("page=" + page); + queryParams.put("page", page); } if (limit > 0) { - params.add("page_size=" + limit); + queryParams.put("page_size", limit); } - if (!params.isEmpty()) { - urlBuilder.append("?").append(String.join("&", params)); - } + // 调用适配器获取文档列表 + PageData result = adapter.getDocumentList(datasetId, queryParams, page, limit); - url = urlBuilder.toString(); - log.debug("请求URL: {}", url); - - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); - - HttpEntity requestEntity = new HttpEntity<>(headers); - - // 发送GET请求 - log.info("发送GET请求到RAGFlow API获取文档列表..."); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - - if (!response.getStatusCode().is2xxSuccessful()) { - log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); - throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); - } - - String responseBody = response.getBody(); - log.debug("RAGFlow API响应内容: {}", responseBody); - - // 解析响应 - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - - if (code != null && code == 0) { - Object dataObj = responseMap.get("data"); - return parseDocumentListResponse(dataObj, page, limit); - } else { - log.error("RAGFlow API调用失败,响应码: {}", code); - // 获取错误消息,如果存在的话 - String apiMessage = (String) responseMap.get("message"); - String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; - log.error("RAGFlow API调用失败,响应码: {}, 错误详情: {}", errorDetail); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); - } + log.info("获取文档列表成功,共{}个文档,总数: {}", result.getList().size(), result.getTotal()); + return result; } catch (Exception e) { log.error("获取文档列表失败: {}", e.getMessage(), e); - String errorMessage = e.getMessage() != null ? e.getMessage() : "null"; if (e instanceof RenException) { throw (RenException) e; } - - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); } finally { log.info("=== 获取文档列表操作结束 ==="); } } /** - * 解析RAGFlow API返回的文档列表响应 + * 解析RAG API返回的文档列表响应 */ private PageData parseDocumentListResponse(Object dataObj, long curPage, long pageSize) { try { @@ -176,7 +137,7 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { List list = (List) entry.getValue(); if (!list.isEmpty() && list.get(0) instanceof Map) { documentsObj = entry.getValue(); - log.warn("自动检测到文档列表字段: '{}',建议检查RAGFlow API文档", entry.getKey()); + log.warn("自动检测到文档列表字段: '{}',建议检查RAG API文档", entry.getKey()); break; } } @@ -190,7 +151,7 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { KnowledgeFilesDTO dto = convertRAGDocumentToDTO(docMap); if (dto != null) { // 在文档列表获取时也进行状态同步检查 - syncDocumentStatusWithRAGFlow(dto); + syncDocumentStatusWithRAG(dto); documents.add(dto); } } @@ -234,11 +195,11 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { } /** - * 同步文档状态与RAGFlow实际状态 + * 同步文档状态与RAG实际状态 * 优化状态同步逻辑,确保解析中状态能够正常显示 * 只有当文档有切片且解析时间超过30秒时,才更新为完成状态 */ - private void syncDocumentStatusWithRAGFlow(KnowledgeFilesDTO dto) { + private void syncDocumentStatusWithRAG(KnowledgeFilesDTO dto) { if (dto == null || StringUtils.isBlank(dto.getDocumentId())) { return; } @@ -252,61 +213,44 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { try { long currentTime = System.currentTimeMillis(); - // 调用RAGFlow API获取文档切片信息 + // 使用适配器获取文档切片信息 String datasetId = dto.getDatasetId(); Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - String url = baseUrl + "/api/v1/documents/" + documentId + "/chunks"; + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); - HttpEntity requestEntity = new HttpEntity<>(headers); + // 构建查询参数 + Map queryParams = new HashMap<>(); + queryParams.put("document_id", documentId); log.debug("检查文档切片状态,documentId: {}", documentId); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, - String.class); - if (response.getStatusCode().is2xxSuccessful()) { - String responseBody = response.getBody(); - Map responseMap = objectMapper.readValue(responseBody, Map.class); + // 使用适配器获取切片列表 + Map chunkResult = adapter.listChunks(datasetId, documentId, null, null, null, null); + List> chunks = (List>) chunkResult.get("chunks"); - Integer code = (Integer) responseMap.get("code"); - if (code != null && code == 0) { - Object dataObj = responseMap.get("data"); - if (dataObj instanceof Map) { - Map dataMap = (Map) dataObj; + // 如果有切片且数量大于0,说明解析已完成 + if (!chunks.isEmpty()) { + // 检查文档创建时间,确保解析过程有足够的时间显示 + Date createdAt = dto.getCreatedAt(); + long parseDuration = currentTime + - (createdAt != null ? createdAt.getTime() : currentTime); - // 检查是否有切片数据 - Object chunksObj = getValueFromMultipleKeys(dataMap, "chunks", "items", "list", "data"); - if (chunksObj instanceof List) { - List chunks = (List) chunksObj; + // 只有当解析时间超过30秒时,才更新为完成状态 + // 这样可以确保解析中状态有足够的时间显示 + if (parseDuration > 30000) { + log.info("状态同步:文档已有切片且解析时间超过30秒,更新为完成状态,documentId: {}, 切片数量: {}, 解析时长: {}ms", + documentId, chunks.size(), parseDuration); - // 如果有切片且数量大于0,说明解析已完成 - if (!chunks.isEmpty()) { - // 检查文档创建时间,确保解析过程有足够的时间显示 - Date createdAt = dto.getCreatedAt(); - long parseDuration = currentTime - - (createdAt != null ? createdAt.getTime() : currentTime); - - // 只有当解析时间超过30秒时,才更新为完成状态 - // 这样可以确保解析中状态有足够的时间显示 - if (parseDuration > 30000) { - log.info("状态同步:文档已有切片且解析时间超过30秒,更新为完成状态,documentId: {}, 切片数量: {}, 解析时长: {}ms", - documentId, chunks.size(), parseDuration); - - // 更新状态为完成(3) - dto.setStatus(3); - } else { - log.debug("文档已有切片但解析时间不足30秒,保持解析中状态,documentId: {}, 解析时长: {}ms", - documentId, parseDuration); - } - } - } - } + // 更新状态为完成(3) + dto.setStatus(3); + } else { + log.debug("文档已有切片但解析时间不足30秒,保持解析中状态,documentId: {}, 解析时长: {}ms", + documentId, parseDuration); } } } catch (Exception e) { @@ -317,7 +261,7 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { } /** - * 将RAGFlow文档数据转换为KnowledgeFilesDTO + * 将RAG文档数据转换为KnowledgeFilesDTO */ private KnowledgeFilesDTO convertRAGDocumentToDTO(Map docMap) { try { @@ -327,8 +271,8 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { KnowledgeFilesDTO dto = new KnowledgeFilesDTO(); // 设置基本字段 - 支持多种可能的字段名 - dto.setId(getStringValueFromMultipleKeys(docMap, "id", "document_id", "doc_id")); // 使用RAGFlow的文档ID作为本地ID - dto.setDocumentId(getStringValueFromMultipleKeys(docMap, "id", "document_id", "doc_id")); // RAGFlow文档ID + dto.setId(getStringValueFromMultipleKeys(docMap, "id", "document_id", "doc_id")); // 使用RAG的文档ID作为本地ID + dto.setDocumentId(getStringValueFromMultipleKeys(docMap, "id", "document_id", "doc_id")); // RAG文档ID dto.setName(getStringValueFromMultipleKeys(docMap, "name", "filename", "file_name", "title")); dto.setDatasetId(getStringValueFromMultipleKeys(docMap, "dataset_id", "dataset", "knowledge_base_id")); @@ -390,16 +334,16 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { } } - // 设置文档解析状态信息 - 直接使用RAGFlow最新状态 + // 设置文档解析状态信息 - 直接使用RAG最新状态 String documentId = dto.getDocumentId(); if (StringUtils.isNotBlank(documentId)) { - // 获取RAGFlow的最新状态 + // 获取RAG的最新状态 Object runObj = getValueFromMultipleKeys(docMap, "run", "status", "parse_status"); Integer ragFlowStatus = null; if (runObj != null) { dto.setRun(runObj.toString()); ragFlowStatus = dto.getParseStatusCode(); - log.debug("获取RAGFlow最新状态,documentId: {}, run: {}, status: {}", + log.debug("获取RAG最新状态,documentId: {}, run: {}, status: {}", documentId, runObj, ragFlowStatus); } @@ -408,7 +352,7 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { return dto; } catch (Exception e) { - log.error("转换RAGFlow文档数据失败: {}", e.getMessage(), e); + log.error("转换RAG文档数据失败: {}", e.getMessage(), e); return null; } } @@ -451,57 +395,21 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { try { // 获取RAG配置 Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - // 修正API路径 - 根据RAGFlow API规范,获取单个文档需要datasetId - String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents/" + documentId; - log.debug("请求URL: {}", url); + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); - HttpEntity requestEntity = new HttpEntity<>(headers); + // 使用适配器获取文档详情 + KnowledgeFilesDTO dto = adapter.getDocumentById(datasetId, documentId); - // 发送GET请求 - log.info("发送GET请求到RAGFlow API获取文档详情..."); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - - if (!response.getStatusCode().is2xxSuccessful()) { - log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); - String responseBody = response.getBody(); - throw new RenException(ErrorCode.RAG_API_ERROR, - response.getStatusCode() + (responseBody != null ? responseBody : "null")); - } - - String responseBody = response.getBody(); - log.debug("RAGFlow API响应内容: {}", responseBody); - - // 解析响应 - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - - if (code != null && code == 0) { - Object dataObj = responseMap.get("data"); - if (dataObj instanceof Map) { - KnowledgeFilesDTO dto = convertRAGDocumentToDTO((Map) dataObj); - if (dto != null) { - log.info("获取文档详情成功,documentId: {}", documentId); - return dto; - } - } - throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS); + if (dto != null) { + log.info("获取文档详情成功,documentId: {}", documentId); + return dto; } else { - log.error("RAGFlow API调用失败,响应码: {}", code); - // 获取错误消息,如果存在的话 - String apiMessage = (String) responseMap.get("message"); - String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; - log.error("RAGFlow API调用失败详情: {}", errorDetail); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); + throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS); } } catch (Exception e) { @@ -529,77 +437,34 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { try { // 获取RAG配置 Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - // 构建请求URL - 获取文档列表 - StringBuilder urlBuilder = new StringBuilder(); - urlBuilder.append(baseUrl).append("/api/v1/datasets/").append(datasetId).append("/documents"); + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); + + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); // 构建查询参数 - List params = new ArrayList<>(); + Map queryParams = new HashMap<>(); if (page != null && page > 0) { - params.add("page=" + page); + queryParams.put("page", page); } if (limit != null && limit > 0) { - params.add("page_size=" + limit); + queryParams.put("page_size", limit); + } + if (status != null) { + queryParams.put("status", status); } - if (!params.isEmpty()) { - urlBuilder.append("?").append(String.join("&", params)); - } - - String url = urlBuilder.toString(); - log.debug("请求URL: {}", url); - - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); - - HttpEntity requestEntity = new HttpEntity<>(headers); - - // 发送GET请求 - log.info("发送GET请求到RAGFlow API获取文档列表..."); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - - if (!response.getStatusCode().is2xxSuccessful()) { - log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); - throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); - } - - String responseBody = response.getBody(); - log.debug("RAGFlow API响应内容: {}", responseBody); - - // 解析响应 - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - - if (code != null && code == 0) { - Object dataObj = responseMap.get("data"); - - // 解析文档列表并过滤状态 - PageData pageData = parseDocumentListResponse(dataObj, page, limit); - - if (status != null) { - // 根据状态过滤文档列表 - List filteredDocuments = pageData.getList().stream() - .filter(doc -> status.equals(doc.getStatus())) - .collect(Collectors.toList()); - - // 更新分页数据 - pageData.setList(filteredDocuments); - pageData.setTotal(filteredDocuments.size()); - } + // 使用适配器获取文档列表 + PageData pageData = adapter.getDocumentList(datasetId, queryParams, page, limit); + if (pageData != null) { log.info("根据状态查询文档列表成功,datasetId: {}, 状态: {}, 文档数量: {}", datasetId, status, pageData.getList().size()); return pageData; } else { - log.error("RAGFlow API调用失败,响应码: {}", code); - throw new RenException(ErrorCode.RAG_API_ERROR, code.toString()); + throw new RenException(ErrorCode.Knowledge_Base_RECORD_NOT_EXISTS); } } catch (Exception e) { @@ -642,21 +507,40 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { throw new RenException(ErrorCode.RAG_FILE_CONTENT_EMPTY); } - log.info("2. 开始流式上传到RAGFlow"); - // 直接调用RAGFlow API上传文档 - 使用流式上传 - String documentId = uploadDocumentToRAGFlow(datasetId, file, name, metaFields, chunkMethod, parserConfig); + log.info("2. 开始使用适配器上传文档"); - log.info("文档上传成功,documentId: {}", documentId); + // 获取RAG配置 + Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - // 返回上传的文档信息 - KnowledgeFilesDTO result = new KnowledgeFilesDTO(); - result.setId(documentId); // 使用documentId作为ID - result.setDocumentId(documentId); - result.setDatasetId(datasetId); - result.setName(StringUtils.isNotBlank(name) ? name : fileName); - result.setFileType(fileType); - result.setFileSize(fileSize); - result.setStatus(1); // 上传成功,设置为处理中状态 + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); + + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); + + // 构建上传参数 + Map uploadParams = new HashMap<>(); + if (StringUtils.isNotBlank(name)) { + uploadParams.put("name", name); + } + if (metaFields != null && !metaFields.isEmpty()) { + uploadParams.put("meta_fields", metaFields); + } + if (StringUtils.isNotBlank(chunkMethod)) { + uploadParams.put("chunk_method", chunkMethod); + } + if (parserConfig != null && !parserConfig.isEmpty()) { + uploadParams.put("parser_config", parserConfig); + } + + // 使用适配器上传文档 + KnowledgeFilesDTO result = adapter.uploadDocument(datasetId, file, + (String) uploadParams.get("name"), + (Map) uploadParams.get("meta_fields"), + (String) uploadParams.get("chunk_method"), + (Map) uploadParams.get("parser_config")); + + log.info("文档上传成功,documentId: {}", result.getDocumentId()); return result; @@ -682,26 +566,33 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { log.info("删除文档documentId: {}, datasetId: {}", documentId, datasetId); try { - // 直接调用RAGFlow API删除文档,不进行前置验证 - // 因为即使文档不存在,RAGFlow API也会返回相应的错误信息 - log.info("开始调用RAGFlow API删除文档"); - deleteDocumentInRAGFlow(documentId, datasetId); - log.info("RAGFlow API删除调用完成"); + // 获取RAG配置 + Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); + + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); + + // 使用适配器工厂获取适配器实例 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); + + // 使用适配器删除文档 + adapter.deleteDocument(datasetId, documentId); + + log.info("文档删除成功"); } catch (Exception e) { log.error("删除文档失败: {}", e.getMessage(), e); - String errorMessage = e.getMessage() != null ? e.getMessage() : "null"; if (e instanceof RenException) { throw (RenException) e; } - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); + throw new RenException(ErrorCode.RAG_API_ERROR, e.getMessage()); } finally { log.info("=== 根据documentId删除文档操作结束 ==="); } } /** - * 获取文件类型 - 支持RAGFlow四种文档格式类型 + * 获取文件类型 - 支持RAG四种文档格式类型 */ private String getFileType(String fileName) { if (StringUtils.isBlank(fileName)) { @@ -748,6 +639,28 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { } } + /** + * 从RAG配置中提取适配器类型 + */ + private String extractAdapterType(Map config) { + if (config == null) { + throw new RenException(ErrorCode.RAG_CONFIG_NOT_FOUND); + } + + // 从配置中提取type字段 + String adapterType = (String) config.get("type"); + if (StringUtils.isBlank(adapterType)) { + throw new RenException(ErrorCode.RAG_ADAPTER_TYPE_NOT_FOUND); + } + + // 验证适配器类型是否已注册 + if (!KnowledgeBaseAdapterFactory.isAdapterTypeRegistered(adapterType)) { + throw new RenException(ErrorCode.RAG_ADAPTER_TYPE_NOT_SUPPORTED, "适配器类型未注册: " + adapterType); + } + + return adapterType; + } + /** * 验证RAG配置中是否包含必要的参数 */ @@ -782,126 +695,60 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { } /** - * 调用RAGFlow API上传文档 - 流式上传版本 + * 调用RAG API上传文档 - 流式上传版本 */ - private String uploadDocumentToRAGFlow(String datasetId, MultipartFile file, String name, + private String uploadDocumentToRAG(String datasetId, MultipartFile file, String name, Map metaFields, String chunkMethod, Map parserConfig) { try { + log.info("开始调用知识库适配器上传文档,datasetId: {}, 文件名: {}", datasetId, file.getOriginalFilename()); + // 获取RAG配置 Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - log.info("开始调用RAGFlow API流式上传文档,datasetId: {}, 文件名: {}", datasetId, file.getOriginalFilename()); - log.debug("RAGFlow配置 - baseUrl: {}, apiKey: {}", baseUrl, StringUtils.isBlank(apiKey) ? "未配置" : "已配置"); + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - // 构建请求URL - String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents"; - log.debug("请求URL: {}", url); + // 获取知识库适配器 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); - // 构建multipart/form-data请求 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - headers.set("Authorization", "Bearer " + apiKey); - - // 创建多部分请求体 - 使用MultipartFileResource进行流式上传 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("file", new MultipartFileResource(file, file.getOriginalFilename())); - - // 添加其他参数 - if (StringUtils.isNotBlank(name)) { - body.add("name", name); - } else { - body.add("name", file.getOriginalFilename()); - } + // 构建上传参数 + Map uploadParams = new HashMap<>(); + uploadParams.put("file", file); + uploadParams.put("name", StringUtils.isNotBlank(name) ? name : file.getOriginalFilename()); if (metaFields != null && !metaFields.isEmpty()) { - try { - body.add("meta_fields", objectMapper.writeValueAsString(metaFields)); - } catch (Exception e) { - log.warn("序列化meta_fields失败: {}", e.getMessage()); - } + uploadParams.put("meta_fields", metaFields); } if (StringUtils.isNotBlank(chunkMethod)) { - body.add("chunk_method", chunkMethod); + uploadParams.put("chunk_method", chunkMethod); } if (parserConfig != null && !parserConfig.isEmpty()) { - try { - body.add("parser_config", objectMapper.writeValueAsString(parserConfig)); - } catch (Exception e) { - log.warn("序列化parser_config失败: {}", e.getMessage()); - } + uploadParams.put("parser_config", parserConfig); } - log.debug("multipart请求体参数数量: {}", body.size()); - log.debug("multipart请求体参数: {}", body.keySet()); + log.debug("上传文档参数: {}", uploadParams.keySet()); - HttpEntity> requestEntity = new HttpEntity<>(body, headers); - - // 发送POST请求 - log.info("发送multipart/form-data POST请求到RAGFlow API..."); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - log.debug("RAGFlow API响应内容: {}", response.getBody()); - - String responseBody = response.getBody(); - String documentId = null; - - if (response.getStatusCode().is2xxSuccessful()) { - try { - Map responseMap = objectMapper.readValue(responseBody, Map.class); - - log.debug("RAGFlow API响应解析结果: {}", responseMap); - - // 首先检查响应码 - Integer code = (Integer) responseMap.get("code"); - if (code != null && code == 0) { - // 响应码为0表示成功,从data字段中获取documentId - Object dataObj = responseMap.get("data"); - - // 增强的documentId提取逻辑 - documentId = extractDocumentIdFromResponse(dataObj); - - // 如果从data字段无法提取,尝试从根级别提取 - if (StringUtils.isBlank(documentId)) { - documentId = extractDocumentIdFromRoot(responseMap); - } - - log.info("文档上传成功,documentId: {}", documentId); - } else { - // 如果响应码不为0,说明API调用失败 - log.error("RAGFlow API调用失败,响应码: {}", code); - // 获取错误消息,如果存在的话 - String apiMessage = (String) responseMap.get("message"); - String errorDetail = apiMessage != null ? apiMessage : "无详细错误信息"; - log.error("RAGFlow API调用失败详情: {}", errorDetail); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); - } - - log.info("从RAGFlow API响应中解析出documentId: {}", documentId); - log.debug("完整响应内容: {}", responseBody); - } catch (Exception e) { - log.error("解析RAGFlow API响应失败: {}", e.getMessage()); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); - } - } else { - log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); - throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode().toString()); - } + // 调用适配器上传文档 + KnowledgeFilesDTO result = adapter.uploadDocument(datasetId, file, + (String) uploadParams.get("name"), + (Map) uploadParams.get("meta_fields"), + (String) uploadParams.get("chunk_method"), + (Map) uploadParams.get("parser_config")); + String documentId = result.getDocumentId(); if (StringUtils.isBlank(documentId)) { - log.error("无法从RAGFlow API响应中获取documentId"); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); + log.error("无法从知识库适配器获取documentId"); + throw new RenException(ErrorCode.RAG_API_ERROR, "上传文档失败,未返回documentId"); } - log.info("RAGFlow文档上传成功,documentId: {},文档已开始自动解析切片", documentId); + + log.info("知识库文档上传成功,documentId: {},文档已开始自动解析切片", documentId); return documentId; } catch (Exception e) { - log.error("RAGFlow API调用失败: {}", e.getMessage(), e); + log.error("知识库适配器调用失败: {}", e.getMessage(), e); String errorMessage = e.getMessage() != null ? e.getMessage() : "null"; if (e instanceof RenException) { throw (RenException) e; @@ -973,77 +820,30 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { } /** - * 调用RAGFlow API删除文档 + * 调用知识库适配器删除文档 */ - private void deleteDocumentInRAGFlow(String documentId, String datasetId) { + private void deleteDocumentInRAG(String documentId, String datasetId) { try { + log.info("开始调用知识库适配器删除文档,documentId: {}, datasetId: {}", documentId, datasetId); + // 获取RAG配置 Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - log.info("开始调用RAGFlow API删除文档,documentId: {}, datasetId: {}", documentId, datasetId); - log.debug("RAGFlow配置 - baseUrl: {}, apiKey: {}", baseUrl, StringUtils.isBlank(apiKey) ? "未配置" : "已配置"); + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - // 构建请求URL - 根据RAGFlow API文档,使用正确的路径参数名称 - String url = baseUrl + "/api/v1/datasets/" + datasetId + "/documents"; - log.debug("请求URL: {}", url); + // 获取知识库适配器 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); - // 构建请求体 - 严格按照API文档格式 - Map requestBody = new HashMap<>(); - requestBody.put("ids", Arrays.asList(documentId)); // 使用Arrays.asList确保序列化正确 - log.debug("请求体: {}", requestBody); + log.debug("删除文档参数: documentId: {}", documentId); - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); + // 调用适配器删除文档 + adapter.deleteDocument(datasetId, documentId); - HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); - - // 发送DELETE请求 - log.info("发送DELETE请求到RAGFlow API..."); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, - String.class); - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - log.debug("RAGFlow API响应内容: {}", response.getBody()); - - String responseBody = response.getBody(); - - if (response.getStatusCode().is2xxSuccessful()) { - // 验证响应格式 - if (responseBody != null) { - try { - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - - if (code != null && code == 0) { - log.info("RAGFlow文档删除成功,documentId: {}", documentId); - return; - } else { - String message = (String) responseMap.get("message"); - log.error("RAGFlow API调用失败,响应码: {}", code); - String errorDetail = message != null ? message : "无详细错误信息"; - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); - } - } catch (Exception e) { - log.warn("解析RAGFlow响应失败,但HTTP状态码成功,视为删除成功: {}", e.getMessage()); - log.info("RAGFlow文档删除成功,documentId: {}", documentId); - return; - } - } else { - log.info("RAGFlow文档删除成功(无响应体),documentId: {}", documentId); - return; - } - } else { - log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); - throw new RenException(ErrorCode.RAG_API_ERROR, - response.getStatusCode() + (responseBody != null ? responseBody : "null")); - } + log.info("知识库文档删除成功,documentId: {}", documentId); } catch (Exception e) { - log.error("RAGFlow API调用失败: {}", e.getMessage(), e); + log.error("知识库适配器调用失败: {}", e.getMessage(), e); String errorMessage = e.getMessage() != null ? e.getMessage() : "null"; if (e instanceof RenException) { throw (RenException) e; @@ -1102,55 +902,27 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { try { // 获取RAG配置 Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - // 构建请求URL - 根据RAGFlow API文档,解析文档的接口 - String url = baseUrl + "/api/v1/datasets/" + datasetId + "/chunks"; - log.debug("请求URL: {}", url); + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - // 构建请求体 - Map requestBody = new HashMap<>(); - requestBody.put("document_ids", documentIds); - log.debug("请求体: {}", requestBody); + // 获取知识库适配器 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); + log.debug("解析文档参数: documentIds: {}", documentIds); - HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + // 调用适配器解析文档 + boolean result = adapter.parseDocuments(datasetId, documentIds); - // 发送POST请求 - log.info("发送POST请求到RAGFlow API解析文档..."); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - - String responseBody = response.getBody(); - log.debug("RAGFlow API响应内容: {}", responseBody); - - if (!response.getStatusCode().is2xxSuccessful()) { - log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); - String errorDetail = responseBody != null ? responseBody : "无响应内容"; - throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode() + errorDetail); - } - - // 解析响应 - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - - if (code != null && code == 0) { + if (result) { log.info("文档解析成功,datasetId: {}, documentIds: {}", datasetId, documentIds); - return true; } else { - // 获取错误消息,如果存在的话 - String message = (String) responseMap.get("message"); - String errorDetail = message != null ? message : "无详细错误信息"; - log.error("RAGFlow API调用失败,响应码: {}, 错误信息: {}", code, errorDetail); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); + log.error("文档解析失败,datasetId: {}, documentIds: {}", datasetId, documentIds); + throw new RenException(ErrorCode.RAG_API_ERROR, "文档解析失败"); } + return result; + } catch (Exception e) { log.error("解析文档失败: {}", e.getMessage(), e); String errorMessage = e.getMessage() != null ? e.getMessage() : "null"; @@ -1177,83 +949,22 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { try { // 获取RAG配置 Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetId); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - // 构建请求URL - 根据RAGFlow API文档,列出切片的接口 - StringBuilder urlBuilder = new StringBuilder(); - urlBuilder.append(baseUrl).append("/api/v1/datasets/").append(datasetId) - .append("/documents/").append(documentId).append("/chunks"); + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - // 添加查询参数 - List params = new ArrayList<>(); + // 获取知识库适配器 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); - if (StringUtils.isNotBlank(keywords)) { - params.add("keywords=" + keywords); - } + log.debug("查询参数: documentId: {}, keywords: {}, page: {}, pageSize: {}, chunkId: {}", + documentId, keywords, page, pageSize, chunkId); - if (page != null && page > 0) { - params.add("page=" + page); - } + // 调用适配器列出切片 + Map result = adapter.listChunks(datasetId, documentId, keywords, page, pageSize, chunkId); - if (pageSize != null && pageSize > 0) { - params.add("page_size=" + pageSize); - } + log.info("切片列表获取成功,datasetId: {}, documentId: {}", datasetId, documentId); + return result; - if (StringUtils.isNotBlank(chunkId)) { - params.add("id=" + chunkId); - } - - if (!params.isEmpty()) { - urlBuilder.append("?").append(String.join("&", params)); - } - - String url = urlBuilder.toString(); - log.debug("请求URL: {}", url); - - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); - - HttpEntity requestEntity = new HttpEntity<>(headers); - - // 发送GET请求 - log.info("发送GET请求到RAGFlow API列出切片..."); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - - String responseBody = response.getBody(); - log.debug("RAGFlow API响应内容: {}", responseBody); - - if (!response.getStatusCode().is2xxSuccessful()) { - log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); - String errorDetail = responseBody != null ? responseBody : "无响应内容"; - throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode() + errorDetail); - } - - // 解析响应 - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - - if (code != null && code == 0) { - log.info("切片列表获取成功,datasetId: {}, documentId: {}", datasetId, documentId); - - // 解析切片数据并格式化返回 - return parseChunkListResponse(responseMap); - } else { - // 获取错误消息,如果存在的话 - String message = (String) responseMap.get("message"); - String errorDetail = message != null ? message : "无详细错误信息"; - log.error("RAGFlow API调用失败,响应码: {}, 错误信息: {}", code, errorDetail); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); - } - - } catch (IOException e) { - log.error("解析RAGFlow API响应失败: {}", e.getMessage(), e); - String errorMessage = e.getMessage() != null ? e.getMessage() : "null"; - throw new RenException(ErrorCode.RAG_API_ERROR, errorMessage); } catch (Exception e) { log.error("列出切片失败: {}", e.getMessage(), e); String errorMessage = e.getMessage() != null ? e.getMessage() : "null"; @@ -1267,7 +978,7 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { } /** - * 解析RAGFlow API返回的切片列表响应 + * 解析RAG API返回的切片列表响应 */ private Map parseChunkListResponse(Map responseMap) { Map result = new HashMap<>(); @@ -1301,7 +1012,7 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { List list = (List) entry.getValue(); if (!list.isEmpty() && list.get(0) instanceof Map) { chunksObj = entry.getValue(); - log.warn("自动检测到切片列表字段: '{}',建议检查RAGFlow API文档", entry.getKey()); + log.warn("自动检测到切片列表字段: '{}',建议检查RAG API文档", entry.getKey()); break; } } @@ -1348,7 +1059,7 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { totalCount = chunkList.size(); } } else { - log.warn("RAGFlow API响应缺少data字段,尝试直接解析响应"); + log.warn("RAG API响应缺少data字段,尝试直接解析响应"); // 如果没有data字段,尝试直接解析响应 Object chunksObj = null; @@ -1575,110 +1286,72 @@ public class KnowledgeFilesServiceImpl implements KnowledgeFilesService { try { // 获取RAG配置 Map ragConfig = knowledgeBaseService.getRAGConfigByDatasetId(datasetIds.get(0)); - String baseUrl = (String) ragConfig.get("base_url"); - String apiKey = (String) ragConfig.get("api_key"); - // 构建请求URL - String url = baseUrl + "/api/v1/retrieval"; - log.debug("请求URL: {}", url); + // 提取适配器类型 + String adapterType = extractAdapterType(ragConfig); - // 构建请求体 - Map requestBody = new HashMap<>(); - requestBody.put("question", question); + // 获取知识库适配器 + KnowledgeBaseAdapter adapter = KnowledgeBaseAdapterFactory.getAdapter(adapterType, ragConfig); + + // 构建检索参数 + Map retrievalParams = new HashMap<>(); + retrievalParams.put("question", question); if (datasetIds != null && !datasetIds.isEmpty()) { - requestBody.put("dataset_ids", datasetIds); + retrievalParams.put("datasetIds", datasetIds); } if (documentIds != null && !documentIds.isEmpty()) { - requestBody.put("document_ids", documentIds); + retrievalParams.put("documentIds", documentIds); } if (page != null && page > 0) { - requestBody.put("page", page); + retrievalParams.put("page", page); } if (pageSize != null && pageSize > 0) { - requestBody.put("page_size", pageSize); + retrievalParams.put("pageSize", pageSize); } if (similarityThreshold != null) { - requestBody.put("similarity_threshold", similarityThreshold); + retrievalParams.put("similarityThreshold", similarityThreshold); } if (vectorSimilarityWeight != null) { - requestBody.put("vector_similarity_weight", vectorSimilarityWeight); + retrievalParams.put("vectorSimilarityWeight", vectorSimilarityWeight); } if (topK != null && topK > 0) { - requestBody.put("top_k", topK); + retrievalParams.put("topK", topK); } if (rerankId != null) { - requestBody.put("rerank_id", rerankId); + retrievalParams.put("rerankId", rerankId); } if (keyword != null) { - requestBody.put("keyword", keyword); + retrievalParams.put("keyword", keyword); } if (highlight != null) { - requestBody.put("highlight", highlight); + retrievalParams.put("highlight", highlight); } if (crossLanguages != null && !crossLanguages.isEmpty()) { - requestBody.put("cross_languages", crossLanguages); + retrievalParams.put("crossLanguages", crossLanguages); } if (metadataCondition != null) { - requestBody.put("metadata_condition", metadataCondition); + retrievalParams.put("metadataCondition", metadataCondition); } - log.debug("请求体: {}", requestBody); + log.debug("检索参数: {}", retrievalParams); - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + apiKey); + // 调用适配器进行检索测试 + Map result = adapter.retrievalTest(question, datasetIds, documentIds, retrievalParams); - HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); - - // 发送POST请求 - log.info("发送POST请求到RAGFlow API进行召回测试..."); - ResponseEntity response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); - - log.info("RAGFlow API响应状态码: {}", response.getStatusCode()); - - String responseBody = response.getBody(); - log.debug("RAGFlow API响应内容: {}", responseBody); - - if (!response.getStatusCode().is2xxSuccessful()) { - log.error("RAGFlow API调用失败,状态码: {}", response.getStatusCode()); - String errorDetail = responseBody != null ? responseBody : "无响应内容"; - throw new RenException(ErrorCode.RAG_API_ERROR, response.getStatusCode() + errorDetail); - } - - // 解析响应 - Map responseMap = objectMapper.readValue(responseBody, Map.class); - Integer code = (Integer) responseMap.get("code"); - - if (code != null && code == 0) { - Object dataObj = responseMap.get("data"); - if (dataObj instanceof Map) { - Map result = (Map) dataObj; - log.info("召回测试成功,返回 {} 条切片", result.get("total")); - return result; - } else { - log.error("RAGFlow API响应格式错误,data字段不是Map类型"); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); - } - } else { - // 获取错误消息,如果存在的话 - String message = (String) responseMap.get("message"); - String errorDetail = message != null ? message : "无详细错误信息"; - log.error("RAGFlow API调用失败,响应码: {}, 错误信息: {}", code, errorDetail); - throw new RenException(ErrorCode.RAG_API_ERROR, responseBody); - } + log.info("召回测试成功,返回 {} 条切片", result.get("total")); + return result; } catch (Exception e) { log.error("召回测试失败: {}", e.getMessage(), e); diff --git a/main/manager-api/src/main/resources/i18n/messages_de_DE.properties b/main/manager-api/src/main/resources/i18n/messages_de_DE.properties index b9e12bea..abde16e5 100644 --- a/main/manager-api/src/main/resources/i18n/messages_de_DE.properties +++ b/main/manager-api/src/main/resources/i18n/messages_de_DE.properties @@ -189,4 +189,13 @@ 10180=Dateiinhalt darf nicht leer sein 10181=Stimmenklon-Name darf nicht leer sein 10182=Stimmenklon-Audio nicht gefunden -10183=Standard-Agent-Vorlage nicht gefunden \ No newline at end of file +10183=Standard-Agent-Vorlage nicht gefunden +10184=Nicht unterstützter Adaptertyp +10185=RAG-Konfigurationsvalidierung fehlgeschlagen +10186=Adapter-Erstellung fehlgeschlagen +10187=Adapter-Initialisierung fehlgeschlagen +10188=Adapter-Verbindungstest fehlgeschlagen +10189=Adapter-Operation fehlgeschlagen +10190=Adapter nicht gefunden +10191=Adapter-Cache-Fehler +10192=Adaptertyp nicht gefunden \ No newline at end of file diff --git a/main/manager-api/src/main/resources/i18n/messages_en_US.properties b/main/manager-api/src/main/resources/i18n/messages_en_US.properties index 75510d37..ea4fca51 100644 --- a/main/manager-api/src/main/resources/i18n/messages_en_US.properties +++ b/main/manager-api/src/main/resources/i18n/messages_en_US.properties @@ -189,4 +189,13 @@ 10180=File content cannot be empty 10181=Voice clone name cannot be empty 10182=Voice clone audio not found -10183=Default agent template not found \ No newline at end of file +10183=Default agent template not found +10184=Unsupported adapter type +10185=RAG configuration validation failed +10186=Adapter creation failed +10187=Adapter initialization failed +10188=Adapter connection test failed +10189=Adapter operation failed +10190=Adapter not found +10191=Adapter cache error +10192=Adapter type not found \ No newline at end of file diff --git a/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties b/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties index 4ad1c0dd..eafe1827 100644 --- a/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties +++ b/main/manager-api/src/main/resources/i18n/messages_vi_VN.properties @@ -189,4 +189,13 @@ 10180=Nội dung tệp không thể để trống 10181=Tên nhân bản giọng nói không thể để trống 10182=Không tìm thấy âm thanh nhân bản giọng nói -10183=Không tìm thấy mẫu agent mặc định \ No newline at end of file +10183=Không tìm thấy mẫu agent mặc định +10184=Loại bộ chuyển đổi không được hỗ trợ +10185=Kiểm tra cấu hình RAG thất bại +10186=Tạo bộ chuyển đổi thất bại +10187=Khởi tạo bộ chuyển đổi thất bại +10188=Kiểm tra kết nối bộ chuyển đổi thất bại +10189=Thao tác bộ chuyển đổi thất bại +10190=Không tìm thấy bộ chuyển đổi +10191=Lỗi bộ nhớ đệm bộ chuyển đổi +10192=Không tìm thấy loại bộ chuyển đổi \ No newline at end of file diff --git a/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties b/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties index 81164dc2..187c0ccb 100644 --- a/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties +++ b/main/manager-api/src/main/resources/i18n/messages_zh_CN.properties @@ -189,4 +189,13 @@ 10180=\u6587\u4EF6\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A 10181=\u97F3\u8272\u514B\u9686\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A 10182=\u97F3\u8272\u514B\u9686\u97F3\u9891\u4E0D\u5B58\u5728 -10183=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230 \ No newline at end of file +10183=\u9ED8\u8BA4\u667A\u80FD\u4F53\u672A\u627E\u5230 +10184=\u4E0D\u652F\u6301\u7684\u9002\u914D\u5668\u7C7B\u578B +10185=RAG\u914D\u7F6E\u9A8C\u8BC1\u5931\u8D25 +10186=\u9002\u914D\u5668\u521B\u5EFA\u5931\u8D25 +10187=\u9002\u914D\u5668\u521D\u59CB\u5316\u5931\u8D25 +10188=\u9002\u914D\u5668\u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25 +10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u8D25 +10190=\u9002\u914D\u5668\u672A\u627E\u5230 +10191=\u9002\u914D\u5668\u7F13\u5B58\u9519\u8BEF +10192=\u9002\u914D\u5668\u7C7B\u578B\u672A\u627E\u5230 \ No newline at end of file diff --git a/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties b/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties index 6a7d7d72..ce1d9ef4 100644 --- a/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties +++ b/main/manager-api/src/main/resources/i18n/messages_zh_TW.properties @@ -189,4 +189,13 @@ 10180=\u6587\u4ef6\u5185\u5bb9\u4e0d\u80fd\u70ba\u7a7a 10181=\u97f3\u8272\u514b\u9686\u540d\u7a31\u4e0d\u80fd\u70ba\u7a7a 10182=\u97f3\u8272\u514b\u9686\u97f3\u983b\u4e0d\u5b58\u5728 -10183=\u9ed8\u8ba4\u667a\u80fd\u4f53\u672a\u627e\u5230 \ No newline at end of file +10183=\u9ed8\u8ba4\u667a\u80fd\u4f53\u672a\u627e\u5230 +10184=\u4E0D\u652F\u6301\u7684\u9002\u914D\u5668\u985E\u578B +10185=RAG\u914D\u7F6E\u9A57\u8B49\u5931\u6557 +10186=\u9002\u914D\u5668\u5275\u5EFA\u5931\u6557 +10187=\u9002\u914D\u5668\u521D\u59CB\u5316\u5931\u6557 +10188=\u9002\u914D\u5668\u9023\u63A5\u6E2C\u8A66\u5931\u6557 +10189=\u9002\u914D\u5668\u64CD\u4F5C\u5931\u6557 +10190=\u9002\u914D\u5668\u672A\u627E\u5230 +10191=\u9002\u914D\u5668\u7F13\u5B58\u932F\u8AA4 +10192=\u9002\u914D\u5668\u985E\u578B\u672A\u627E\u5230 \ No newline at end of file