Commit e30795fb69ccbb1f77271abafa4b8bc75e35d3d7

Authored by YunaiV
1 parent 261011d1

【代码评审】AI 大模型:知识库的逻辑

Showing 21 changed files with 111 additions and 112 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
... ... @@ -52,7 +52,6 @@ public interface ErrorCodeConstants {
52 52 // ========== API 思维导图 1-040-008-000 ==========
53 53 ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!");
54 54  
55   -
56 55 // ========== API 知识库 1-022-008-000 ==========
57 56 ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!");
58 57 ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!");
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
... ... @@ -22,6 +22,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
22 22 @Tag(name = "管理后台 - AI 知识库")
23 23 @RestController
24 24 @RequestMapping("/ai/knowledge")
  25 +@Validated
25 26 public class AiKnowledgeController {
26 27  
27 28 @Resource
... ... @@ -34,14 +35,12 @@ public class AiKnowledgeController {
34 35 return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
35 36 }
36 37  
37   -
38 38 @PostMapping("/create-my")
39 39 @Operation(summary = "创建【我的】知识库")
40 40 public CommonResult<Long> createKnowledgeMy(@RequestBody @Valid AiKnowledgeCreateMyReqVO createReqVO) {
41 41 return success(knowledgeService.createKnowledgeMy(createReqVO, getLoginUserId()));
42 42 }
43 43  
44   -
45 44 @PutMapping("/update-my")
46 45 @Operation(summary = "更新【我的】知识库")
47 46 public CommonResult<Boolean> updateKnowledgeMy(@RequestBody @Valid AiKnowledgeUpdateMyReqVO updateReqVO) {
... ... @@ -49,5 +48,4 @@ public class AiKnowledgeController {
49 48 return success(true);
50 49 }
51 50  
52   -
53 51 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
... ... @@ -12,43 +12,40 @@ import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
12 12 import io.swagger.v3.oas.annotations.Operation;
13 13 import io.swagger.v3.oas.annotations.tags.Tag;
14 14 import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
15 16 import org.springframework.validation.annotation.Validated;
16 17 import org.springframework.web.bind.annotation.*;
17 18  
18 19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
19 20  
20   -
21   -@Tag(name = "管理后台 - AI 知识库-文档")
  21 +@Tag(name = "管理后台 - AI 知识库文档")
22 22 @RestController
23 23 @RequestMapping("/ai/knowledge/document")
  24 +@Validated
24 25 public class AiKnowledgeDocumentController {
25 26  
26 27 @Resource
27 28 private AiKnowledgeDocumentService documentService;
28 29  
29   -
30 30 @PostMapping("/create")
31 31 @Operation(summary = "新建文档")
32   - public CommonResult<Long> createKnowledgeDocument(@Validated AiKnowledgeDocumentCreateReqVO reqVO) {
  32 + public CommonResult<Long> createKnowledgeDocument(@Valid AiKnowledgeDocumentCreateReqVO reqVO) {
33 33 Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO);
34 34 return success(knowledgeDocumentId);
35 35 }
36 36  
37   -
38 37 @GetMapping("/page")
39 38 @Operation(summary = "获取文档分页")
40   - public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPageMy(@Validated AiKnowledgeDocumentPageReqVO pageReqVO) {
  39 + public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPageMy(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
41 40 PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
42 41 return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
43 42 }
44 43  
45   -
46 44 @PutMapping("/update")
47 45 @Operation(summary = "更新文档")
48   - public CommonResult<Boolean> updateKnowledgeDocument(@Validated @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
  46 + public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
49 47 documentService.updateKnowledgeDocument(reqVO);
50 48 return success(true);
51 49 }
52 50  
53   -
54 51 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
... ... @@ -12,15 +12,16 @@ import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
12 12 import io.swagger.v3.oas.annotations.Operation;
13 13 import io.swagger.v3.oas.annotations.tags.Tag;
14 14 import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
15 16 import org.springframework.validation.annotation.Validated;
16 17 import org.springframework.web.bind.annotation.*;
17 18  
18 19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
19 20  
20   -
21   -@Tag(name = "管理后台 - AI 知识库-段落")
  21 +@Tag(name = "管理后台 - AI 知识库段落")
22 22 @RestController
23 23 @RequestMapping("/ai/knowledge/segment")
  24 +@Validated
24 25 public class AiKnowledgeSegmentController {
25 26  
26 27 @Resource
... ... @@ -28,22 +29,21 @@ public class AiKnowledgeSegmentController {
28 29  
29 30 @GetMapping("/page")
30 31 @Operation(summary = "获取段落分页")
31   - public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPageMy(@Validated AiKnowledgeSegmentPageReqVO pageReqVO) {
  32 + public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPageMy(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
32 33 PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
33 34 return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
34 35 }
35 36  
36   -
37 37 @PutMapping("/update")
38 38 @Operation(summary = "更新段落内容")
39   - public CommonResult<Boolean> updateKnowledgeSegment(@Validated @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) {
  39 + public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) {
40 40 segmentService.updateKnowledgeSegment(reqVO);
41 41 return success(true);
42 42 }
43 43  
44 44 @PutMapping("/update-status")
45 45 @Operation(summary = "启禁用段落内容")
46   - public CommonResult<Boolean> updateKnowledgeSegmentStatus(@Validated @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
  46 + public CommonResult<Boolean> updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
47 47 segmentService.updateKnowledgeSegmentStatus(reqVO);
48 48 return success(true);
49 49 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java
... ... @@ -4,10 +4,11 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 4 import io.swagger.v3.oas.annotations.media.Schema;
5 5 import lombok.Data;
6 6  
7   -@Schema(description = "管理后台 - AI 知识库-文档 分页 Request VO")
  7 +@Schema(description = "管理后台 - AI 知识库文档的分页 Request VO")
8 8 @Data
9 9 public class AiKnowledgeDocumentPageReqVO extends PageParam {
10 10  
11 11 @Schema(description = "文档名称", example = "Java 开发手册")
12 12 private String name;
  13 +
13 14 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
... ... @@ -17,21 +17,22 @@ public class AiKnowledgeDocumentRespVO extends PageParam {
17 17 @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
18 18 private String name;
19 19  
20   - @Schema(description = "内容", example = "Java 是一门面向对象的语言.....")
  20 + @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
21 21 private String content;
22 22  
23 23 @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
24 24 private String url;
25 25  
26   - @Schema(description = "token 数量", example = "1024")
  26 + @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
27 27 private Integer tokens;
28 28  
29   - @Schema(description = "字符数", example = "1008")
  29 + @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
30 30 private Integer wordCount;
31 31  
32   - @Schema(description = "切片状态", example = "1")
  32 + @Schema(description = "切片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
33 33 private Integer sliceStatus;
34 34  
35   - @Schema(description = "文档状态", example = "1")
  35 + @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
36 36 private Integer status;
  37 +
37 38 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
3 5 import io.swagger.v3.oas.annotations.media.Schema;
4 6 import jakarta.validation.constraints.NotNull;
5 7 import lombok.Data;
... ... @@ -15,6 +17,7 @@ public class AiKnowledgeDocumentUpdateReqVO {
15 17 private Long id;
16 18  
17 19 @Schema(description = "是否启用", example = "1")
  20 + @InEnum(CommonStatusEnum.class)
18 21 private Integer status;
19 22  
20 23 @Schema(description = "名称", example = "Java 开发手册")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
... ... @@ -7,7 +7,7 @@ import lombok.Data;
7 7 import org.hibernate.validator.constraints.URL;
8 8  
9 9  
10   -@Schema(description = "管理后台 - AI 知识库创建【文档】 Request VO")
  10 +@Schema(description = "管理后台 - AI 知识库文档的创建 Request VO")
11 11 @Data
12 12 public class AiKnowledgeDocumentCreateReqVO {
13 13  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java
... ... @@ -14,12 +14,13 @@ public class AiKnowledgeRespVO {
14 14 @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
15 15 private String name;
16 16  
17   - @Schema(description = "知识库描述", example = "ruoyi-vue-pro 用户指南")
  17 + @Schema(description = "知识库描述", example = "帮助你快速构建系统")
18 18 private String description;
19 19  
20 20 @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
21 21 private Long modelId;
22 22  
23   - @Schema(description = "模型标识", example = "qwen-72b-chat")
  23 + @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
24 24 private String model;
  25 +
25 26 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java
... ... @@ -4,15 +4,14 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 4 import io.swagger.v3.oas.annotations.media.Schema;
5 5 import lombok.Data;
6 6  
7   -@Schema(description = "管理后台 - AI 知识库分页 Request VO")
  7 +@Schema(description = "管理后台 - AI 知识库分段的分页 Request VO")
8 8 @Data
9 9 public class AiKnowledgeSegmentPageReqVO extends PageParam {
10 10  
11   -
12 11 @Schema(description = "分段状态", example = "1")
13 12 private Integer status;
14 13  
15   - @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  14 + @Schema(description = "文档编号", example = "1")
16 15 private Integer documentId;
17 16  
18 17 @Schema(description = "分段内容关键字", example = "Java 开发")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
... ... @@ -22,12 +22,13 @@ public class AiKnowledgeSegmentRespVO {
22 22 @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
23 23 private String content;
24 24  
25   - @Schema(description = "token 数量", example = "1024")
  25 + @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
26 26 private Integer tokens;
27 27  
28   - @Schema(description = "字符数", example = "1008")
  28 + @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
29 29 private Integer wordCount;
30 30  
31   - @Schema(description = "文档状态", example = "1")
  31 + @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
32 32 private Integer status;
  33 +
33 34 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
3 5 import io.swagger.v3.oas.annotations.media.Schema;
  6 +import jakarta.validation.constraints.NotNull;
4 7 import lombok.Data;
5 8  
6 9  
7   -@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO")
  10 +@Schema(description = "管理后台 - AI 知识库段落的更新状态 Request VO")
8 11 @Data
9 12 public class AiKnowledgeSegmentUpdateStatusReqVO {
10 13  
... ... @@ -12,6 +15,8 @@ public class AiKnowledgeSegmentUpdateStatusReqVO {
12 15 private Long id;
13 16  
14 17 @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  18 + @NotNull(message = "是否启用不能为空")
  19 + @InEnum(CommonStatusEnum.class)
15 20 private Integer status;
16 21  
17 22 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
... ... @@ -28,12 +28,13 @@ public class AiKnowledgeSegmentDO extends BaseDO {
28 28 private String vectorId;
29 29 /**
30 30 * 知识库编号
  31 + *
31 32 * 关联 {@link AiKnowledgeDO#getId()}
32 33 */
33 34 private Long knowledgeId;
34 35 /**
35 36 * 文档编号
36   - * <p>
  37 + *
37 38 * 关联 {@link AiKnowledgeDocumentDO#getId()}
38 39 */
39 40 private Long documentId;
... ... @@ -51,7 +52,7 @@ public class AiKnowledgeSegmentDO extends BaseDO {
51 52 private Integer tokens;
52 53 /**
53 54 * 状态
54   - * <p>
  55 + *
55 56 * 枚举 {@link CommonStatusEnum}
56 57 */
57 58 private Integer status;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
... ... @@ -30,13 +30,12 @@ import org.springframework.stereotype.Service;
30 30 import org.springframework.transaction.annotation.Transactional;
31 31  
32 32 import java.util.List;
33   -import java.util.Map;
34 33  
35 34 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
36 35 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS;
37 36  
38 37 /**
39   - * AI 知识库-文档 Service 实现类
  38 + * AI 知识库文档 Service 实现类
40 39 *
41 40 * @author xiaoxin
42 41 */
... ... @@ -61,24 +60,21 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
61 60 @Resource
62 61 private AiChatModelService chatModelService;
63 62  
64   -
65   - // TODO 芋艿:需要 review 下,代码格式;
66 63 @Override
67 64 @Transactional(rollbackFor = Exception.class)
68 65 public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) {
  66 + // 0. 校验
  67 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
  68 + AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
  69 +
69 70 // 1.1 下载文档
70   - String url = createReqVO.getUrl();
71   - // 1.2 加载文档
72   - TikaDocumentReader loader = new TikaDocumentReader(downloadFile(url));
  71 + TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl()));
73 72 List<Document> documents = loader.get();
74 73 Document document = CollUtil.getFirst(documents);
  74 + // 1.2 文档记录入库
75 75 String content = document.getContent();
76   - Integer tokens = tokenCountEstimator.estimate(content);
77   - Integer wordCount = content.length();
78   -
79   - // 1.3 文档记录入库
80 76 AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class)
81   - .setTokens(tokens).setWordCount(wordCount)
  77 + .setTokens(tokenCountEstimator.estimate(content)).setWordCount(content.length())
82 78 .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus());
83 79 documentMapper.insert(documentDO);
84 80 Long documentId = documentDO.getId();
... ... @@ -90,22 +86,16 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
90 86 List<Document> segments = tokenTextSplitter.apply(documents);
91 87 // 2.2 分段内容入库
92 88 List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments,
93   - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId).setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId())
  89 + segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId)
  90 + .setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId())
94 91 .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length())
95 92 .setStatus(CommonStatusEnum.ENABLE.getStatus()));
96 93 segmentMapper.insertBatch(segmentDOList);
97 94  
98   - // 3.1 document 补充源数据
99   - segments.forEach(segment -> {
100   - Map<String, Object> metadata = segment.getMetadata();
101   - metadata.put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId());
102   - });
103   -
104   - AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
105   - AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
106   - // 3.2 获取向量存储实例
  95 + // 3.1 获取向量存储实例
107 96 VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
108   - // 3.3 向量化并存储
  97 + // 3.2 向量化并存储
  98 + segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId()));
109 99 vectorStore.add(segments);
110 100 return documentId;
111 101 }
... ... @@ -117,7 +107,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
117 107  
118 108 @Override
119 109 public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) {
  110 + // 1. 校验文档是否存在
120 111 validateKnowledgeDocumentExists(reqVO.getId());
  112 + // 2. 更新文档
121 113 AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class);
122 114 documentMapper.updateById(document);
123 115 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
... ... @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle
7 7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
8 8  
9 9 /**
10   - * AI 知识库分片 Service 接口
  10 + * AI 知识库段落 Service 接口
11 11 *
12 12 * @author xiaoxin
13 13 */
... ... @@ -22,16 +22,17 @@ public interface AiKnowledgeSegmentService {
22 22 PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);
23 23  
24 24 /**
25   - * 更新段落内容
  25 + * 更新段落的内容
26 26 *
27 27 * @param reqVO 更新内容
28 28 */
29 29 void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO);
30 30  
31 31 /**
32   - * 更新状态
  32 + * 更新段落的状态
33 33 *
34 34 * @param reqVO 更新内容
35 35 */
36 36 void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
  37 +
37 38 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
... ... @@ -86,14 +86,6 @@ public interface AiApiKeyService {
86 86 ChatModel getChatModel(Long id);
87 87  
88 88 /**
89   - * 获得 EmbeddingModel 对象
90   - *
91   - * @param id 编号
92   - * @return EmbeddingModel 对象
93   - */
94   - EmbeddingModel getEmbeddingModel(Long id);
95   -
96   - /**
97 89 * 获得 ImageModel 对象
98 90 *
99 91 * TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择
... ... @@ -122,7 +114,15 @@ public interface AiApiKeyService {
122 114 SunoApi getSunoApi();
123 115  
124 116 /**
125   - * 获得 vector 对象
  117 + * 获得 EmbeddingModel 对象
  118 + *
  119 + * @param id 编号
  120 + * @return EmbeddingModel 对象
  121 + */
  122 + EmbeddingModel getEmbeddingModel(Long id);
  123 +
  124 + /**
  125 + * 获得 VectorStore 对象
126 126 *
127 127 * @param id 编号
128 128 * @return VectorStore 对象
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
... ... @@ -110,13 +110,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
110 110 }
111 111  
112 112 @Override
113   - public EmbeddingModel getEmbeddingModel(Long id) {
114   - AiApiKeyDO apiKey = validateApiKey(id);
115   - AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
116   - return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl());
117   - }
118   -
119   - @Override
120 113 public ImageModel getImageModel(AiPlatformEnum platform) {
121 114 AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
122 115 if (apiKey == null) {
... ... @@ -146,9 +139,17 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
146 139 }
147 140  
148 141 @Override
  142 + public EmbeddingModel getEmbeddingModel(Long id) {
  143 + AiApiKeyDO apiKey = validateApiKey(id);
  144 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
  145 + return modelFactory.getOrCreateEmbeddingModel(platform, apiKey.getApiKey(), apiKey.getUrl());
  146 + }
  147 +
  148 + @Override
149 149 public VectorStore getOrCreateVectorStore(Long id) {
150 150 AiApiKeyDO apiKey = validateApiKey(id);
151 151 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
152 152 return vectorFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl());
153 153 }
  154 +
154 155 }
155 156 \ No newline at end of file
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java
... ... @@ -27,18 +27,6 @@ public interface AiModelFactory {
27 27 ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url);
28 28  
29 29 /**
30   - * 基于指定配置,获得 EmbeddingModel 对象
31   - * <p>
32   - * 如果不存在,则进行创建
33   - *
34   - * @param platform 平台
35   - * @param apiKey API KEY
36   - * @param url API URL
37   - * @return ChatModel 对象
38   - */
39   - EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url);
40   -
41   - /**
42 30 * 基于默认配置,获得 ChatModel 对象
43 31 *
44 32 * 默认配置,指的是在 application.yaml 配置文件中的 spring.ai 相关的配置
... ... @@ -92,4 +80,16 @@ public interface AiModelFactory {
92 80 */
93 81 SunoApi getOrCreateSunoApi(String apiKey, String url);
94 82  
  83 + /**
  84 + * 基于指定配置,获得 EmbeddingModel 对象
  85 + *
  86 + * 如果不存在,则进行创建
  87 + *
  88 + * @param platform 平台
  89 + * @param apiKey API KEY
  90 + * @param url API URL
  91 + * @return ChatModel 对象
  92 + */
  93 + EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url);
  94 +
95 95 }
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
... ... @@ -100,21 +100,6 @@ public class AiModelFactoryImpl implements AiModelFactory {
100 100 }
101 101  
102 102 @Override
103   - public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url) {
104   - String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url);
105   - return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> {
106   - // TODO @xin 先测试一个
107   - switch (platform) {
108   - case TONG_YI:
109   - return buildTongYiEmbeddingModel(apiKey);
110   - default:
111   - throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
112   - }
113   - });
114   - }
115   -
116   -
117   - @Override
118 103 public ChatModel getDefaultChatModel(AiPlatformEnum platform) {
119 104 //noinspection EnhancedSwitchMigration
120 105 switch (platform) {
... ... @@ -192,6 +177,20 @@ public class AiModelFactoryImpl implements AiModelFactory {
192 177 return Singleton.get(cacheKey, (Func0<SunoApi>) () -> new SunoApi(url));
193 178 }
194 179  
  180 + @Override
  181 + public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url) {
  182 + String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url);
  183 + return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> {
  184 + // TODO @xin 先测试一个
  185 + switch (platform) {
  186 + case TONG_YI:
  187 + return buildTongYiEmbeddingModel(apiKey);
  188 + default:
  189 + throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
  190 + }
  191 + });
  192 + }
  193 +
195 194 private static String buildClientCacheKey(Class<?> clazz, Object... params) {
196 195 if (ArrayUtil.isEmpty(params)) {
197 196 return clazz.getName();
... ... @@ -255,8 +254,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
255 254 }
256 255  
257 256 /**
258   - * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(
259   - *ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
  257 + * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
260 258 */
261 259 private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {
262 260 url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
... ... @@ -265,8 +263,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
265 263 }
266 264  
267 265 /**
268   - * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel(
269   - *ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
  266 + * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel(ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
270 267 */
271 268 private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) {
272 269 url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java
... ... @@ -4,13 +4,14 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 4 import org.springframework.ai.embedding.EmbeddingModel;
5 5 import org.springframework.ai.vectorstore.VectorStore;
6 6  
  7 +// TODO @xin:也放到 AiModelFactory 里面好了,后续改成 AiFactory
7 8 /**
8 9 * AI Vector 模型工厂的接口类
  10 + *
9 11 * @author xiaoxin
10 12 */
11 13 public interface AiVectorStoreFactory {
12 14  
13   -
14 15 /**
15 16 * 基于指定配置,获得 VectorStore 对象
16 17 * <p>
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java
... ... @@ -26,6 +26,7 @@ public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory {
26 26 return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {
27 27 // TODO 芋艿 @xin 这两个配置取哪好呢
28 28 // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上
  29 + // TODO 回复:好的哈
29 30 String index = "default-index";
30 31 String prefix = "default:";
31 32 var config = RedisVectorStore.RedisVectorStoreConfig.builder()
... ... @@ -41,11 +42,11 @@ public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory {
41 42 });
42 43 }
43 44  
44   -
45 45 private static String buildClientCacheKey(Class<?> clazz, Object... params) {
46 46 if (ArrayUtil.isEmpty(params)) {
47 47 return clazz.getName();
48 48 }
49 49 return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_"));
50 50 }
  51 +
51 52 }
... ...