Commit d1e207899a1b7e4497104cd3321f4d2fcf5c3a4c

Authored by YunaiV
1 parent e5cc9d2a

【功能新增】AI:知识库,新增切片接口

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http 0 → 100644
  1 +### 切片内容
  2 +GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800
  3 +Content-Type: application/json
  4 +Authorization: Bearer {{token}}
  5 +tenant-id: {{adminTenantId}}
0 6 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
... ... @@ -10,12 +10,16 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle
10 10 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
11 11 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
12 12 import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
  14 +import io.swagger.v3.oas.annotations.Parameters;
13 15 import io.swagger.v3.oas.annotations.tags.Tag;
14 16 import jakarta.annotation.Resource;
15 17 import jakarta.validation.Valid;
16 18 import org.springframework.validation.annotation.Validated;
17 19 import org.springframework.web.bind.annotation.*;
18 20  
  21 +import java.util.List;
  22 +
19 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
20 24  
21 25 // TODO @芋艿:增加权限标识
... ... @@ -30,7 +34,8 @@ public class AiKnowledgeSegmentController {
30 34  
31 35 @GetMapping("/page")
32 36 @Operation(summary = "获取段落分页")
33   - public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
  37 + public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(
  38 + @Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
34 39 PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
35 40 return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
36 41 }
... ... @@ -44,9 +49,23 @@ public class AiKnowledgeSegmentController {
44 49  
45 50 @PutMapping("/update-status")
46 51 @Operation(summary = "启禁用段落内容")
47   - public CommonResult<Boolean> updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
  52 + public CommonResult<Boolean> updateKnowledgeSegmentStatus(
  53 + @Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
48 54 segmentService.updateKnowledgeSegmentStatus(reqVO);
49 55 return success(true);
50 56 }
51 57  
  58 + @GetMapping("/split")
  59 + @Operation(summary = "切片内容")
  60 + @Parameters({
  61 + @Parameter(name = "url", description = "文档 URL", required = true),
  62 + @Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true)
  63 + })
  64 + public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent(
  65 + @RequestParam("url") String url,
  66 + @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) {
  67 + List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens);
  68 + return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class));
  69 + }
  70 +
52 71 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import lombok.Data;
5 5  
6   -@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
  6 +@Schema(description = "管理后台 - AI 知识库文档分片 Response VO")
7 7 @Data
8 8 public class AiKnowledgeSegmentRespVO {
9 9  
... ... @@ -22,13 +22,19 @@ public class AiKnowledgeSegmentRespVO {
22 22 @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
23 23 private String content;
24 24  
  25 + @Schema(description = "切片内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  26 + private Integer contentLength;
  27 +
25 28 @Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
26 29 private Integer tokens;
27 30  
28   - @Schema(description = "字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
29   - private Integer wordCount;
  31 + @Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
  32 + private Integer retrievalCount;
30 33  
31 34 @Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
32 35 private Integer status;
33 36  
  37 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + private Long createTime;
  39 +
34 40 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java
... ... @@ -63,4 +63,12 @@ public interface AiKnowledgeDocumentService {
63 63 */
64 64 AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id);
65 65  
  66 + /**
  67 + * 读取 URL 内容
  68 + *
  69 + * @param url URL
  70 + * @return 内容
  71 + */
  72 + String readUrl(String url);
  73 +
66 74 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
... ... @@ -127,9 +127,10 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
127 127 return knowledgeDocument;
128 128 }
129 129  
130   - private static String readUrl(String url) {
  130 + @Override
  131 + public String readUrl(String url) {
131 132 // 下载文件
132   - ByteArrayResource resource = null;
  133 + ByteArrayResource resource;
133 134 try {
134 135 byte[] bytes = HttpUtil.downloadBytes(url);
135 136 if (bytes.length == 0) {
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
... ... @@ -29,7 +29,7 @@ public interface AiKnowledgeSegmentService {
29 29 * 基于 content 内容,切片创建多个段落
30 30 *
31 31 * @param documentId 知识库文档编号
32   - * @param content 文档内容
  32 + * @param content 文档内容
33 33 */
34 34 void createKnowledgeSegmentBySplitContent(Long documentId, String content);
35 35  
... ... @@ -37,7 +37,7 @@ public interface AiKnowledgeSegmentService {
37 37 * 【异步】基于 content 内容,切片创建多个段落
38 38 *
39 39 * @param documentId 知识库文档编号
40   - * @param content 文档内容
  40 + * @param content 文档内容
41 41 */
42 42 @Async
43 43 default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) {
... ... @@ -66,4 +66,13 @@ public interface AiKnowledgeSegmentService {
66 66 */
67 67 List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO);
68 68  
  69 + /**
  70 + * 根据 URL 内容,切片创建多个段落
  71 + *
  72 + * @param url URL 地址
  73 + * @param segmentMaxTokens 段落最大 Token 数
  74 + * @return 切片后的段落列表
  75 + */
  76 + List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens);
  77 +
69 78 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
... ... @@ -75,9 +75,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
75 75 VectorStore vectorStore = getVectorStoreById(knowledgeDO);
76 76  
77 77 // 2. 文档切片
78   - Document document = new Document(content);
79   - TextSplitter textSplitter = buildTokenTextSplitter(documentDO.getSegmentMaxTokens());
80   - List<Document> documentSegments = textSplitter.apply(Collections.singletonList(document));
  78 + List<Document> documentSegments = splitContentByToken(content, documentDO.getSegmentMaxTokens());
81 79  
82 80 // 3.1 存储切片
83 81 List<AiKnowledgeSegmentDO> segmentDOs = convertList(documentSegments, segment -> {
... ... @@ -86,7 +84,8 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
86 84 }
87 85 return new AiKnowledgeSegmentDO().setKnowledgeId(documentDO.getKnowledgeId()).setDocumentId(documentId)
88 86 .setContent(segment.getText()).setContentLength(segment.getText().length())
89   - .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY).setTokens(tokenCountEstimator.estimate(segment.getText()))
  87 + .setVectorId(AiKnowledgeSegmentDO.VECTOR_ID_EMPTY)
  88 + .setTokens(tokenCountEstimator.estimate(segment.getText()))
90 89 .setStatus(CommonStatusEnum.ENABLE.getStatus());
91 90 });
92 91 segmentMapper.insertBatch(segmentDOs);
... ... @@ -180,6 +179,26 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
180 179 return segmentMapper.selectListByVectorIds(convertList(documents, Document::getId));
181 180 }
182 181  
  182 + @Override
  183 + public List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens) {
  184 + // 1. 读取 URL 内容
  185 + String content = knowledgeDocumentService.readUrl(url);
  186 +
  187 + // 2. 文档切片
  188 + List<Document> documentSegments = splitContentByToken(content, segmentMaxTokens);
  189 +
  190 + // 3. 转换为段落对象
  191 + return convertList(documentSegments, segment -> {
  192 + if (StrUtil.isEmpty(segment.getText())) {
  193 + return null;
  194 + }
  195 + return new AiKnowledgeSegmentDO()
  196 + .setContent(segment.getText())
  197 + .setContentLength(segment.getText().length())
  198 + .setTokens(tokenCountEstimator.estimate(segment.getText()));
  199 + });
  200 + }
  201 +
183 202 /**
184 203 * 校验段落是否存在
185 204 *
... ... @@ -202,6 +221,11 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
202 221 return getVectorStoreById(knowledgeService.validateKnowledgeExists(knowledgeId));
203 222 }
204 223  
  224 + private static List<Document> splitContentByToken(String content, Integer segmentMaxTokens) {
  225 + TextSplitter textSplitter = buildTokenTextSplitter(segmentMaxTokens);
  226 + return textSplitter.apply(Collections.singletonList(new Document(content)));
  227 + }
  228 +
205 229 private static TextSplitter buildTokenTextSplitter(Integer segmentMaxTokens) {
206 230 return TokenTextSplitter.builder()
207 231 .withChunkSize(segmentMaxTokens)
... ...