From 8e012b10bb28d16cdde68c715babbcddb88e17b7 Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Tue, 3 Sep 2024 16:50:42 +0800
Subject: [PATCH 01/10] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI=20?=
=?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20VectorStore=20=E8=8E=B7=E5=8F=96?=
=?UTF-8?q?=20=E6=8A=BD=E5=88=B0=20AiModelFactory?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ai/service/model/AiApiKeyServiceImpl.java | 5 +-
.../ai/config/YudaoAiAutoConfiguration.java | 7 ---
.../ai/core/factory/AiModelFactory.java | 14 +++++
.../ai/core/factory/AiModelFactoryImpl.java | 27 ++++++++++
.../ai/core/factory/AiVectorStoreFactory.java | 28 ----------
.../factory/AiVectorStoreFactoryImpl.java | 52 -------------------
6 files changed, 42 insertions(+), 91 deletions(-)
delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java
delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
index bf11ec218..b2807905a 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.service.model;
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
-import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory;
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@@ -39,8 +38,6 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
@Resource
private AiModelFactory modelFactory;
- @Resource
- private AiVectorStoreFactory vectorFactory;
@Override
public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
@@ -149,7 +146,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
public VectorStore getOrCreateVectorStore(Long id) {
AiApiKeyDO apiKey = validateApiKey(id);
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- return vectorFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl());
+ return modelFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl());
}
}
\ No newline at end of file
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
index 79a1f345b..cd5cfc58b 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
@@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.ai.config;
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl;
-import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactory;
-import cn.iocoder.yudao.framework.ai.core.factory.AiVectorStoreFactoryImpl;
import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions;
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
@@ -38,11 +36,6 @@ public class YudaoAiAutoConfiguration {
return new AiModelFactoryImpl();
}
- @Bean
- public AiVectorStoreFactory aiVectorFactory() {
- return new AiVectorStoreFactoryImpl();
- }
-
// ========== 各种 AI Client 创建 ==========
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java
index 7e8465375..243c4ae4b 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.image.ImageModel;
+import org.springframework.ai.vectorstore.VectorStore;
/**
* AI Model 模型工厂的接口类
@@ -92,4 +93,17 @@ public interface AiModelFactory {
*/
EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url);
+ /**
+ * 基于指定配置,获得 VectorStore 对象
+ *
+ * 如果不存在,则进行创建
+ *
+ * @param embeddingModel 嵌入模型
+ * @param platform 平台
+ * @param apiKey API KEY
+ * @param url API URL
+ * @return VectorStore 对象
+ */
+ VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url);
+
}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
index aa46c45f2..dfe029796 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
+import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration;
import com.alibaba.cloud.ai.tongyi.TongYiConnectionProperties;
import com.alibaba.cloud.ai.tongyi.chat.TongYiChatModel;
@@ -54,13 +55,17 @@ import org.springframework.ai.qianfan.api.QianFanApi;
import org.springframework.ai.qianfan.api.QianFanImageApi;
import org.springframework.ai.stabilityai.StabilityAiImageModel;
import org.springframework.ai.stabilityai.api.StabilityAiApi;
+import org.springframework.ai.vectorstore.RedisVectorStore;
+import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
+import redis.clients.jedis.JedisPooled;
import java.util.List;
@@ -191,6 +196,28 @@ public class AiModelFactoryImpl implements AiModelFactory {
});
}
+ @Override
+ public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
+ String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
+ return Singleton.get(cacheKey, (Func0) () -> {
+ // TODO 芋艿 @xin 这两个配置取哪好呢
+ // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上
+ // TODO 回复:好的哈
+ String index = "default-index";
+ String prefix = "default:";
+ var config = RedisVectorStore.RedisVectorStoreConfig.builder()
+ .withIndexName(index)
+ .withPrefix(prefix)
+ .build();
+ RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);
+ RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,
+ new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
+ true);
+ redisVectorStore.afterPropertiesSet();
+ return redisVectorStore;
+ });
+ }
+
private static String buildClientCacheKey(Class> clazz, Object... params) {
if (ArrayUtil.isEmpty(params)) {
return clazz.getName();
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java
deleted file mode 100644
index dad58a2c0..000000000
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactory.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.framework.ai.core.factory;
-
-import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
-import org.springframework.ai.embedding.EmbeddingModel;
-import org.springframework.ai.vectorstore.VectorStore;
-
-// TODO @xin:也放到 AiModelFactory 里面好了,后续改成 AiFactory
-/**
- * AI Vector 模型工厂的接口类
- *
- * @author xiaoxin
- */
-public interface AiVectorStoreFactory {
-
- /**
- * 基于指定配置,获得 VectorStore 对象
- *
- * 如果不存在,则进行创建
- *
- * @param embeddingModel 嵌入模型
- * @param platform 平台
- * @param apiKey API KEY
- * @param url API URL
- * @return VectorStore 对象
- */
- VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url);
-
-}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java
deleted file mode 100644
index ec04c5e88..000000000
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiVectorStoreFactoryImpl.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package cn.iocoder.yudao.framework.ai.core.factory;
-
-import cn.hutool.core.lang.Singleton;
-import cn.hutool.core.lang.func.Func0;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
-import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
-import org.springframework.ai.embedding.EmbeddingModel;
-import org.springframework.ai.vectorstore.RedisVectorStore;
-import org.springframework.ai.vectorstore.VectorStore;
-import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
-import redis.clients.jedis.JedisPooled;
-
-/**
- * AI Vector 模型工厂的实现类
- * 使用 redisVectorStore 实现 VectorStore
- *
- * @author xiaoxin
- */
-public class AiVectorStoreFactoryImpl implements AiVectorStoreFactory {
-
- @Override
- public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
- String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
- return Singleton.get(cacheKey, (Func0) () -> {
- // TODO 芋艿 @xin 这两个配置取哪好呢
- // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上
- // TODO 回复:好的哈
- String index = "default-index";
- String prefix = "default:";
- var config = RedisVectorStore.RedisVectorStoreConfig.builder()
- .withIndexName(index)
- .withPrefix(prefix)
- .build();
- RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);
- RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,
- new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
- true);
- redisVectorStore.afterPropertiesSet();
- return redisVectorStore;
- });
- }
-
- private static String buildClientCacheKey(Class> clazz, Object... params) {
- if (ArrayUtil.isEmpty(params)) {
- return clazz.getName();
- }
- return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_"));
- }
-
-}
From c26cecaed6fa237987e1ca562ac05f0a5803dd1f Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Tue, 3 Sep 2024 16:54:51 +0800
Subject: [PATCH 02/10] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI=20?=
=?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20=E4=B8=8D=E5=81=9A=E7=BB=8F?=
=?UTF-8?q?=E6=B5=8E=E5=9E=8B=20=E5=85=88=E6=B3=A8=E9=87=8A=20spring-ai-tr?=
=?UTF-8?q?ansformers-spring-boot-starter?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
index 85996cb82..27c762a72 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
@@ -46,11 +46,13 @@
-
- ${spring-ai.groupId}
- spring-ai-transformers-spring-boot-starter
- ${spring-ai.version}
-
+
+
+
+
+
+
+
${spring-ai.groupId}
spring-ai-tika-document-reader
From 92d32b652e05639b6b9407930e9218b75cdbb8f7 Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Thu, 5 Sep 2024 13:32:16 +0800
Subject: [PATCH 03/10] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=20?=
=?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20=E6=AE=B5=E8=90=BD=E5=8F=AC?=
=?UTF-8?q?=E5=9B=9E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../AiKnowledgeSegmentSearchReqVO.java | 17 ++++++++
.../knowledge/AiKnowledgeSegmentMapper.java | 8 ++++
.../knowledge/AiKnowledgeSegmentService.java | 12 ++++++
.../AiKnowledgeSegmentServiceImpl.java | 42 +++++++++++++++++++
.../ai/config/YudaoAiAutoConfiguration.java | 9 ++--
.../ai/core/factory/AiModelFactoryImpl.java | 10 ++---
6 files changed, 88 insertions(+), 10 deletions(-)
create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
new file mode 100644
index 000000000..75349df62
--- /dev/null
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+
+@Schema(description = "管理后台 - AI 知识库段落召回 Request VO")
+@Data
+public class AiKnowledgeSegmentSearchReqVO {
+
+ @Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+ private Long knowledgeId;
+
+ @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线")
+ private String content;
+
+}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
index 912d18cbc..ea238b826 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowle
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
import org.apache.ibatis.annotations.Mapper;
+import java.util.List;
+
/**
* AI 知识库-分片 Mapper
*
@@ -22,4 +24,10 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX selectList(List vectorIdList) {
+ return selectList(new LambdaQueryWrapperX()
+ .in(AiKnowledgeSegmentDO::getVectorId, vectorIdList)
+ .orderByDesc(AiKnowledgeSegmentDO::getId));
+ }
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
index 8ecb2d24a..49ed67135 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
@@ -2,10 +2,13 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
+import java.util.List;
+
/**
* AI 知识库段落 Service 接口
*
@@ -35,4 +38,13 @@ public interface AiKnowledgeSegmentService {
*/
void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
+
+ /**
+ * 段落召回
+ *
+ * @param reqVO 召回请求信息
+ * @return 召回的段落
+ */
+ List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO);
+
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
index 7f751b176..3bcf5d692 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -1,16 +1,29 @@
package cn.iocoder.yudao.module.ai.service.knowledge;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
+import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
+import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.document.Document;
+import org.springframework.ai.vectorstore.SearchRequest;
+import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.stereotype.Service;
+import java.util.List;
+
/**
* AI 知识库分片 Service 实现类
*
@@ -23,6 +36,13 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
@Resource
private AiKnowledgeSegmentMapper segmentMapper;
+ @Resource
+ private AiKnowledgeService knowledgeService;
+ @Resource
+ private AiChatModelService chatModelService;
+ @Resource
+ private AiApiKeyService apiKeyService;
+
@Override
public PageResult getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO) {
return segmentMapper.selectPage(pageReqVO);
@@ -39,4 +59,26 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
segmentMapper.updateById(BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class));
// TODO @xin 1.禁用删除向量 2.启用重新向量化
}
+
+ @Override
+ public List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) {
+ // 0. 校验
+ AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId());
+ AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
+
+ // 1.1 获取向量存储实例
+ VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
+
+ // 1.2 向量检索
+ List documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent())
+ //TODO @xin 配置提取
+ .withTopK(5)
+ .withSimilarityThreshold(0.5d)
+ .withFilterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build()));
+ if (CollUtil.isEmpty(documentList)) {
+ return ListUtil.empty();
+ }
+ // 2.1 段落召回
+ return segmentMapper.selectList(CollUtil.getFieldValues(documentList, "id", String.class));
+ }
}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
index cd5cfc58b..0d2620b0c 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
@@ -82,7 +82,7 @@ public class YudaoAiAutoConfiguration {
// TODO @xin 免费版本
// @Bean
// @Lazy // TODO 芋艿:临时注释,避免无法启动」
-// public EmbeddingModel transformersEmbeddingClient() {
+// public TransformersEmbeddingModel transformersEmbeddingClient() {
// return new TransformersEmbeddingModel(MetadataMode.EMBED);
// }
@@ -91,23 +91,24 @@ public class YudaoAiAutoConfiguration {
*/
// @Bean
// @Lazy // TODO 芋艿:临时注释,避免无法启动
-// public RedisVectorStore vectorStore(TongYiTextEmbeddingModel tongYiTextEmbeddingModel, RedisVectorStoreProperties properties,
+// public RedisVectorStore vectorStore(TransformersEmbeddingModel embeddingModel, RedisVectorStoreProperties properties,
// RedisProperties redisProperties) {
// var config = RedisVectorStore.RedisVectorStoreConfig.builder()
// .withIndexName(properties.getIndex())
// .withPrefix(properties.getPrefix())
+// .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC))
// .build();
//
-// RedisVectorStore redisVectorStore = new RedisVectorStore(config, tongYiTextEmbeddingModel,
+// RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,
// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
// properties.isInitializeSchema());
// redisVectorStore.afterPropertiesSet();
// return redisVectorStore;
// }
-
@Bean
@Lazy // TODO 芋艿:临时注释,避免无法启动
public TokenTextSplitter tokenTextSplitter() {
+ //TODO @xin 配置提取
return new TokenTextSplitter(500, 100, 5, 10000, true);
}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
index dfe029796..7acd24769 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
@@ -66,6 +66,7 @@ import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestClient;
import redis.clients.jedis.JedisPooled;
+import redis.clients.jedis.search.Schema;
import java.util.List;
@@ -200,14 +201,11 @@ public class AiModelFactoryImpl implements AiModelFactory {
public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
return Singleton.get(cacheKey, (Func0) () -> {
- // TODO 芋艿 @xin 这两个配置取哪好呢
- // TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上
- // TODO 回复:好的哈
- String index = "default-index";
- String prefix = "default:";
+ String prefix = StrUtil.format("{}#{}:", platform.getPlatform(), apiKey);
var config = RedisVectorStore.RedisVectorStoreConfig.builder()
- .withIndexName(index)
+ .withIndexName(cacheKey)
.withPrefix(prefix)
+ .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC))
.build();
RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);
RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,
From 9b8136ef303568302131a1fd3914b47a19268d4f Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Thu, 5 Sep 2024 17:11:56 +0800
Subject: [PATCH 04/10] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI=20?=
=?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93:=20=E9=85=8D=E7=BD=AE=E8=87=AA?=
=?UTF-8?q?=E5=AE=9A=E4=B9=89=E3=80=81=E6=AE=B5=E8=90=BD=E5=90=AF=E7=A6=81?=
=?UTF-8?q?=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../knowledge/AiKnowledgeCreateMyReqVO.java | 7 ++
.../AiKnowledgeDocumentCreateReqVO.java | 19 ++++++
.../dataobject/knowledge/AiKnowledgeDO.java | 12 ++++
.../knowledge/AiKnowledgeDocumentDO.java | 22 ++++++-
.../knowledge/AiKnowledgeSegmentDO.java | 9 ++-
.../AiKnowledgeDocumentServiceImpl.java | 21 ++----
.../AiKnowledgeSegmentServiceImpl.java | 64 +++++++++++++++++--
.../service/knowledge/AiKnowledgeService.java | 9 +++
.../knowledge/AiKnowledgeServiceImpl.java | 13 ++++
9 files changed, 149 insertions(+), 27 deletions(-)
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
index 44a5e87ee..58a89caee 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
@@ -25,4 +25,11 @@ public class AiKnowledgeCreateMyReqVO {
@NotNull(message = "嵌入模型不能为空")
private Long modelId;
+ @Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5")
+ @NotNull(message = "相似性阈值不能为空")
+ private Double similarityThreshold;
+
+ @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
+ @NotNull(message = "topK 不能为空")
+ private Integer topK;
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
index 9cc5290ab..651bdc0f7 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
@@ -23,4 +23,23 @@ public class AiKnowledgeDocumentCreateReqVO {
@URL(message = "文档 URL 格式不正确")
private String url;
+ @Schema(description = "每个文本块的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
+ @NotNull(message = "每个文本块的目标 token 数不能为空")
+ private Integer defaultChunkSize;
+
+ @Schema(description = "每个文本块的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350")
+ @NotNull(message = "每个文本块的最小字符数不能为空")
+ private Integer minChunkSizeChars;
+
+ @Schema(description = "丢弃阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+ @NotNull(message = "丢弃阈值不能为空")
+ private Integer minChunkLengthToEmbed;
+
+ @Schema(description = "最大块数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
+ @NotNull(message = "最大块数不能为空")
+ private Integer maxNumChunks;
+
+ @Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
+ @NotNull(message = "分块是否保留分隔符不能为空")
+ private Boolean keepSeparator;
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
index 756d8cdb3..5db631dd4 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
@@ -52,6 +52,18 @@ public class AiKnowledgeDO extends BaseDO {
* 模型标识
*/
private String model;
+
+ /**
+ * topK
+ */
+ private Integer topK;
+
+ /**
+ * 相似度阈值
+ */
+ private Double similarityThreshold;
+
+
/**
* 状态
*
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
index c5e526cce..18fa46c3a 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
@@ -23,7 +23,7 @@ public class AiKnowledgeDocumentDO extends BaseDO {
private Long id;
/**
* 知识库编号
- *
+ *
* 关联 {@link AiKnowledgeDO#getId()}
*/
private Long knowledgeId;
@@ -47,6 +47,26 @@ public class AiKnowledgeDocumentDO extends BaseDO {
* 字符数
*/
private Integer wordCount;
+ /**
+ * 每个文本块的目标 token 数
+ */
+ private Integer defaultChunkSize;
+ /**
+ * 每个文本块的最小字符数
+ */
+ private Integer minChunkSizeChars;
+ /**
+ * 低于此值的块会被丢弃
+ */
+ private Integer minChunkLengthToEmbed;
+ /**
+ * 最大块数
+ */
+ private Integer maxNumChunks;
+ /**
+ * 分块是否保留分隔符
+ */
+ private Boolean keepSeparator;
/**
* 切片状态
*
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
index 84f7de654..be57265e1 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -25,16 +27,17 @@ public class AiKnowledgeSegmentDO extends BaseDO {
/**
* 向量库的编号
*/
+ @TableField(updateStrategy = FieldStrategy.ALWAYS)
private String vectorId;
/**
* 知识库编号
- *
+ *
* 关联 {@link AiKnowledgeDO#getId()}
*/
private Long knowledgeId;
/**
* 文档编号
- *
+ *
* 关联 {@link AiKnowledgeDocumentDO#getId()}
*/
private Long documentId;
@@ -52,7 +55,7 @@ public class AiKnowledgeSegmentDO extends BaseDO {
private Integer tokens;
/**
* 状态
- *
+ *
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
index 99f0621c8..05a9dce22 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
@@ -9,15 +9,11 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeDocumentMapper;
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeSegmentMapper;
import cn.iocoder.yudao.module.ai.enums.knowledge.AiKnowledgeDocumentStatusEnum;
-import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
-import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
@@ -48,24 +44,16 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
@Resource
private AiKnowledgeSegmentMapper segmentMapper;
- @Resource
- private TokenTextSplitter tokenTextSplitter;
@Resource
private TokenCountEstimator tokenCountEstimator;
-
- @Resource
- private AiApiKeyService apiKeyService;
@Resource
private AiKnowledgeService knowledgeService;
- @Resource
- private AiChatModelService chatModelService;
@Override
@Transactional(rollbackFor = Exception.class)
public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) {
- // 0. 校验
- AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
- AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
+ // 0. 校验并获取向量存储实例
+ VectorStore vectorStore = knowledgeService.getVectorStoreById(createReqVO.getKnowledgeId());
// 1.1 下载文档
TikaDocumentReader loader = new TikaDocumentReader(downloadFile(createReqVO.getUrl()));
@@ -82,6 +70,9 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
return documentId;
}
+ // 2 构造文本分段器
+ TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultChunkSize(), createReqVO.getMinChunkSizeChars(), createReqVO.getMinChunkLengthToEmbed(),
+ createReqVO.getMaxNumChunks(), createReqVO.getKeepSeparator());
// 2.1 文档分段
List segments = tokenTextSplitter.apply(documents);
// 2.2 分段内容入库
@@ -92,8 +83,6 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
segmentMapper.insertBatch(segmentDOList);
- // 3.1 获取向量存储实例
- VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
// 3.2 向量化并存储
segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId()));
vectorStore.add(segments);
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
index 3bcf5d692..1813d0b48 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
@@ -23,6 +24,10 @@ import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.stereotype.Service;
import java.util.List;
+import java.util.Objects;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGMENT_NOT_EXISTS;
/**
* AI 知识库分片 Service 实现类
@@ -50,14 +55,45 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
@Override
public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) {
- segmentMapper.updateById(BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class));
- // TODO @xin 重新向量化
+ // 0 校验
+ AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId());
+ // 2.1 获取知识库向量实例
+ VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId());
+ // 2.2 删除原向量
+ vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
+
+ // 2.3 重新向量化
+ Document document = new Document(reqVO.getContent());
+ document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId());
+ vectorStore.add(List.of(document));
+
+ // 2.1 更新段落内容
+ AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
+ knowledgeSegment.setVectorId(document.getId());
+ segmentMapper.updateById(knowledgeSegment);
}
@Override
public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
- segmentMapper.updateById(BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class));
- // TODO @xin 1.禁用删除向量 2.启用重新向量化
+ // 0 校验
+ AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId());
+ // 1 获取知识库向量实例
+ VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId());
+ AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
+
+ if (Objects.equals(reqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) {
+ // 2.1 启用重新向量化
+ Document document = new Document(oldKnowledgeSegment.getContent());
+ document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId());
+ vectorStore.add(List.of(document));
+ knowledgeSegment.setVectorId(document.getId());
+ } else {
+ // 2.2 禁用删除向量
+ vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
+ knowledgeSegment.setVectorId(null);
+ }
+ // 3 更新段落状态
+ segmentMapper.updateById(knowledgeSegment);
}
@Override
@@ -71,9 +107,8 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
// 1.2 向量检索
List documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent())
- //TODO @xin 配置提取
- .withTopK(5)
- .withSimilarityThreshold(0.5d)
+ .withTopK(knowledge.getTopK())
+ .withSimilarityThreshold(knowledge.getSimilarityThreshold())
.withFilterExpression(new FilterExpressionBuilder().eq(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, reqVO.getKnowledgeId()).build()));
if (CollUtil.isEmpty(documentList)) {
return ListUtil.empty();
@@ -81,4 +116,19 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
// 2.1 段落召回
return segmentMapper.selectList(CollUtil.getFieldValues(documentList, "id", String.class));
}
+
+
+ /**
+ * 校验段落是否存在
+ *
+ * @param id 文档编号
+ * @return 段落信息
+ */
+ private AiKnowledgeSegmentDO validateKnowledgeSegmentExists(Long id) {
+ AiKnowledgeSegmentDO knowledgeSegment = segmentMapper.selectById(id);
+ if (knowledgeSegment == null) {
+ throw exception(KNOWLEDGE_SEGMENT_NOT_EXISTS);
+ }
+ return knowledgeSegment;
+ }
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
index 9f43c5328..d9770f452 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+import org.springframework.ai.vectorstore.VectorStore;
/**
* AI 知识库-基础信息 Service 接口
@@ -47,4 +48,12 @@ public interface AiKnowledgeService {
* @return 知识库分页
*/
PageResult getKnowledgePageMy(Long userId, PageParam pageReqVO);
+
+ /**
+ * 根据知识库编号获取向量存储实例
+ *
+ * @param knowledgeId 知识库编号
+ * @return 向量存储实例
+ */
+ VectorStore getVectorStoreById(Long knowledgeId);
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
index 1948bb00e..7a145d734 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
@@ -10,9 +10,11 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnow
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
+import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -32,6 +34,10 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
@Resource
private AiKnowledgeMapper knowledgeMapper;
+ @Resource
+ private AiChatModelService chatModelService;
+ @Resource
+ private AiApiKeyService apiKeyService;
@Override
public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) {
@@ -75,4 +81,11 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
return knowledgeMapper.selectPageByMy(userId, pageReqVO);
}
+ @Override
+ public VectorStore getVectorStoreById(Long knowledgeId) {
+ AiKnowledgeDO knowledge = validateKnowledgeExists(knowledgeId);
+ AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
+ return apiKeyService.getOrCreateVectorStore(model.getKeyId());
+ }
+
}
From 8e56b81a3a73de6e6d2d4d2e8da9b1285ef69df8 Mon Sep 17 00:00:00 2001
From: YunaiV
Date: Sat, 7 Sep 2024 20:12:37 +0800
Subject: [PATCH 05/10] =?UTF-8?q?=E3=80=90=E4=BB=A3=E7=A0=81=E8=AF=84?=
=?UTF-8?q?=E5=AE=A1=E3=80=91AI=20=E5=A4=A7=E6=A8=A1=E5=9E=8B=EF=BC=9A?=
=?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=E7=9A=84=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../vo/knowledge/AiKnowledgeCreateMyReqVO.java | 1 +
.../AiKnowledgeDocumentCreateReqVO.java | 1 +
.../dal/dataobject/knowledge/AiKnowledgeDO.java | 3 +--
.../knowledge/AiKnowledgeDocumentDO.java | 2 ++
.../knowledge/AiKnowledgeSegmentDO.java | 2 +-
.../knowledge/AiKnowledgeSegmentMapper.java | 2 ++
.../AiKnowledgeDocumentServiceImpl.java | 2 +-
.../knowledge/AiKnowledgeSegmentService.java | 3 +--
.../knowledge/AiKnowledgeSegmentServiceImpl.java | 16 ++++++++--------
.../ai/service/knowledge/AiKnowledgeService.java | 4 ++--
.../knowledge/AiKnowledgeServiceImpl.java | 2 ++
.../ai/service/model/AiApiKeyServiceImpl.java | 1 +
.../yudao-spring-boot-starter-ai/pom.xml | 2 +-
.../ai/core/factory/AiModelFactoryImpl.java | 1 +
14 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
index 58a89caee..a1e23ca64 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
@@ -32,4 +32,5 @@ public class AiKnowledgeCreateMyReqVO {
@Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
@NotNull(message = "topK 不能为空")
private Integer topK;
+
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
index 651bdc0f7..d393fb672 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
@@ -42,4 +42,5 @@ public class AiKnowledgeDocumentCreateReqVO {
@Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "分块是否保留分隔符不能为空")
private Boolean keepSeparator;
+
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
index 5db631dd4..5d7556235 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
@@ -57,17 +57,16 @@ public class AiKnowledgeDO extends BaseDO {
* topK
*/
private Integer topK;
-
/**
* 相似度阈值
*/
private Double similarityThreshold;
-
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
+
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
index 18fa46c3a..8b82a55db 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
@@ -47,10 +47,12 @@ public class AiKnowledgeDocumentDO extends BaseDO {
* 字符数
*/
private Integer wordCount;
+ // TODO @新:chunk 1)是不是 segment,这样命名保持一致会好点哈?2)Size 是不是改成 Tokens 会统一点;3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。
/**
* 每个文本块的目标 token 数
*/
private Integer defaultChunkSize;
+ // TODO @xin:SizeChars 和 wordCount 好像是一个意思,是不是也要统一哈。
/**
* 每个文本块的最小字符数
*/
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
index be57265e1..70d85dc60 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
@@ -27,7 +27,7 @@ public class AiKnowledgeSegmentDO extends BaseDO {
/**
* 向量库的编号
*/
- @TableField(updateStrategy = FieldStrategy.ALWAYS)
+ @TableField(updateStrategy = FieldStrategy.ALWAYS) // TODO @新:尽量规避要这个注解。万一后面加个 status 单独更新,可能会踩坑。
private String vectorId;
/**
* 知识库编号
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
index ea238b826..bda05989b 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
@@ -25,9 +25,11 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX selectList(List vectorIdList) {
return selectList(new LambdaQueryWrapperX()
.in(AiKnowledgeSegmentDO::getVectorId, vectorIdList)
.orderByDesc(AiKnowledgeSegmentDO::getId));
}
+
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
index 05a9dce22..807509d2b 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
@@ -83,7 +83,7 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
segmentMapper.insertBatch(segmentDOList);
- // 3.2 向量化并存储
+ // 3. 向量化并存储
segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId()));
vectorStore.add(segments);
return documentId;
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
index 49ed67135..91bffc276 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
@@ -38,9 +38,8 @@ public interface AiKnowledgeSegmentService {
*/
void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
-
/**
- * 段落召回
+ * 召回段落
*
* @param reqVO 召回请求信息
* @return 召回的段落
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
index 1813d0b48..e1386c936 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -55,19 +55,19 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
@Override
public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) {
- // 0 校验
+ // 1. 校验
AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId());
+
// 2.1 获取知识库向量实例
VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId());
// 2.2 删除原向量
vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
-
// 2.3 重新向量化
Document document = new Document(reqVO.getContent());
document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId());
vectorStore.add(List.of(document));
- // 2.1 更新段落内容
+ // 3. 更新段落内容
AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
knowledgeSegment.setVectorId(document.getId());
segmentMapper.updateById(knowledgeSegment);
@@ -98,14 +98,14 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
@Override
public List similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) {
- // 0. 校验
+ // 1. 校验
AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId());
AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
- // 1.1 获取向量存储实例
+ // 2. 获取向量存储实例
VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
- // 1.2 向量检索
+ // 3.1 向量检索
List documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent())
.withTopK(knowledge.getTopK())
.withSimilarityThreshold(knowledge.getSimilarityThreshold())
@@ -113,11 +113,10 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
if (CollUtil.isEmpty(documentList)) {
return ListUtil.empty();
}
- // 2.1 段落召回
+ // 3.2 段落召回
return segmentMapper.selectList(CollUtil.getFieldValues(documentList, "id", String.class));
}
-
/**
* 校验段落是否存在
*
@@ -131,4 +130,5 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
}
return knowledgeSegment;
}
+
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
index d9770f452..8cf07d91a 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
@@ -23,7 +23,6 @@ public interface AiKnowledgeService {
*/
Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId);
-
/**
* 创建【我的】知识库
*
@@ -32,7 +31,6 @@ public interface AiKnowledgeService {
*/
void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId);
-
/**
* 校验知识库是否存在
*
@@ -49,6 +47,7 @@ public interface AiKnowledgeService {
*/
PageResult getKnowledgePageMy(Long userId, PageParam pageReqVO);
+ // TODO @新:knowledgeId 和 validateKnowledgeExists 的 id 是同一个么?如果是的话,建议变量也用 id 哈,然后两边的 id 注释,保持一致
/**
* 根据知识库编号获取向量存储实例
*
@@ -56,4 +55,5 @@ public interface AiKnowledgeService {
* @return 向量存储实例
*/
VectorStore getVectorStoreById(Long knowledgeId);
+
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
index 7a145d734..7b0c37b5f 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
@@ -38,6 +38,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
private AiChatModelService chatModelService;
@Resource
private AiApiKeyService apiKeyService;
+ // TODO @新:chatModelService 和 apiKeyService 可以放到 33 行的 chatModalService 后面。尽量保持,想通类型的变量在一块。例如说,Service 一块,Mapper 一块。
@Override
public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) {
@@ -85,6 +86,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
public VectorStore getVectorStoreById(Long knowledgeId) {
AiKnowledgeDO knowledge = validateKnowledgeExists(knowledgeId);
AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
+ // 创建或获取 VectorStore 对象
return apiKeyService.getOrCreateVectorStore(model.getKeyId());
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
index b2807905a..50e1fbd7a 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
@@ -146,6 +146,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
public VectorStore getOrCreateVectorStore(Long id) {
AiApiKeyDO apiKey = validateApiKey(id);
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
+ // 创建或获取 VectorStore 对象
return modelFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl());
}
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
index 27c762a72..7de6534d7 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
@@ -47,7 +47,7 @@
-
+
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
index 7acd24769..a7f7aa2f6 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
+++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
@@ -197,6 +197,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
});
}
+ // TODO @新:貌似可以创建一个大的 VectorStore。然后搜的时候,通过 Filter.Expression 过滤对应的数据。
@Override
public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
From 5cd870748df348ef43b99b19de358d10aaac3b3c Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Sun, 22 Sep 2024 15:20:55 +0800
Subject: [PATCH 06/10] =?UTF-8?q?=E3=80=90=E8=A7=A3=E5=86=B3todo=E3=80=91A?=
=?UTF-8?q?I=20=E7=9F=A5=E8=AF=86=E5=BA=93:=20=E5=AD=97=E6=AE=B5=E5=91=BD?=
=?UTF-8?q?=E5=90=8D=E7=BB=9F=E4=B8=80=20=E8=A1=A5=E5=85=85=E6=B3=A8?=
=?UTF-8?q?=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../AiKnowledgeSegmentController.java | 2 +-
.../AiKnowledgeDocumentCreateReqVO.java | 20 +++++++++----------
.../dataobject/knowledge/AiKnowledgeDO.java | 6 ++++--
.../knowledge/AiKnowledgeDocumentDO.java | 18 ++++++++++-------
.../knowledge/AiKnowledgeSegmentDO.java | 3 ---
.../knowledge/AiKnowledgeSegmentMapper.java | 3 +--
.../AiKnowledgeDocumentServiceImpl.java | 4 ++--
.../AiKnowledgeSegmentServiceImpl.java | 4 ++--
.../service/knowledge/AiKnowledgeService.java | 5 ++---
.../knowledge/AiKnowledgeServiceImpl.java | 13 +++++-------
.../ai/core/factory/AiModelFactoryImpl.java | 1 -
11 files changed, 38 insertions(+), 41 deletions(-)
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
index a0d0952a8..d4ca7ca49 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
+++ b/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 {
@GetMapping("/page")
@Operation(summary = "获取段落分页")
- public CommonResult> getKnowledgeSegmentPageMy(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
+ public CommonResult> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
PageResult pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
index d393fb672..df6b6821d 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
+++ b/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 {
@URL(message = "文档 URL 格式不正确")
private String url;
- @Schema(description = "每个文本块的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
- @NotNull(message = "每个文本块的目标 token 数不能为空")
- private Integer defaultChunkSize;
+ @Schema(description = "每个段落的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
+ @NotNull(message = "每个段落的目标 token 数不能为空")
+ private Integer defaultSegmentTokens;
- @Schema(description = "每个文本块的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350")
- @NotNull(message = "每个文本块的最小字符数不能为空")
- private Integer minChunkSizeChars;
+ @Schema(description = "每个段落的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350")
+ @NotNull(message = "每个段落的最小字符数不能为空")
+ private Integer minSegmentWordCount;
- @Schema(description = "丢弃阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+ @Schema(description = "丢弃阈值:低于此阈值的段落会被丢弃", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
@NotNull(message = "丢弃阈值不能为空")
private Integer minChunkLengthToEmbed;
- @Schema(description = "最大块数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
- @NotNull(message = "最大块数不能为空")
- private Integer maxNumChunks;
+ @Schema(description = "最大段落数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
+ @NotNull(message = "最大段落数不能为空")
+ private Integer maxNumSegments;
@Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "分块是否保留分隔符不能为空")
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
index 5d7556235..1551b8ac8 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
@@ -38,9 +38,11 @@ public class AiKnowledgeDO extends BaseDO {
* 知识库描述
*/
private String description;
- // TODO @新:如果全部可见,需要怎么设置?
+
/**
- * 可见权限,只能选择哪些人可见
+ * 可见权限,选择哪些人可见
+ *
+ * -1 所有人可见,其他为各自用户编号
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List visibilityPermissions;
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
index 8b82a55db..297944611 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
+++ b/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 {
*/
private String url;
/**
- * token 数量
+ * 文档 token 数量
*/
private Integer tokens;
/**
- * 字符数
+ * 文档字符数
*/
private Integer wordCount;
- // TODO @新:chunk 1)是不是 segment,这样命名保持一致会好点哈?2)Size 是不是改成 Tokens 会统一点;3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。
+
+
+ // ========== 自定义分段所用参数 ==========
+ // TODO @新:3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。
/**
* 每个文本块的目标 token 数
*/
- private Integer defaultChunkSize;
- // TODO @xin:SizeChars 和 wordCount 好像是一个意思,是不是也要统一哈。
+ private Integer defaultSegmentTokens;
/**
* 每个文本块的最小字符数
*/
- private Integer minChunkSizeChars;
+ private Integer minSegmentWordCount;
/**
* 低于此值的块会被丢弃
*/
@@ -64,11 +66,13 @@ public class AiKnowledgeDocumentDO extends BaseDO {
/**
* 最大块数
*/
- private Integer maxNumChunks;
+ private Integer maxNumSegments;
/**
* 分块是否保留分隔符
*/
private Boolean keepSeparator;
+ // ===================================
+
/**
* 切片状态
*
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
index 70d85dc60..9bb3d3338 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
+++ b/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;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import com.baomidou.mybatisplus.annotation.FieldStrategy;
-import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -27,7 +25,6 @@ public class AiKnowledgeSegmentDO extends BaseDO {
/**
* 向量库的编号
*/
- @TableField(updateStrategy = FieldStrategy.ALWAYS) // TODO @新:尽量规避要这个注解。万一后面加个 status 单独更新,可能会踩坑。
private String vectorId;
/**
* 知识库编号
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
index bda05989b..094f19b52 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
+++ b/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 selectList(List vectorIdList) {
+ default List selectListByVectorIds(List vectorIdList) {
return selectList(new LambdaQueryWrapperX()
.in(AiKnowledgeSegmentDO::getVectorId, vectorIdList)
.orderByDesc(AiKnowledgeSegmentDO::getId));
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
index 807509d2b..ff475f92c 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
+++ b/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
}
// 2 构造文本分段器
- TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultChunkSize(), createReqVO.getMinChunkSizeChars(), createReqVO.getMinChunkLengthToEmbed(),
- createReqVO.getMaxNumChunks(), createReqVO.getKeepSeparator());
+ TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(createReqVO.getDefaultSegmentTokens(), createReqVO.getMinSegmentWordCount(), createReqVO.getMinChunkLengthToEmbed(),
+ createReqVO.getMaxNumSegments(), createReqVO.getKeepSeparator());
// 2.1 文档分段
List segments = tokenTextSplitter.apply(documents);
// 2.2 分段内容入库
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
index e1386c936..5523fe278 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
+++ b/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
} else {
// 2.2 禁用删除向量
vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
- knowledgeSegment.setVectorId(null);
+ knowledgeSegment.setVectorId("");
}
// 3 更新段落状态
segmentMapper.updateById(knowledgeSegment);
@@ -114,7 +114,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
return ListUtil.empty();
}
// 3.2 段落召回
- return segmentMapper.selectList(CollUtil.getFieldValues(documentList, "id", String.class));
+ return segmentMapper.selectListByVectorIds(CollUtil.getFieldValues(documentList, "id", String.class));
}
/**
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
index 8cf07d91a..6ff878d19 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
@@ -47,13 +47,12 @@ public interface AiKnowledgeService {
*/
PageResult getKnowledgePageMy(Long userId, PageParam pageReqVO);
- // TODO @新:knowledgeId 和 validateKnowledgeExists 的 id 是同一个么?如果是的话,建议变量也用 id 哈,然后两边的 id 注释,保持一致
/**
* 根据知识库编号获取向量存储实例
*
- * @param knowledgeId 知识库编号
+ * @param id 知识库编号
* @return 向量存储实例
*/
- VectorStore getVectorStoreById(Long knowledgeId);
+ VectorStore getVectorStoreById(Long id);
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
index 7b0c37b5f..9269582ac 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
@@ -29,21 +29,18 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_
@Slf4j
public class AiKnowledgeServiceImpl implements AiKnowledgeService {
- @Resource
- private AiChatModelService chatModalService;
-
@Resource
private AiKnowledgeMapper knowledgeMapper;
+
@Resource
private AiChatModelService chatModelService;
@Resource
private AiApiKeyService apiKeyService;
- // TODO @新:chatModelService 和 apiKeyService 可以放到 33 行的 chatModalService 后面。尽量保持,想通类型的变量在一块。例如说,Service 一块,Mapper 一块。
@Override
public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) {
// 1. 校验模型配置
- AiChatModelDO model = chatModalService.validateChatModel(createReqVO.getModelId());
+ AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getModelId());
// 2. 插入知识库
AiKnowledgeDO knowledgeBase = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
@@ -60,7 +57,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
throw exception(KNOWLEDGE_NOT_EXISTS);
}
// 1.2 校验模型配置
- AiChatModelDO model = chatModalService.validateChatModel(updateReqVO.getModelId());
+ AiChatModelDO model = chatModelService.validateChatModel(updateReqVO.getModelId());
// 2. 更新知识库
AiKnowledgeDO updateDO = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class);
@@ -83,8 +80,8 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
}
@Override
- public VectorStore getVectorStoreById(Long knowledgeId) {
- AiKnowledgeDO knowledge = validateKnowledgeExists(knowledgeId);
+ public VectorStore getVectorStoreById(Long id) {
+ AiKnowledgeDO knowledge = validateKnowledgeExists(id);
AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
// 创建或获取 VectorStore 对象
return apiKeyService.getOrCreateVectorStore(model.getKeyId());
diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
index a7f7aa2f6..7acd24769 100644
--- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
+++ b/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 {
});
}
- // TODO @新:貌似可以创建一个大的 VectorStore。然后搜的时候,通过 Filter.Expression 过滤对应的数据。
@Override
public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
From 0700c3f15eb38e4f6b39d07e7632a75641def31a Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Sun, 22 Sep 2024 18:13:21 +0800
Subject: [PATCH 07/10] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?=
=?UTF-8?q?=EF=BC=9A=E8=81=8A=E5=A4=A9=E6=8E=A5=E5=85=A5=E7=9F=A5=E8=AF=86?=
=?UTF-8?q?=E5=BA=93?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../yudao/module/ai/enums/AiChatRoleEnum.java | 7 +++-
.../AiChatConversationCreateMyReqVO.java | 3 ++
.../AiChatConversationUpdateMyReqVO.java | 3 ++
.../dataobject/chat/AiChatConversationDO.java | 8 ++++
.../chat/AiChatConversationServiceImpl.java | 16 +++++++-
.../chat/AiChatMessageServiceImpl.java | 39 +++++++++++++++----
6 files changed, 66 insertions(+), 10 deletions(-)
diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
index 029961bf3..6cb98c562 100644
--- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
+++ b/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 {
### 支付宝
### 微信
除此之外不要任何解释性语句。
- """);
+ """),
+
+ AI_KNOWLEDGE_ROLE("知识库助手", """
+ 给你提供一些数据参考:{info},请回答我的问题。
+ 请你跟进数据参考与工具返回结果回复用户的请求。
+ """);
/**
* 角色名
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java
index c13200b6a..84595bea2 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java
+++ b/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 {
@Schema(description = "聊天角色编号", example = "666")
private Long roleId;
+ @Schema(description = "知识库编号", example = "1204")
+ private Long knowledgeId;
+
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
index f9ce64bae..2b57572c4 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
+++ b/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 {
@Schema(description = "模型编号", example = "1")
private Long modelId;
+ @Schema(description = "知识库编号", example = "1")
+ private Long knowledgeId;
+
@Schema(description = "角色设定", example = "一个快乐的程序员")
private String systemMessage;
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
index 0b7eb0233..7d9625f58 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -64,6 +65,13 @@ public class AiChatConversationDO extends BaseDO {
*/
private Long roleId;
+ /**
+ * 知识库编号
+ *
+ * 关联 {@link AiKnowledgeDO#getId()}
+ */
+ private Long knowledgeId;
+
/**
* 模型编号
*
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
index 83dcd8dff..8f094087f 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
+++ b/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;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
+import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
import jakarta.annotation.Resource;
@@ -22,6 +23,7 @@ import org.springframework.validation.annotation.Validated;
import java.time.LocalDateTime;
import java.util.List;
+import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@@ -45,6 +47,8 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
private AiChatModelService chatModalService;
@Resource
private AiChatRoleService chatRoleService;
+ @Resource
+ private AiKnowledgeService knowledgeService;
@Override
public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
@@ -56,9 +60,14 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
Assert.notNull(model, "必须找到默认模型");
validateChatModel(model);
+ // 1.3 校验知识库
+ if (Objects.nonNull(createReqVO.getKnowledgeId())) {
+ knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
+ }
+
// 2. 创建 AiChatConversationDO 聊天对话
AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)
- .setModelId(model.getId()).setModel(model.getModel())
+ .setModelId(model.getId()).setModel(model.getModel()).setKnowledgeId(createReqVO.getKnowledgeId())
.setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
if (role != null) {
conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());
@@ -82,6 +91,11 @@ public class AiChatConversationServiceImpl implements AiChatConversationService
model = chatModalService.validateChatModel(updateReqVO.getModelId());
}
+ // 1.3 校验知识库是否存在
+ if (updateReqVO.getKnowledgeId() != null) {
+ knowledgeService.validateKnowledgeExists(updateReqVO.getKnowledgeId());
+ }
+
// 2. 更新对话信息
AiChatConversationDO updateObj = BeanUtils.toBean(updateReqVO, AiChatConversationDO.class);
if (Boolean.TRUE.equals(updateReqVO.getPinned())) {
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
index 72fa06a79..4ef5af8ee 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
+++ b/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;
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSearchReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
+import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
+import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
import cn.iocoder.yudao.module.ai.service.model.AiChatModelService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.messages.*;
+import org.springframework.ai.chat.messages.Message;
+import org.springframework.ai.chat.messages.MessageType;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.StreamingChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
@@ -59,6 +67,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
private AiChatModelService chatModalService;
@Resource
private AiApiKeyService apiKeyService;
+ @Resource
+ private AiKnowledgeSegmentService knowledgeSegmentService;
@Transactional(rollbackFor = Exception.class)
public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
@@ -141,14 +151,27 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) {
// 1. 构建 Prompt Message 列表
List chatMessages = new ArrayList<>();
- // 1.1 system context 角色设定
+
+ // 1.1 知识库召回
+ if (Objects.nonNull(conversation.getKnowledgeId())) {
+ List segmentList = knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(conversation.getKnowledgeId()).setContent(sendReqVO.getContent()));
+ if (CollUtil.isNotEmpty(segmentList)) {
+ PromptTemplate promptTemplate = new PromptTemplate(AiChatRoleEnum.AI_KNOWLEDGE_ROLE.getSystemMessage());
+ StringBuilder infoBuilder = StrUtil.builder();
+ segmentList.forEach(segment -> infoBuilder.append(System.lineSeparator()).append(segment.getContent()));
+ Message message = promptTemplate.createMessage(Map.of("info", infoBuilder.toString()));
+ chatMessages.add(message);
+ }
+ }
+
+ // 1.2 system context 角色设定
if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
chatMessages.add(new SystemMessage(conversation.getSystemMessage()));
}
- // 1.2 history message 历史消息
+ // 1.3 history message 历史消息
List contextMessages = filterContextMessages(messages, conversation, sendReqVO);
contextMessages.forEach(message -> chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent())));
- // 1.3 user message 新发送消息
+ // 1.4 user message 新发送消息
chatMessages.add(new UserMessage(sendReqVO.getContent()));
// 2. 构建 ChatOptions 对象
@@ -160,12 +183,12 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
/**
* 从历史消息中,获得倒序的 n 组消息作为消息上下文
- *
+ *
* n 组:指的是 user + assistant 形成一组
*
- * @param messages 消息列表
+ * @param messages 消息列表
* @param conversation 对话
- * @param sendReqVO 发送请求
+ * @param sendReqVO 发送请求
* @return 消息上下文
*/
private List filterContextMessages(List messages,
@@ -182,7 +205,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
}
AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
if (userMessage == null || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
- || StrUtil.isEmpty(assistantMessage.getContent())) {
+ || StrUtil.isEmpty(assistantMessage.getContent())) {
continue;
}
// 由于后续要 reverse 反转,所以先添加 assistantMessage
From 6b651baeed316f9963d420b1eb72161b809e1a1c Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Wed, 25 Sep 2024 16:06:35 +0800
Subject: [PATCH 08/10] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?=
=?UTF-8?q?=EF=BC=9A=E7=9F=A5=E8=AF=86=E5=BA=93=E5=8F=AF=E8=A7=81=E6=9D=83?=
=?UTF-8?q?=E9=99=90=E6=95=B4=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../knowledge/AiKnowledgeController.java | 31 +++++++++----------
.../AiKnowledgeDocumentController.java | 2 +-
...ReqVO.java => AiKnowledgeCreateReqVO.java} | 10 +++---
.../vo/knowledge/AiKnowledgePageReqVO.java | 14 +++++++++
...ReqVO.java => AiKnowledgeUpdateReqVO.java} | 8 ++---
.../dataobject/knowledge/AiKnowledgeDO.java | 7 +----
.../mysql/knowledge/AiKnowledgeMapper.java | 7 +++--
.../service/knowledge/AiKnowledgeService.java | 22 ++++++-------
.../knowledge/AiKnowledgeServiceImpl.java | 14 ++++-----
9 files changed, 60 insertions(+), 55 deletions(-)
rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/{AiKnowledgeCreateMyReqVO.java => AiKnowledgeCreateReqVO.java} (86%)
create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/{AiKnowledgeUpdateMyReqVO.java => AiKnowledgeUpdateReqVO.java} (90%)
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
index dc2c8e3ae..3ffea5e80 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
@@ -1,12 +1,12 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
import io.swagger.v3.oas.annotations.Operation;
@@ -28,24 +28,23 @@ public class AiKnowledgeController {
@Resource
private AiKnowledgeService knowledgeService;
- @GetMapping("/my-page")
- @Operation(summary = "获取【我的】知识库分页")
- public CommonResult> getKnowledgePageMy(@Validated PageParam pageReqVO) {
- PageResult pageResult = knowledgeService.getKnowledgePageMy(getLoginUserId(), pageReqVO);
+ @GetMapping("/page")
+ @Operation(summary = "获取知识库分页")
+ public CommonResult> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) {
+ PageResult pageResult = knowledgeService.getKnowledgePage(getLoginUserId(), pageReqVO);
return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
}
- @PostMapping("/create-my")
- @Operation(summary = "创建【我的】知识库")
- public CommonResult createKnowledgeMy(@RequestBody @Valid AiKnowledgeCreateMyReqVO createReqVO) {
- return success(knowledgeService.createKnowledgeMy(createReqVO, getLoginUserId()));
+ @PostMapping("/create")
+ @Operation(summary = "创建知识库")
+ public CommonResult createKnowledge(@RequestBody @Valid AiKnowledgeCreateReqVO createReqVO) {
+ return success(knowledgeService.createKnowledge(createReqVO, getLoginUserId()));
}
- @PutMapping("/update-my")
- @Operation(summary = "更新【我的】知识库")
- public CommonResult updateKnowledgeMy(@RequestBody @Valid AiKnowledgeUpdateMyReqVO updateReqVO) {
- knowledgeService.updateKnowledgeMy(updateReqVO, getLoginUserId());
+ @PutMapping("/update")
+ @Operation(summary = "更新知识库")
+ public CommonResult updateKnowledge(@RequestBody @Valid AiKnowledgeUpdateReqVO updateReqVO) {
+ knowledgeService.updateKnowledge(updateReqVO, getLoginUserId());
return success(true);
}
-
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
index d86210556..75c4d805b 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
+++ b/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 {
@GetMapping("/page")
@Operation(summary = "获取文档分页")
- public CommonResult> getKnowledgeDocumentPageMy(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
+ public CommonResult> getKnowledgeDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
PageResult pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java
similarity index 86%
rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java
index a1e23ca64..bae20d936 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java
@@ -5,11 +5,9 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
-import java.util.List;
-
-@Schema(description = "管理后台 - AI 知识库创建【我的】 Request VO")
+@Schema(description = "管理后台 - AI 知识库创建 Request VO")
@Data
-public class AiKnowledgeCreateMyReqVO {
+public class AiKnowledgeCreateReqVO {
@Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
@NotBlank(message = "知识库名称不能为空")
@@ -18,8 +16,8 @@ public class AiKnowledgeCreateMyReqVO {
@Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档")
private String description;
- @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]")
- private List visibilityPermissions;
+ @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
+ private String visibilityPermissions;
@Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "嵌入模型不能为空")
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
new file mode 100644
index 000000000..941732f1a
--- /dev/null
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
@@ -0,0 +1,14 @@
+package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 知识库的分页 Request VO")
+@Data
+public class AiKnowledgePageReqVO extends PageParam {
+
+ @Schema(description = "知识库名称", example = "Java 开发手册")
+ private String name;
+
+}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateMyReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java
similarity index 90%
rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateMyReqVO.java
rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java
index 987c9bf4a..91925f53a 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateMyReqVO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java
@@ -5,11 +5,9 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
-import java.util.List;
-
@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO")
@Data
-public class AiKnowledgeUpdateMyReqVO {
+public class AiKnowledgeUpdateReqVO {
@Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
@NotNull(message = "知识库编号不能为空")
@@ -22,8 +20,8 @@ public class AiKnowledgeUpdateMyReqVO {
@Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
private String description;
- @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1]")
- private List visibilityPermissions;
+ @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
+ private String visibilityPermissions;
@Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "嵌入模型不能为空")
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
index 1551b8ac8..5ad2dd05c 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
@@ -2,14 +2,10 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.Data;
-import java.util.List;
-
/**
* AI 知识库 DO
*
@@ -44,8 +40,7 @@ public class AiKnowledgeDO extends BaseDO {
*
* -1 所有人可见,其他为各自用户编号
*/
- @TableField(typeHandler = JacksonTypeHandler.class)
- private List visibilityPermissions;
+ private String visibilityPermissions;
/**
* 嵌入模型编号
*/
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
index 2bf23411a..f07a9a2af 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
@@ -1,10 +1,10 @@
package cn.iocoder.yudao.module.ai.dal.mysql.knowledge;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import org.apache.ibatis.annotations.Mapper;
@@ -16,10 +16,11 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AiKnowledgeMapper extends BaseMapperX {
- default PageResult selectPageByMy(Long userId, PageParam pageReqVO) {
+ default PageResult selectPage(Long userId, AiKnowledgePageReqVO pageReqVO) {
return selectPage(pageReqVO, new LambdaQueryWrapperX()
- .eq(AiKnowledgeDO::getUserId, userId)
.eq(AiKnowledgeDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
+ .likeIfPresent(AiKnowledgeDO::getName, pageReqVO.getName())
+ .and(e -> e.apply("FIND_IN_SET(" + userId + ",visibility_permissions)").or(m -> m.apply("FIND_IN_SET(-1,visibility_permissions)")))
.orderByDesc(AiKnowledgeDO::getId));
}
}
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
index 6ff878d19..7060076a4 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
@@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.ai.service.knowledge;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import org.springframework.ai.vectorstore.VectorStore;
@@ -15,21 +15,21 @@ import org.springframework.ai.vectorstore.VectorStore;
public interface AiKnowledgeService {
/**
- * 创建【我的】知识库
+ * 创建知识库
*
* @param createReqVO 创建信息
* @param userId 用户编号
* @return 编号
*/
- Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId);
+ Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId);
/**
- * 创建【我的】知识库
+ * 更新知识库
*
* @param updateReqVO 更新信息
* @param userId 用户编号
*/
- void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId);
+ void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId);
/**
* 校验知识库是否存在
@@ -39,13 +39,13 @@ public interface AiKnowledgeService {
AiKnowledgeDO validateKnowledgeExists(Long id);
/**
- * 获得【我的】知识库分页
+ * 获得知识库分页
*
- * @param userId 用户编号
- * @param pageReqVO 分页查询
+ * @param userId 用户编号
+ * @param pageReqVO 分页查询
* @return 知识库分页
*/
- PageResult getKnowledgePageMy(Long userId, PageParam pageReqVO);
+ PageResult getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO);
/**
* 根据知识库编号获取向量存储实例
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
index 9269582ac..1a000c19d 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
+++ b/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;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateMyReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
@@ -38,7 +38,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
private AiApiKeyService apiKeyService;
@Override
- public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) {
+ public Long createKnowledge(AiKnowledgeCreateReqVO createReqVO, Long userId) {
// 1. 校验模型配置
AiChatModelDO model = chatModelService.validateChatModel(createReqVO.getModelId());
@@ -50,7 +50,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
}
@Override
- public void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId) {
+ public void updateKnowledge(AiKnowledgeUpdateReqVO updateReqVO, Long userId) {
// 1.1 校验知识库存在
AiKnowledgeDO knowledgeBaseDO = validateKnowledgeExists(updateReqVO.getId());
if (ObjUtil.notEqual(knowledgeBaseDO.getUserId(), userId)) {
@@ -75,8 +75,8 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
}
@Override
- public PageResult getKnowledgePageMy(Long userId, PageParam pageReqVO) {
- return knowledgeMapper.selectPageByMy(userId, pageReqVO);
+ public PageResult getKnowledgePage(Long userId, AiKnowledgePageReqVO pageReqVO) {
+ return knowledgeMapper.selectPage(userId, pageReqVO);
}
@Override
From c05d7c9f9521ad2b1d1534c66423f8ce4fa9f535 Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Thu, 26 Sep 2024 15:10:55 +0800
Subject: [PATCH 09/10] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?=
=?UTF-8?q?=EF=BC=9A=E5=AF=B9=E8=AF=9D=E6=B6=88=E6=81=AF=E8=AE=B0=E5=BD=95?=
=?UTF-8?q?=E5=8F=AC=E5=9B=9E=E6=AE=B5=E8=90=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../dal/dataobject/chat/AiChatMessageDO.java | 18 ++++++-
.../chat/AiChatMessageServiceImpl.java | 51 ++++++++++++-------
2 files changed, 48 insertions(+), 21 deletions(-)
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
index 973c593ce..ecd10609f 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
@@ -1,13 +1,18 @@
package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
-import com.baomidou.mybatisplus.annotation.TableId;
-import org.springframework.ai.chat.messages.MessageType;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
+import org.springframework.ai.chat.messages.MessageType;
+
+import java.util.List;
/**
* AI Chat 消息 DO
@@ -66,6 +71,15 @@ public class AiChatMessageDO extends BaseDO {
*/
private Long roleId;
+
+ /**
+ * 段落编号数组
+ *
+ * 关联 {@link AiKnowledgeSegmentDO#getId()} 字段
+ */
+ @TableField(typeHandler = JacksonTypeHandler.class)
+ private List segmentIds;
+
/**
* 模型标志
*/
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
index 4ef5af8ee..1247ce12d 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
@@ -90,13 +90,16 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
- // 3.2 创建 chat 需要的 Prompt
- Prompt prompt = buildPrompt(conversation, historyMessages, model, sendReqVO);
+ // 3.2 召回段落
+ List segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId());
+
+ // 3.3 创建 chat 需要的 Prompt
+ Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO);
ChatResponse chatResponse = chatModel.call(prompt);
- // 3.3 段式返回
+ // 3.4 段式返回
String newContent = chatResponse.getResult().getOutput().getContent();
- chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(newContent));
+ chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId)).setContent(newContent));
return new AiChatMessageSendRespVO().setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
.setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class).setContent(newContent));
}
@@ -121,11 +124,15 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext());
- // 3.2 构建 Prompt,并进行调用
- Prompt prompt = buildPrompt(conversation, historyMessages, model, sendReqVO);
+
+ // 3.2 召回段落
+ List segmentList = recallSegment(sendReqVO.getContent(), conversation.getKnowledgeId());
+
+ // 3.3 构建 Prompt,并进行调用
+ Prompt prompt = buildPrompt(conversation, historyMessages, segmentList, model, sendReqVO);
Flux streamResponse = chatModel.stream(prompt);
- // 3.3 流式返回
+ // 3.4 流式返回
// TODO 注意:Schedulers.immediate() 目的是,避免默认 Schedulers.parallel() 并发消费 chunk 导致 SSE 响应前端会乱序问题
StringBuffer contentBuffer = new StringBuffer();
return streamResponse.map(chunk -> {
@@ -138,7 +145,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
}).doOnComplete(() -> {
// 忽略租户,因为 Flux 异步无法透传租户
TenantUtils.executeIgnore(() ->
- chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())));
+ chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setSegmentIds(convertList(segmentList, AiKnowledgeSegmentDO::getId))
+ .setContent(contentBuffer.toString())));
}).doOnError(throwable -> {
log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
// 忽略租户,因为 Flux 异步无法透传租户
@@ -147,21 +155,26 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
}).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
}
- private Prompt buildPrompt(AiChatConversationDO conversation, List messages,
+ private List recallSegment(String content, Long knowledgeId) {
+ List segmentList = new ArrayList<>();
+ if (Objects.nonNull(knowledgeId)) {
+ segmentList = knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content));
+ }
+ return segmentList;
+ }
+
+ private Prompt buildPrompt(AiChatConversationDO conversation, List messages,List segmentList,
AiChatModelDO model, AiChatMessageSendReqVO sendReqVO) {
// 1. 构建 Prompt Message 列表
List chatMessages = new ArrayList<>();
- // 1.1 知识库召回
- if (Objects.nonNull(conversation.getKnowledgeId())) {
- List segmentList = knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(conversation.getKnowledgeId()).setContent(sendReqVO.getContent()));
- if (CollUtil.isNotEmpty(segmentList)) {
- PromptTemplate promptTemplate = new PromptTemplate(AiChatRoleEnum.AI_KNOWLEDGE_ROLE.getSystemMessage());
- StringBuilder infoBuilder = StrUtil.builder();
- segmentList.forEach(segment -> infoBuilder.append(System.lineSeparator()).append(segment.getContent()));
- Message message = promptTemplate.createMessage(Map.of("info", infoBuilder.toString()));
- chatMessages.add(message);
- }
+ // 1.1 召回内容消息构建
+ if (CollUtil.isNotEmpty(segmentList)) {
+ PromptTemplate promptTemplate = new PromptTemplate(AiChatRoleEnum.AI_KNOWLEDGE_ROLE.getSystemMessage());
+ StringBuilder infoBuilder = StrUtil.builder();
+ segmentList.forEach(segment -> infoBuilder.append(System.lineSeparator()).append(segment.getContent()));
+ Message message = promptTemplate.createMessage(Map.of("info", infoBuilder.toString()));
+ chatMessages.add(message);
}
// 1.2 system context 角色设定
From 1de70eeeb30868204e920ddf286c5d3357823264 Mon Sep 17 00:00:00 2001
From: xiaoxin <718949661@qq.com>
Date: Sun, 29 Sep 2024 10:01:49 +0800
Subject: [PATCH 10/10] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91AI?=
=?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93=EF=BC=9AvisibilityPermissions=20?=
=?UTF-8?q?=E4=BD=BF=E7=94=A8=20list?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java | 6 ++++--
.../knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java | 4 +++-
.../module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java | 7 ++++++-
.../module/ai/service/chat/AiChatMessageServiceImpl.java | 7 +++----
4 files changed, 16 insertions(+), 8 deletions(-)
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java
index bae20d936..00843665c 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateReqVO.java
@@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import java.util.List;
+
@Schema(description = "管理后台 - AI 知识库创建 Request VO")
@Data
public class AiKnowledgeCreateReqVO {
@@ -16,8 +18,8 @@ public class AiKnowledgeCreateReqVO {
@Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档")
private String description;
- @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
- private String visibilityPermissions;
+ @Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
+ private List visibilityPermissions;
@Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "嵌入模型不能为空")
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java
index 91925f53a..ba98bf0c7 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeUpdateReqVO.java
@@ -5,6 +5,8 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
+import java.util.List;
+
@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO")
@Data
public class AiKnowledgeUpdateReqVO {
@@ -21,7 +23,7 @@ public class AiKnowledgeUpdateReqVO {
private String description;
@Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
- private String visibilityPermissions;
+ private List visibilityPermissions;
@Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "嵌入模型不能为空")
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
index 5ad2dd05c..b1706154a 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
@@ -2,10 +2,14 @@ package cn.iocoder.yudao.module.ai.dal.dataobject.knowledge;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
+import java.util.List;
+
/**
* AI 知识库 DO
*
@@ -40,7 +44,8 @@ public class AiKnowledgeDO extends BaseDO {
*
* -1 所有人可见,其他为各自用户编号
*/
- private String visibilityPermissions;
+ @TableField(typeHandler = LongListTypeHandler.class)
+ private List visibilityPermissions;
/**
* 嵌入模型编号
*/
diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
index 1247ce12d..d332fbf1a 100644
--- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
+++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
@@ -156,11 +156,10 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
}
private List recallSegment(String content, Long knowledgeId) {
- List segmentList = new ArrayList<>();
- if (Objects.nonNull(knowledgeId)) {
- segmentList = knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content));
+ if (Objects.isNull(knowledgeId)) {
+ return Collections.emptyList();
}
- return segmentList;
+ return knowledgeSegmentService.similaritySearch(new AiKnowledgeSegmentSearchReqVO().setKnowledgeId(knowledgeId).setContent(content));
}
private Prompt buildPrompt(AiChatConversationDO conversation, List messages,List segmentList,