【Spring AI & LangChain4j 进阶篇】Milvus/Redis向量库整合Java:RAG核心环节手把手代码实现

【Spring AI & LangChain4j 进阶篇】Milvus/Redis向量库整合Java:RAG核心环节手把手代码实现

在这里插入图片描述


🍃 予枫个人主页
📚 个人专栏: 《Java 从入门到起飞》《读研码农的干货日常

💻 Debug 这个世界,Return 更好的自己!


引言

大模型幻觉是落地路上的“绊脚石”,而RAG(检索增强生成)正是破解这一痛点的核心方案。作为Java开发者,如何依托Spring AI、LangChain4j等生态,实现从文档解析、向量存储到检索生成的全链路落地?本文拆解RAG核心逻辑,手把手教你用Java搭建可直接复用的RAG系统,覆盖多格式文档处理、主流向量库整合与全流程优化,干货拉满,建议收藏!

文章目录

一、RAG核心认知:为什么它能解决大模型幻觉?

1.1 RAG是什么?核心逻辑拆解 🧩

RAG(Retrieval-Augmented Generation,检索增强生成),简单来说就是“先检索、再生成”——在大模型生成回答前,先从私有文档/知识库中检索相关信息,将其作为上下文喂给大模型,让生成的内容更精准、更贴合实际需求,从根源上减少幻觉。

核心优势对比传统大模型生成:

  • 传统大模型:依赖训练数据,对私有数据、实时数据一无所知,易“一本正经地胡说八道”;
  • RAG:结合检索能力,可接入私有文档,无需重新训练大模型,低成本实现精准回答,且支持数据实时更新。
划重点:RAG不是替换大模型,而是给大模型“装一个外挂知识库”,让它的回答有迹可循、有据可依。

1.2 RAG适用场景与全链路流程

1.2.1 核心适用场景

  • 企业知识库问答(如产品手册、内部文档查询);
  • 客服机器人(精准回复用户问题,减少人工干预);
  • 专业领域问答(医疗、法律、技术文档检索与生成);
  • 私有数据生成(如公司财报、内部资料总结)。

1.2.2 RAG全链路核心流程(Java视角)

  1. 文档处理:加载PDF/Word/Markdown等多格式文档,进行解析与文本分块;
  2. 向量转换:通过Embedding模型将文本块转换为向量,存入向量数据库;
  3. 检索环节:接收用户查询,转换为向量后在向量库中检索相关文本块,优化检索结果;
  4. 生成环节:将检索到的相关文本作为上下文,调用大模型生成精准回答。

✅ 温馨提示:觉得这部分内容有帮助的话,麻烦点赞收藏,后续实操环节需要反复对照哦~

二、文档处理环节:Java实现多格式文档解析与分块

文档处理是RAG的“基础工程”,核心目标是将非结构化文档(PDF/Word等)转换为可处理的文本块,既要保证信息不丢失,又要兼顾后续检索的精准度。这里我们采用Spring AI与LangChain4j结合的方式,实现多格式文档的高效解析。

2.1 依赖引入(Maven)

<!-- Spring AI 核心依赖 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-core</artifactId><version>1.0.0-M1</version></dependency><!-- 文档加载器(支持PDF/Word/Markdown) --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-document-reader</artifactId><version>1.0.0-M1</version></dependency><!-- LangChain4j 依赖(增强文档处理能力) --><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-core</artifactId><version>0.24.0</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-document-parser</artifactId><version>0.24.0</version></dependency>

2.2 多格式文档加载与解析(Java代码实现)

支持PDF、Word(.docx)、Markdown三种主流格式,核心是通过Spring AI的DocumentReader和LangChain4j的Parser实现解析:

importorg.springframework.ai.document.Document;importorg.springframework.ai.document.reader.pdf.PdfDocumentReader;importorg.springframework.ai.document.reader.word.DocxDocumentReader;importorg.springframework.ai.document.reader.markdown.MarkdownDocumentReader;importjava.io.File;importjava.util.List;publicclassDocumentLoaderUtil{// 加载PDF文档publicstaticList<Document>loadPdf(String filePath){File file =newFile(filePath);PdfDocumentReader reader =newPdfDocumentReader(file);return reader.read();}// 加载Word文档publicstaticList<Document>loadDocx(String filePath){File file =newFile(filePath);DocxDocumentReader reader =newDocxDocumentReader(file);return reader.read();}// 加载Markdown文档publicstaticList<Document>loadMarkdown(String filePath){File file =newFile(filePath);MarkdownDocumentReader reader =newMarkdownDocumentReader(file);return reader.read();}publicstaticvoidmain(String[] args){// 测试加载PDF文档List<Document> pdfDocs =loadPdf("D:/test/rag_demo.pdf");System.out.println("PDF文档解析完成,共"+ pdfDocs.size()+"个文档块");}}

2.3 文本分块策略:平衡检索精度与效率

文本分块是关键一步——分块太大,检索时精准度低;分块太小,会丢失上下文关联。推荐两种主流分块策略,可根据文档类型灵活选择:

策略1:固定长度分块(基础款)

适合结构化文档(如技术手册),设置固定块大小(如500字符),重叠长度(如50字符),避免上下文断裂:

importdev.langchain4j.data.document.splitter.DocumentSplitter;importdev.langchain4j.data.document.splitter.TokenSplitter;importdev.langchain4j.data.document.Document;importjava.util.List;publicclassTextSplitterUtil{// 固定长度分块(按字符数)publicstaticList<Document>splitByChar(List<Document> documents){// 块大小:500字符,重叠:50字符DocumentSplitter splitter =newTokenSplitter(500,50);return splitter.split(documents);}}

策略2:语义分块(进阶款)

适合非结构化文档(如报告、论文),基于语义相似度分块,保留完整语义单元,需结合Embedding模型:

// 语义分块(依赖Embedding模型,后续章节会讲解)publicstaticList<Document>splitBySemantic(List<Document> documents,EmbeddingModel embeddingModel){// 基于语义相似度分块,最小块大小100字符,最大500字符SemanticDocumentSplitter splitter =newSemanticDocumentSplitter(embeddingModel,100,500,0.3);return splitter.split(documents);}
小技巧:对于技术文档,优先使用固定长度分块,效率更高;对于散文、报告类文档,用语义分块更能保证上下文完整性。

三、向量环节:Embedding模型接入与向量数据库整合

向量环节是RAG的“核心引擎”——将文本块转换为向量(Embedding),存入向量数据库,才能实现高效的相似性检索。本节讲解Java生态下Embedding模型接入,以及Milvus、Redis Stack两种主流向量数据库的整合。

3.1 Embedding模型接入(Java实现)

Embedding模型的核心作用是将文本转换为高维向量,这里推荐使用开源的BGE模型,或接入第三方API(如百度文心一言、阿里通义千问),以下是开源BGE模型的接入示例:

importdev.langchain4j.model.embedding.EmbeddingModel;importdev.langchain4j.model.embedding.bge.BigGeometryEmbeddingModel;importdev.langchain4j.data.embedding.Embedding;importjava.util.List;publicclassEmbeddingUtil{// 初始化BGE开源Embedding模型(本地部署,无需APIKey)publicstaticEmbeddingModelinitBgeEmbedding(){// 模型路径:本地部署的BGE模型路径returnnewBigGeometryEmbeddingModel("D:/models/bge-large-zh-v1.5");}// 将文本块转换为向量publicstaticList<Embedding>embedDocuments(List<Document> documents,EmbeddingModel embeddingModel){return embeddingModel.embedDocuments(documents);}// 将用户查询转换为向量publicstaticEmbeddingembedQuery(String query,EmbeddingModel embeddingModel){return embeddingModel.embedQuery(query);}}
补充:如果不想本地部署模型,也可以使用Spring AI接入第三方Embedding API,只需配置APIKey即可,代码更简洁,适合快速落地。

3.2 主流向量数据库整合(Java代码)

3.2.1 Redis Stack整合(轻量首选)

Redis Stack支持向量存储与相似性检索,适合中小规模RAG系统,整合步骤如下:

  1. 引入依赖:
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-redis</artifactId><version>1.0.0-M1</version></dependency>
  1. 配置Redis连接与向量存储:
importorg.springframework.ai.redis.RedisVectorStore;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.data.redis.core.RedisTemplate;publicclassRedisVectorStoreUtil{// 初始化Redis向量存储publicstaticRedisVectorStoreinitRedisVectorStore(RedisConnectionFactory connectionFactory,EmbeddingModel embeddingModel){// 向量维度:BGE模型输出维度为1024returnnewRedisVectorStore(connectionFactory,"rag_vector_store",1024, embeddingModel);}// 存储向量(文本块+向量)publicstaticvoidstoreVectors(RedisVectorStore vectorStore,List<Document> documents){ vectorStore.add(documents);}// 相似性检索publicstaticList<Document>searchVectors(RedisVectorStore vectorStore,String query,int topK){return vectorStore.similaritySearch(query, topK);}}

3.2.2 Milvus整合(大规模首选)

Milvus是开源向量数据库,适合大规模向量存储(百万级以上),支持更复杂的检索策略,整合示例:

  1. 引入依赖:
<dependency><groupId>io.milvus</groupId><artifactId>milvus-sdk-java</artifactId><version>2.4.4</version></dependency>
  1. Milvus向量存储与检索实现:
importio.milvus.client.MilvusClient;importio.milvus.client.MilvusClientBuilder;importio.milvus.param.ConnectParam;importio.milvus.param.collection.CreateCollectionParam;importio.milvus.param.collection.FieldType;importjava.util.List;publicclassMilvusVectorStoreUtil{// 初始化Milvus客户端publicstaticMilvusClientinitMilvusClient(){ConnectParam connectParam =ConnectParam.newBuilder().withHost("localhost").withPort(19530).build();returnnewMilvusClientBuilder().withConnectParam(connectParam).build();}// 创建向量集合(表)publicstaticvoidcreateCollection(MilvusClient client,String collectionName){FieldType idField =FieldType.newBuilder().withName("id").withDataType(FieldType.DataType.Int64).withPrimaryKey(true).withAutoID(true).build();FieldType vectorField =FieldType.newBuilder().withName("vector").withDataType(FieldType.DataType.FloatVector).withDimension(1024)// 与Embedding模型维度一致.build();CreateCollectionParam createParam =CreateCollectionParam.newBuilder().withCollectionName(collectionName).addFieldType(idField).addFieldType(vectorField).build(); client.createCollection(createParam);}// 存储向量与检索逻辑(省略,核心是将Embedding向量存入Milvus,调用相似性检索接口)}

✅ 互动提问:你平时做RAG开发,更倾向于用Redis还是Milvus?评论区留言交流~

四、检索与生成环节:混合检索优化与大模型整合

检索与生成是RAG的“最终输出环节”——通过优化检索策略提升结果精准度,再将检索到的上下文与大模型结合,生成符合需求的回答。本节讲解混合检索、重排序优化,以及Java与大模型的无缝整合。

4.1 混合检索:提升检索精准度的关键

单一的向量检索可能存在“语义偏差”,混合检索(向量检索+关键词检索)能兼顾语义相似度与关键词匹配,适合技术文档等场景:

importorg.springframework.ai.vectorstore.VectorStore;importjava.util.List;importjava.util.stream.Collectors;publicclassHybridSearchUtil{// 混合检索(向量检索+关键词检索)publicstaticList<Document>hybridSearch(VectorStore vectorStore,String query,int topK){// 1. 向量检索(获取语义相似的文本块)List<Document> vectorDocs = vectorStore.similaritySearch(query, topK *2);// 2. 关键词检索(筛选包含查询关键词的文本块)List<Document> keywordDocs = vectorDocs.stream().filter(doc -> doc.getContent().contains(query)).collect(Collectors.toList());// 3. 合并去重,取前topK个结果return keywordDocs.stream().distinct().limit(topK).collect(Collectors.toList());}}

4.2 检索结果重排序:优化上下文质量

检索到的文本块可能存在相关性高低不一的情况,通过重排序(基于文本相似度得分),将最相关的文本块放在前面,提升大模型生成质量:

importdev.langchain4j.data.document.Document;importjava.util.Comparator;importjava.util.List;publicclassReRankUtil{// 基于相似度得分重排序(降序,得分越高越相关)publicstaticList<Document>reRankDocuments(List<Document> documents){return documents.stream().sorted(Comparator.comparingDouble(Document::getScore).reversed()).collect(Collectors.toList());}}

4.3 与大模型整合:生成精准回答(Java实现)

这里以接入阿里通义千问大模型为例,结合检索到的上下文,生成回答:

  1. 引入依赖:
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-tongyi</artifactId><version>1.0.0-M1</version></dependency>
  1. 大模型调用与回答生成:
importorg.springframework.ai.tongyi.TongYiChatClient;importorg.springframework.ai.tongyi.TongYiChatOptions;importjava.util.List;importjava.util.stream.Collectors;publicclassLLMGenerateUtil{// 初始化通义千问客户端publicstaticTongYiChatClientinitTongYiClient(String apiKey){TongYiChatOptions options =TongYiChatOptions.builder().apiKey(apiKey).model("qwen-plus")// 选择合适的模型.temperature(0.7)// 生成多样性,0-1之间.build();returnnewTongYiChatClient(options);}// 结合检索上下文生成回答publicstaticStringgenerateAnswer(TongYiChatClient llmClient,String query,List<Document> retrievedDocs){// 构建上下文(将检索到的文本块拼接)String context = retrievedDocs.stream().map(Document::getContent).collect(Collectors.joining("\n\n"));// 构建提示词(Prompt)String prompt =String.format("请根据以下上下文,回答用户的问题:\n上下文:%s\n用户问题:%s\n要求:回答准确,基于上下文,不要编造信息。", context, query);// 调用大模型生成回答return llmClient.call(prompt);}}

五、RAG全链路代码整合与常见问题优化

5.1 全链路代码整合(完整示例)

将前面的文档处理、向量存储、检索生成环节整合,形成完整的Java版RAG系统:

publicclassRAGFullLinkDemo{publicstaticvoidmain(String[] args){// 1. 初始化核心组件EmbeddingModel embeddingModel =EmbeddingUtil.initBgeEmbedding();RedisConnectionFactory redisConnectionFactory =getRedisConnectionFactory();// 自定义Redis连接配置RedisVectorStore vectorStore =RedisVectorStoreUtil.initRedisVectorStore(redisConnectionFactory, embeddingModel);TongYiChatClient llmClient =LLMGenerateUtil.initTongYiClient("你的通义千问APIKey");// 2. 文档处理(加载PDF文档,分块)List<Document> rawDocs =DocumentLoaderUtil.loadPdf("D:/test/rag_demo.pdf");List<Document> splitDocs =TextSplitterUtil.splitByChar(rawDocs);// 3. 向量存储(将分块后的文本转换为向量,存入Redis)RedisVectorStoreUtil.storeVectors(vectorStore, splitDocs);// 4. 检索环节(混合检索+重排序)String userQuery ="RAG的核心流程是什么?";List<Document> retrievedDocs =HybridSearchUtil.hybridSearch(vectorStore, userQuery,5);List<Document> rankedDocs =ReRankUtil.reRankDocuments(retrievedDocs);// 5. 生成回答String answer =LLMGenerateUtil.generateAnswer(llmClient, userQuery, rankedDocs);System.out.println("RAG生成回答:\n"+ answer);}// 自定义Redis连接配置privatestaticRedisConnectionFactorygetRedisConnectionFactory(){// 此处省略Redis连接配置,根据实际环境调整returnnull;}}

5.2 常见问题与优化方案

问题1:检索结果不精准,大模型仍有幻觉

  • 优化方案:
    1. 调整分块策略,缩小块大小,增加重叠长度;
    2. 采用混合检索(向量+关键词),提升匹配度;
    3. 更换更精准的Embedding模型(如BGE-large、通义千问Embedding)。

问题2:向量存储速度慢,检索效率低

  • 优化方案:
    1. 中小规模数据用Redis Stack,大规模数据用Milvus;
    2. 对文本块进行去重、过滤(过滤无效内容,如空白、重复文本);
    3. 降低Embedding向量维度(如从1024维降至512维,牺牲少量精度换取效率)。

问题3:大模型生成回答冗长,偏离主题

  • 优化方案:
    1. 优化Prompt,明确要求“简洁、精准,基于上下文”;
    2. 限制检索结果的数量(如top3-top5),避免上下文过多;
    3. 调整大模型的temperature参数(降低至0.5以下),减少生成多样性。

六、结尾总结

本文从RAG核心原理出发,拆解了检索增强生成的全链路流程,重点讲解了Java生态下的落地实现——包括Spring AI/LangChain4j的文档处理、Embedding模型接入、主流向量数据库(Redis/Milvus)整合,以及混合检索、重排序与大模型的无缝衔接,最后提供了全链路代码示例与常见问题优化方案。

RAG是解决大模型幻觉的关键技术,而Java作为企业级开发的主流语言,在RAG落地中具有天然的生态优势。掌握本文的内容,你可以快速搭建属于自己的Java版RAG系统,适配企业知识库、客服机器人等多种场景。

Read more

Claude Code 接入 MySQL 实战:让 AI 看懂你的表结构和数据

Claude Code 接入 MySQL 实战:让 AI 看懂你的表结构和数据

背景 在使用Claude code 进行日常业务开发过程中,面临新的表结构和接口时,常常需要跟AI介绍表字段和接口以便它能顺利的开发接口、写Sql等,此等场景下,会造成很长的上下文,且比较麻烦,同时,针对一些表结构索引优化、Sql优化等工作,也需要和AI进行长篇大论的交流,比较繁琐。 解决方案:通过引入开源MySql mcp让大模型可以直接连接数据库进行读取,极大方便了开发者,在测试环境下,测试数据对安全性没太大要求,因此可以让大模型自己读数据库辅助开发和编码。 一、MCP Server for MySQL 安装 依赖:node.js、claude code cli 1.MySql MCP安装命令: # Using npm npm install -g @benborla29/mcp-server-mysql # Using pnpm pnpm add -g @benborla29/

By Ne0inhk
【MySQL】90% 的 MySQL 性能问题都和它有关!索引的正确打开方式,看完少走 3 年弯路

【MySQL】90% 的 MySQL 性能问题都和它有关!索引的正确打开方式,看完少走 3 年弯路

我的个人主页我的专栏:人工智能领域、java-数据结构、Javase、C语言,MySQL,希望能帮助到大家!!!点赞👍收藏❤ 引言: 在后端开发的日常工作中,你是否遇到过这样的场景:本地测试时 MySQL 查询秒级响应,一到生产环境就突然“卡壳”,页面加载转圈半分钟才出结果;或者随着业务数据量从几万条涨到几百万条,原本流畅的列表查询,慢慢变成了“龟速”操作? 如果你曾为这些问题头疼,那大概率没绕开 MySQL 性能优化的核心——索引。有数据统计显示,90% 的 MySQL 性能瓶颈,根源都在于索引使用不当:要么没建索引让查询“全表扫描”,要么建了冗余索引浪费资源,要么索引设计不合理导致优化器“弃用”。今天这篇文章,我们就从“为什么索引重要”讲到“怎么用对索引”,帮你避开新手常踩的坑,少走 3 年弯路。 * 一:什么是索引

By Ne0inhk
Spring Boot 实战:MyBatis 操作数据库(上)

Spring Boot 实战:MyBatis 操作数据库(上)

—JavaEE专栏— Spring Boot 实战:MyBatis 操作数据库(上) 摘要 本文深度解析了 Spring Boot 环境下 MyBatis 的集成与应用。通过回顾传统 JDBC 的局限性,详细展示了 MyBatis 在日志配置、CRUD 操作、自增主键返回及多表查询中的实战用法。同时,文章深入探讨了 #{} 与 ${} 的底层预编译差异及安全风险,并分享了企业级开发中的数据库命名规范与 Druid 连接池配置,助力开发者构建稳健的持久层架构。 文章目录 * Spring Boot 实战:MyBatis 操作数据库(上) * 摘要 * @[toc] * 1. 为什么持久层开发需要 MyBatis? * 1.1 传统 JDBC 的局限性 * 1.2

By Ne0inhk
Flutter 组件 dart_nostr 适配鸿蒙 HarmonyOS 实战:去中心化通讯,构建分布式 Relay 订阅与非对称加密架构

Flutter 组件 dart_nostr 适配鸿蒙 HarmonyOS 实战:去中心化通讯,构建分布式 Relay 订阅与非对称加密架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 dart_nostr 适配鸿蒙 HarmonyOS 实战:去中心化通讯,构建分布式 Relay 订阅与非对称加密架构 前言 在鸿蒙(OpenHarmony)生态迈向万物智联、涉及去中心化社交(DeSo)、分布式身份(DID)及抵御单点崩溃的通讯环境背景下,如何构建一套不依赖中心化服务器、具备绝对抗审查性且数据主权归属于用户的通讯协议,已成为决定新一代互联网应用“生命力”的关键。在鸿蒙设备这类强调分布式软总线与端侧安全治理的环境下,如果应用依然依赖脆弱的中心化中转机,由于由于网络链路的单一性,极易由于由于“中心节点宕机”导致全球范围内的业务中断。 我们需要一种能够基于简单非对称加密、支持全球 Relay(中继器)分发且具备“无主化”特性的抗毁协议。 dart_nostr 为 Flutter 开发者引入了 Nostr(Notes

By Ne0inhk