【SpringAIAlibaba新手村系列】(12)RAG 检索增强生成技术

0 阅读7分钟

第十二章 RAG 检索增强生成技术

版本标注

  • Spring AI: 1.1.2
  • Spring AI Alibaba: 1.1.2.0

章节定位

  • 本章的 RetrievalAugmentationAdvisor + VectorStore 仍然是经典 RAG 入门方案。
  • 但 Spring AI Alibaba 1.1.2.x 官方代码已经进一步演进到 RAG Workflow 思路,典型流程是:Query -> Rewrite -> Retrieve -> Prepare -> Agent -> Response
  • 因此,本章建议理解为“基础版 RAG”;复杂业务更推荐升级为 Graph/Workflow 版本的 RAG。

s01 > s02 > s03 > s04 > s05 > s06 > s07 > s08 > s09 > s10 > s11 > [ s12 ] s13 > s14 > s15 > s16 > s17 > s18

"先查资料, 再作回答" -- RAG 的核心不是更会编, 而是更少胡说。


一、为什么需要 RAG?

1.1 AI 的知识局限

AI 大模型虽然很强,但它有以下局限:

  1. 知识有时效性:训练数据截止某个时间点,之后的事不知道
  2. 专业知识缺乏:某些垂直领域的知识可能不准确
  3. 企业内部信息:它不知道你公司的业务和数据

1.2 传统解决方式

微调(Fine-tuning) :让 AI 重新训练

  • 缺点:成本高、需要大量数据、时间长
  • 适合:通用知识需要调整的场景

1.3 RAG 的诞生

RAG(Retrieval-Augmented Generation,检索增强生成) 的核心思想是:

"不改变 AI 的基础能力,而是在它回答问题时,先去知识库查找相关信息,然后把信息连同问题一起发给 AI"

传统方式:
用户 ──→ AI ──→ 直接回答(AI靠记忆)

RAG方式:
用户 ──→ 向量搜索 ──→ 知识库查找相关信息 ──→ 
      ──→ 把"问题+找到的信息"一起发给 AI ──→ AI结合信息回答

二、RAG 工作流程

2.0 工作流化的 RAG 思路

在很多入门教程里,我们通常把 RAG 理解成:

  1. 用户提问
  2. 向量检索
  3. 把检索结果塞进 Prompt
  4. 调用 LLM 回答

这条链路没有问题,但在 Spring AI Alibaba 1.1.2.x 的官方 workflow 示例中,RAG 已经进一步升级成更可控的多步骤流程:

Query -> Rewrite -> Retrieve -> Prepare -> Agent -> Response

各步骤的含义如下:

  • Rewrite:先让模型把原始问题改写成更适合检索的查询语句
  • Retrieve:基于改写后的查询做向量检索
  • Prepare:把命中的文档、用户问题、约束条件整理成更稳定的上下文
  • Agent:再交给 ReactAgent 或普通聊天模型生成最终答案

这种做法的好处是:

  1. 检索命中率通常更高
  2. Prompt 注入更清晰
  3. 更容易做调试、观测和节点拆分
  4. 后续更容易迁移到 Graph 和工作流编排

所以你可以把本章先学成“基础 RAG”,再进一步理解成可拆分、可编排的工作流化 RAG。

2.1 完整的 RAG 流程

┌─────────────────────────────────────────────────────────────┐
│                    RAG 完整流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   阶段一:数据准备(离线)                                    │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐             │
│   │ 文档集合 │───→│ 文本分块 │───→│ 向量化   │───→存入向量库│
│   └──────────┘    └──────────┘    └──────────┘             │
│                                                             │
│   阶段二:问答推理(在线)                                    │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐             │
│   │ 用户问题 │───→│ 向量化   │───→│ 相似度搜索│───→获取相关 │
│   └──────────┘    └──────────┘    └──────────┘    文档      │
│                                            ↓                │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐             │
│   │ 构建Prompt│←── │ 拼接上下文│←── │ 注入知识  │            │
│   └──────────┘    └──────────┘    └──────────┘             │
│                                            ↓                │
│                                   ┌──────────┐             │
│                                   │ 调用LLM │              │
│                                   └──────────┘             │
│                                            ↓                │
│                                   返回结合知识后的答案        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.2 本章场景:运维故障查询

这一章不是直接让大模型“凭空回答运维问题”,而是先给它准备一份本地知识资料,再通过 RAG 的方式让它去查资料后回答。

这份资料就放在项目的 resources 目录下,文件名通常叫:

ops.txt

你可以把它理解成一份简化版的“运维故障知识库”。里面会提前写好一些故障编码及对应说明,例如:

  • 故障编码:A0001
  • 故障编码:C2222
  • 对应解释:存储系统异常、网络不通、服务不可用等

也就是说,本章真正要做的事情不是“训练一个懂运维的大模型”,而是:

  1. 先把 ops.txt 里的故障资料读出来
  2. 将这些文本切分、向量化并存入向量数据库
  3. 用户提问时,先去向量库里检索最相关的故障说明
  4. 再把检索结果和用户问题一起交给大模型生成答案

这样一来,用户在调用接口时,不需要自己知道完整故障说明,只需要问类似:

  • A0001 是什么故障?
  • C2222 怎么处理?
  • 网络不通对应哪个故障编码?

系统就能先从 ops.txt 这份知识资料里找到相关内容,再让 AI 组织成更自然的回答。

所以这一章的场景,准确地说应该是:

基于 resources/ops.txt 构建一个小型运维知识库,然后通过 RAG 实现“查故障码、问故障原因、做运维问答”的效果。


三、项目代码详解

3.1 配置类

package com.atguigu.study.config;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig
{
    /**
     * 注册 Qwen 的 ChatClient
     */
    @Bean("qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("dashScopeChatModel") ChatModel chatModel)
    {
        return ChatClient.builder(chatModel).build();
    }

    /**
     * 注入已经自动配置好的 VectorStore
     */
    @Bean
    public RagService ragService(ChatClient qwenChatClient, VectorStore vectorStore)
    {
        return new RagService(qwenChatClient, vectorStore);
    }
}

上面这段代码想表达的重点不是“必须手写某一种向量库实现”,而是:

  1. EmbeddingModel 负责文本向量化
  2. VectorStore 负责向量检索
  3. ChatClient + Advisor 负责把检索结果注入到问答链路中

如果你选择 Redis、Milvus、PGVector 或其他实现,核心思路都一样。

3.2 RAG 控制器

package com.atguigu.study.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

/**
 * RAG 控制器
 * 展示如何使用检索增强生成来回答专业问题
 */
@RestController
public class RagController
{
    @Resource(name = "qwenChatClient")
    private ChatClient chatClient;

    // 注入已配置的向量数据库
    @Resource
    private VectorStore vectorStore;

    /**
     * RAG 问答接口
     * 
     * 接口:http://localhost:8012/rag4aiops?msg=00000
     * 接口:http://localhost:8012/rag4aiops?msg=C2222
     * 
     * 测试步骤:
     * 1. 先调用 add 接口(如果没有历史数据)
     * 2. 再调用 rag4aiops 接口询问故障码
     * 
     * @param msg 用户的故障编码查询
     * @return AI 结合知识库返回的答案
     */
    @GetMapping("/rag4aiops")
    public Flux<String> rag(String msg)
    {
        // 1. 定义系统提示词
        //    告诉 AI 它的角色是"运维工程师",按照知识库中的编码给出解释
        String systemInfo = """
                你是一个运维工程师,按照给出的编码给出对应故障解释,否则回复找不到信息。
                """;

        // 2. 创建检索增强Advisor
        //    这是 RAG 的核心组件!
        //    - VectorStoreDocumentRetriever 负责从向量数据库中检索相关内容
        //    - RetrievalAugmentationAdvisor 负责把检索结果注入到Prompt中
        RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
                // 指定使用向量数据库进行检索
                .documentRetriever(
                    VectorStoreDocumentRetriever.builder()
                        .vectorStore(vectorStore)
                        .build()
                )
                .build();

        // 3. 构建并发送请求
        //    在 .advisors() 中添加 RAG Advisor
        //    它会自动执行:检索 → 注入 → 调用AI → 返回结果
        return chatClient
                .prompt()
                .system(systemInfo)
                .user(msg)
                .advisors(advisor)     // 关键:添加RAG增强
                .stream()
                .content();
    }
}

四、RAG 的核心组件

4.1 RetrievalAugmentationAdvisor

这是 Spring AI 提供的 RAG 增强组件,它的作用:

  1. 拦截用户请求
  2. 从向量数据库检索相关信息
  3. 将检索结果注入到 Prompt 中
  4. 调用 AI,让它基于检索结果回答
  5. 返回最终答案

4.2 DocumentRetriever

文档检索器,负责:

  • 将用户问题转为向量
  • 在向量数据库中执行相似度搜索
  • 返回匹配度最高的文档

五、本章小结

5.1 核心概念

概念说明
RAG检索增强生成,让AI结合知识库回答问题
VectorStore向量数据库,存储文档的向量
RetrievalAugmentationAdvisorRAG核心组件,自动检索+注入
DocumentRetriever从向量数据库检索文档的组件

5.2 RAG 工作流程

提问 → 向量化 → 相似度搜索 → 提取相关文档 → 注入Prompt → 调用AI → 返回答案

5.3 应用场景

  • 企业内部知识问答
  • 产品手册查询
  • 客服问答系统
  • 专业领域知识库

本章重点

  1. 理解 RAG 的核心原理
  2. 掌握 RetrievalAugmentationAdvisor 的使用
  3. 理解 RAG 在实际场景中的应用

下章剧透(s13):

学会了 RAG 技术后,下一章我们将学习 Tool Calling(工具调用)——让 AI 能够"调用外部函数"来完成真实任务!


📝 编辑者:Flittly
📅 更新时间:2026年4月
🔗 相关资源Spring AI RAG 官方文档