Commit 1596ca330987a6e1baf9ab8ceebc959004dd15c6

Authored by 芋道源码
Committed by Gitee
2 parents 8e56b81a 1de70eee

!21 【新增】AI:聊天对接知识库

Merge pull request !21 from 小新/master-jdk21-ai
Showing 24 changed files with 200 additions and 107 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
... ... @@ -34,7 +34,12 @@ public enum AiChatRoleEnum {
34 34 ### 支付宝
35 35 ### 微信
36 36 除此之外不要任何解释性语句。
37   - """);
  37 + """),
  38 +
  39 + AI_KNOWLEDGE_ROLE("知识库助手", """
  40 + 给你提供一些数据参考:{info},请回答我的问题。
  41 + 请你跟进数据参考与工具返回结果回复用户的请求。
  42 + """);
38 43  
39 44 /**
40 45 * 角色名
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java
... ... @@ -10,4 +10,7 @@ public class AiChatConversationCreateMyReqVO {
10 10 @Schema(description = "聊天角色编号", example = "666")
11 11 private Long roleId;
12 12  
  13 + @Schema(description = "知识库编号", example = "1204")
  14 + private Long knowledgeId;
  15 +
13 16 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
... ... @@ -21,6 +21,9 @@ public class AiChatConversationUpdateMyReqVO {
21 21 @Schema(description = "模型编号", example = "1")
22 22 private Long modelId;
23 23  
  24 + @Schema(description = "知识库编号", example = "1")
  25 + private Long knowledgeId;
  26 +
24 27 @Schema(description = "角色设定", example = "一个快乐的程序员")
25 28 private String systemMessage;
26 29  
... ...
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.framework.common.pojo.PageParam;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7   -import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
8 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;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
10 10 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
11 11 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
12 12 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -28,24 +28,23 @@ public class AiKnowledgeController {
28 28 @Resource
29 29 private AiKnowledgeService knowledgeService;
30 30  
31   - @GetMapping("/my-page")
32   - @Operation(summary = "获取【我的】知识库分页")
33   - public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePageMy(@Validated PageParam pageReqVO) {
34   - PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePageMy(getLoginUserId(), pageReqVO);
  31 + @GetMapping("/page")
  32 + @Operation(summary = "获取知识库分页")
  33 + public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) {
  34 + PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(getLoginUserId(), pageReqVO);
35 35 return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
36 36 }
37 37  
38   - @PostMapping("/create-my")
39   - @Operation(summary = "创建【我的】知识库")
40   - public CommonResult<Long> createKnowledgeMy(@RequestBody @Valid AiKnowledgeCreateMyReqVO createReqVO) {
41   - return success(knowledgeService.createKnowledgeMy(createReqVO, getLoginUserId()));
  38 + @PostMapping("/create")
  39 + @Operation(summary = "创建知识库")
  40 + public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeCreateReqVO createReqVO) {
  41 + return success(knowledgeService.createKnowledge(createReqVO, getLoginUserId()));
42 42 }
43 43  
44   - @PutMapping("/update-my")
45   - @Operation(summary = "更新【我的】知识库")
46   - public CommonResult<Boolean> updateKnowledgeMy(@RequestBody @Valid AiKnowledgeUpdateMyReqVO updateReqVO) {
47   - knowledgeService.updateKnowledgeMy(updateReqVO, getLoginUserId());
  44 + @PutMapping("/update")
  45 + @Operation(summary = "更新知识库")
  46 + public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeUpdateReqVO updateReqVO) {
  47 + knowledgeService.updateKnowledge(updateReqVO, getLoginUserId());
48 48 return success(true);
49 49 }
50   -
51 50 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
... ... @@ -36,7 +36,7 @@ public class AiKnowledgeDocumentController {
36 36  
37 37 @GetMapping("/page")
38 38 @Operation(summary = "获取文档分页")
39   - public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPageMy(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
  39 + public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
40 40 PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
41 41 return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
42 42 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
... ... @@ -29,7 +29,7 @@ public class AiKnowledgeSegmentController {
29 29  
30 30 @GetMapping("/page")
31 31 @Operation(summary = "获取段落分页")
32   - public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPageMy(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
  32 + public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
33 33 PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
34 34 return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
35 35 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java
... ... @@ -7,9 +7,9 @@ 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   -public class AiKnowledgeCreateMyReqVO {
  12 +public class AiKnowledgeCreateReqVO {
13 13  
14 14 @Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
15 15 @NotBlank(message = "知识库名称不能为空")
... ... @@ -18,7 +18,7 @@ public class AiKnowledgeCreateMyReqVO {
18 18 @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档")
19 19 private String description;
20 20  
21   - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]")
  21 + @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
22 22 private List<Long> visibilityPermissions;
23 23  
24 24 @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
... ... @@ -23,21 +23,21 @@ public class AiKnowledgeDocumentCreateReqVO {
23 23 @URL(message = "文档 URL 格式不正确")
24 24 private String url;
25 25  
26   - @Schema(description = "每个文本块的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
27   - @NotNull(message = "每个文本块的目标 token 数不能为空")
28   - private Integer defaultChunkSize;
  26 + @Schema(description = "每个段落的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
  27 + @NotNull(message = "每个段落的目标 token 数不能为空")
  28 + private Integer defaultSegmentTokens;
29 29  
30   - @Schema(description = "每个文本块的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350")
31   - @NotNull(message = "每个文本块的最小字符数不能为空")
32   - private Integer minChunkSizeChars;
  30 + @Schema(description = "每个段落的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350")
  31 + @NotNull(message = "每个段落的最小字符数不能为空")
  32 + private Integer minSegmentWordCount;
33 33  
34   - @Schema(description = "丢弃阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
  34 + @Schema(description = "丢弃阈值:低于此阈值的段落会被丢弃", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
35 35 @NotNull(message = "丢弃阈值不能为空")
36 36 private Integer minChunkLengthToEmbed;
37 37  
38   - @Schema(description = "最大块数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
39   - @NotNull(message = "最大块数不能为空")
40   - private Integer maxNumChunks;
  38 + @Schema(description = "最大段落数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
  39 + @NotNull(message = "最大段落数不能为空")
  40 + private Integer maxNumSegments;
41 41  
42 42 @Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
43 43 @NotNull(message = "分块是否保留分隔符不能为空")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
  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 AiKnowledgePageReqVO extends PageParam {
  10 +
  11 + @Schema(description = "知识库名称", example = "Java 开发手册")
  12 + private String name;
  13 +
  14 +}
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateMyReqVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java
... ... @@ -9,7 +9,7 @@ import java.util.List;
9 9  
10 10 @Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO")
11 11 @Data
12   -public class AiKnowledgeUpdateMyReqVO {
  12 +public class AiKnowledgeUpdateReqVO {
13 13  
14 14 @Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
15 15 @NotNull(message = "知识库编号不能为空")
... ... @@ -22,7 +22,7 @@ public class AiKnowledgeUpdateMyReqVO {
22 22 @Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
23 23 private String description;
24 24  
25   - @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]")
  25 + @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
26 26 private List<Long> visibilityPermissions;
27 27  
28 28 @Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
1 1 package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
4 5 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
5 6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
6 7 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -65,6 +66,13 @@ public class AiChatConversationDO extends BaseDO {
65 66 private Long roleId;
66 67  
67 68 /**
  69 + * 知识库编号
  70 + * <p>
  71 + * 关联 {@link AiKnowledgeDO#getId()}
  72 + */
  73 + private Long knowledgeId;
  74 +
  75 + /**
68 76 * 模型编号
69 77 *
70 78 * 关联 {@link AiChatModelDO#getId()} 字段
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
1 1 package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
2 2  
3   -import com.baomidou.mybatisplus.annotation.TableId;
4   -import org.springframework.ai.chat.messages.MessageType;
5 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
6 5 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
7 6 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
8 7 import com.baomidou.mybatisplus.annotation.KeySequence;
  8 +import com.baomidou.mybatisplus.annotation.TableField;
  9 +import com.baomidou.mybatisplus.annotation.TableId;
9 10 import com.baomidou.mybatisplus.annotation.TableName;
  11 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
10 12 import lombok.*;
  13 +import org.springframework.ai.chat.messages.MessageType;
  14 +
  15 +import java.util.List;
11 16  
12 17 /**
13 18 * AI Chat 消息 DO
... ... @@ -66,6 +71,15 @@ public class AiChatMessageDO extends BaseDO {
66 71 */
67 72 private Long roleId;
68 73  
  74 +
  75 + /**
  76 + * 段落编号数组
  77 + *
  78 + * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段
  79 + */
  80 + @TableField(typeHandler = JacksonTypeHandler.class)
  81 + private List<Long> segmentIds;
  82 +
69 83 /**
70 84 * 模型标志
71 85 */
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
... ... @@ -2,10 +2,10 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
5 6 import com.baomidou.mybatisplus.annotation.TableField;
6 7 import com.baomidou.mybatisplus.annotation.TableId;
7 8 import com.baomidou.mybatisplus.annotation.TableName;
8   -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
9 9 import lombok.Data;
10 10  
11 11 import java.util.List;
... ... @@ -38,11 +38,13 @@ public class AiKnowledgeDO extends BaseDO {
38 38 * 知识库描述
39 39 */
40 40 private String description;
41   - // TODO @新:如果全部可见,需要怎么设置?
  41 +
42 42 /**
43   - * 可见权限,只能选择哪些人可见
  43 + * 可见权限,选择哪些人可见
  44 + * <p>
  45 + * -1 所有人可见,其他为各自用户编号
44 46 */
45   - @TableField(typeHandler = JacksonTypeHandler.class)
  47 + @TableField(typeHandler = LongListTypeHandler.class)
46 48 private List<Long> visibilityPermissions;
47 49 /**
48 50 * 嵌入模型编号
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
... ... @@ -40,23 +40,25 @@ public class AiKnowledgeDocumentDO extends BaseDO {
40 40 */
41 41 private String url;
42 42 /**
43   - * token 数量
  43 + * 文档 token 数量
44 44 */
45 45 private Integer tokens;
46 46 /**
47   - * 字符数
  47 + * 文档字符数
48 48 */
49 49 private Integer wordCount;
50   - // TODO @新:chunk 1)是不是 segment,这样命名保持一致会好点哈?2)Size 是不是改成 Tokens 会统一点;3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。
  50 +
  51 +
  52 + // ========== 自定义分段所用参数 ==========
  53 + // TODO @新:3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。
51 54 /**
52 55 * 每个文本块的目标 token 数
53 56 */
54   - private Integer defaultChunkSize;
55   - // TODO @xin:SizeChars 和 wordCount 好像是一个意思,是不是也要统一哈。
  57 + private Integer defaultSegmentTokens;
56 58 /**
57 59 * 每个文本块的最小字符数
58 60 */
59   - private Integer minChunkSizeChars;
  61 + private Integer minSegmentWordCount;
60 62 /**
61 63 * 低于此值的块会被丢弃
62 64 */
... ... @@ -64,11 +66,13 @@ public class AiKnowledgeDocumentDO extends BaseDO {
64 66 /**
65 67 * 最大块数
66 68 */
67   - private Integer maxNumChunks;
  69 + private Integer maxNumSegments;
68 70 /**
69 71 * 分块是否保留分隔符
70 72 */
71 73 private Boolean keepSeparator;
  74 + // ===================================
  75 +
72 76 /**
73 77 * 切片状态
74 78 * <p>
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
... ... @@ -2,8 +2,6 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5   -import com.baomidou.mybatisplus.annotation.FieldStrategy;
6   -import com.baomidou.mybatisplus.annotation.TableField;
7 5 import com.baomidou.mybatisplus.annotation.TableId;
8 6 import com.baomidou.mybatisplus.annotation.TableName;
9 7 import lombok.Data;
... ... @@ -27,7 +25,6 @@ public class AiKnowledgeSegmentDO extends BaseDO {
27 25 /**
28 26 * 向量库的编号
29 27 */
30   - @TableField(updateStrategy = FieldStrategy.ALWAYS) // TODO @新:尽量规避要这个注解。万一后面加个 status 单独更新,可能会踩坑。
31 28 private String vectorId;
32 29 /**
33 30 * 知识库编号
... ...
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 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
8 8 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
9 9 import org.apache.ibatis.annotations.Mapper;
10 10  
... ... @@ -16,10 +16,11 @@ import org.apache.ibatis.annotations.Mapper;
16 16 @Mapper
17 17 public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> {
18 18  
19   - default PageResult<AiKnowledgeDO> selectPageByMy(Long userId, PageParam pageReqVO) {
  19 + default PageResult<AiKnowledgeDO> selectPage(Long userId, AiKnowledgePageReqVO pageReqVO) {
20 20 return selectPage(pageReqVO, new LambdaQueryWrapperX<AiKnowledgeDO>()
21   - .eq(AiKnowledgeDO::getUserId, userId)
22 21 .eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
  22 + .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())
  23 + .and(e -> e.apply("FIND_IN_SET(" + userId + ",visibility_permissions)").or(m -> m.apply("FIND_IN_SET(-1,visibility_permissions)")))
23 24 .orderByDesc(AiKnowledgeDO::getId));
24 25 }
25 26 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
... ... @@ -25,8 +25,7 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX&lt;AiKnowledgeSegment
25 25 .orderByDesc(AiKnowledgeSegmentDO::getId));
26 26 }
27 27  
28   - // TODO @新:selectListByXXX 哈
29   - default List<AiKnowledgeSegmentDO> selectList(List<String> vectorIdList) {
  28 + default List<AiKnowledgeSegmentDO> selectListByVectorIds(List<String> vectorIdList) {
30 29 return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
31 30 .in(AiKnowledgeSegmentDO::getVectorId, vectorIdList)
32 31 .orderByDesc(AiKnowledgeSegmentDO::getId));
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
... ... @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
13 13 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
14 14 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
15 15 import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
  16 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
16 17 import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
17 18 import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
18 19 import jakarta.annotation.Resource;
... ... @@ -22,6 +23,7 @@ import org.springframework.validation.annotation.Validated;
22 23  
23 24 import java.time.LocalDateTime;
24 25 import java.util.List;
  26 +import java.util.Objects;
25 27  
26 28 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 29 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
... ... @@ -45,6 +47,8 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
45 47 private AiChatModelService chatModalService;
46 48 @Resource
47 49 private AiChatRoleService chatRoleService;
  50 + @Resource
  51 + private AiKnowledgeService knowledgeService;
48 52  
49 53 @Override
50 54 public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
... ... @@ -56,9 +60,14 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
56 60 Assert.notNull(model, "必须找到默认模型");
57 61 validateChatModel(model);
58 62  
  63 + // 1.3 校验知识库
  64 + if (Objects.nonNull(createReqVO.getKnowledgeId())) {
  65 + knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
  66 + }
  67 +
59 68 // 2. 创建 AiChatConversationDO 聊天对话
60 69 AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)
61   - .setModelId(model.getId()).setModel(model.getModel())
  70 + .setModelId(model.getId()).setModel(model.getModel()).setKnowledgeId(createReqVO.getKnowledgeId())
62 71 .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
63 72 if (role != null) {
64 73 conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());
... ... @@ -82,6 +91,11 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
82 91 model = chatModalService.validateChatModel(updateReqVO.getModelId());
83 92 }
84 93  
  94 + // 1.3 校验知识库是否存在
  95 + if (updateReqVO.getKnowledgeId() != null) {
  96 + knowledgeService.validateKnowledgeExists(updateReqVO.getKnowledgeId());
  97 + }
  98 +
85 99 // 2. 更新对话信息
86 100 AiChatConversationDO updateObj = BeanUtils.toBean(updateReqVO, AiChatConversationDO.class);
87 101 if (Boolean.TRUE.equals(updateReqVO.getPinned())) {
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
... ... @@ -12,21 +12,29 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
12 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
13 13 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
14 14 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
  15 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
15 16 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
16 17 import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
  18 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
17 19 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
18 20 import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
  21 +import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
19 22 import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
  23 +import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
20 24 import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
21 25 import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
22 26 import jakarta.annotation.Resource;
23 27 import lombok.extern.slf4j.Slf4j;
24   -import org.springframework.ai.chat.messages.*;
  28 +import org.springframework.ai.chat.messages.Message;
  29 +import org.springframework.ai.chat.messages.MessageType;
  30 +import org.springframework.ai.chat.messages.SystemMessage;
  31 +import org.springframework.ai.chat.messages.UserMessage;
25 32 import org.springframework.ai.chat.model.ChatModel;
26 33 import org.springframework.ai.chat.model.ChatResponse;
27 34 import org.springframework.ai.chat.model.StreamingChatModel;
28 35 import org.springframework.ai.chat.prompt.ChatOptions;
29 36 import org.springframework.ai.chat.prompt.Prompt;
  37 +import org.springframework.ai.chat.prompt.PromptTemplate;
30 38 import org.springframework.stereotype.Service;
31 39 import org.springframework.transaction.annotation.Transactional;
32 40 import reactor.core.publisher.Flux;
... ... @@ -59,6 +67,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
59 67 private AiChatModelService chatModalService;
60 68 @Resource
61 69 private AiApiKeyService apiKeyService;
  70 + @Resource
  71 + private AiKnowledgeSegmentService knowledgeSegmentService;
62 72  
63 73 @Transactional(rollbackFor = Exception.class)
64 74 public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
... ... @@ -80,13 +90,16 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
80 90 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
81 91 userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
82 92  
83   - // 3.2 创建 chat 需要的 Prompt
84   - Prompt prompt = buildPrompt(conversation, historyMessages, model, sendReqVO);
  93 + // 3.2 召回段落
  94 + List<AiKnowledgeSegmentDO> segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId());
  95 +
  96 + // 3.3 创建 chat 需要的 Prompt
  97 + Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO);
85 98 ChatResponse chatResponse = chatModel.call(prompt);
86 99  
87   - // 3.3 段式返回
  100 + // 3.4 段式返回
88 101 String newContent = chatResponse.getResult().getOutput().getContent();
89   - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
  102 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent));
90 103 return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
91 104 .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent));
92 105 }
... ... @@ -111,11 +124,15 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
111 124 AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
112 125 userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
113 126  
114   - // 3.2 构建 Prompt,并进行调用
115   - Prompt prompt = buildPrompt(conversation, historyMessages, model, sendReqVO);
  127 +
  128 + // 3.2 召回段落
  129 + List<AiKnowledgeSegmentDO> segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId());
  130 +
  131 + // 3.3 构建 Prompt,并进行调用
  132 + Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO);
116 133 Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
117 134  
118   - // 3.3 流式返回
  135 + // 3.4 流式返回
119 136 // TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题
120 137 StringBuffer contentBuffer = new StringBuffer();
121 138 return streamResponse.map(chunk -> {
... ... @@ -128,7 +145,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
128 145 }).doOnComplete(() -> {
129 146 // 忽略租户,因为 Flux 异步无法透传租户
130 147 TenantUtils.executeIgnore(() ->
131   - chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
  148 + chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId))
  149 + .setContent(contentBuffer.toString())));
132 150 }).doOnError(throwable -> {
133 151 log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
134 152 // 忽略租户,因为 Flux 异步无法透传租户
... ... @@ -137,18 +155,35 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
137 155 }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
138 156 }
139 157  
140   - private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
  158 + private List<AiKnowledgeSegmentDO> recallSegment(String content, Long knowledgeId) {
  159 + if (Objects.isNull(knowledgeId)) {
  160 + return Collections.emptyList();
  161 + }
  162 + return knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content));
  163 + }
  164 +
  165 + private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,List<AiKnowledgeSegmentDO> segmentList,
141 166 AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) {
142 167 // 1. 构建 Prompt Message 列表
143 168 List<Message> chatMessages = new ArrayList<>();
144   - // 1.1 system context 角色设定
  169 +
  170 + // 1.1 召回内容消息构建
  171 + if (CollUtil.isNotEmpty(segmentList)) {
  172 + PromptTemplate promptTemplate = new PromptTemplate(AiChatRoleEnum.AI_KNOWLEDGE_ROLE.getSystemMessage());
  173 + StringBuilder infoBuilder = StrUtil.builder();
  174 + segmentList.forEach(segment -> infoBuilder.append(System.lineSeparator()).append(segment.getContent()));
  175 + Message message = promptTemplate.createMessage(Map.of("info", infoBuilder.toString()));
  176 + chatMessages.add(message);
  177 + }
  178 +
  179 + // 1.2 system context 角色设定
145 180 if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
146 181 chatMessages.add(new SystemMessage(conversation.getSystemMessage()));
147 182 }
148   - // 1.2 history message 历史消息
  183 + // 1.3 history message 历史消息
149 184 List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);
150 185 contextMessages.forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent())));
151   - // 1.3 user message 新发送消息
  186 + // 1.4 user message 新发送消息
152 187 chatMessages.add(new UserMessage(sendReqVO.getContent()));
153 188  
154 189 // 2. 构建 ChatOptions 对象
... ... @@ -160,12 +195,12 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
160 195  
161 196 /**
162 197 * 从历史消息中,获得倒序的 n 组消息作为消息上下文
163   - *
  198 + * <p>
164 199 * n 组:指的是 user + assistant 形成一组
165 200 *
166   - * @param messages 消息列表
  201 + * @param messages 消息列表
167 202 * @param conversation 对话
168   - * @param sendReqVO 发送请求
  203 + * @param sendReqVO 发送请求
169 204 * @return 消息上下文
170 205 */
171 206 private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages,
... ... @@ -182,7 +217,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
182 217 }
183 218 AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
184 219 if (userMessage == null || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
185   - || StrUtil.isEmpty(assistantMessage.getContent())) {
  220 + || StrUtil.isEmpty(assistantMessage.getContent())) {
186 221 continue;
187 222 }
188 223 // 由于后续要 reverse 反转,所以先添加 assistantMessage
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
... ... @@ -71,8 +71,8 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
71 71 }
72 72  
73 73 // 2 构造文本分段器
74   - TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultChunkSize(), createReqVO.getMinChunkSizeChars(), createReqVO.getMinChunkLengthToEmbed(),
75   - createReqVO.getMaxNumChunks(), createReqVO.getKeepSeparator());
  74 + TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultSegmentTokens(), createReqVO.getMinSegmentWordCount(), createReqVO.getMinChunkLengthToEmbed(),
  75 + createReqVO.getMaxNumSegments(), createReqVO.getKeepSeparator());
76 76 // 2.1 文档分段
77 77 List<Document> segments = tokenTextSplitter.apply(documents);
78 78 // 2.2 分段内容入库
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
... ... @@ -90,7 +90,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
90 90 } else {
91 91 // 2.2 禁用删除向量
92 92 vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
93   - knowledgeSegment.setVectorId(null);
  93 + knowledgeSegment.setVectorId("");
94 94 }
95 95 // 3 更新段落状态
96 96 segmentMapper.updateById(knowledgeSegment);
... ... @@ -114,7 +114,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
114 114 return ListUtil.empty();
115 115 }
116 116 // 3.2 段落召回
117   - return segmentMapper.selectList(CollUtil.getFieldValues(documentList, "id", String.class));
  117 + return segmentMapper.selectListByVectorIds(CollUtil.getFieldValues(documentList, "id", String.class));
118 118 }
119 119  
120 120 /**
... ...
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 2  
3   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 3 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;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
7 7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
8 8 import org.springframework.ai.vectorstore.VectorStore;
9 9  
... ... @@ -15,21 +15,21 @@ import org.springframework.ai.vectorstore.VectorStore;
15 15 public interface AiKnowledgeService {
16 16  
17 17 /**
18   - * 创建【我的】知识库
  18 + * 创建知识库
19 19 *
20 20 * @param createReqVO 创建信息
21 21 * @param userId 用户编号
22 22 * @return 编号
23 23 */
24   - Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId);
  24 + Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId);
25 25  
26 26 /**
27   - * 创建【我的】知识库
  27 + * 更新知识库
28 28 *
29 29 * @param updateReqVO 更新信息
30 30 * @param userId 用户编号
31 31 */
32   - void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId);
  32 + void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId);
33 33  
34 34 /**
35 35 * 校验知识库是否存在
... ... @@ -39,21 +39,20 @@ public interface AiKnowledgeService {
39 39 AiKnowledgeDO validateKnowledgeExists(Long id);
40 40  
41 41 /**
42   - * 获得【我的】知识库分页
  42 + * 获得知识库分页
43 43 *
44   - * @param userId 用户编号
45   - * @param pageReqVO 分页查询
  44 + * @param userId 用户编号
  45 + * @param pageReqVO 分页查询
46 46 * @return 知识库分页
47 47 */
48   - PageResult<AiKnowledgeDO> getKnowledgePageMy(Long userId, PageParam pageReqVO);
  48 + PageResult<AiKnowledgeDO> getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO);
49 49  
50   - // TODO @新:knowledgeId 和 validateKnowledgeExists 的 id 是同一个么?如果是的话,建议变量也用 id 哈,然后两边的 id 注释,保持一致
51 50 /**
52 51 * 根据知识库编号获取向量存储实例
53 52 *
54   - * @param knowledgeId 知识库编号
  53 + * @param id 知识库编号
55 54 * @return 向量存储实例
56 55 */
57   - VectorStore getVectorStoreById(Long knowledgeId);
  56 + VectorStore getVectorStoreById(Long id);
58 57  
59 58 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
... ... @@ -2,11 +2,11 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
2 2  
3 3 import cn.hutool.core.util.ObjUtil;
4 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
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;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
10 10 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
11 11 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
12 12 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
... ... @@ -30,20 +30,17 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_
30 30 public class AiKnowledgeServiceImpl implements AiKnowledgeService {
31 31  
32 32 @Resource
33   - private AiChatModelService chatModalService;
34   -
35   - @Resource
36 33 private AiKnowledgeMapper knowledgeMapper;
  34 +
37 35 @Resource
38 36 private AiChatModelService chatModelService;
39 37 @Resource
40 38 private AiApiKeyService apiKeyService;
41   - // TODO @新:chatModelService 和 apiKeyService 可以放到 33 行的 chatModalService 后面。尽量保持,想通类型的变量在一块。例如说,Service 一块,Mapper 一块。
42 39  
43 40 @Override
44   - public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) {
  41 + public Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId) {
45 42 // 1. 校验模型配置
46   - AiChatModelDO model = chatModalService.validateChatModel(createReqVO.getModelId());
  43 + AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getModelId());
47 44  
48 45 // 2. 插入知识库
49 46 AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
... ... @@ -53,14 +50,14 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
53 50 }
54 51  
55 52 @Override
56   - public void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId) {
  53 + public void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId) {
57 54 // 1.1 校验知识库存在
58 55 AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId());
59 56 if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) {
60 57 throw exception(KNOWLEDGE_NOT_EXISTS);
61 58 }
62 59 // 1.2 校验模型配置
63   - AiChatModelDO model = chatModalService.validateChatModel(updateReqVO.getModelId());
  60 + AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getModelId());
64 61  
65 62 // 2. 更新知识库
66 63 AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class);
... ... @@ -78,13 +75,13 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
78 75 }
79 76  
80 77 @Override
81   - public PageResult<AiKnowledgeDO> getKnowledgePageMy(Long userId, PageParam pageReqVO) {
82   - return knowledgeMapper.selectPageByMy(userId, pageReqVO);
  78 + public PageResult<AiKnowledgeDO> getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO) {
  79 + return knowledgeMapper.selectPage(userId, pageReqVO);
83 80 }
84 81  
85 82 @Override
86   - public VectorStore getVectorStoreById(Long knowledgeId) {
87   - AiKnowledgeDO knowledge = validateKnowledgeExists(knowledgeId);
  83 + public VectorStore getVectorStoreById(Long id) {
  84 + AiKnowledgeDO knowledge = validateKnowledgeExists(id);
88 85 AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
89 86 // 创建或获取 VectorStore 对象
90 87 return apiKeyService.getOrCreateVectorStore(model.getKeyId());
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
... ... @@ -197,7 +197,6 @@ public class AiModelFactoryImpl implements AiModelFactory {
197 197 });
198 198 }
199 199  
200   - // TODO @新:貌似可以创建一个大的 VectorStore。然后搜的时候,通过 Filter.Expression 过滤对应的数据。
201 200 @Override
202 201 public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
203 202 String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
... ...