一、RAG知识库基础:
什么是RAG呢?
RAG(Retrieval-Augmented Generation):是检索增强生成,是一种结合信息检索技术和AI内容生成的混合架构,可以解决大模型的知识时效性限制和幻觉问题。
Spring AI框架实现RAG的两种方式:
1、Spring AI+本地知识库
2、Spring AI+云知识库服务
RAG工作流程(核心重要):
主要包含以下4个步骤:
1、文档收集和切割
2、向量转换和存储
3、文档过滤和检索
4、查询增强和关联
- 文档收集和切割:
文档收集:从各种来源收集原始文档(网页、PDF、数据库)
文档预处理:清洗、标准化文本格式
文档切割:将长文档分割成适当大小的片段(chunks)—-基于固定大小、语义边界、递归分割策略,来分割
- 向量转换和存储:
向量转换:通过Embedding嵌入模型将文本块转换为高维向量表示,可以捕获到文本的语义特征
向量存储:将生成的向量和对应的文本存储到向量数据库,支持高效的相似性搜索
- 文档过滤和检索:
查询处理:将用户的问题也转换为向量表示
过滤机制:基于元数据、关键词、自定义规则进行过滤
相似度搜索:在向量数据库中查找与问题向量最相似的文档块,常用的相似度搜索算法有余弦相似度、欧式距离等
上下文组装:将检索到的多个文档块组装成连贯的上下文,通过Rank模型进行精排然后TopK最相关的文档切片
- 查询增强和关联:
提示词组装:将检索到的相关文档和用户问题组合成增强提示
上下文融合:大模型基于增强提示生成回答
源引用:在回答中添加信息来源引用
后处理:格式化、摘要、其他处理来优化最终输出
- Embedding模型:嵌入模型(Embedding Model)是一种将复杂的非结构化数据(如文本、图像、音频等)转换为机器可处理的低维向量表示的技术。通过这种方式,嵌入模型能够捕捉数据的语义信息,使得相似的数据在向量空间中具有相近的位置。在这个空间中,向量的距离表示相似度,方向则反映语义关系,例如,“猫”和“狗”的嵌入向量在空间中距离较近,表示它们语义相似。
- 向量数据库:并不是只有向量数据库才能存储向量数据,只不过和传统数据库不同,向量数据库优化了高维向量的存储和检索,向量数据库如Milvus、Pinecone,一些传统数据库也可以通过安装插件实现向量存储和检索,如PGVector、Redis Stack的RediSearch等。
- 粗排、精排、Dify支持混合检索
RAG实战:
- Spring AI+本地知识库:1、文档准备 2、文档读取 3、向量转换和存储 4、查询增强
- 文档准备可以利用AI生成,提示词如下:帮我生成 3 篇 Markdown 文章,主题是【旅游常见问题和回答】,3 篇文章的问题分别针对旅游前、旅游中、旅游后,内容形式为 1 问 1 答,每个问题标题使用 4 级标题,每篇内容需要有至少 5 个问题,要求每个问题中推荐一个相关的课程,附带课程链接
- 在resources目录下新建一个document目录,将准备的文档粘贴进去
- 读取文档:对准备好的知识库文档进行处理,然后保存到向量数据库里。这个过程俗称ETL(抽取、转换、加载),Spring AI提供了对ETL的支持,参考Spring AI的官方文档介绍
- ETL 管道有三个主要组件,按照顺序读取:
1、DocumentReader实现Supplier<List<Document>>读取文档,得到文档列表
2、DocumentTransformer实现Function<List<Document>, List<Document>>转换文档,得到处理后的转换文档
3、DocumentWriter实现Consumer<List<Document>>将文档列表保存到存储中
- Spring AI提供了很多种DocumentReaders,用于加载不同类型的文件,我们这边使用MarkdownDocumentReader来读取我们准备的文档。
首先引入依赖(官方没提,可以在Maven中央仓库找到)
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-markdown-document-reader</artifactId>
<version>1.0.0-M6</version>
</dependency>
在dogaiagent包下新建一个rag包,在包下建一个TravelAppDocumentLoader类,编写以下代码:
@Component
@Slf4j
class TravelAppDocumentLoader {
private final ResourcePatternResolver resourcePatternResolver; TravelAppDocumentLoader(ResourcePatternResolver resourcePatternResolver) { this.resourcePatternResolver = resourcePatternResolver;
}
public List<Document> loadMarkdowns() {
List<Document> allDocuments = new ArrayList<>();
try {
// 这里可以修改为你要加载的多个 Markdown 文件的路径模式
Resource[] resources =
resourcePatternResolver.getResources("classpath:document/*.md");
for (Resource resource : resources) {
String fileName = resource.getFilename();
MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder() .withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.withAdditionalMetadata("filename", fileName)
.build();
MarkdownDocumentReader reader = new MarkdownDocumentReader(resource, config);
allDocuments.addAll(reader.get());
}
} catch (IOException e) {
log.error("Markdown 文档加载失败", e);
}
return allDocuments;
}
}
生成单元测试进行Debug测试一下:
@SpringBootTest
class TravelAppDocumentLoaderTest {
@Resource
private TravelAppDocumentLoader travelAppDocumentLoader;
@Test
void loadMarkdowns() {
travelAppDocumentLoader.loadMarkdowns();
}
}
注意:如果下面这行代码报错,说明你引入的Document类引错了,正确的应该引的是Spring AI的类
allDocuments.addAll(reader.get());
向量转换和存储:将document写入向量数据库,在rag包下新建一个TravelAppVectorStoreConfig类,编写以下代码:
@Configuration
public class TravelAppVectorStoreConfig {
@Resource
private TravelAppDocumentLoader travelAppDocumentLoader;
@Bean
//注意这里的EmbeddingModel导入的是Spring AI的
VectorStore travelAppVectorStore(EmbeddingModel dashscopeEmbeddingModel){
SimpleVectorStore simpleVectorStore =
SimpleVectorStore.builder(dashscopeEmbeddingModel).build();
List<Document> documentList = travelAppDocumentLoader.loadMarkdowns();
simpleVectorStore.add(documentList);
return simpleVectorStore;
}
}
- 查询增强:Spring AI通过Advisor特性提供了开箱即用的RAG功能,主要是QuestionAnswerAdvisor问答拦截器和RetrievalAugmentationAdvisor检索增强拦截器,前者简单易用,后者灵活强大。
根据Spring AI官方文档引入依赖,然后在TravelApp编写以下代码:
//知识库问答功能
@Resource
private VectorStore travelAppVectorStore;
/**
* RAG知识库进行对话
* @param message
* @param chatId
* @return
*/
public String doChatWithRag(String message,String chatId){
ChatResponse chatResponse = chatClient
.prompt()
.user(message)
.advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY,10))
//开启日志顾问,观察效果
.advisors(new MyLoggerAdvisor())
//应用RAG知识库进行问答
.advisors(new QuestionAnswerAdvisor(travelAppVectorStore))
.call()
.chatResponse();
String content = chatResponse.getResult().getOutput().getText();
log.info("content: {}",content);
return content;
}
接着生成单元测试进行测试一下,这边的message需要根据前面写的markdown文档特定设计一下,测试是否会根据向量知识库回答,测试代码如下:
@Test
void doChatWithRag() {
String chatId = UUID.randomUUID().toString();
String message ="我的预算不多,只有500块钱怎么规划才能穷游3天";
String answer = travelApp.doChatWithRag(message, chatId);
Assertions.assertNotNull(answer);
}
- Spring AI+云知识库服务:1、准备云知识库 2、RAG开发
- 准备云知识库:用别人的知识库平台相比于我们本地的知识库,能帮我们拆分切片,在阿里云百炼的知识库中创建知识库,注意在创建知识库时里面有一个metadata抽取一定要打开,它其实就相当于标签,打上标签后搜索会更准确,比如,向量数据库的两个向量一样但是文本内容不一样,如果没打上关键字的标签,会把两个文本内容都检索到,而不是我们要的那个。然后呢,要在创建知识库时就打开,因为创建完后就开不了了好吧。
- RAG开发:可以根据Spring AI Alibaba文档的Document Retriever有相关介绍和步骤,接下来让我们来实现吧!
在rag包下新建TravelAppRagCloudAdvisorConfig类,编写以下代码:
/**
* 自定义基于阿里云知识库服务的 RAG 增强顾问
*/
@Configuration
@Slf4j
public class TravelAppRagCloudAdvisorConfig {
@Value("${spring.ai.dashscope.api-key}")
private String dashScopeApiKey;
@Bean
public Advisor loveAppRagCloudAdvisor() {
DashScopeApi dashScopeApi = new DashScopeApi(dashScopeApiKey);
final String KNOWLEDGE_INDEX = "AI旅游规划大师";
DocumentRetriever dashScopeDocumentRetriever = new DashScopeDocumentRetriever(dashScopeApi,
DashScopeDocumentRetrieverOptions.builder()
.withIndexName(KNOWLEDGE_INDEX)
.build());
return RetrievalAugmentationAdvisor.builder()
.documentRetriever(dashScopeDocumentRetriever)
.build();
}
}
注意:这边的@Value注解是用的字段级别注解,不是lombok的类级别注解
然后在TravelApp里调用Bean管理的travelAppRagCloudAdvisor,补充后的代码如下:
@Resource
private Advisor travelAppRagCloudAdvisor;
public String doChatWithRag(String message,String chatId){
ChatResponse chatResponse = chatClient
.prompt()
.user(message)
.advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY,10))
//开启日志顾问,观察效果
.advisors(new MyLoggerAdvisor())
//本地RAG和云知识库开启一个就好了
//应用本地RAG知识库进行问答
.advisors(new QuestionAnswerAdvisor(travelAppVectorStore))
//应用RAG检索增强服务(基于阿里云知识库服务进行问答)
.advisors(travelAppRagCloudAdvisor)
.call()
.chatResponse();
String content = chatResponse.getResult().getOutput().getText();
log.info("content: {}",content);
return content;
}
最后Debug测试运行一下
(重点)总结RAG整个工作流程:首先建立原始文档,收集处理切割,然后进行向量转换存到向量数据库中,用户提问时候,将用户的问题通过Embedding模型转换成向量,再通过一些相似度搜索或条件搜索从向量数据库中把相关的文档检索出来,然后通过Rank模型等进行一个排序,取出最相关的知识点,最后把相关的知识点和用户的问题结合在一起,变成一个提示词再输入给AI大模型,得到最终的回答。
二、RAG知识库进阶:
- RAG核心特性详解:
1、文档收集和切割:整个过程俗称,ETL(抽取、转换、加载)
- ETL流程:通过Document Reader文档读取器读取源文件,再通过Document Transformer文档转换器根据需求转换成适合后续处理的格式,最后通过Document Writer文档写入器来写入向量数据库等存储
- Spring AI的文档是个Document对象,能存额外的信息
- Tika是Apache的一个多格式转换库,可以把各种文档进行读取加载,更灵活,Spring AI集成了它,引入依赖使用
- 文档读取器不用记了解即可,要用到的时候去搜就可以,另外,Spring AI Alibaba社区也是提供了很多的文档读取器
2、向量转换和存储:
- Document Transformer有多种实现类,主要分3种:1、TextSplitter文本分割器,TokenTextSplitter是它的实现类 2、MetadataEnricher元数据增强器 3、ContentFormatter内容格式化工具(官方介绍很少)
- Document Writer接口实现了Consumer
- >接口,Spring AI提供了两种内置的DocumentWriter实现:1、FileDocumentWriter将文档写入文件系统 2、VectorStoreWriter将文档写入向量数据库
-
基于PGVector实现向量存储:它是一个插件,是经典数据库PostgreSQL的扩展,为PostgreSQL提供了存储和检索高维向量数据能力。
- 准备PostgreSQL数据库,在自己本地或服务器安装,这边因为我们是学习,就不用准备数据库的这种方法了,直接使用现成的云数据库来实操。
- 首先进入阿里云PostgreSQL官网的产品搜索云数据库RDS PostgreSQL版,开通Serverless版本,选择基础版,它是按照使用量计费,对学习很友好。步骤:创建实例–>创建账号–>创建数据库–>安装vector插件–>进入数据库连接开通外网地址–>打开idea进入Database的PostgreSQL进行登录连接或者直接在云平台的登录数据库直接登录也可以,测试一下。
- 参考Spring AI官方文档引入依赖,版本号到Maven中央仓库找:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
<version>1.0.0-M7</version>
</dependency>
- yml中编写配置,建立数据库连接:
spring:
datasource:
url: jdbc:postgresql://改为你的公网地址/yu_ai_agent
username: 改为你的用户名
password: 改为你的密码
ai:
vectorstore:
pgvector:
index-type: HNSW
dimensions: 1536
distance-type: COSINE_DISTANCE
max-document-batch-size: 10000 # Optional: Maximum number of documents per batch
注意:这里的datasource:url、username、password写到application-local.yml中,不然git开源提交会暴露出去,然后呢就是这个dimensions是向量维度值,越高越精确,如果不指定会默认,建表前一定要选择好,因为建完后就不能改了
- 不过使用这种方式会报错,因为我们前面注入了ollama和dashscope两个大模型,VectorStore依赖EmbeddingModel对象,有两个EmbeddingModel的Bean,spring不知道注入哪个。所以我们不用上面那个自动整合的方法,使用下面这个更灵活的手动整合的方法来初始化VectorStore:
<!-- 手动整合向量数据库-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store</artifactId>
<version>1.0.0-M6</version>
</dependency>
@Configuration
public class PgVectorVectorStoreConfig {
@Resource
private TravelAppDocumentLoader travelAppDocumentLoader;
@Bean
public VectorStore pgVectorVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel
dashscopeEmbeddingModel){
PgVectorStore vectorStore = PgVectorStore.builder(jdbcTemplate,
dashscopeEmbeddingModel)
.dimensions(1536) // Optional: defaults to model dimensions or 1536
.distanceType(COSINE_DISTANCE) // Optional: defaults to COSINE_DISTANCE
.indexType(HNSW) // Optional: defaults to HNSW
.initializeSchema(true) // Optional: defaults to false
.schemaName("public") // Optional: defaults to "public"
.vectorTableName("vector_store") // Optional: defaults to "vector_store"
.maxDocumentBatchSize(10000) // Optional: defaults to 10000
.build();
return vectorStore;
}
}
然后创建对应单元测试,复制粘贴官方给的代码,测试的代码如下:
@SpringBootTest
class PgVectorVectorStoreConfigTest {
@Resource
private VectorStore pgVectorVectorStore;
@Test
void pgVectorVectorStore() {
List<Document> documents = List.of(
new Document("我是小楼,很高兴认识你", Map.of("meta1", "meta1")),
new Document("旅游如何预订景点"),
new Document("有什么美食推荐", Map.of("meta2", "meta2")));
// 添加文件
pgVectorVectorStore.add(documents);
// 相似度查询
List<Document> results =
this.pgVectorVectorStore.similaritySearch(SearchRequest.builder().query("我是谁,阿巴阿巴").topK(3).build());
}
}
注意:启动时会自动加载PGVectorStore,会出现需要1个但是找到2个情况的报错,这里需要给启动类如下添加来配合使用:
@SpringBootApplication(exclude = {
// 为了便于大家开发调试和部署,取消数据库自动配置,需要使用 PgVector 时把 DataSourceAutoConfiguration.class 删除
DataSourceAutoConfiguration.class
})
public class DogAiAgentApplication {
public static void main(String[] args) {
SpringApplication.run(DogAiAgentApplication.class, args);}
}
然后,如果你要用这个PGVector向量存储来替代原本的本地VectorStore,就按以下操作调用,然后将TravelApp类里之前的基于本地和基于云知识库服务注释掉:
@Resource
private VectorStore pgVectorVectorStore;
//本地RAG和云知识库开启一个就好了
//应用本地RAG知识库进行问答
// .advisors(new QuestionAnswerAdvisor(travelAppVectorStore))
//应用RAG检索增强服务(基于阿里云知识库服务进行问答)
// .advisors(travelAppRagCloudAdvisor)
//应用RAG检索增强服务(基于PGVector向量存储)
.advisors(new QuestionAnswerAdvisor(pgVectorVectorStore))
然后在PgVectorVectorStoreConfig类中加上两行加载文档的代码即可:
//载入文档
List<Document> documents =travelAppDocumentLoader.loadMarkdowns();
vectorStore.add(documents);
return vectorStore;
这里由于没有花钱去开通PGVector,给大家演示一下,项目里我都注释掉了就不用了,就继续用之前的云知识库服务了。
(了解)批处理策略:Spring AI实现了批处理策略(Batching Strategy),将大量文档分解为较小的批次,使其适合嵌入模型的最大上下文窗口,还可以提高性能更有效的利用API速率限制。
3、文档过滤和检索:Spring AI官方提供了一个“模块化”的RAG架构,就是把整个文档过滤检索拆为:检索前、检索时、检索后,然后针对每个阶段提供了可自定义的组件。
- 预检索:优化用户查询:
查询转换-查询重写、查询转换-查询翻译、查询转换-查询压缩
查询扩展-多查询扩展
- 检索:提高查询相关性:
查询搜索、文档合并
- 检索后:文档处理优化
4、查询增强和关联:QuestionAnswerAdvisor和RetrievalAugmentationAdvisor
三、RAG最佳实践和调优技巧:
1、文档收集和切割:
1、优化原始文档:内容结构化、内容规范化、格式标准化
2、文档切片:结合智能分块算法和人工二次校验
让我们的来试试自定义基于token的切词器,Spring AI的RAG的ETL Pipeline内提供了,在rag包下新建MyTokenTextSplitter类,编写以下代码:
/**
* 自定义基于token的切词器
*/
@Component
class MyTokenTextSplitter {
public List<Document> splitDocuments(List<Document> documents) {
TokenTextSplitter splitter = new TokenTextSplitter();
return splitter.apply(documents);
}
public List<Document> splitCustomized(List<Document> documents) {
TokenTextSplitter splitter = new TokenTextSplitter(200, 100, 10, 5000, true);
return splitter.apply(documents);
}
}
然后在TravelAppVectorStoreConfig类里调用加上这个切词器,最终代码如下:
@Resource
private MyTokenTextSplitter myTokenTextSplitter;
@Bean
VectorStore travelAppVectorStore(EmbeddingModel dashscopeEmbeddingModel){
SimpleVectorStore simpleVectorStore =
SimpleVectorStore.builder(dashscopeEmbeddingModel).build();
//加载文档
List<Document> documentList = travelAppDocumentLoader.loadMarkdowns();
//自主切分文档
List<Document> splitDocuments = myTokenTextSplitter.splitCustomized(documentList);
simpleVectorStore.add(splitDocuments);
return simpleVectorStore;
}
当然了,这种切词器不好,切出来的一整段会被分开,不完整。如果使用云服务推荐在创建知识库时使用阿里云百炼的智能切分。
3、元数据标注:1、在文档手动添加元信息 2、利用DocumentReader批量添加元信息 3、自动添加元信息
利用DocumentReader批量添加元信息:在TravelAppDocumentLoader类中添加状态标签即可:
//提取文档状态作为标签
String status = filename.substring(filename.length() - 6, filename.length() - 3);
MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
.withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.withAdditionalMetadata("filename", filename)
.withAdditionalMetadata("status", status)
4、自动添加元信息:Spring AI提供了生成元信息的Transformer组件,可以基于AI自动解析关键词并添加到元信息中,在rag包下新建MyKeywordEnricher类,编写以下代码:
@Component
public class MyKeywordEnricher {
@Resource
private ChatModel dashscopeChatModel;
List<Document> enrichDocuments(List<Document> documents){
KeywordMetadataEnricher enricher = new
KeywordMetadataEnricher(this.dashscopeChatModel,5);
return enricher.apply(documents);
}
}
然后在TravelAppVectorStoreConfig类里使用它,将前面的token自动切分文档注释掉,最终代码如下:
@Bean
VectorStore travelAppVectorStore(EmbeddingModeldashscopeEmbeddingModel){
SimpleVectorStore simpleVectorStore =
SimpleVectorStore.builder(dashscopeEmbeddingModel).build();
//加载文档
List<Document> documentList = travelAppDocumentLoader.loadMarkdowns();
//自主切分文档//
List<Document> splitDocuments = myTokenTextSplitter.splitCustomized(documentList);
//自动补充关键词元信息
List<Document> enrichedDocuments = myKeywordEnricher.enrichDocuments(documentList);
simpleVectorStore.add(enrichedDocuments);
return simpleVectorStore;
}
最后Debug测试一下
2、向量转换和存储:
1、向量存储配置
2、选择合适的嵌入模型
3、文档过滤和检索:
1、多查询扩展
演示多查询扩展,在demo包下新建一个rag包,再新建一个MultiQueryExpanderDemo类,编写以下代码:
@Component
public class MultiQueryExpanderDemo {
private final ChatClient.Builder chatClientBuilder;
public MultiQueryExpanderDemo(ChatModel dashscopeChatModel) {
this.chatClientBuilder = ChatClient.builder(dashscopeChatModel);
}
public List<Query> expand(String query) {
MultiQueryExpander queryExpander = MultiQueryExpander.builder()
.chatClientBuilder(chatClientBuilder)
.numberOfQueries(3)
.build();
List<Query> queries = queryExpander.expand(new Query("谁是小楼?"));
return queries;
}
}
然后编写对应的单元测试,进行测试:
@SpringBootTest
class MultiQueryExpanderDemoTest {
@Resource
private MultiQueryExpanderDemo multiQueryExpanderDemo;
@Test
void expand() {
List<Query> queries = multiQueryExpanderDemo.expand("谁是小楼?搞快点回答我谢谢");
Assertions.assertNotNull(queries);
}
}
获得多查询后,可以用于检索文档、或者提取查询文本,来改写提示词,不过不建议使用多查询扩展这种优化方式,因为会增加查询次数和成本,不易于量化评估。
2、查询重写和翻译
查询重写:可参考Spring AI Alibaba官方文档实现查询重写器,在rag下新建一个QueryRewriter类,编写以下代码:
/**
* 查询重写器
*/
@Component
public class QueryRewriter {
private final QueryTransformer queryTransformer;
public QueryRewriter(ChatModel dashscopeChatModel) {
ChatClient.Builder builder = ChatClient.builder(dashscopeChatModel);
queryTransformer = RewriteQueryTransformer.builder()
.chatClientBuilder(builder)
.build();
}
public String doQueryRewriter(String prompt){
Query query = new Query(prompt);
//执行查询重写
Query transformedQuery = queryTransformer.transform(query);
//输出重写后的查询
return transformedQuery.text();
}
}
然后在TravelApp中使用调用查询重写器,最终代码如下:
@Resource
private QueryRewriter queryRewriter;
public String doChat(String message,String chatId){
//查询重写
String rewrittenMessage = queryRewriter.doQueryRewriter(message);
ChatResponse chatResponse = chatClient
.prompt()
//使用改写后的查询信息
.user(rewrittenMessage)
最后Debug测试一下
当然要是在云服务中,创建时可以勾选多轮会话改写,自动将用户的提示词转换为更完整的形式。
检索器配置:
1、在云服务平台创建时设置合理的相似度阈值
2、在云服务平台的应用管理中,控制返回文档数量(召回片段数topK),即选相似度最高的K个
3、配置文档过滤规则,根据场景不同可以为文档添加标签或提取元数据
实现配置文档过滤:在rag包下新建一个TravelAppRagCustomAdvisorFactory类,编写以下代码:
/**
* 创建自定义RAG检索增强顾问工厂
*/
public class TravelAppRagCustomAdvisorFactory {
public static Advisor createTravelAppRagCustomAdvisorFactory(VectorStore vectorStore,
String status){
//过滤特定状态的文档
Filter.Expression expression = new FilterExpressionBuilder()
.eq("status", status)
.build();
DocumentRetriever documentRetriever = VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.filterExpression(expression) // 过滤条件
.similarityThreshold(0.5) // 相似度阈值
.topK(3) // 返回文档数量
.build();
return RetrievalAugmentationAdvisor.builder()
.documentRetriever(documentRetriever)
// .queryAugmenter()
.build();
}
}
然后在TravelApp类里添加上RAG检索增强顾问工厂,代码如下:.
advisors(
TravelAppRagCustomAdvisorFactory.createTravelAppRagCustomAdvisorFactory(
travelAppVectorStore,"钱"
)
最后Debug测试一下。
ⅰ. 4、查询增强和关联:
1、错误处理机制:主要包括允许空上下文查询(即处理边界情况)、提供友好的错误提示、引导用户提供必要信息
空上下文查询(边界情况处理):可以使用Spring AI的ContextualQueryAugmenter上下文查询增强来实现或者未启用“允许空上下文”选项,系统找不到相关文档会默认改写用户的userText:
The user query is outside your knowledge base.
Politely inform the user that you can't answer it.
因此,我们需要运用工厂模式自定义处理逻辑,首先将TravelAppRagCustomAdvisorFactory类的.queryAugmenter()开启,然后在rag包下新建一个TravelAppContextualQueryAugmenterFactory类,编写以下代码:
public class TravelAppContextualQueryAugmenterFactory {
public static ContextualQueryAugmenter createInstance(){
PromptTemplate emptyContextPromptTemplate = new PromptTemplate("""你应该输出下面的内 容: 抱歉,我只能回答旅游规划相关的问题,别的没办法帮到您嘞,请见谅,有问题可以联系人工客服 """);
return ContextualQueryAugmenter.builder()
.allowEmptyContext(false)
.emptyContextPromptTemplate(emptyContextPromptTemplate)
.build();
}
}
如果这里找不到相关的问题时,要让它继续按大模型回答就把false改成true。
然后在TravelAppRagCustomAdvisorFactory类的.queryAugmenter()运用它,代码如下:
.queryAugmenter(TravelAppContextualQueryAugmenterFactory.createInstance())
最后Debug测试一下看看是否回答的是我们设定的结果。测试的结果:抱歉,我只能回答旅游规划相关的问题,别的没办法帮到您嘞,请见谅,有问题可以联系人工客服。
如果项目不用它可以将下面这段代码注释掉:
// advisors(
// TravelAppRagCustomAdvisorFactory.createTravelAppRagCustomAdvisorFactory(
// travelAppVectorStore,"钱"
// )
建议:除了上述的优化策略外,还可以选择合适的大模型、优化提示词模板、开启拒识功能(限制只基于知识库回答)、调整大模型参数,来优化。
四、RAG高级知识:
1、混合检索策略:并行混合检索、级联混合检索、动态混合检索
2、大模型幻觉:事实性幻觉、逻辑性幻觉、自洽性幻觉
3、RAG应用评估
4、高级RAG架构:自纠错RAG、自省式RAG、检索树RAG、多智能体RAG系统