一、前言:为什么 RAG 是企业AI落地的刚需?

前面我们完成了基础对话、多轮记忆、MCP工具调用、Agent智能体,让AI具备了聊天、思考、做事的能力。

但所有通用大模型,都存在三个无法规避的硬伤:

  • 知识静态滞后:训练数据固定,无法学习企业最新制度、业务文档、产品手册

  • 无私有业务认知:完全不懂公司内部业务规则、专属流程、私有资料

  • 严重幻觉问题:面对未知业务问题,会凭空编造答案,绝对无法上线生产

想要让AI 适配企业业务、基于内部文档精准答疑、杜绝幻觉,唯一低成本、高效率、可迭代的落地方案就是 RAG(检索增强生成)

本篇采用 Spring AI 原生高阶写法:使用 QuestionAnswerAdvisor 自动实现检索增强,告别手动拼接上下文,架构更标准、代码更简洁、完全贴合生产规范。

二、RAG核心原理(极简通俗版)

2.1 核心思想:先检索,后生成

传统大模型问答:用户提问 → 模型凭历史训练数据回答 → 易滞后、易幻觉。

RAG 增强问答:用户提问 → 向量库语义检索私有文档 → 模型仅基于检索到的真实资料生成答案 → 数据真实、无幻觉、可随时更新知识库

2.2 标准RAG两大阶段

阶段一:知识库离线构建(初始化执行一次)

文档加载 → 文本切片 → 向量化 → 存入向量库

阶段二:在线智能问答(用户每次提问)

问题向量化 → 语义相似度检索 → 自动拼接上下文Prompt → 模型精准生成答案

2.3 RAG vs 模型微调(企业选型结论)

  • 微调:改模型权重、成本极高、训练周期长、文档更新需要重新训练、容易灾难性遗忘

  • RAG:不改模型、只改数据、零训练成本、支持动态增量更新、随时更新业务文档

企业业务文档频繁变更,RAG 是绝对最优解

三、项目核心依赖

本篇所需完整依赖,适配文档解析、向量存储、AI问答全套能力,完整的pom.xml依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.14</version>
        <relativePath/>
    </parent>

    <groupId>demo.ai</groupId>
    <artifactId>spring-ai-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.ai.version>1.1.6</spring.ai.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring.ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-client-chat</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-zhipuai</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pdf-document-reader</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-advisors-vector-store</artifactId>
        </dependency>
    </dependencies>

</project>

四、企业级RAG核心配置类

4.1 统一向量库+切片+记忆全局配置

package demo.ai.conf;

import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.document.DocumentTransformer;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class VectorStoreConfig {

    /**
     * 初始化向量库
     * SimpleVectorStore:内存向量库,开箱即用,适合开发、测试、小型项目
     * 生产建议替换:Milvus / PgVector / RedisVector
     */
    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel) {
        return SimpleVectorStore.builder(embeddingModel).build();
    }

    /**
     * 全局文本切片器
     * 单片段800字符,兼顾语义完整性与检索精度
     */
    @Bean
    public DocumentTransformer textSplitter() {
        return TokenTextSplitter.builder().withChunkSize(800).build();
    }

    /**
     * 窗口式会话记忆
     * 保留最近10条对话,防止上下文溢出,支撑多轮RAG追问
     */
    @Bean
    public MessageWindowChatMemory chatMemory() {
        return MessageWindowChatMemory.builder()
                .maxMessages(10)
                .build();
    }

    /**
     * 构建带记忆的ChatClient
     * 为RAG多轮问答、上下文联动提供基础能力
     */
    @Bean
    public ChatClient chatClient(ChatClient.Builder builder, MessageWindowChatMemory chatMemory) {
        return builder
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
    }
}

4.2 核心配置说明

  • VectorStore:依托框架自动注入的EmbeddingModel实现文本向量化,统一向量入口

  • TokenTextSplitter:全局统一切片规则800字符,相比默认配置,检索精度更高、语义更完整

  • MessageWindowChatMemory:限制最大对话数,解决多轮RAG上下文过长、Token溢出问题

  • ChatClient:全局注入记忆顾问,天然支持RAG多轮关联追问

五、知识库加载工具类(PDF文档解析入库)

新版知识库加载工具类做了全面升级,摒弃了简陋的异常捕获,采用标准ETL流水线设计,新增日志可视化、文本增量入库能力、自定义文档路径配置。同时拆分PDF加载、纯文本加载双方法,既支持静态PDF知识库初始化,也支持动态文本实时入库,适配更多业务场景,结构更贴合生产级ETL规范。

package demo.ai.rag;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentReader;
import org.springframework.ai.document.DocumentTransformer;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
@Slf4j
public class KnowledgeBaseLoader {

    private final VectorStore vectorStore;
    private final DocumentTransformer textSplitter;

    @Value("classpath:doc/公司考勤制度.pdf")
    private org.springframework.core.io.Resource pdfResource;


    /**
     * 加载 PDF 文档到向量数据库
     * 标准ETL流水线:抽取(Extract) - 转换(Transform) - 加载(Load)
     */
    public void loadPdfToVectorStore() {

        // 1. 抽取(Extract):使用 PdfDocumentReader 读取 PDF
        DocumentReader pdfReader = new PagePdfDocumentReader(pdfResource);
        List<Document> documents = pdfReader.get();

        log.info("抽取完成,共 {} 个页面", documents.size());

        // 2. 转换(Transform):对文档进行文本分割,生成便于检索的知识碎片
        List<Document> chunks = textSplitter.apply(documents);
        log.info("切分完成,共 {} 个知识片段", chunks.size());

        // 3. 加载(Load):将向量存储到 VectorStore
        // ETL 流水线链式调用风格
        vectorStore.write(chunks);
        log.info("向量存储完成");
    }

    /**
     * 加载纯文本内容到向量数据库
     * 支持动态传入文本与元数据,适配实时增量入库场景
     */
    public void loadTextToVectorStore(String textContent, Map<String, Object> metadata) {
        Document doc = new Document(textContent, metadata);
        List<Document> chunks = textSplitter.apply(List.of(doc));
        vectorStore.write(chunks);
        log.info("文本内容已加载,共 {} 个知识片段", chunks.size());
    }

}

六、RAG核心控制器

package demo.ai.controller;

import demo.ai.rag.KnowledgeBaseLoader;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/rag")
public class RagController {

    @Resource
    private ChatClient chatClient;

    @Resource
    private KnowledgeBaseLoader knowledgeBaseLoader;

    @Resource
    private VectorStore vectorStore;

    /**
     * 手动加载/刷新知识库
     * 适合文档更新后手动刷新,无需重启项目
     * 访问:http://localhost:8080/rag/load
     */
    @GetMapping("/load")
    public String loadKnowledgeBase() {
        knowledgeBaseLoader.loadPdfToVectorStore();
        return "✅ 知识库加载成功";
    }

    /**
     * 标准RAG智能问答接口
     * 自动语义检索 + 多轮上下文记忆 + 私有文档答疑
     * 访问:http://localhost:8080/rag/ask?msg=你的问题
     */
    @GetMapping("/ask")
    public String ask(@RequestParam String msg) {
        return chatClient.prompt()
                .user("简单介绍下" + msg)
                // 核心:开启官方RAG检索增强顾问
                .advisors(QuestionAnswerAdvisor.builder(vectorStore).build())
                // 绑定固定会话ID,实现多轮追问上下文关联
                .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, "default-001"))
                .call()
                .content();
    }
}

七、完整测试流程(可直接复现)

7.1 准备私有知识库文档

下载文章顶部的公司考勤制度.pdf(也可以ai生成一个pdf),存放到项目 resources/doc/公司考勤制度.pdf,放入企业考勤、薪酬、加班补偿等私有业务制度内容,适配本次RAG问答测试场景。

7.2 初始化加载知识库

访问接口:http://localhost:8080/rag/load

控制台打印切片数量,代表向量化入库成功。

7.3 单轮RAG精准问答

示例提问:加班怎么补偿的?

RAG执行逻辑:

  1. 自动将问题向量化

  2. 在向量库中语义匹配相关文档片段

  3. 自动拼接参考文档进Prompt

  4. 模型仅基于私有文档作答,杜绝幻觉

7.4 强关联多轮追问(核心能力)

依托全局会话记忆,天然支持上下文关联追问:

第一轮:加班怎么补偿的?

第二轮(无任何参数):那工作日和周末补偿有什么区别?

AI自动读取上一轮对话上下文 + 知识库数据,精准迭代回答。

八、生产环境选型 & 最佳实践

✅ 最佳实践

  • 开发测试:使用 SimpleVectorStore 内存向量库,零配置、快速开发

  • 生产环境:替换为 Milvus、PgVector、RedisVector 持久化向量库

  • 切片规范:通用业务文档采用800字符分块,保证语义完整

  • 知识库动态更新:提供接口手动刷新,适配业务文档迭代

  • 固定会话ID:业务问答统一会话标识,保证多轮连贯

❌ 高频踩坑点

  • 文档路径错误导致加载失败,必须统一放在 resources/doc 下

  • 切片过大导致语义混杂、过小导致上下文断裂

  • 生产使用内存向量库,重启项目知识库丢失

  • 未开启会话记忆,多轮追问无法关联上下文

九、本篇学习总结

本篇基于企业级标准源码,从零落地 Spring AI 高阶RAG知识库能力,核心收获:

  • 掌握分层RAG架构:向量库、切片器、会话记忆、ChatClient解耦配置

  • 熟练使用 QuestionAnswerAdvisor 官方自动化RAG方案,告别手写冗余代码

  • 实现PDF私有文档解析、向量化、入库、智能问答全流程

  • 掌握可动态刷新的知识库加载方案,适配业务迭代

  • 实现RAG+多轮记忆联动,完成强关联上下文智能答疑

  • 分清开发/生产向量库选型方案,具备项目上线能力

至此,我们已经集齐 Spring AI 基础对话、多轮记忆、MCP工具调用、Agent智能体、企业级RAG知识库 全套核心能力。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐