场景

Spring AI ChatMemory 对话记忆配置JDBC方式到Mysql数据库实战示例与原理讲解:

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/161040572

基于上述基础,学习RAG的使用。

大语言模型(LLM)存在知识滞后、易产生幻觉、领域知识不足等局限性。

检索增强生成(Retrieval Augmented Generation,RAG)技术通过“外部知识检索 + LLM生成”的模式,

在回答问题前先从外部知识源中查找相关信息,再结合自身能力组织语言输出,有效弥补了这些缺陷。

本文将基于之前的 Spring AI 1.1.2 + Ollama 技术栈,系统性地介绍 RAG 的核心概念、Spring AI 中的实现方式,

并提供可直接运行的完整实例代码。

一、RAG 核心概念与工作流程

1.1 什么是 RAG?

RAG 技术的核心在于将检索与生成相结合。传统的生成模型依赖训练数据来生成回答,

当面对新问题或新兴领域知识时,可能产生不准确或不合逻辑的回答。

RAG 引入检索机制,在接收到用户问题后,首先从外部知识库中检索相关信息,

然后将其与原始问题一同输入给生成模型,从而产生更准确、上下文相关的答案。

1.2 RAG 四步流程

步骤 说明 关键技术
① 文档加载与分块(Ingestion) 将原始文档(PDF、TXT 等)加载进来,切割成适合嵌入的小片段(chunks) DocumentReader、TokenTextSplitter
② 向量化与存储(Embedding & Store) 用 Embedding 模型将文本片段转换为高维向量,存入向量数据库 EmbeddingModel、VectorStore
③ 语义检索(Retrieval) 用户提问时,将问题同样向量化,在数据库中执行相似度搜索,检索最相关片段 SearchRequest、相似度计算
④ 增强生成(Generation) 将检索到的相关文档片段作为上下文,与用户问题一起提交给 LLM,生成精准回答 ChatClient + PromptTemplate

1.3 向量数据库

向量数据库专门用于存储、管理和高效检索高维向量数据。

其核心优势是支持语义级搜索——例如“苹果手机”和“iPhone”虽关键词不同,但语义相近,其向量距离很小,可被精准匹配到。

Spring AI 通过 VectorStore 接口抽象了向量数据库的访问,支持多种实现:

SimpleVectorStore(内存向量库,适合原型开发)

Elasticsearch

PgVector

Milvus / Weaviate / Chroma 等

1.4 关键组件依赖关系

组件 角色 Spring AI 接口
Document 表示原始文档及其元数据 org.springframework.ai.document.Document
DocumentReader 从文件系统加载文档 JsonReaderTextReaderPagePdfDocumentReader
TextSplitter 将长文本切割成小块 TokenTextSplitter
EmbeddingModel 将文本转换为向量 EmbeddingModel(Ollama 提供)
VectorStore 存储和检索向量 SimpleVectorStoreElasticsearchVectorStore 等
QuestionAnswerAdvisor 拦截用户请求,自动检索并注入上下文 QuestionAnswerAdvisor.builder(vectorStore)

二、Spring AI RAG 实现方式

Spring AI 通过 Advisor API 为 RAG 提供了开箱即用的支持,核心组件是 QuestionAnswerAdvisor。

使用时需要添加 spring-ai-advisors-vector-store 依赖

2.1 QuestionAnswerAdvisor 工作原理

当用户问题发送到 AI 模型时,QuestionAnswerAdvisor 会查询向量数据库以获取与用户问题相关的文档,

将这些文档附加到用户文本中作为上下文,然后再提交给 AI 模型生成回答。其可配置项为:

配置项 说明 示例
similarityThreshold 相似度阈值,低于此值的文档将被过滤 0.8
topK 返回最相关的前 K 个文档 6
FILTER_EXPRESSION 动态过滤表达式(类似 SQL WHERE) "type == 'Spring'"
promptTemplate 自定义提示词模板,控制上下文与用户问题的拼接方式 包含 {query} 和 {question_answer_context} 占位符

2.2 检索与生成分离的 ETL 模型

Spring AI RAG 遵循 ETL(Extract-Transform-Load)模型:

Extract(提取):从知识库中读取文档。

Transform(转换):将文档分割为小块,并通过 Embedding 模型转换为向量。

Load(加载):将向量数据写入向量数据库。

这三个步骤通常在应用启动时完成。

运行时,基于 ETL 模型实现真正的 RAG——用户的查询被嵌入,向量数据库被查询,检索到的文档被注入到 LLM 的提示词中。

2.3 RAG Advisor 与其他 Advisor 的组合

RAG 的 QuestionAnswerAdvisor 可以与之前介绍的 Advisor 自由组合:

组合方式 效果
QuestionAnswerAdvisor + MessageChatMemoryAdvisor 带记忆的 RAG 问答
QuestionAnswerAdvisor + SensitiveWordGuardAdvisor 安全过滤 + RAG
QuestionAnswerAdvisor + SimpleLoggerAdvisor 调试日志
全部组合 安全过滤 → 日志记录 → 对话记忆 → 文档检索 → LLM 生成

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi

实现

以下示例基于 Spring AI 1.1.2 + Ollama + SimpleVectorStore,演示如何从零构建一个本地 RAG 知识库问答系统。

pom.xml

    <properties>
        <java.version>17</java.version>
        <spring-ai.version>1.1.2</spring-ai.version>
    </properties>

    <!-- 新增:使用 Spring AI BOM 统一管理所有模块版本 -->
    <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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring AI Ollama 核心 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-ollama</artifactId>
        </dependency>

        <!-- RAG Advisor(核心:提供 QuestionAnswerAdvisor) -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-advisors-vector-store</artifactId>
        </dependency>

        <!-- Tika 文档读取器(支持 PDF、Word、TXT 等多种格式) -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-tika-document-reader</artifactId>
        </dependency>

    </dependencies>

示例知识库文件

在src/main/resources/knowledge-base/badao-internal.txt 中创建:

Badao科技内部知识库

## 公司政策
- 员工年假:入职满1年享受10天带薪年假,满3年增加到15天。
- 远程办公:每周三、周五为固定远程办公日。
- 报销流程:所有报销需在“易报销”系统提交,金额超过500元需部门经理审批。

## 内部项目
- 项目代号“天枢”:基于Spring AI与Ollama构建的企业智能助手,负责人为张三。
- 项目代号“瑶光”:下一代实时数据湖方案,采用Apache Iceberg + Flink,负责人为李四。
- 内部编码规范:所有Java项目必须使用Lombok,禁止使用var关键字,SQL必须使用参数化查询。

## 技术选型
- 消息中间件:统一使用RocketMQ,禁止引入RabbitMQ。
- 向量数据库:生产环境采用PgVector,开发环境可使用SimpleVectorStore。
- 前端框架:统一使用React 18 + Ant Design 5.x。

## 团队联系人
- 基础架构组:赵六(zhaoliu@badao.com)
- AI组:王五(wangwu@badao.com)
- 数据组:钱七(qianqi@badao.com)

application.yml

​
server:
  port: 886

spring:                       # 替换为实际密码
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: qwen2.5:7b-instruct
        options:
          temperature: 0.3           # RAG 场景建议使用较低温度,减少幻觉
      embedding:
        model: nomic-embed-text      # Embedding 模型(用于文档向量化)
        options:
          num-batch: 4               # 一次处理的文本数量


logging:
  level:
    org.springframework.ai.rag: DEBUG
    org.springframework.ai.vectorstore: DEBUG

​

Embedding 模型下载(项目启动前执行)

ollama pull nomic-embed-text

模型选型说明:

Chat 模型:

qwen2.5:7b-instruct 支持工具调用且中文能力强,适合生成自然语言回答。也可选择 deepseek-r1:8b、llama3.1:8b 等。

Embedding 模型:

nomic-embed-text 生成 768 维向量,免费且质量好。也可选择 bge-m3(1024 维)、mxbai-embed-large(1024 维)等,

但需修改配置文件中 dimensions 参数。

核心要求:

Chat 模型和 Embedding 模型都必须是 Ollama 本地已下载的模型,

且 Embedding 模型的向量维度必须与向量数据库的 dimensions 配置一致。

VectorStoreConfig — 文档加载与向量存储初始化

Spring AI 在 RAG 流程中默认不负责文档分词,文档的加载与分词需要开发者显式调用 DocumentReader 和 TextSplitter 来完成。

package com.badao.ai.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentReader;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;

import java.util.List;

@Configuration
public class VectorStoreConfig {

    private static final Logger logger = LoggerFactory.getLogger(VectorStoreConfig.class);

    @Value("classpath:knowledge-base/badao-internal.txt")
    private Resource knowledgeResource;

    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel) {
        // 创建内存向量库(开发演示用)
        return SimpleVectorStore.builder(embeddingModel).build();
    }

    @Bean
    public CommandLineRunner loadDocuments(VectorStore vectorStore) {
        return args -> {
            // 1. 使用 Tika 读取文档(自动检测文件类型)
            DocumentReader reader = new TikaDocumentReader(knowledgeResource);
            List<Document> documents = reader.get();

            logger.info("共读取到 {} 个文档", documents.size());

            // 2. 文本分块(TokenTextSplitter 按语义切分,更适合中文)
            TokenTextSplitter splitter = TokenTextSplitter.builder()
                    .withChunkSize(300)               // 每个块最多 300 token
                    .withMinChunkSizeChars(50)        // 最小块字符数,避免出现极短碎片(替代原来 minChunkSize 的功能)
                    .withMinChunkLengthToEmbed(5)     // 保留默认,过滤极短内容
                    .withKeepSeparator(true)          // 保留原文换行等分隔符
                    .build();
            List<Document> chunks = splitter.apply(documents);

            logger.info("文本切分为 {} 个片段", chunks.size());

            // 3. 写入向量数据库(自动调用 EmbeddingModel 向量化)
            vectorStore.add(chunks);

            logger.info("向量化完成,向量库初始化成功!");
        };
    }
}

要点说明:

SimpleVectorStore 是 Spring AI 内置的内存向量库实现,适合原型开发和小规模知识库(文档量不超过几千条)。

数据存在内存中,服务重启后需要重新加载,生产环境请替换为 PgVectorStore、ElasticsearchVectorStore 等持久化方案。

TokenTextSplitter 按 Token 数量切分文本,避免切断句子。chunkOverlap 设置重叠区域,保证语义连贯性,防止关键信息被切断。

TikaDocumentReader 基于 Apache Tika,可自动识别 PDF、Word、TXT、HTML 等多种文档格式,无需手动判断文件类型。

RagConfig — 注册 RAG Advisor

package com.badao.ai.config;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RagConfig {

    @Bean
    public ChatClient chatClient(ChatModel chatModel, VectorStore vectorStore) {
        return ChatClient.builder(chatModel)
                .defaultAdvisors(
                        QuestionAnswerAdvisor.builder(vectorStore)
                                .searchRequest(SearchRequest.builder()
                                        .similarityThreshold(0.7)      // 相似度阈值
                                        .topK(3)                       // 返回前 3 个最相关文档
                                        .build())
                                .build()
                )
                .build();
    }
}

RagService

package com.badao.ai.service;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;

@Service
public class RagService {

    private final ChatClient chatClient;

    public RagService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }

    /**
     * 基于知识库的 RAG 问答
     */
    public String ask(String question) {
        return chatClient.prompt()
                .user(question)
                .call()
                .content();
    }
}

控制器

package com.badao.ai.controller;

import com.badao.ai.service.RagService;
import org.springframework.web.bind.annotation.*;

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

    private final RagService ragService;

    public RagController(RagService ragService) {
        this.ragService = ragService;
    }

    @PostMapping("/rag")
    public ChatResponse rag(@RequestBody ChatRequest request) {
        String result = ragService.ask(request.message());
        return new ChatResponse(200, "success", result);
    }

    public record ChatRequest(String message) {}
    public record ChatResponse(int code, String msg, String data) {}
}

测试验证

常用优化策略

策略 说明 配置示例
提高相似度阈值 相同 topK 下减少候选文档量,剔除不相关文档 similarityThreshold(0.8)
降低相似度阈值 相同 topK 下增加候选文档量,适合文档量大的场景 similarityThreshold(0.5)
控制返回数量 topK 不宜过大,否则会超出模型上下文窗口 topK(3)
文档分块优化 调整 chunkSize 和 chunkOverlap 平衡精度和召回率 chunkSize(300)chunkOverlap(50)
动态过滤 按文档类型、日期等元数据过滤 .param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'manual'")
自定义提示词模板 控制上下文与用户问题的拼接格式,提升回答质量 见 上文

总结

环节 核心组件 关键操作
文档加载 TikaDocumentReader 自动识别多种文档格式
文本分块 TokenTextSplitter chunkSize(300)chunkOverlap(30)
向量化 EmbeddingModel Ollama 提供 nomic-embed-text 模型
向量存储 VectorStore 开发用 SimpleVectorStore,生产用 PgVector / Elasticsearch
检索 + 生成 QuestionAnswerAdvisor similarityThreshold + topK 控制检索质量
增强生成 ChatClient 检索结果自动注入上下文

RAG 技术让通用大模型能够“读懂”你的私有文档,在面对特定领域问题时给出精准答案。

结合 Spring AI 的模块化设计和 Ollama 的本地模型能力,Java 开发者可以轻松构建安全、高效、低成本的本地知识库问答系统。

本文系统地介绍了 Spring AI RAG 的核心概念与完整实现方式。关键要点回顾:

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐