夜雨聆风学习资料网

ARTICLE · 1034191

第4集:让AI学会看文档:Spring AI RAG入门实战

第4集:让AI学会看文档:Spring AI RAG入门实战

上集回顾:老张给客服AI装上了"海马体",多轮对话终于连贯了。但新问题来了——用户问"你们ERP系统的功能有哪些?"AI傻眼了:“我不知道你们公司有什么系统…”


一、故事继续:知识鸿沟

周一早上,张姐又来了:

“老张,有个大客户在咨询咱们的ERP系统,AI怎么啥都不知道?客户都快跑了!”

老张一看后台:

用户:你们的ERP系统支持什么功能?AI:抱歉,我不了解贵公司的产品...

老张的顿悟:“AI虽然会聊天、有记忆,但它从来没读过我们的文档!它不知道我们公司是做什么的!”

这就好比你有个聪明的秘书,但他从没看过公司的产品手册——你让他回答业务问题,他只能瞎猜。


二、技术引入:什么是RAG?

2.1 RAG是什么?

RAG = Retrieval-Augmented Generation(检索增强生成)

传统大模型
RAG增强
只靠训练数据回答
先查资料,再生成答案
不知道公司内部信息
能基于公司文档回答
幻觉问题严重
有资料可查,有据可依
知识更新需重新训练
上传新文档即可

老张的类比:“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);

⚠️ 代码验证说明:早期版本有 PdfReaderWordDocumentReader 等独立类,Spring AI 1.0 起统一收敛为 TikaDocumentReader(按页/按段拆分可配 ExtractedTextFormatter)。本教程统一用 TikaDocumentReader,避免类名随版本变动。

3.2 实战:解析PDF

@Servicepublic class DocumentService {    public List<DocumentparsePdf(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?不行!

问题
原因
Token限制
大模型有输入长度上限
检索精度
整篇文档太粗,定位不准
语义完整
需要按段落/句子切分

4.2 TokenTextSplitter

// Spring AI 1.0:TokenTextSplitter 构造参数为// (chunkSize, minChunkSizeChars, maxChunkSizeChars, overlap, maxNumChunks)TextSplitter splitter = new TokenTextSplitter(    500,   // 每个 chunk 最大 500 个 token    100,   // 最小 chunk 字符数    800,   // 最大 chunk 字符数    50,    // 重叠 50 个 token(保持上下文连贯)    10000  // 最大 chunk 数量上限);// 分块List<Document> chunks = splitter.apply(documents);

老张的比喻:“就像把一本书切成章节,每章几百字,方便查找和记忆。”


五、第三步:向量化

5.1 EmbeddingModel

把文字转成向量(数字列表),这样计算机才能"理解"语义:

@Servicepublic class EmbeddingService {    @Autowired    private EmbeddingModel embeddingModel;    public float[] embed(String text) {        // 调用智谱Embedding-2模型        EmbeddingResponse response = embeddingModel.call(text);        return response.getResults().get(0).getEmbedding();    }}

输出示例

原文:"退货政策是什么?"向量:[0.023, -0.1560.789, ...](1536维)

5.2 语义相似度

"退货政策"的向量  ≈  "退款规定"的向量  →  相似度0.85"订单查询"的向量  ≠  "退货政策"的向量  →  相似度0.32

老张的理解:“意思相近的文字,向量距离就近;意思无关的,距离就远。AI就是靠这个’理解’文字!”


六、第四步:存入向量库

6.1 主流向量库对比

向量库
优点
缺点
适用场景
Redis
快、简单
非专用
开发测试
PgVector
PostgreSQL扩展
需PostgreSQL
中小型项目
Milvus
性能好
部署复杂
大规模生产
Chroma
轻量本地
功能有限
个人项目

6.2 Redis向量库(推荐入门)

<!-- 依赖 --><dependency>    <groupId>org.springframework.ai</groupId>    <artifactId>spring-ai-starter-vector-store-redis</artifactId></dependency>
# 配置spring:  ai:    vectorstore:      redis:        host: localhost        port: 6379        index-name: spring-ai-index        prefix: "spring-ai:"

6.3 存储文档

@Servicepublic class KnowledgeBaseService {    @Autowired    private VectorStore vectorStore;    @Autowired    private TextSplitter splitter;    @Autowired    private 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 {    @Autowired    private ChatClient chatClient;    @Autowired    private 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. 财务管理:应收应付、成本核算、报表分析*/

八、效果对比

场景
无RAG
有RAG
问公司产品
瞎编或说不知道
准确引用文档
问内部流程
通用回答
基于内部资料
知识更新
需重新训练
上传新文档
幻觉问题
严重
大幅降低

张姐的评价:“现在AI回答都有依据了,客户也放心了!”


九、下一集预告

RAG跑通了,但老张发现一个问题:检索质量不稳定。有时能召回正确答案,有时却找到了无关内容。

第5集《向量数据库是什么?AI的第二大脑》将深入讲解:Embedding原理、向量相似度计算、以及如何调优检索效果。


📦 完整代码

GitHub分支:chapter04-rag-intro


💬 互动环节

老兵问答

你的项目中有需要接入AI知识库的场景吗? A. 已有,正在做 B. 计划做,还没开始 C. 暂时没有 D. 其他(评论区聊聊)

下周三晚9点,我们继续!


作者:Java老兵搞AI | 关注我,看Java如何玩转AI

相关学习资料