之前做个几个大模型的应用,都是使用Python语言,后来有一个项目使用了Java,并使用了Spring AI框架。随着Spring AI不断地完善,最近它发布了1.0正式版,意味着它已经能很好的作为企业级生产环境的使用。对于Java开发者来说真是一个福音,其功能已经能满足基于大模型开发企业级应用。借着这次机会,给大家分享一下Spring AI框架。
注意:由于框架不同版本改造会有些使用的不同,因此本次系列中使用基本框架是 Spring AI-1.0.0,JDK版本使用的是19。
代码参考: https://github.com/forever1986/springai-study
目录
通过上一章对RAG的入门进行讲解,相信你对于RAG的场景其实已经比较熟悉,同时也了解到Spring AI对RAG有比较好的封装,这一章来讲一下Spring AI的RAG的实现底层原理
1 Spring AI的RAG实现
Spring AI 通过提供模块化架构来支持 RAG 技术,使用户能够自行构建自定义的 RAG 流程,或者使用内置的 RAG 流程通过 Advisor API 进行操作。其底层都是基于Advisors接口(注意:关于Advisors后续会讲一下这方面的原理,这里先知道RAG是基于Advisors实现即可)
而基于Advisors接口上,实现了2个用于RAG的类,分别是QuestionAnswerAdvisor和RetrievalAugmentationAdvisor。
1.1 QuestionAnswerAdvisor源码分析
1)QuestionAnswerAdvisor类实现了BaseAdvisor接口,该BaseAdvisor接口主要有2个方法before和after,这个两个方法分别是调用Advisor的核心方法之前和之后执行的方法
2)QuestionAnswerAdvisor是相对简单的RAG,其主要实现都在before方法,如下图:
3)SearchRequest是一个封装的查询
说明:有几个参数需要关注
1)query:查询的问题
2)topK:返回前几个文档
3)similarityThreshold:相似度,大于等于这个值之上的相似度才会匹配
4)filterExpression:元数据的过滤条件,主要是过滤Document的metadata元数据属性
1.2 RetrievalAugmentationAdvisor
QuestionAnswerAdvisor对于RAG来说还是相对比较简单。如果看过本人之前写过《检索增强生成RAG系列》的文章,可以知道要做好一个RAG,需要做的内容还是比较多。一个好的RAG对于每一步都要做一些优化,比如问题优化、重排等功能。而RetrievalAugmentationAdvisor则比较好的将这些优化做个结构化步骤,关于Spring AI中的RetrievalAugmentationAdvisor内容,下章再讲。这一章先了解QuestionAnswerAdvisor,下面将演示QuestionAnswerAdvisor的一些常见用法和配置。
2 QuestionAnswerAdvisor示例演示
代码参考lesson11子模块下面的simple-advisor子模块
示例说明:通过设置查询条件,使用QuestionAnswerAdvisor实现文档存入,查询过滤,调用大模型回答用户问题
1)在lesson11子模块下,新建simple-advisor子模块,其pom引入如下:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-zhipuai</artifactId>
</dependency>
<!-- 引入向量数据库插件 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>
</dependencies>
2)新建配置application.properties文件
# 聊天模型
spring.ai.zhipuai.api-key=你的智谱的API KEY
spring.ai.zhipuai.chat.options.model=GLM-4-Flash-250414
spring.ai.zhipuai.chat.options.temperature=0.7
3)创建向量数据库的配置VectorStoreConfig:
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class VectorStoreConfig {
/**
* @param embeddingModel - 这里如果没有特殊配置,默认使用的是你引入model的embedding模型
*/
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel){
SimpleVectorStore simpleVectorStore = SimpleVectorStore.builder(embeddingModel).build();
simpleVectorStore.add(getDocuments());
return simpleVectorStore;
}
private static List<Document> getDocuments(){
Document document1 = new Document("""
ChatGLM3 是北京智谱华章科技有限公司和清华大学 KEG 实验室联合发布的对话预训练模型。ChatGLM3-6B 是 ChatGLM3 系列中的开源模型,在保留了前两代模型对话流畅、部署门槛低等众多优秀特性的基础上,ChatGLM3-6B 引入了更多的特性。\n
北京智谱华章科技有限公司是一家来自中国的公司,致力于打造新一代认知智能大模型,专注于做大模型创新。
""");
document1.getMetadata().put("source", "官方网站");
Document document2 = new Document("""
OpenAI,是一家开放人工智能研究和部署公司,其使命是确保通用人工智能造福全人类。创立于2015年12月,总部位于美国旧金山。现由营利性公司OpenAI LP及非营利性母公司OpenAI Inc组成。
""");
document2.getMetadata().put("source", "官方网站");
Document document3 = new Document("""
ChatGLM3是由智谱清言开发的,是北京智谱华章科技有限公司推出的生成式AI助手,于2023年8月31日正式上线。 2024年8月29日,智谱清言APP支持视频通话功能。
智谱清言基于智谱AI自主研发的中英双语对话模型还开发了ChatGLM、ChatGLM2、ChatGLM3、ChatGLM4等大模型。
""");
document3.getMetadata().put("source", "官方网站");
Document document4 = new Document("""
ChatGLM3 是由清华大学技术成果转化企业智谱AI研发的支持中英双语的对话机器人,基于千亿参数基座模型GLM架构开发。该模型通过多阶段训练流程形成通用对话能力,具备问答交互、代码生成、创意写作等功能,其开源版本ChatGLM-6B自2023年3月启动内测以来已形成广泛影响力。\n
截至2024年3月,智谱AI通过该技术实现了2000多家生态合作伙伴的应用落地,并在多模态技术上持续突破,推出了视频生成等创新功能。清华大学是中国排名top2的学校。
""");
document4.getMetadata().put("source", "百度百科");
List<Document> list = new ArrayList<>();
list.add(document1);
list.add(document2);
list.add(document3);
list.add(document4);
return list;
}
}
4)创建演示类RAGController :
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RAGController {
private ChatClient chatClient;
public RAGController(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) {
PromptTemplate promptTemplate = PromptTemplate.builder()
.template("""
{query}
参考信息如下,使用“---------------------”标识包裹在里面的为参考信息。
---------------------
{question_answer_context}
---------------------
鉴于当前的参考信息以及所提供的历史信息(而非任何先入为主的了解),请回复用户评论。如果答案不在上述背景信息中,告知用户您无法回答该问题。
""")
.build();
var b = new FilterExpressionBuilder().eq("source", "官方网站").build();
QuestionAnswerAdvisor questionAnswerAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
.promptTemplate(promptTemplate)
.searchRequest(SearchRequest.builder()
.similarityThreshold(0.2) //相似度阈值,只有大于等于该值才会被返回(取值范围:0-1),默认是0(没有相似度排除)
.topK(2) // 返回相似度排名前2的文档,默认是4
.filterExpression(b) // 通过文档的元数据过滤
.build())
.build();
this.chatClient = chatClientBuilder
// 通过Advisors方式,对向量数据库进行封装
.defaultAdvisors(questionAnswerAdvisor)
.build();
}
/**
* @param message 问题
*/
@GetMapping("/ai/rag")
public String rag(@RequestParam(value = "message", required = true, defaultValue = "ChatGLM3是哪个国家的大模型?") String message) {
return this.chatClient.prompt()
.user(message)
.call()
.content();
}
}
5)新建启动类Lesson11SimpleApplication :
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Lesson11SimpleApplication {
public static void main(String[] args) {
SpringApplication.run(Lesson11SimpleApplication.class, args);
}
}
6)演示结果:
http://localhost:8080/ai/rag
说明:这个示例主要是通过对QuestionAnswerAdvisor的一些设置进行验证
1)similarityThreshold设置0.2,这样对于一些极其不相关文档排除掉
2)topK设置2,只返回2份文档(如果不配置,这里会返回3份文档)
3)filterExpression设置过滤元数据,source=官方网站(如果这里不配置,会匹配到第4份文档)
7)通过debug查看,这里可以通过debug一下QuestionAnswerAdvisor的122行,可以看到如下只返回2份文档
结语:本章讲解的简单版RAG的实现QuestionAnswerAdvisor,并对其内部原理进行解析。但是对于一个真正的RAG来说,QuestionAnswerAdvisor还是远远没法满足条件。下一章将讲解RetrievalAugmentationAdvisor。