Spring AI实现基于知识库对话(实战篇)
AI实现基于知识库对话
·
介绍:之前博客说过通过Spring AI搭建知识库,但复杂度较高,融合了企业需求,不少人半途而废,此次Demo版
需要具备初级Java能力,即可通过~
此篇需要用到milvus、minio,如果还不会的同学们,可以参考上篇博客
项目环境
jdk版本:17
spring-ai版本:1.0.0
spring-ai-alibaba版本:1.0.0.2
spring-boot版本:3.4.5
步骤一:引入依赖
依赖管理
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.4.5</spring-boot.version>
<spring-ai.version>1.0.0</spring-ai.version>
<spring-ai-alibaba.version>1.0.0.2</spring-ai-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
具体依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-milvus</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
注:不能使用依赖spring-ai-milvus-store,否则需要手动注入!
步骤二:配置信息
application.yml
server:
port: 8093
spring:
application:
name: knowledge-demo
ai:
dashscope:
api-key: 个人阿里云百炼账号
chat:
options:
model: qwen-plus // 聊天模型
temperature: 0.8
embedding:
options:
model: text-embedding-v2 // 向量模型
enabled: true
rerank:
options:
model: gte-rerank-v2 // 检索模型
top-n: 3
vectorstore:
milvus:
client:
host: localhost
port: 19530
username: 账号
password: 密码
initialize-schema: true
minio:
endpoint: http://localhost:9000
bucketName: test
accessKey: 账号
secretKey: 密码
chunk-size: 1048576
max-connections: 6
此次选择阿里云百炼模型,引入依赖、配置即可生效
排序模型
向量模型
聊天模型
需要配置Milvus、minio信息,如何安装,可参考以下博客
步骤三:核心代码
[1]ChatClientConfig
@Configuration
@Slf4j
public class ChatClientConfig {
// 系统提示词
private static final String SYSTEM_PROMPT = """
你是一个专业的知识库智能助手,基于RAG(检索增强生成)技术构建.你的主要职责是:
- 根据用户问题,从提供的知识库中检索最相关信息;
- 基于检索到的内容,生成准确、专业、有帮助的回答;
- 回答时保持专业、友好且易于理解的风格;
请遵循以下原则:
- 严格基于知识库内容回答,不编造不存在的信息;
- 如果知识库中没有相关信息,明确告知用户"根据现有资料,我无法回答这个问题";
- 回答要结构清晰,重要信息优先展示;
- 对于复杂问题,可以分步骤或分点解答;
- 保持中立客观,不表达个人观点或情感;
""";
/**
* rag助手配置
*
* @param chatClientBuilder
* @return
*/
@Bean(value = "ragAssist")
public ChatClient getChatClient(ChatClient.Builder chatClientBuilder) {
log.info("getChatClient()...");
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
.maxMessages(10)
.build();
ChatClient chatClient = chatClientBuilder
.defaultSystem(SYSTEM_PROMPT)
.defaultAdvisors(
PromptChatMemoryAdvisor.builder(chatMemory).build()
)
.build();
log.info("chatClient success...");
return chatClient;
}
}
[2]RetrievalRerankAdvisorConfig
@Configuration
@RequiredArgsConstructor
@Slf4j
public class RetrievalRerankAdvisorConfig {
private final MilvusVectorStore vectorStore;
private final RerankModel rerankModel;
// 用户提示词模板
private final static String USER_PROMPT_RERANK = """
以下是知识库检索信息:
--------------------
{question_answer_context}
--------------------
请基于上述信息,结合历史对话内容回答用户问题.回答应:
- 内容完整且紧扣问题核心;
- 避免直接摘抄原文,需语义转化;
- 语言简洁专业,逻辑清晰连贯;
- 若信息不足请礼貌告知用户;
回答建议: 先直接回应问题关键,再展开必要解释,确保用户获得准确且易于理解的信息.
""";
@Bean
public Advisor retrievalRerankAdvisor() {
// 配置搜索参数
SearchRequest searchRequest = SearchRequest.builder()
.similarityThreshold(0.2)
.topK(7)
.build();
PromptTemplate promptTemplate = new PromptTemplate(USER_PROMPT_RERANK);
return new RetrievalRerankAdvisor(
vectorStore,
rerankModel,
searchRequest,
promptTemplate,
0.6
);
}
}
[3]RagAdvisorFactory
public class RagAdvisorFactory {
/**
* 自定义的rag检索增强
*
* @param vectorStore
* @param status
* @return
*/
public static Advisor createRagCustomAdvisor(VectorStore vectorStore, String status) {
// 创建文档检索器
DocumentRetriever documentRetriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
// 相似度阈值
.similarityThreshold(0.3)
// 返回文档数量
.topK(5)
.build();
return RetrievalAugmentationAdvisor.builder()
.documentRetriever(documentRetriever)
.queryAugmenter(ContextQueryAugumentFactory.createInstance())
.build();
}
}
步骤四:接口编写
[1]RagController
@RestController
@RequestMapping("/rag")
@RequiredArgsConstructor
@Slf4j
public class RagController {
private final RagService ragService;
private final TokenTextSplitter tokenTextSplitter;
private final VectorStore vectorStore;
/**
* 基于知识库对话
*
* @param message
* @param chatId
* @return
*/
@GetMapping(value = "/chatWithRag", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatWithRag(@RequestParam(value = "message") String message, @RequestParam(value = "chatId") String chatId) {
return ragService.chatWithRag(message, chatId);
}
/**
* 入向量库
*
* @return
*/
@GetMapping(value = "/saveData")
public String saveData() {
// ELT三步走:提取(读取)、转换(分隔)和加载(写入)
// 步骤一:读取
DocumentReader reader = new TextReader("/static/personalInfo.txt");
List<Document> documents = reader.read();
// 步骤二:转换
List<Document> splitCustomized = tokenTextSplitter.splitCustomized(documents);
log.info("切片大小: {}", splitCustomized.size());
// 步骤三:加载
vectorStore.add(splitCustomized);
return "success";
}
}
[2]RagService
public interface RagService {
/**
* 基于知识库对话
*
* @param message
* @param chatId
* @return
*/
public Flux<String> chatWithRag(String message, String chatId);
}
[3]RagServiceImpl
@Service
@Slf4j
public class RagServiceImpl implements RagService {
@Resource(name = "ragAssist")
private ChatClient chatClient;
@Resource
private VectorStore vectorStore;
@Resource
private Advisor retrievalRerankAdvisor;
/**
* 基于知识库对话
*
* @param message
* @param chatId
* @return
*/
@Override
public Flux<String> chatWithRag(String message, String chatId) {
String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Flux<String> content = chatClient
.prompt()
.user(message)
.system(s -> s.param("current_date", currentTime))
.advisors(a -> a.param(CONVERSATION_ID, chatId))
.advisors(
RagAdvisorFactory.createRagCustomAdvisor(
vectorStore, ""
)
)
.advisors(retrievalRerankAdvisor)
.stream()
.content();
return content;
}
}
步骤五:测试
在resources目录下,新建static目录并创建personalinfo.txt
1.小研身份介绍
性别:男
岗位:Java+AI领域工程师
职责:带领热爱学习的伙伴们,跟着小研学AI、Java,月薪过万不是梦
启动成功
接口测试
验证
对话测试
至此,基于知识库对话Demo版本结束啦,需要资料可以加入技术群获取!
本人正在打造技术交流群,欢迎志同道合的朋友一起探讨,一起努力,通过自己的努力,在技术岗位这条道路上走得更远。QQ群号:925317809 备注:技术交流 即可通过!
加入技术群可以获取资料,含AI资料、Spring AI中文文档等,等你加入~
更多推荐
所有评论(0)