第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:

java 复制代码
// 一份代码通吃所有格式(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

java 复制代码
@Service
public 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?不行!

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

4.2 TokenTextSplitter

java 复制代码
// 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

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

java 复制代码
@Service
public 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.156, 0.789, ...](1536维)

5.2 语义相似度

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

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


六、第四步:存入向量库

6.1 主流向量库对比

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

6.2 Redis向量库(推荐入门)

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

6.3 存储文档

java 复制代码
@Service
public 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助手,一行代码搞定:

java 复制代码
@Service
public 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 完整流程演示

java 复制代码
// 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

相关推荐
MobotStone4 小时前
做了几个 Agent 项目后,我发现:真正拉开 AI 产品经理差距的,不是技术
人工智能·架构
孟健4 小时前
Qwen3.8-27B 本地推理实测:从 14 tok/s 到 159 tok/s 的投机解码调优
人工智能·llm·ai编程
Escalating_xu4 小时前
【C 语言】数据在内存中的存储:补码、大小端、整型陷阱与 IEEE 754 全解析
java·c语言·网络
思无邪664 小时前
用 AI 做 JS 逆向:从抓包到复现的完整方法论
开发语言·javascript·人工智能
灯澜忆梦4 小时前
【面向对象编程C++】| 基础语法
java·c++·算法
KeyAction66665 小时前
AI改写战争规则,也在改写商业规则:体系对抗时代已经到来
大数据·人工智能
小蒋观天下5 小时前
专项方案:大场景港口AI安防、多干扰环境下的算法调优与落地实操
人工智能·深度学习·算法·安全·机器学习·计算机视觉·ai大模型
冬奇Lab5 小时前
一天一个开源项目(第231篇):MiniMind —— 花3块钱、2小时,从零训练一个 64M 参数的大语言模型
人工智能·开源·资讯
一个风轻云淡5 小时前
GCC 和 GDB命令简单解读
java·linux·前端