SpringAI RAG 智能问答实战:用自然语言查询知识库

1. 引言

在之前的文章中,我们体验了 SpringAI 的基本对话能力。但在实际业务中,大模型往往需要结合企业内部的私有知识库来回答问题,这就是 RAG(检索增强生成)的核心场景。

本文将带你从零开始,使用 SpringAI 构建一个完整的 RAG 智能问答系统。我们将以"美团问答"知识库为例,逐步实现知识库的索引、检索,并与大模型整合,最终让 AI 能够基于我们提供的知识准确回答用户问题。

2. RAG 基本流程回顾

RAG 全称 Retrieval-Augmented Generation(检索增强生成),其核心思想是:在让大模型回答问题之前,先从知识库中检索出与问题相关的信息,然后将这些信息作为上下文提供给大模型,从而生成更准确、更可靠的答案。

RAG 整体分为两个阶段:

2.1 Index(索引阶段)

这一阶段主要是对已有知识库进行处理:

  1. 加载知识库文件:读取本地的文本、PDF、JSON 等文件。
  2. 拆分知识条目:将长文本拆分成一个个独立的知识单元(如问答对)。
  3. 向量化并存入向量数据库:将每个知识条目通过 Embedding 模型转换为向量,并存入向量数据库(如 Redis、Pgvector 等)。

2.2 Retrieval(检索阶段)

当用户提出问题时:

  1. 问题向量化:将用户的问题同样进行向量化处理。
  2. 相似度检索:到向量数据库中检索出与问题最相关的 Top-K 条知识。
  3. 组装 Prompt:将用户问题 + 检索到的知识 + 系统提示词组装成一个完整的 Prompt。
  4. 生成回答:将 Prompt 发送给大模型,由大模型分析整合后给出最终答案。

3. 环境准备

3.1 依赖配置

首先,在 pom.xml 中引入 SpringAI 的核心依赖以及 Redis 向量数据库支持:

xml 复制代码
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-redis-store-spring-boot-starter</artifactId>
</dependency>

3.2 配置文件

application.yml 中配置 Redis 连接和向量数据库索引信息:

yaml 复制代码
spring:
  ai:
    vectorstore:
      redis:
        index: meituan-rag          # Redis 中的索引名称
        initialize-schema: true     # 是否初始化 Redis 中的索引,默认 false
  data:
    redis:
      host: 127.0.0.1
      # password: 123qweasd
      # port: 6379

注意 :Redis 需要安装 redisSearchredisJSON 插件才能支持向量检索功能。

4. Index 索引阶段实战

4.1 加载知识库文件

SpringAI 提供了 DocumentReader 接口来读取知识库文件。目前内置了 JsonReaderTextReader 两个实现类。

我们将 meituan-questions.txt 文件放到项目的 classpath 目录下,然后使用 TextReader 读取:

java 复制代码
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class DocumentService {

    @Value("classpath:meituan-questions.txt")
    private Resource resource;

    public List<Document> loadDocuments() {
        TextReader textReader = new TextReader(resource);
        textReader.getCustomMetadata().put("filename", "meituan-questions.txt");
        List<Document> documents = textReader.get();
        return documents;
    }
}

4.2 拆分知识条目

加载出来的文件内容是一个整体,我们需要将其拆分成一个个独立的问答条目。SpringAI 提供了 DocumentTransformer 接口,但默认的 TokenTextSplitter 往往不能满足业务需求,因此我们需要自定义拆分逻辑。

这里我们借助 TextSplitter 抽象类,实现一个按双换行符拆分的工具类:

java 复制代码
import org.springframework.ai.transformer.splitter.TextSplitter;

import java.util.List;

public class MeituanTextSplitter extends TextSplitter {

    @Override
    protected List<String> splitText(String text) {
        return List.of(text.split("\\s*\\R\\s*\\R\\s*"));
    }
}

然后在 DocumentService 中应用这个拆分器:

java 复制代码
@Service
public class DocumentService {

    @Value("classpath:meituan-questions.txt")
    private Resource resource;

    public List<Document> loadDocuments() {
        TextReader textReader = new TextReader(resource);
        textReader.getCustomMetadata().put("filename", "meituan-questions.txt");
        List<Document> documents = textReader.get();

        // 拆分文件
        DocumentTransformer transformer = new MeituanTextSplitter();
        List<Document> list = transformer.apply(documents);
        return list;
    }
}

4.3 向量化并存入向量数据库

SpringAI 通过 VectorStore 接口统一操作各种向量数据库。我们使用 Redis 作为向量数据库,将拆分后的知识条目向量化并存储:

java 复制代码
import jakarta.annotation.Resource;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class MeituanRagController {

    @Resource
    private DocumentService documentService;

    @Resource
    private VectorStore vectorStore;

    @GetMapping("/loadDocument")
    public List<Document> loadDocument() {
        // 解析知识库文件
        List<Document> documents = documentService.loadDocuments();
        // 保存到向量数据库
        vectorStore.add(documents);
        return documents;
    }
}

访问 /loadDocument 接口后,SpringAI 会自动调用容器中的 EmbeddingModel 组件对知识条目进行向量化,并将结果存入 Redis。至此,Index 索引阶段完成。

5. Retrieval 检索阶段实战

5.1 基础检索

有了向量数据库后,检索就变得非常简单。直接使用 VectorStoresimilaritySearch 方法即可:

java 复制代码
@Resource
private VectorStore vectorStore;

public List<Document> search(String query) {
    return vectorStore.similaritySearch(
        SearchRequest.query(query).withTopK(5)
    );
}

5.2 组装 Prompt 并调用大模型

检索到相关知识后,我们需要将用户问题、检索结果和系统提示词组装成一个完整的 Prompt,再发送给大模型。

首先,在 classpath 下创建提示词模板文件 rag-prompt-template.st

复制代码
{question}

Context information is below.
---------------------
{question_answer_context}
---------------------
Given the context and provided history information and not prior knowledge,
reply to the user comment. If the answer is not in the context, inform
the user that you can't answer the question.

然后编写检索并回答的接口:

java 复制代码
@Value("classpath:/rag-prompt-template.st")
private Resource ragPromptResource;

@Resource
private ChatClient.Builder builder;

@Resource
private VectorStore vectorStore;

@GetMapping("/ragask")
public String vectorSearch(@RequestParam("message") String message) {
    // 查询相关知识条目
    List<Document> documents = vectorStore.similaritySearch(
        SearchRequest.query(message).withTopK(5)
    );

    // 请求 AI 大模型
    ChatClient client = builder.build();
    String result = client.prompt()
        .user(promptUserSpec -> promptUserSpec
            .text(ragPromptResource)
            .param("question", message)
            .param("question_answer_context", documents)
        )
        .call()
        .content();
    return result;
}

现在,访问 /ragask?message=如何退款,AI 就会基于知识库中的内容给出准确回答。

6. 使用 Advisor 简化检索流程

SpringAI 提供了一个更简洁的方式------QuestionAnswerAdvisor。它是一个 Advisor 切片,可以在与大模型交互之前自动完成检索和消息组装:

java 复制代码
@Resource
private ChatClient.Builder builder;

@Resource
private VectorStore vectorStore;

@GetMapping("/ragdemo")
public String rag(@RequestParam("message") String message) {
    ChatClient client = builder.build();
    return client.prompt(message)
        .advisors(new QuestionAnswerAdvisor(vectorStore))
        .call()
        .content();
}

QuestionAnswerAdvisor 内部使用的默认消息模板与我们之前手动编写的模板类似:

复制代码
Context information is below.
---------------------
{question_answer_context}
---------------------
Given the context and provided history information and not prior knowledge,
reply to the user comment. If the answer is not in the context, inform
the user that you can't answer the question.

使用 Advisor 后,代码量大幅减少,逻辑也更加清晰。

7. 总结

本文我们完整地体验了使用 SpringAI 构建 RAG 智能问答系统的全过程:

阶段 核心操作 SpringAI 组件
Index 加载文件 → 拆分条目 → 向量化存储 DocumentReaderDocumentTransformerVectorStore
Retrieval 问题向量化 → 相似度检索 → 组装 Prompt → 调用大模型 VectorStoreChatClientQuestionAnswerAdvisor

SpringAI 目前仍处于快速迭代阶段,API 可能会有较大变化,但 RAG 的核心思想是通用的。掌握了本文的思路,无论未来框架如何演进,你都能快速上手。

下一步,你可以尝试:

  • 替换为其他向量数据库(如 Pgvector、Milvus)
  • 使用不同的 Embedding 模型
  • 优化知识库的拆分策略
  • 结合多轮对话历史提升问答体验
相关推荐
四六的六19 小时前
端侧模型多端部署实战:从格式转换到灰度发布,Web 和移动端统一部署流水线
前端·人工智能·大模型·ai编程·ai模型·ai产品·端侧ai
计算机小白一个20 小时前
蓝桥杯 Java B 组之哈希表应用(两数之和、重复元素判断)
java·数据结构·算法·蓝桥杯
深小乐20 小时前
用AI做了6首歌曲MV后,我最大的收获不是会做了,而是干中学
人工智能
郑午时光20 小时前
全球首发!2026年适合IP短剧长视频的AI视频生成工具,即梦seedance2.5轻松做短剧
人工智能·tcp/ip·音视频
智道天成20 小时前
浙江代办食品生产许可证怎么办理,哪些企业可以申请全程代办服务
人工智能
汇策研习社20 小时前
双指标共振交易体系:GMMA趋势骨架 + MACD动能验证实战全解
大数据·人工智能·信息可视化·金融·区块链·fastbull
皮皮虾❀20 小时前
阿里巴巴“千问办公”公测深度解析:企业级Agent如何重塑智能办公
人工智能·阿里云·云计算
G311354227320 小时前
大模型不可用时,业务还能不能继续:企业需要设计降级方案
大数据·服务器·数据库·人工智能·深度学习
TechEdu20260620 小时前
[人工智能]TensorFlow深度学习框架工程实践概览
人工智能·深度学习·ai·tensorflow
qq_4542450320 小时前
大模型循环调用:从协议级到应用级的循环控制谱系
人工智能