> 技术文档 > spring-AI集成向量数据库redis使用实现RAG_让springai实现rag

spring-AI集成向量数据库redis使用实现RAG_让springai实现rag


前言

什么是RAG,为什么需要RAG?
RAG(Retrieval-Augmented Generation)叫做检索增强生成。简单来说就是把信息检索技术模型结合的方案。

RAG(检索增强生成):打破大模型的知识边界
——当信息检索遇上生成式AI

一、大模型的知识困境
尽管大语言模型(LLM)在理解与生成能力上表现卓越,但其知识体系存在明显短板:

时间盲区 训练数据的截止日期(如GPT-3到2021年)使其无法感知近期事件,例如无法回答\"2023年诺贝尔奖得主是谁?\"
领域缺口 通用训练数据难以覆盖垂直领域(如医疗病历、法律条款),导致专业问答准确率骤降
上下文限制 即使最新模型支持200K token上下文(约15万字),仍无法直接处理整本《临床医学手册》级别的知识库

💡 常见误区:直接拼接海量文本到prompt
这会导致关键信息被截断,且模型可能因信息过载而出现\"注意力涣散\",回答质量反而下降

二、RAG的核心架构
通过检索与生成的协同工作流实现知识动态扩展:

1.检索模块——知识库的\"搜索引擎\"
智能分块 采用滑动窗口/**语义分割算法,保持文本逻辑完整性(如将论文按\"摘要-方法-结论\"拆分)
向量化编码 使用
BERT/Embedding**模型将文本转换为高维向量,相似内容在向量空间中邻近聚集
精准召回 通过相似度计算(如余弦相似度)从百万级片段中毫秒级检索TOP-K相关内容
2.生成模块——知识的\"智能加工厂\"

  • 上下文工程 采用\"角色设定-检索内容-用户问题\"的三段式prompt架构,例如:
【角色】你是一名具有半导体行业知识的分析师 【知识】<插入检索到的3篇晶圆制造技术专利摘要> 【问题】请对比FinFETGAA晶体管的优缺点
  • 生成优化 结合检索内容进行事实校准,显著降低模型幻觉(Hallucination)概率

三、RAG的典型应用场景

场景 传统LLM痛点 RAG解决方案 企业知识库问答 无法获取内部文档 接入Confluence/PDF等私有数据源 实时资讯分析 无法知晓最新事件 检索新闻数据库/社交媒体 跨语言专业咨询 非英语内容匮乏 混合检索多语言知识库

RAG就是利用信息检索技术来拓展大模型的知识库,解决大模型的知识限制。整体来说RAG分为两个模块:

  • 检索模块(Retrieval):负责存储和检索拓展的知识库
    • 文本拆分:将文本按照某种规则拆分为很多片段
    • 文本嵌入(Embedding):根据文本片段内容,将文本片段归类存储
    • 文本检索:根据用户提问的问题,找出最相关的文本片段
  • 生成模块(Generation)
    • 组合提示词:将检索到的片段与用户提问组织成提示词,形成更丰富的上下文信息
    • 生成结果:调用生成式模型(例如DeepSeek)根据提示词,生成更准确的回答

由于每次都是从向量库中找出与用户问题相关的数据,而不是整个知识库,所以上下文就不会超过大模型的限制,同时又保证了大模型回答问题是基于知识库中的内容,完美!
流程如图
spring-AI集成向量数据库redis使用实现RAG_让springai实现rag
而提到RAG,就不得不提到向量数据库了:
向量数据库(Vector Database)是RAG架构中的核心基础设施,其作用类似于AI的“外部记忆库”,专门用于高效存储和检索非结构化的语义信息。下面就开始讲解springAI中向量数据库的使用,以及使用Redis向量数据库来实现RAG

1.向量数据库

  • 向量数据库的主要作用有两个:

    • 存储向量数据;
    • 基于相似度检索数据;
  • SpringAI支持很多向量数据库,并且都进行了封装,可以用统一的API去访问:下面讲解两种常见的向量数据库。

    • Redis Vector Store - The Redis vector store.
    • SimpleVectorStore - A simple implementation of persistent vector storage, good for educational purposes.

这些库都实现了统一的接口:VectorStore,因此操作方式一模一样,只要学会任意一个,其它就都不是问题;

2. SimpleVectorStore

  • 最后一个SimpleVectorStore向量库是基于内存实现,是一个专门用来测试、教学用的库,非常适合此处案例的使用;
  • 修改配置类,添加一个VectorStore的Bean:
@Beanpublic VectorStore vectorStore(OpenAiEmbeddingModel embeddingModel) { return SimpleVectorStore.builder(embeddingModel).build();}

查看 VectorStore接口:里面主要有这些方法

public interface VectorStore extends DocumentWriter { default String getName() { return this.getClass().getSimpleName(); } // 保存文档到向量库 void add(List<Document> documents); // 根据文档id删除文档 void delete(List<String> idList); void delete(Filter.Expression filterExpression); default void delete(String filterExpression) { ... }; // 根据条件检索文档 List<Document> similaritySearch(String query); // 根据条件检索文档 List<Document> similaritySearch(SearchRequest request); default <T> Optional<T> getNativeClient() { return Optional.empty(); }}
  • 注意,VectorStore操作向量化的基本单位是Document,在使用时需要将自己的知识库分割转换为一个个的Document,然后写入VectorStore
  • 那么问题来了,该如何把各种不同的知识库文件转为Document呢?

在SpringAI中提供了各种文档读取的工具,可以参考官网:ETL Pipeline :: Spring AI Reference;

这里我们使用pdf-document-reade
首先应该引入依赖

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pdf-document-reader</artifactId></dependency>

然后就可以利用工具把PDF文件读取并处理成Document了

编写一个单元测试

// 自动注入向量库 @Autowired private VectorStore vectorStore; @Test public void testVectorStore(){ Resource resource = new FileSystemResource(\"E:\\\\aa\\\\中二知识笔记.pdf\"); // 1.创建PDF的读取器 PagePdfDocumentReader reader = new PagePdfDocumentReader( resource, // 文件源 PdfDocumentReaderConfig.builder() .withPageExtractedTextFormatter(ExtractedTextFormatter.defaults()) .withPagesPerDocument(1) // 每1页PDF作为一个Document .build() ); // 2.读取PDF文档,拆分为Document List<Document> documents = reader.read(); // 3.写入向量库 vectorStore.add(documents); // 4.搜索 //List docs = vectorStore.similaritySearch(\"论语中教育的目的是什么\"); //原始搜索 SearchRequest request = SearchRequest.builder() .query(\"论语中教育的目的是什么\") // 查询 .topK(1) // 返回的相似文档数量 .similarityThreshold(0.6) // 相似度阈值 .filterExpression(\"file_name == \'中二知识笔记.pdf\'\") // 过滤条件 .build(); List<Document> docs = vectorStore.similaritySearch(request); if (docs == null) { System.out.println(\"没有搜索到任何内容\"); return; } for (Document doc : docs) { System.out.println(doc.getId()); System.out.println(doc.getScore()); System.out.println(doc.getText()); } }

但是这个存在一个问题,就是不能够持久化保存,接下来将会用redis来讲解如何持久化保存向量

3.RedisVectorStore

3.1安装redis-stack

可以存储向量的redis模型与普通版本不太通,比较占用内存

  • 需要安装一个Redis Stack,这是Redis官方提供的拓展版本,其中有向量库的功能;
  • 可以使用Docker安装:
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

通过浏览器访问控制台:http://192.168.185.131:8001 (注意换成自己的ip)

3.2引入依赖

在项目中引入RedisVectorStore的依赖:

<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-redis-store-spring-boot-starter</artifactId></dependency>

引入这个依赖就不能用普通的redis依赖了,否则会报错

3.3相关配置

application.yml配置Redis:

spring: ai: vectorstore: redis: index: spring_ai_index # 向量库索引名 initialize-schema: true # 是否初始化向量库索引结构 prefix: \"doc:\" # 向量库key前缀 data: redis: host: 192.168.185.131 # redis地址

由于在spring-ai-redis-store-spring-boot-starter引入了自动配置,所以无需声明bean,直接就可以直接使用VectorStore了。

3.4测试

 // 自动注入向量库 @Autowired private VectorStore vectorStore;@Test public void testVectorStore(){ Resource resource = new FileSystemResource(\"E:\\\\aa\\\\中二知识笔记.pdf\"); // 1.创建PDF的读取器 PagePdfDocumentReader reader = new PagePdfDocumentReader( resource, // 文件源 PdfDocumentReaderConfig.builder() .withPageExtractedTextFormatter(ExtractedTextFormatter.defaults()) .withPagesPerDocument(1) // 每1页PDF作为一个Document .build() ); // 2.读取PDF文档,拆分为Document List<Document> documents = reader.read(); // 3.写入向量库 vectorStore.add(documents);  } @Test public void testRedisVectorStore(){ //List docs = vectorStore.similaritySearch(\"论语中教育的目的是什么\"); SearchRequest request = SearchRequest.builder() .query(\"论语中教育的目的是什么\") // 查询 .topK(5) // 返回的相似文档数量 .similarityThreshold(0.6) // 相似度阈值 //.filterExpression(\"file_name == \'中二知识笔记.pdf\'\") // 过滤条件 .build(); List<Document> docs = vectorStore.similaritySearch(request); if (docs == null) { System.out.println(\"没有搜索到任何内容\"); return; } for (Document doc : docs) { System.out.println(doc.getId()); System.out.println(doc.getScore()); System.out.println(doc.getText()); } }

通过浏览器访问控制台:http://192.168.185.131:8001 (注意换成自己的ip)
spring-AI集成向量数据库redis使用实现RAG_让springai实现rag
可以看到向量切片已经存入到redis了

4.集成配置ChatClient

这里演示一个可以读取pdf的chatClient

 @Bean public ChatClient pdfChatClient(OpenAiChatModel model, ChatMemory chatMemory, VectorStore vectorStore) { return ChatClient.builder(model) .defaultSystem(\"请根据提供的上下文回答问题,遇到上下文没有的问题,不要自己随意编造。\") .defaultAdvisors( new MessageChatMemoryAdvisor(chatMemory), // 聊天记忆 new SimpleLoggerAdvisor(), //日志 new QuestionAnswerAdvisor( vectorStore, // 向量库(这里注入的就是redisvectorStore) SearchRequest.builder() // 向量检索的请求参数  .similarityThreshold(0.5d) // 相似度阈值  .topK(2) // 返回的文档片段数量  .build() ) ) .build(); }

然后测试一下

 @Autowired private ChatClient pdfChatClient; public String chatpdf(String prompt, String chatId) { //3. 请求模型 return pdfChatClient.prompt() .user(prompt) .advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)) //聊天记忆 .call() //阻塞式调用 .content(); } @Test public void testPdfChat() { String prompt=\"论语中教育的目的是什么\"; String chatId = \"15\"; System.out.println(chatpdf(prompt, chatId)); }

结果发现和pdf中的内容一致
spring-AI集成向量数据库redis使用实现RAG_让springai实现rag

spring-AI集成向量数据库redis使用实现RAG_让springai实现rag如果不想下载redis,可以使用线上的向量数据库pinecone
关于spring-AI集成向量数据库pinecone使用,可以查看博主的另外一篇文章spring-AI集成向量数据库pinecone使用