Commit 261011d1a8b8f80d0c45f99a0f34d2e725d85eca

Authored by 芋道源码
Committed by Gitee
2 parents 6cceab5b 96fb9537

!19 AI 知识库

Merge pull request !19 from 小新/master-jdk21-ai
Showing 39 changed files with 773 additions and 191 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
1 1 package cn.iocoder.yudao.module.ai.enums;
2 2  
3   -import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
4 3 import lombok.AllArgsConstructor;
5 4 import lombok.Getter;
6 5  
7   -import java.util.Arrays;
8   -
9 6 /**
10 7 * AI 内置聊天角色的枚举
11 8 *
... ... @@ -13,16 +10,16 @@ import java.util.Arrays;
13 10 */
14 11 @AllArgsConstructor
15 12 @Getter
16   -public enum AiChatRoleEnum implements IntArrayValuable {
  13 +public enum AiChatRoleEnum {
17 14  
18   - AI_WRITE_ROLE(1, "写作助手", """
  15 + AI_WRITE_ROLE("写作助手", """
19 16 你是一位出色的写作助手,能够帮助用户生成创意和灵感,并在用户提供场景和提示词时生成对应的回复。你的任务包括:
20 17 1. 撰写建议:根据用户提供的主题或问题,提供详细的写作建议、情节发展方向、角色设定以及背景描写,确保内容结构清晰、有逻辑。
21 18 2. 回复生成:根据用户提供的场景和提示词,生成合适的对话或文字回复,确保语气和风格符合场景需求。
22 19 除此之外不需要除了正文内容外的其他回复,如标题、开头、任何解释性语句或道歉。
23 20 """),
24 21  
25   - AI_MIND_MAP_ROLE(2, "导图助手", """
  22 + AI_MIND_MAP_ROLE("导图助手", """
26 23 你是一位非常优秀的思维导图助手,你会把用户的所有提问都总结成思维导图,然后以 Markdown 格式输出。markdown 只需要输出一级标题,二级标题,三级标题,四级标题,最多输出四级,除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子:
27 24 # Geek-AI 助手
28 25 ## 完整的开源系统
... ... @@ -39,11 +36,6 @@ public enum AiChatRoleEnum implements IntArrayValuable {
39 36 除此之外不要任何解释性语句。
40 37 """);
41 38  
42   - // TODO @xin:这个 role 是不是删除掉好点哈。= = 目前主要是没做角色枚举。这里多了 role 反倒容易误解哈
43   - /**
44   - * 角色
45   - */
46   - private final Integer role;
47 39 /**
48 40 * 角色名
49 41 */
... ... @@ -54,11 +46,4 @@ public enum AiChatRoleEnum implements IntArrayValuable {
54 46 */
55 47 private final String systemMessage;
56 48  
57   - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiChatRoleEnum::getRole).toArray();
58   -
59   - @Override
60   - public int[] array() {
61   - return ARRAYS;
62   - }
63   -
64 49 }
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
... ... @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 4  
5 5 /**
6 6 * AI 错误码枚举类
7   - *
  7 + * <p>
8 8 * ai 系统,使用 1-040-000-000 段
9 9 */
10 10 public interface ErrorCodeConstants {
... ... @@ -55,5 +55,7 @@ public interface ErrorCodeConstants {
55 55  
56 56 // ========== API 知识库 1-022-008-000 ==========
57 57 ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!");
  58 + ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!");
  59 + ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_002, "段落不存在!");
58 60  
59 61 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO;
5   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO;
6   -import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeBaseService;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
  11 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
7 12 import io.swagger.v3.oas.annotations.Operation;
8 13 import io.swagger.v3.oas.annotations.tags.Tag;
9 14 import jakarta.annotation.Resource;
10 15 import jakarta.validation.Valid;
  16 +import org.springframework.validation.annotation.Validated;
11 17 import org.springframework.web.bind.annotation.*;
12 18  
13 19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
... ... @@ -19,19 +25,27 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
19 25 public class AiKnowledgeController {
20 26  
21 27 @Resource
22   - private AiKnowledgeBaseService knowledgeBaseService;
  28 + private AiKnowledgeService knowledgeService;
  29 +
  30 + @GetMapping("/my-page")
  31 + @Operation(summary = "获取【我的】知识库分页")
  32 + public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePageMy(@Validated PageParam pageReqVO) {
  33 + PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePageMy(getLoginUserId(), pageReqVO);
  34 + return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
  35 + }
  36 +
23 37  
24 38 @PostMapping("/create-my")
25 39 @Operation(summary = "创建【我的】知识库")
26 40 public CommonResult<Long> createKnowledgeMy(@RequestBody @Valid AiKnowledgeCreateMyReqVO createReqVO) {
27   - return success(knowledgeBaseService.createKnowledgeMy(createReqVO, getLoginUserId()));
  41 + return success(knowledgeService.createKnowledgeMy(createReqVO, getLoginUserId()));
28 42 }
29 43  
30 44  
31 45 @PutMapping("/update-my")
32 46 @Operation(summary = "更新【我的】知识库")
33 47 public CommonResult<Boolean> updateKnowledgeMy(@RequestBody @Valid AiKnowledgeUpdateMyReqVO updateReqVO) {
34   - knowledgeBaseService.updateKnowledgeMy(updateReqVO, getLoginUserId());
  48 + knowledgeService.updateKnowledgeMy(updateReqVO, getLoginUserId());
35 49 return success(true);
36 50 }
37 51  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
  11 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import org.springframework.validation.annotation.Validated;
  16 +import org.springframework.web.bind.annotation.*;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  19 +
  20 +
  21 +@Tag(name = "管理后台 - AI 知识库-文档")
  22 +@RestController
  23 +@RequestMapping("/ai/knowledge/document")
  24 +public class AiKnowledgeDocumentController {
  25 +
  26 + @Resource
  27 + private AiKnowledgeDocumentService documentService;
  28 +
  29 +
  30 + @PostMapping("/create")
  31 + @Operation(summary = "新建文档")
  32 + public CommonResult<Long> createKnowledgeDocument(@Validated AiKnowledgeDocumentCreateReqVO reqVO) {
  33 + Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO);
  34 + return success(knowledgeDocumentId);
  35 + }
  36 +
  37 +
  38 + @GetMapping("/page")
  39 + @Operation(summary = "获取文档分页")
  40 + public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPageMy(@Validated AiKnowledgeDocumentPageReqVO pageReqVO) {
  41 + PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
  42 + return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
  43 + }
  44 +
  45 +
  46 + @PutMapping("/update")
  47 + @Operation(summary = "更新文档")
  48 + public CommonResult<Boolean> updateKnowledgeDocument(@Validated @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
  49 + documentService.updateKnowledgeDocument(reqVO);
  50 + return success(true);
  51 + }
  52 +
  53 +
  54 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
  11 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
  12 +import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import org.springframework.validation.annotation.Validated;
  16 +import org.springframework.web.bind.annotation.*;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  19 +
  20 +
  21 +@Tag(name = "管理后台 - AI 知识库-段落")
  22 +@RestController
  23 +@RequestMapping("/ai/knowledge/segment")
  24 +public class AiKnowledgeSegmentController {
  25 +
  26 + @Resource
  27 + private AiKnowledgeSegmentService segmentService;
  28 +
  29 + @GetMapping("/page")
  30 + @Operation(summary = "获取段落分页")
  31 + public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPageMy(@Validated AiKnowledgeSegmentPageReqVO pageReqVO) {
  32 + PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
  33 + return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
  34 + }
  35 +
  36 +
  37 + @PutMapping("/update")
  38 + @Operation(summary = "更新段落内容")
  39 + public CommonResult<Boolean> updateKnowledgeSegment(@Validated @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) {
  40 + segmentService.updateKnowledgeSegment(reqVO);
  41 + return success(true);
  42 + }
  43 +
  44 + @PutMapping("/update-status")
  45 + @Operation(summary = "启禁用段落内容")
  46 + public CommonResult<Boolean> updateKnowledgeSegmentStatus(@Validated @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
  47 + segmentService.updateKnowledgeSegmentStatus(reqVO);
  48 + return success(true);
  49 + }
  50 +
  51 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 知识库-文档 分页 Request VO")
  8 +@Data
  9 +public class AiKnowledgeDocumentPageReqVO extends PageParam {
  10 +
  11 + @Schema(description = "文档名称", example = "Java 开发手册")
  12 + private String name;
  13 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
  8 +@Data
  9 +public class AiKnowledgeDocumentRespVO extends PageParam {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  12 + private Long id;
  13 +
  14 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  15 + private Long knowledgeId;
  16 +
  17 + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
  18 + private String name;
  19 +
  20 + @Schema(description = "内容", example = "Java 是一门面向对象的语言.....")
  21 + private String content;
  22 +
  23 + @Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
  24 + private String url;
  25 +
  26 + @Schema(description = "token 数量", example = "1024")
  27 + private Integer tokens;
  28 +
  29 + @Schema(description = "字符数", example = "1008")
  30 + private Integer wordCount;
  31 +
  32 + @Schema(description = "切片状态", example = "1")
  33 + private Integer sliceStatus;
  34 +
  35 + @Schema(description = "文档状态", example = "1")
  36 + private Integer status;
  37 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +
  8 +@Schema(description = "管理后台 - AI 更新 知识库-文档 Request VO")
  9 +@Data
  10 +public class AiKnowledgeDocumentUpdateReqVO {
  11 +
  12 +
  13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
  14 + @NotNull(message = "编号不能为空")
  15 + private Long id;
  16 +
  17 + @Schema(description = "是否启用", example = "1")
  18 + private Integer status;
  19 +
  20 + @Schema(description = "名称", example = "Java 开发手册")
  21 + private String name;
  22 +
  23 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeCreateMyReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import jakarta.validation.constraints.NotBlank;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeDocumentCreateReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import jakarta.validation.constraints.NotBlank;
... ... @@ -6,10 +6,8 @@ import jakarta.validation.constraints.NotNull;
6 6 import lombok.Data;
7 7 import org.hibernate.validator.constraints.URL;
8 8  
9   -/**
10   - * @author xiaoxin
11   - */
12   -@Schema(description = "管理后台 - AI 知识库【创建文档】 Request VO")
  9 +
  10 +@Schema(description = "管理后台 - AI 知识库创建【文档】 Request VO")
13 11 @Data
14 12 public class AiKnowledgeDocumentCreateReqVO {
15 13  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +
  7 +@Schema(description = "管理后台 - AI 知识库 Response VO")
  8 +@Data
  9 +public class AiKnowledgeRespVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  12 + private Long id;
  13 +
  14 + @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
  15 + private String name;
  16 +
  17 + @Schema(description = "知识库描述", example = "ruoyi-vue-pro 用户指南")
  18 + private String description;
  19 +
  20 + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
  21 + private Long modelId;
  22 +
  23 + @Schema(description = "模型标识", example = "qwen-72b-chat")
  24 + private String model;
  25 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/AiKnowledgeUpdateMyReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateMyReqVO.java
1   -package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo;
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
2 2  
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4 import jakarta.validation.constraints.NotBlank;
... ... @@ -7,7 +7,7 @@ import lombok.Data;
7 7  
8 8 import java.util.List;
9 9  
10   -@Schema(description = "管理后台 - AI 知识库创建【我的】 Request VO")
  10 +@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO")
11 11 @Data
12 12 public class AiKnowledgeUpdateMyReqVO {
13 13  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 知识库分页 Request VO")
  8 +@Data
  9 +public class AiKnowledgeSegmentPageReqVO extends PageParam {
  10 +
  11 +
  12 + @Schema(description = "分段状态", example = "1")
  13 + private Integer status;
  14 +
  15 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  16 + private Integer documentId;
  17 +
  18 + @Schema(description = "分段内容关键字", example = "Java 开发")
  19 + private String keyword;
  20 +
  21 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
  7 +@Data
  8 +public class AiKnowledgeSegmentRespVO {
  9 +
  10 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  11 + private Long id;
  12 +
  13 + @Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  14 + private Long documentId;
  15 +
  16 + @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  17 + private Long knowledgeId;
  18 +
  19 + @Schema(description = "向量库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1858496a-1dde-4edf-a43e-0aed08f37f8c")
  20 + private String vectorId;
  21 +
  22 + @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
  23 + private String content;
  24 +
  25 + @Schema(description = "token 数量", example = "1024")
  26 + private Integer tokens;
  27 +
  28 + @Schema(description = "字符数", example = "1008")
  29 + private Integer wordCount;
  30 +
  31 + @Schema(description = "文档状态", example = "1")
  32 + private Integer status;
  33 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +
  7 +@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO")
  8 +@Data
  9 +public class AiKnowledgeSegmentUpdateReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  12 + private Long id;
  13 +
  14 + @Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
  15 + private String content;
  16 +
  17 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +
  7 +@Schema(description = "管理后台 - AI 更新 知识库-段落 request VO")
  8 +@Data
  9 +public class AiKnowledgeSegmentUpdateStatusReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  12 + private Long id;
  13 +
  14 + @Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  15 + private Integer status;
  16 +
  17 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeBaseDO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
... ... @@ -10,15 +10,14 @@ import lombok.Data;
10 10  
11 11 import java.util.List;
12 12  
13   -// TODO @xin:要不把 AiKnowledgeBaseDO 改成 AiKnowledgeDO。感觉 base 后缀,感觉有点奇怪(让人以为是基类)。然后,我们很多地方的外键编号,都是 knowledgeId
14 13 /**
15 14 * AI 知识库 DO
16 15 *
17 16 * @author xiaoxin
18 17 */
19   -@TableName(value = "ai_knowledge_base", autoResultMap = true)
  18 +@TableName(value = "ai_knowledge", autoResultMap = true)
20 19 @Data
21   -public class AiKnowledgeBaseDO extends BaseDO {
  20 +public class AiKnowledgeDO extends BaseDO {
22 21  
23 22 /**
24 23 * 编号
... ... @@ -46,7 +45,7 @@ public class AiKnowledgeBaseDO extends BaseDO {
46 45 @TableField(typeHandler = JacksonTypeHandler.class)
47 46 private List<Long> visibilityPermissions;
48 47 /**
49   - * 嵌入模型编号,高质量模式时维护
  48 + * 嵌入模型编号
50 49 */
51 50 private Long modelId;
52 51 /**
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
... ... @@ -24,7 +24,7 @@ public class AiKnowledgeDocumentDO extends BaseDO {
24 24 /**
25 25 * 知识库编号
26 26 *
27   - * 关联 {@link AiKnowledgeBaseDO#getId()}
  27 + * 关联 {@link AiKnowledgeDO#getId()}
28 28 */
29 29 private Long knowledgeId;
30 30 /**
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
... ... @@ -15,6 +15,8 @@ import lombok.Data;
15 15 @Data
16 16 public class AiKnowledgeSegmentDO extends BaseDO {
17 17  
  18 + public static final String FIELD_KNOWLEDGE_ID = "knowledgeId";
  19 +
18 20 /**
19 21 * 编号
20 22 */
... ... @@ -24,10 +26,14 @@ public class AiKnowledgeSegmentDO extends BaseDO {
24 26 * 向量库的编号
25 27 */
26 28 private String vectorId;
27   - // TODO @新:knowledgeId 加个,会方便点
  29 + /**
  30 + * 知识库编号
  31 + * 关联 {@link AiKnowledgeDO#getId()}
  32 + */
  33 + private Long knowledgeId;
28 34 /**
29 35 * 文档编号
30   - *
  36 + * <p>
31 37 * 关联 {@link AiKnowledgeDocumentDO#getId()}
32 38 */
33 39 private Long documentId;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java
1 1 package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
4 7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
5 8 import org.apache.ibatis.annotations.Mapper;
6 9  
... ... @@ -11,4 +14,11 @@ import org.apache.ibatis.annotations.Mapper;
11 14 */
12 15 @Mapper
13 16 public interface AiKnowledgeDocumentMapper extends BaseMapperX<AiKnowledgeDocumentDO> {
  17 +
  18 + default PageResult<AiKnowledgeDocumentDO> selectPage(AiKnowledgeDocumentPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeDocumentDO>()
  20 + .likeIfPresent(AiKnowledgeDocumentDO::getName, reqVO.getName())
  21 + .orderByDesc(AiKnowledgeDocumentDO::getId));
  22 + }
  23 +
14 24 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeBaseMapper.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
1 1 package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
2 2  
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4   -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeBaseDO;
  7 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
5 9 import org.apache.ibatis.annotations.Mapper;
6 10  
7 11 /**
... ... @@ -10,5 +14,12 @@ import org.apache.ibatis.annotations.Mapper;
10 14 * @author xiaoxin
11 15 */
12 16 @Mapper
13   -public interface AiKnowledgeBaseMapper extends BaseMapperX<AiKnowledgeBaseDO> {
  17 +public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> {
  18 +
  19 + default PageResult<AiKnowledgeDO> selectPageByMy(Long userId, PageParam pageReqVO) {
  20 + return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>()
  21 + .eq(AiKnowledgeDO::getUserId, userId)
  22 + .eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
  23 + .orderByDesc(AiKnowledgeDO::getId));
  24 + }
14 25 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
1 1 package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
4 7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
5 8 import org.apache.ibatis.annotations.Mapper;
6 9  
... ... @@ -11,4 +14,12 @@ import org.apache.ibatis.annotations.Mapper;
11 14 */
12 15 @Mapper
13 16 public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegmentDO> {
  17 +
  18 + default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
  20 + .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId())
  21 + .eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus())
  22 + .likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getKeyword())
  23 + .orderByDesc(AiKnowledgeSegmentDO::getId));
  24 + }
14 25 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingService.java deleted
1   -package cn.iocoder.yudao.module.ai.service.knowledge;
2   -
3   -import org.springframework.ai.document.Document;
4   -import org.springframework.ai.vectorstore.SearchRequest;
5   -
6   -import java.util.List;
7   -
8   -/**
9   - * AI 嵌入 Service 接口
10   - *
11   - * @author xiaoxin
12   - */
13   -public interface AiEmbeddingService {
14   -
15   - /**
16   - * 向量化文档并存储
17   - */
18   - void add(List<Document> documents);
19   -
20   - /**
21   - * 相似查询
22   - *
23   - * @param request 查询实体
24   - */
25   - List<Document> similaritySearch(SearchRequest request);
26   -
27   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiEmbeddingServiceImpl.java deleted
1   -package cn.iocoder.yudao.module.ai.service.knowledge;
2   -
3   -import jakarta.annotation.Resource;
4   -import org.springframework.ai.document.Document;
5   -import org.springframework.ai.vectorstore.RedisVectorStore;
6   -import org.springframework.ai.vectorstore.SearchRequest;
7   -import org.springframework.stereotype.Service;
8   -
9   -import java.util.List;
10   -
11   -// TODO @xin:是不是不用 AiEmbeddingServiceImpl,直接 vectorStore 注入到需要的地方就好啦。通过 KnowledgeDocumentService 返回就好。
12   -/**
13   - * AI 嵌入 Service 实现类
14   - *
15   - * @author xiaoxin
16   - */
17   -@Service
18   -public class AiEmbeddingServiceImpl implements AiEmbeddingService {
19   -
20   - @Resource
21   - private RedisVectorStore vectorStore;
22   -
23   - @Override
24   -// @Async
25   - // TODO xiaoxin 报错先注释
26   - public void add(List<Document> documents) {
27   - vectorStore.add(documents);
28   - }
29   -
30   - @Override
31   - public List<Document> similaritySearch(SearchRequest request) {
32   - return vectorStore.similaritySearch(request);
33   - }
34   -
35   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO;
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
4 8  
5 9 /**
6 10 * AI 知识库-文档 Service 接口
... ... @@ -17,4 +21,19 @@ public interface AiKnowledgeDocumentService {
17 21 */
18 22 Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO);
19 23  
  24 +
  25 + /**
  26 + * 获取文档分页
  27 + *
  28 + * @param pageReqVO 分页参数
  29 + * @return 文档分页
  30 + */
  31 + PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO);
  32 +
  33 + /**
  34 + * 更新文档
  35 + *
  36 + * @param reqVO 更新信息
  37 + */
  38 + void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO);
20 39 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.http.HttpUtil;
4 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
6 8 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeDocumentCreateReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
  12 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
8 13 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
9 14 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
  15 +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
10 16 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper;
11 17 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
12 18 import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum;
  19 +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
  20 +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
13 21 import jakarta.annotation.Resource;
14 22 import lombok.extern.slf4j.Slf4j;
15 23 import org.springframework.ai.document.Document;
16 24 import org.springframework.ai.reader.tika.TikaDocumentReader;
17   -import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
  25 +import org.springframework.ai.tokenizer.TokenCountEstimator;
18 26 import org.springframework.ai.transformer.splitter.TokenTextSplitter;
19   -import org.springframework.beans.factory.annotation.Value;
  27 +import org.springframework.ai.vectorstore.VectorStore;
  28 +import org.springframework.core.io.ByteArrayResource;
20 29 import org.springframework.stereotype.Service;
21 30 import org.springframework.transaction.annotation.Transactional;
22 31  
23 32 import java.util.List;
24   -import java.util.Objects;
  33 +import java.util.Map;
  34 +
  35 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  36 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_DOCUMENT_NOT_EXISTS;
25 37  
26 38 /**
27 39 * AI 知识库-文档 Service 实现类
... ... @@ -39,53 +51,99 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
39 51  
40 52 @Resource
41 53 private TokenTextSplitter tokenTextSplitter;
42   -
43 54 @Resource
44   - private AiEmbeddingService embeddingService;
  55 + private TokenCountEstimator tokenCountEstimator;
45 56  
46   - // TODO @xin:@Resource 注入
47   - private static final JTokkitTokenCountEstimator TOKEN_COUNT_ESTIMATOR = new JTokkitTokenCountEstimator();
  57 + @Resource
  58 + private AiApiKeyService apiKeyService;
  59 + @Resource
  60 + private AiKnowledgeService knowledgeService;
  61 + @Resource
  62 + private AiChatModelService chatModelService;
48 63  
49   - // TODO xiaoxin 临时测试用,后续删
50   - @Value("classpath:/webapp/test/Fel.pdf")
51   - private org.springframework.core.io.Resource data;
52 64  
53 65 // TODO 芋艿:需要 review 下,代码格式;
54   - // TODO @xin:最好有 1、/2、/3 这种,让代码更有层次感
55 66 @Override
56 67 @Transactional(rollbackFor = Exception.class)
57 68 public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) {
58   - // TODO xiaoxin 后续从 url 加载
59   - TikaDocumentReader loader = new TikaDocumentReader(data);
60   - // 加载文档
  69 + // 1.1 下载文档
  70 + String url = createReqVO.getUrl();
  71 + // 1.2 加载文档
  72 + TikaDocumentReader loader = new TikaDocumentReader(downloadFile(url));
61 73 List<Document> documents = loader.get();
62 74 Document document = CollUtil.getFirst(documents);
63   - // TODO @xin:是不是不存在,就抛出异常呀;厚泽 return 呀;
64   - // TODO 芋艿 文档层面有没有可能会比较大,这两个字段是否可以从分段表计算得出?回复:先直接算;
65   - Integer tokens = Objects.nonNull(document) ? TOKEN_COUNT_ESTIMATOR.estimate(document.getContent()) : 0;
66   - Integer wordCount = Objects.nonNull(document) ? document.getContent().length() : 0;
  75 + String content = document.getContent();
  76 + Integer tokens = tokenCountEstimator.estimate(content);
  77 + Integer wordCount = content.length();
67 78  
  79 + // 1.3 文档记录入库
68 80 AiKnowledgeDocumentDO documentDO = BeanUtils.toBean(createReqVO, AiKnowledgeDocumentDO.class)
69 81 .setTokens(tokens).setWordCount(wordCount)
70 82 .setStatus(CommonStatusEnum.ENABLE.getStatus()).setSliceStatus(AiKnowledgeDocumentStatusEnum.SUCCESS.getStatus());
71   - // 文档记录入库
72 83 documentMapper.insert(documentDO);
73 84 Long documentId = documentDO.getId();
74 85 if (CollUtil.isEmpty(documents)) {
75 86 return documentId;
76 87 }
77 88  
78   - // 文档分段
  89 + // 2.1 文档分段
79 90 List<Document> segments = tokenTextSplitter.apply(documents);
80   - // 分段内容入库
  91 + // 2.2 分段内容入库
81 92 List<AiKnowledgeSegmentDO> segmentDOList = CollectionUtils.convertList(segments,
82   - segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId)
83   - .setTokens(TOKEN_COUNT_ESTIMATOR.estimate(segment.getContent())).setWordCount(segment.getContent().length())
  93 + segment -> new AiKnowledgeSegmentDO().setContent(segment.getContent()).setDocumentId(documentId).setKnowledgeId(createReqVO.getKnowledgeId()).setVectorId(segment.getId())
  94 + .setTokens(tokenCountEstimator.estimate(segment.getContent())).setWordCount(segment.getContent().length())
84 95 .setStatus(CommonStatusEnum.ENABLE.getStatus()));
85 96 segmentMapper.insertBatch(segmentDOList);
86   - // 向量化并存储
87   - embeddingService.add(segments);
  97 +
  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 获取向量存储实例
  107 + VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
  108 + // 3.3 向量化并存储
  109 + vectorStore.add(segments);
88 110 return documentId;
89 111 }
90 112  
  113 + @Override
  114 + public PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO) {
  115 + return documentMapper.selectPage(pageReqVO);
  116 + }
  117 +
  118 + @Override
  119 + public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) {
  120 + validateKnowledgeDocumentExists(reqVO.getId());
  121 + AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class);
  122 + documentMapper.updateById(document);
  123 + }
  124 +
  125 + /**
  126 + * 校验文档是否存在
  127 + *
  128 + * @param id 文档编号
  129 + * @return 文档信息
  130 + */
  131 + private AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id) {
  132 + AiKnowledgeDocumentDO knowledgeDocument = documentMapper.selectById(id);
  133 + if (knowledgeDocument == null) {
  134 + throw exception(KNOWLEDGE_DOCUMENT_NOT_EXISTS);
  135 + }
  136 + return knowledgeDocument;
  137 + }
  138 +
  139 + private org.springframework.core.io.Resource downloadFile(String url) {
  140 + try {
  141 + byte[] bytes = HttpUtil.downloadBytes(url);
  142 + return new ByteArrayResource(bytes);
  143 + } catch (Exception e) {
  144 + log.error("[downloadFile][url({}) 下载失败]", url, e);
  145 + throw new RuntimeException(e);
  146 + }
  147 + }
  148 +
91 149 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
  8 +
3 9 /**
4 10 * AI 知识库分片 Service 接口
5 11 *
... ... @@ -7,4 +13,25 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
7 13 */
8 14 public interface AiKnowledgeSegmentService {
9 15  
  16 + /**
  17 + * 获取段落分页
  18 + *
  19 + * @param pageReqVO 分页查询
  20 + * @return 文档分页
  21 + */
  22 + PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);
  23 +
  24 + /**
  25 + * 更新段落内容
  26 + *
  27 + * @param reqVO 更新内容
  28 + */
  29 + void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO);
  30 +
  31 + /**
  32 + * 更新状态
  33 + *
  34 + * @param reqVO 更新内容
  35 + */
  36 + void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
10 37 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
  8 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
  9 +import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
  10 +import jakarta.annotation.Resource;
3 11 import lombok.extern.slf4j.Slf4j;
4 12 import org.springframework.stereotype.Service;
5 13  
... ... @@ -12,4 +20,23 @@ import org.springframework.stereotype.Service;
12 20 @Slf4j
13 21 public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService {
14 22  
  23 + @Resource
  24 + private AiKnowledgeSegmentMapper segmentMapper;
  25 +
  26 + @Override
  27 + public PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) {
  28 + return segmentMapper.selectPage(pageReqVO);
  29 + }
  30 +
  31 + @Override
  32 + public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) {
  33 + segmentMapper.updateById(BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class));
  34 + // TODO @xin 重新向量化
  35 + }
  36 +
  37 + @Override
  38 + public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
  39 + segmentMapper.updateById(BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class));
  40 + // TODO @xin 1.禁用删除向量 2.启用重新向量化
  41 + }
15 42 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseService.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO;
3   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
4 8  
5 9 /**
6 10 * AI 知识库-基础信息 Service 接口
7 11 *
8 12 * @author xiaoxin
9 13 */
10   -public interface AiKnowledgeBaseService {
  14 +public interface AiKnowledgeService {
11 15  
12 16 /**
13 17 * 创建【我的】知识库
14 18 *
15 19 * @param createReqVO 创建信息
16   - * @param userId 用户编号
  20 + * @param userId 用户编号
17 21 * @return 编号
18 22 */
19 23 Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId);
... ... @@ -23,8 +27,24 @@ public interface AiKnowledgeBaseService {
23 27 * 创建【我的】知识库
24 28 *
25 29 * @param updateReqVO 更新信息
26   - * @param userId 用户编号
  30 + * @param userId 用户编号
27 31 */
28 32 void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId);
29 33  
  34 +
  35 + /**
  36 + * 校验知识库是否存在
  37 + *
  38 + * @param id 记录编号
  39 + */
  40 + AiKnowledgeDO validateKnowledgeExists(Long id);
  41 +
  42 + /**
  43 + * 获得【我的】知识库分页
  44 + *
  45 + * @param userId 用户编号
  46 + * @param pageReqVO 分页查询
  47 + * @return 知识库分页
  48 + */
  49 + PageResult<AiKnowledgeDO> getKnowledgePageMy(Long userId, PageParam pageReqVO);
30 50 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeBaseServiceImpl.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3   -import cn.hutool.core.lang.Assert;
4 3 import cn.hutool.core.util.ObjUtil;
5 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeCreateMyReqVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.AiKnowledgeUpdateMyReqVO;
9   -import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeBaseDO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
10 11 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
11   -import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeBaseMapper;
  12 +import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
12 13 import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
13 14 import jakarta.annotation.Resource;
14 15 import lombok.extern.slf4j.Slf4j;
... ... @@ -24,56 +25,54 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_
24 25 */
25 26 @Service
26 27 @Slf4j
27   -public class AiKnowledgeBaseServiceImpl implements AiKnowledgeBaseService {
  28 +public class AiKnowledgeServiceImpl implements AiKnowledgeService {
28 29  
29 30 @Resource
30 31 private AiChatModelService chatModalService;
31 32  
32 33 @Resource
33   - private AiKnowledgeBaseMapper knowledgeBaseMapper;
  34 + private AiKnowledgeMapper knowledgeMapper;
34 35  
35 36 @Override
36 37 public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) {
37   - // TODO @xin:貌似直接调用 chatModalService.validateChatModel(id) 完事,不用搞个方法
38 38 // 1. 校验模型配置
39   - AiChatModelDO model = validateChatModel(createReqVO.getModelId());
  39 + AiChatModelDO model = chatModalService.validateChatModel(createReqVO.getModelId());
40 40  
41 41 // 2. 插入知识库
42   - // TODO @xin:不用 DO 结尾
43   - AiKnowledgeBaseDO knowledgeBaseDO = BeanUtils.toBean(createReqVO, AiKnowledgeBaseDO.class)
  42 + AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
44 43 .setModel(model.getModel()).setUserId(userId).setStatus(CommonStatusEnum.ENABLE.getStatus());
45   - knowledgeBaseMapper.insert(knowledgeBaseDO);
46   - return knowledgeBaseDO.getId();
  44 + knowledgeMapper.insert(knowledgeBase);
  45 + return knowledgeBase.getId();
47 46 }
48 47  
49 48 @Override
50 49 public void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId) {
51 50 // 1.1 校验知识库存在
52   - AiKnowledgeBaseDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId());
  51 + AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId());
53 52 if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) {
54 53 throw exception(KNOWLEDGE_NOT_EXISTS);
55 54 }
56 55 // 1.2 校验模型配置
57   - AiChatModelDO model = validateChatModel(updateReqVO.getModelId());
  56 + AiChatModelDO model = chatModalService.validateChatModel(updateReqVO.getModelId());
58 57  
59 58 // 2. 更新知识库
60   - AiKnowledgeBaseDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeBaseDO.class);
  59 + AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class);
61 60 updateDO.setModel(model.getModel());
62   - knowledgeBaseMapper.updateById(updateDO);
  61 + knowledgeMapper.updateById(updateDO);
63 62 }
64 63  
65   - private AiChatModelDO validateChatModel(Long id) {
66   - AiChatModelDO model = chatModalService.validateChatModel(id);
67   - Assert.notNull(model, "未找到对应嵌入模型");
68   - return model;
69   - }
70   -
71   - public AiKnowledgeBaseDO validateKnowledgeExists(Long id) {
72   - AiKnowledgeBaseDO knowledgeBase = knowledgeBaseMapper.selectById(id);
  64 + @Override
  65 + public AiKnowledgeDO validateKnowledgeExists(Long id) {
  66 + AiKnowledgeDO knowledgeBase = knowledgeMapper.selectById(id);
73 67 if (knowledgeBase == null) {
74 68 throw exception(KNOWLEDGE_NOT_EXISTS);
75 69 }
76 70 return knowledgeBase;
77 71 }
78 72  
  73 + @Override
  74 + public PageResult<AiKnowledgeDO> getKnowledgePageMy(Long userId, PageParam pageReqVO) {
  75 + return knowledgeMapper.selectPageByMy(userId, pageReqVO);
  76 + }
  77 +
79 78 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
... ... @@ -9,7 +9,9 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveR
9 9 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
10 10 import jakarta.validation.Valid;
11 11 import org.springframework.ai.chat.model.ChatModel;
  12 +import org.springframework.ai.embedding.EmbeddingModel;
12 13 import org.springframework.ai.image.ImageModel;
  14 +import org.springframework.ai.vectorstore.VectorStore;
13 15  
14 16 import java.util.List;
15 17  
... ... @@ -84,6 +86,14 @@ public interface AiApiKeyService {
84 86 ChatModel getChatModel(Long id);
85 87  
86 88 /**
  89 + * 获得 EmbeddingModel 对象
  90 + *
  91 + * @param id 编号
  92 + * @return EmbeddingModel 对象
  93 + */
  94 + EmbeddingModel getEmbeddingModel(Long id);
  95 +
  96 + /**
87 97 * 获得 ImageModel 对象
88 98 *
89 99 * TODO 可优化点:目前默认获取 platform 对应的第一个开启的配置用于绘画;后续可以支持配置选择
... ... @@ -111,4 +121,12 @@ public interface AiApiKeyService {
111 121 */
112 122 SunoApi getSunoApi();
113 123  
  124 + /**
  125 + * 获得 vector 对象
  126 + *
  127 + * @param id 编号
  128 + * @return VectorStore 对象
  129 + */
  130 + VectorStore getOrCreateVectorStore(Long id);
  131 +
114 132 }
115 133 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service.model;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 4 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
  5 +import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory;
5 6 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
6 7 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
7 8 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
... ... @@ -13,7 +14,9 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
13 14 import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
14 15 import jakarta.annotation.Resource;
15 16 import org.springframework.ai.chat.model.ChatModel;
  17 +import org.springframework.ai.embedding.EmbeddingModel;
16 18 import org.springframework.ai.image.ImageModel;
  19 +import org.springframework.ai.vectorstore.VectorStore;
17 20 import org.springframework.stereotype.Service;
18 21 import org.springframework.validation.annotation.Validated;
19 22  
... ... @@ -36,6 +39,8 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
36 39  
37 40 @Resource
38 41 private AiModelFactory modelFactory;
  42 + @Resource
  43 + private AiVectorStoreFactory vectorFactory;
39 44  
40 45 @Override
41 46 public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
... ... @@ -105,6 +110,13 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
105 110 }
106 111  
107 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
108 120 public ImageModel getImageModel(AiPlatformEnum platform) {
109 121 AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
110 122 if (apiKey == null) {
... ... @@ -132,4 +144,11 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
132 144 }
133 145 return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
134 146 }
  147 +
  148 + @Override
  149 + public VectorStore getOrCreateVectorStore(Long id) {
  150 + AiApiKeyDO apiKey = validateApiKey(id);
  151 + AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
  152 + return vectorFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl());
  153 + }
135 154 }
136 155 \ No newline at end of file
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
... ... @@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.ai.config;
2 2  
3 3 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
4 4 import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl;
  5 +import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory;
  6 +import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactoryImpl;
5 7 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
6 8 import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions;
7 9 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
... ... @@ -10,20 +12,15 @@ import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
10 12 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions;
11 13 import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration;
12 14 import lombok.extern.slf4j.Slf4j;
13   -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
14   -import org.springframework.ai.document.MetadataMode;
15   -import org.springframework.ai.embedding.EmbeddingModel;
  15 +import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
  16 +import org.springframework.ai.tokenizer.TokenCountEstimator;
16 17 import org.springframework.ai.transformer.splitter.TokenTextSplitter;
17   -import org.springframework.ai.transformers.TransformersEmbeddingModel;
18   -import org.springframework.ai.vectorstore.RedisVectorStore;
19 18 import org.springframework.boot.autoconfigure.AutoConfiguration;
20 19 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
21   -import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
22 20 import org.springframework.boot.context.properties.EnableConfigurationProperties;
23 21 import org.springframework.context.annotation.Bean;
24 22 import org.springframework.context.annotation.Import;
25 23 import org.springframework.context.annotation.Lazy;
26   -import redis.clients.jedis.JedisPooled;
27 24  
28 25 /**
29 26 * 芋道 AI 自动配置
... ... @@ -41,6 +38,12 @@ public class YudaoAiAutoConfiguration {
41 38 return new AiModelFactoryImpl();
42 39 }
43 40  
  41 + @Bean
  42 + public AiVectorStoreFactory aiVectorFactory() {
  43 + return new AiVectorStoreFactoryImpl();
  44 + }
  45 +
  46 +
44 47 // ========== 各种 AI Client 创建 ==========
45 48  
46 49 @Bean
... ... @@ -83,35 +86,42 @@ public class YudaoAiAutoConfiguration {
83 86 }
84 87  
85 88 // ========== rag 相关 ==========
86   - @Bean
87   - @Lazy // TODO 芋艿:临时注释,避免无法启动
88   - public EmbeddingModel transformersEmbeddingClient() {
89   - return new TransformersEmbeddingModel(MetadataMode.EMBED);
90   - }
  89 + // TODO @xin 免费版本
  90 +// @Bean
  91 +// @Lazy // TODO 芋艿:临时注释,避免无法启动」
  92 +// public EmbeddingModel transformersEmbeddingClient() {
  93 +// return new TransformersEmbeddingModel(MetadataMode.EMBED);
  94 +// }
91 95  
92 96 /**
93   - * 我们启动有加载很多 Embedding 模型,不晓得取哪个好,先 new 个 TransformersEmbeddingModel 跑
  97 + * TODO @xin 默认版本先不弄,目前都先取对应的 EmbeddingModel
94 98 */
  99 +// @Bean
  100 +// @Lazy // TODO 芋艿:临时注释,避免无法启动
  101 +// public RedisVectorStore vectorStore(TongYiTextEmbeddingModel tongYiTextEmbeddingModel, RedisVectorStoreProperties properties,
  102 +// RedisProperties redisProperties) {
  103 +// var config = RedisVectorStore.RedisVectorStoreConfig.builder()
  104 +// .withIndexName(properties.getIndex())
  105 +// .withPrefix(properties.getPrefix())
  106 +// .build();
  107 +//
  108 +// RedisVectorStore redisVectorStore = new RedisVectorStore(config, tongYiTextEmbeddingModel,
  109 +// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
  110 +// properties.isInitializeSchema());
  111 +// redisVectorStore.afterPropertiesSet();
  112 +// return redisVectorStore;
  113 +// }
  114 +
95 115 @Bean
96 116 @Lazy // TODO 芋艿:临时注释,避免无法启动
97   - public RedisVectorStore vectorStore(TransformersEmbeddingModel transformersEmbeddingModel, RedisVectorStoreProperties properties,
98   - RedisProperties redisProperties) {
99   - var config = RedisVectorStore.RedisVectorStoreConfig.builder()
100   - .withIndexName(properties.getIndex())
101   - .withPrefix(properties.getPrefix())
102   - .build();
103   -
104   - RedisVectorStore redisVectorStore = new RedisVectorStore(config, transformersEmbeddingModel,
105   - new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
106   - properties.isInitializeSchema());
107   - redisVectorStore.afterPropertiesSet();
108   - return redisVectorStore;
  117 + public TokenTextSplitter tokenTextSplitter() {
  118 + return new TokenTextSplitter(500, 100, 5, 10000, true);
109 119 }
110 120  
111 121 @Bean
112 122 @Lazy // TODO 芋艿:临时注释,避免无法启动
113   - public TokenTextSplitter tokenTextSplitter() {
114   - return new TokenTextSplitter(500, 100, 5, 10000, true);
  123 + public TokenCountEstimator tokenCountEstimator() {
  124 + return new JTokkitTokenCountEstimator();
115 125 }
116 126  
117 127 }
118 128 \ 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
... ... @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
4 4 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
5 5 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
6 6 import org.springframework.ai.chat.model.ChatModel;
  7 +import org.springframework.ai.embedding.EmbeddingModel;
7 8 import org.springframework.ai.image.ImageModel;
8 9  
9 10 /**
... ... @@ -26,6 +27,18 @@ public interface AiModelFactory {
26 27 ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url);
27 28  
28 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 + /**
29 42 * 基于默认配置,获得 ChatModel 对象
30 43 *
31 44 * 默认配置,指的是在 application.yaml 配置文件中的 spring.ai 相关的配置
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
... ... @@ -21,6 +21,7 @@ import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel;
21 21 import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties;
22 22 import com.alibaba.dashscope.aigc.generation.Generation;
23 23 import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis;
  24 +import com.alibaba.dashscope.embeddings.TextEmbedding;
24 25 import com.azure.ai.openai.OpenAIClient;
25 26 import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration;
26 27 import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties;
... ... @@ -37,6 +38,7 @@ import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties;
37 38 import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiImageProperties;
38 39 import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
39 40 import org.springframework.ai.chat.model.ChatModel;
  41 +import org.springframework.ai.embedding.EmbeddingModel;
40 42 import org.springframework.ai.image.ImageModel;
41 43 import org.springframework.ai.model.function.FunctionCallbackContext;
42 44 import org.springframework.ai.ollama.OllamaChatModel;
... ... @@ -98,6 +100,21 @@ public class AiModelFactoryImpl implements AiModelFactory {
98 100 }
99 101  
100 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
101 118 public ChatModel getDefaultChatModel(AiPlatformEnum platform) {
102 119 //noinspection EnhancedSwitchMigration
103 120 switch (platform) {
... ... @@ -239,7 +256,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
239 256  
240 257 /**
241 258 * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(
242   - * ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
  259 + *ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
243 260 */
244 261 private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {
245 262 url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
... ... @@ -249,7 +266,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
249 266  
250 267 /**
251 268 * 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel(
252   - * ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
  269 + *ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
253 270 */
254 271 private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) {
255 272 url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
... ... @@ -315,4 +332,15 @@ public class AiModelFactoryImpl implements AiModelFactory {
315 332 return new StabilityAiImageModel(stabilityAiApi);
316 333 }
317 334  
  335 + // ========== 各种创建 EmbeddingModel 的方法 ==========
  336 +
  337 + /**
  338 + * 可参考 {@link TongYiAutoConfiguration#tongYiTextEmbeddingClient(TextEmbedding, TongYiConnectionProperties)}
  339 + */
  340 + private EmbeddingModel buildTongYiEmbeddingModel(String apiKey) {
  341 + TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties();
  342 + connectionProperties.setApiKey(apiKey);
  343 + return new TongYiAutoConfiguration().tongYiTextEmbeddingClient(SpringUtil.getBean(TextEmbedding.class), connectionProperties);
  344 + }
  345 +
318 346 }
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.core.factory;
  2 +
  3 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  4 +import org.springframework.ai.embedding.EmbeddingModel;
  5 +import org.springframework.ai.vectorstore.VectorStore;
  6 +
  7 +/**
  8 + * AI Vector 模型工厂的接口类
  9 + * @author xiaoxin
  10 + */
  11 +public interface AiVectorStoreFactory {
  12 +
  13 +
  14 + /**
  15 + * 基于指定配置,获得 VectorStore 对象
  16 + * <p>
  17 + * 如果不存在,则进行创建
  18 + *
  19 + * @param embeddingModel 嵌入模型
  20 + * @param platform 平台
  21 + * @param apiKey API KEY
  22 + * @param url API URL
  23 + * @return VectorStore 对象
  24 + */
  25 + VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url);
  26 +
  27 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.core.factory;
  2 +
  3 +import cn.hutool.core.lang.Singleton;
  4 +import cn.hutool.core.lang.func.Func0;
  5 +import cn.hutool.core.util.ArrayUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
  8 +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
  9 +import org.springframework.ai.embedding.EmbeddingModel;
  10 +import org.springframework.ai.vectorstore.RedisVectorStore;
  11 +import org.springframework.ai.vectorstore.VectorStore;
  12 +import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
  13 +import redis.clients.jedis.JedisPooled;
  14 +
  15 +/**
  16 + * AI Vector 模型工厂的实现类
  17 + * 使用 redisVectorStore 实现 VectorStore
  18 + *
  19 + * @author xiaoxin
  20 + */
  21 +public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory {
  22 +
  23 + @Override
  24 + public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
  25 + String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
  26 + return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {
  27 + // TODO 芋艿 @xin 这两个配置取哪好呢
  28 + // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上
  29 + String index = "default-index";
  30 + String prefix = "default:";
  31 + var config = RedisVectorStore.RedisVectorStoreConfig.builder()
  32 + .withIndexName(index)
  33 + .withPrefix(prefix)
  34 + .build();
  35 + RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);
  36 + RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,
  37 + new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
  38 + true);
  39 + redisVectorStore.afterPropertiesSet();
  40 + return redisVectorStore;
  41 + });
  42 + }
  43 +
  44 +
  45 + private static String buildClientCacheKey(Class<?> clazz, Object... params) {
  46 + if (ArrayUtil.isEmpty(params)) {
  47 + return clazz.getName();
  48 + }
  49 + return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_"));
  50 + }
  51 +}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/org/springframework/ai/autoconfigure/vectorstore/redis/RedisVectorStoreAutoConfiguration.java
... ... @@ -19,6 +19,7 @@ import org.springframework.ai.embedding.EmbeddingModel;
19 19 import org.springframework.ai.vectorstore.RedisVectorStore;
20 20 import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig;
21 21 import org.springframework.boot.autoconfigure.AutoConfiguration;
  22 +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
22 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
23 24 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24 25 import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
... ... @@ -38,7 +39,7 @@ import redis.clients.jedis.JedisPooled;
38 39 */
39 40 @AutoConfiguration(after = RedisAutoConfiguration.class)
40 41 @ConditionalOnClass({JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class})
41   -//@ConditionalOnBean(JedisConnectionFactory.class)
  42 +@ConditionalOnBean(JedisConnectionFactory.class)
42 43 @EnableConfigurationProperties(RedisVectorStoreProperties.class)
43 44 public class RedisVectorStoreAutoConfiguration {
44 45  
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/webapp/test/Fel.pdf deleted
No preview for this file type