Commit 261011d1a8b8f80d0c45f99a0f34d2e725d85eca
Committed by
Gitee
!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
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
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