【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

一文搞懂 Linux 进程替换:从 fork 到 exec 的完整链路

一文搞懂 Linux 进程替换:从 fork 到 exec 的完整链路

目录 进程替换是什么? ----------- 进程替换原理 ---------- 1、进程替换会发生写实拷贝吗? 2、普通只读 vs COW 只读 3、exec 函数执行后,后续代码还会执行吗? 4、CPU 如何知道程序的入口地址? 5、子进程进行程序替换后,会影响父进程的代码和数据吗? ---------- exec 系列接口 ---------- 1、execl 2、execlp 3、execv 4、execvp 5、execle 6、execvpe exec 系列库函数与系统调用的关系 ------------- 其他问题 ------------- 问题:exec 系列函数只能执行系统命令吗?能不能执行自己写的程序? 问题:为什么我们的可执行程序、脚本,

By Ne0inhk

Openclaw ubuntu 22.04部署,超详细,对接百炼模型(中文社区版)

一、安装要求 1、node版本必须>=22.0 node下载网址:https://nodejs.org/en/download 2、linux系统版本大于centos7,推荐用centos8或者ubuntu22或更高版本 3、提前准备好对接的AI平台的ApiKey秘钥,例如百炼,Kimi,MiniMax,openai等 4、安装openclaw的机器可访问公网 5、参考文档 官网:https://openclaw.ai/ 中文社区官网:https://clawd.org.cn/ 二、安装步骤 1、安装git sudo apt update && sudo apt install git -y git

By Ne0inhk
时序数据库选型指南:用工程视角理解 Apache IoTDB

时序数据库选型指南:用工程视角理解 Apache IoTDB

摘要:在工业物联网(IIoT)数据爆发式增长的今天,通用数据库已难以应对海量测点的高频写入与复杂聚合查询。本文将从工程落地的角度出发,探讨时序数据库(TSDB)的选型关键维度,并深入解析 Apache IoTDB 在架构设计、数据模型及端边云协同方面的技术特性。 文章目录 * 一、 引言:为什么我们需要专用的时序数据库? * 二、 选型核心维度与 IoTDB 的设计哲学 * 2.1 数据模型:树形结构 vs 标签模型 * 2.2 存储引擎:LSM Tree 与 TsFile 的深度优化 * 核心技术拆解 * 架构流程图:IoTDB 写入与压缩流程 * 2.3 分布式架构:MPP 与 共识协议 * 三、 实战演练:从定义到分析 * 3.

By Ne0inhk

告别VNC!Ubuntu 22.04原生RDP远程桌面配置全攻略(含高分屏适配技巧)

告别VNC!Ubuntu 22.04原生RDP远程桌面配置全攻略(含高分屏适配技巧) 如果你和我一样,长期在Windows和Linux双系统之间切换,或者需要远程管理一台Ubuntu桌面服务器,那么“远程桌面”这个需求一定不陌生。过去,我们通常会选择VNC方案,比如TigerVNC、RealVNC,但体验过的人都知道,VNC在跨平台、网络带宽占用、尤其是高分屏支持上,总是差那么点意思——画面卡顿、色彩失真、缩放模糊,这些问题在需要精细操作的设计或开发工作中尤为恼人。 好消息是,从Ubuntu 22.04 LTS “Jammy Jellyfish”开始,GNOME桌面环境正式集成了对微软RDP(Remote Desktop Protocol) 协议的原生支持。这意味着,你现在可以直接使用Windows系统自带的“远程桌面连接”(mstsc)或macOS上的Microsoft Remote Desktop,像连接另一台Windows电脑一样,无缝接入你的Ubuntu桌面。这不仅仅是换了个协议那么简单,它带来的是更低的延迟、更好的图形压缩效率、原生剪贴板共享、

By Ne0inhk