ARTICLE · 1034191
第4集:让AI学会看文档:Spring AI RAG入门实战
上集回顾:老张给客服AI装上了"海马体",多轮对话终于连贯了。但新问题来了——用户问"你们ERP系统的功能有哪些?"AI傻眼了:“我不知道你们公司有什么系统…”
一、故事继续:知识鸿沟
周一早上,张姐又来了:
“老张,有个大客户在咨询咱们的ERP系统,AI怎么啥都不知道?客户都快跑了!”
老张一看后台:
用户:你们的ERP系统支持什么功能?AI:抱歉,我不了解贵公司的产品...
老张的顿悟:“AI虽然会聊天、有记忆,但它从来没读过我们的文档!它不知道我们公司是做什么的!”
这就好比你有个聪明的秘书,但他从没看过公司的产品手册——你让他回答业务问题,他只能瞎猜。
二、技术引入:什么是RAG?
2.1 RAG是什么?
RAG = Retrieval-Augmented Generation(检索增强生成)
老张的类比:“RAG就像开卷考试——以前AI是闭卷,现在允许它翻书了!”
2.2 RAG的工作流程
用户提问↓① 检索:在知识库中找相关资料↓② 增强:把资料塞进Prompt↓③ 生成:AI基于资料生成答案↓返回答案(带来源引用)
三、第一步:文档解析
3.1 Spring AI的DocumentReader
Spring AI 通过 Apache Tika 提供了统一的多格式解析器 TikaDocumentReader,一份代码就能读 PDF / Word / TXT / Markdown / HTML:
// 一份代码通吃所有格式(PDF、Word、TXT、MD、HTML)DocumentReader reader = new TikaDocumentReader(resource);List<Document> documents = reader.read();// 如果是纯 PDF,也可以用更细的 PagePdfDocumentReader / ParagraphPdfDocumentReader// DocumentReader pdfReader = new PagePdfDocumentReader(resource);
⚠️ 代码验证说明:早期版本有
PdfReader、WordDocumentReader等独立类,Spring AI 1.0 起统一收敛为TikaDocumentReader(按页/按段拆分可配ExtractedTextFormatter)。本教程统一用TikaDocumentReader,避免类名随版本变动。
3.2 实战:解析PDF
@Servicepublic class DocumentService {public List<Document> parsePdf(String filePath) {Resource resource = new FileSystemResource(filePath);// 统一用 TikaDocumentReader 解析(PDF/Word/TXT 都支持)DocumentReader reader = new TikaDocumentReader(resource);// 解析并返回文档列表return reader.read();}}
老张的发现:“原来Spring AI已经封装好了各种解析器,跟Spring Boot自动装配一样省心!”
四、第二步:文本分块
4.1 为什么要分块?
直接扔整个PDF给AI?不行!
4.2 TokenTextSplitter
// Spring AI 1.0:TokenTextSplitter 构造参数为// (chunkSize, minChunkSizeChars, maxChunkSizeChars, overlap, maxNumChunks)TextSplitter splitter = new TokenTextSplitter(500, // 每个 chunk 最大 500 个 token100, // 最小 chunk 字符数800, // 最大 chunk 字符数50, // 重叠 50 个 token(保持上下文连贯)10000 // 最大 chunk 数量上限);// 分块List<Document> chunks = splitter.apply(documents);
老张的比喻:“就像把一本书切成章节,每章几百字,方便查找和记忆。”
五、第三步:向量化
5.1 EmbeddingModel
把文字转成向量(数字列表),这样计算机才能"理解"语义:
@Servicepublic class EmbeddingService {@Autowiredprivate EmbeddingModel embeddingModel;public float[] embed(String text) {// 调用智谱Embedding-2模型EmbeddingResponse response = embeddingModel.call(text);return response.getResults().get(0).getEmbedding();}}
输出示例:
原文:"退货政策是什么?"向量:[0.023, -0.156, 0.789, ...](1536维)
5.2 语义相似度
"退货政策"的向量 ≈ "退款规定"的向量 → 相似度0.85"订单查询"的向量 ≠ "退货政策"的向量 → 相似度0.32
老张的理解:“意思相近的文字,向量距离就近;意思无关的,距离就远。AI就是靠这个’理解’文字!”
六、第四步:存入向量库
6.1 主流向量库对比
6.2 Redis向量库(推荐入门)
<!-- 依赖 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-vector-store-redis</artifactId></dependency>
# 配置spring:ai:vectorstore:redis:host: localhostport: 6379index-name: spring-ai-indexprefix: "spring-ai:"
6.3 存储文档
@Servicepublic class KnowledgeBaseService {@Autowiredprivate VectorStore vectorStore;@Autowiredprivate TextSplitter splitter;@Autowiredprivate EmbeddingModel embeddingModel;public void addDocument(Document doc) {// 1. 分块List<Document> chunks = splitter.apply(Collections.singletonList(doc));// 2. 生成向量并存储vectorStore.add(chunks);}}
七、第五步:检索增强生成
7.1 使用QuestionAnswerAdvisor
Spring AI内置了RAG助手,一行代码搞定:
@Servicepublic class RagChatService {@Autowiredprivate ChatClient chatClient;@Autowiredprivate VectorStore vectorStore;public String chatWithRag(String question) {return chatClient.prompt().user(question).advisors(new QuestionAnswerAdvisor(vectorStore)) // 添加RAG助手.call().content();}}
老张的惊喜:“就一行?不需要手写检索逻辑?Spring AI太贴心了!”
7.2 完整流程演示
// 1. 上传文档到知识库KnowledgeBaseService.addDocument(new Document("我们的ERP系统支持:采购管理、销售管理、库存管理、财务管理..."));// 2. 用户提问String answer = ragChatService.chatWithRag("你们的ERP支持什么功能?");// 3. AI回答(基于文档)/*根据我们的产品文档,我们的ERP系统主要支持以下功能:1. 采购管理:供应商管理、采购订单、入库管理2. 销售管理:客户管理、销售订单、出库管理3. 库存管理:仓库管理、库存调拨、盘点管理4. 财务管理:应收应付、成本核算、报表分析*/
八、效果对比
张姐的评价:“现在AI回答都有依据了,客户也放心了!”
九、下一集预告
RAG跑通了,但老张发现一个问题:检索质量不稳定。有时能召回正确答案,有时却找到了无关内容。
第5集《向量数据库是什么?AI的第二大脑》将深入讲解:Embedding原理、向量相似度计算、以及如何调优检索效果。
📦 完整代码
GitHub分支:chapter04-rag-intro
💬 互动环节
老兵问答:
你的项目中有需要接入AI知识库的场景吗? A. 已有,正在做 B. 计划做,还没开始 C. 暂时没有 D. 其他(评论区聊聊)
下周三晚9点,我们继续!
作者:Java老兵搞AI | 关注我,看Java如何玩转AI