Spring AI:两种内置 Advisor 快速实现 RAG

Spring AI 实战笔记:两种内置 Advisor 快速实现 RAG 检索增强

前言

之前手写 RAG 总要自己写向量检索、手动拼接 Prompt、处理空上下文等逻辑,重复且容易出问题。Spring AI 官方提供了封装好的 Advisor 组件,基于 ChatClient 的拦截机制,几行配置就能落地一套完整的 RAG 流程。

本文基于 Spring AI 1.0.0-SNAPSHOT 版本,搭配 DeepSeek 大模型与内存向量库,演示 QuestionAnswerAdvisorRetrievalAugmentationAdvisor 两种官方 RAG 实现方式,附完整代码、原理解析与踩坑总结。


一、项目环境与依赖

1. 基础环境

  • Spring Boot 3.5.3
  • JDK 17
  • Spring AI 1.0.0-SNAPSHOT
  • 底层模型:DeepSeek 对话模型 + Embedding 向量模型

2. pom.xml 核心依赖

通过 BOM 统一管理 Spring AI 版本,两个 RAG Advisor 分属不同依赖包,注意不要漏导。

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 https://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.3</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>SpringAIRAGWithAdvisor</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>17</java.version>
    </properties>

    <!-- Spring AI 版本统一管理 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.0-SNAPSHOT</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- DeepSeek 模型启动器:自动装配 ChatModel + EmbeddingModel -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-deepseek</artifactId>
        </dependency>

        <!-- QuestionAnswerAdvisor 所属依赖包 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-advisors-vector-store</artifactId>
        </dependency>

        <!-- RetrievalAugmentationAdvisor 所属依赖包(RAG 核心抽象) -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-rag</artifactId>
        </dependency>
    </dependencies>

    <!-- SNAPSHOT 版本仓库 -->
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases><enabled>false</enabled></releases>
        </repository>
        <repository>
            <id>central-portal-snapshots</id>
            <name>Central Portal Snapshots</name>
            <url>https://central.sonatype.com/repository/maven-snapshots/</url>
            <releases><enabled>false</enabled></releases>
            <snapshots><enabled>true</enabled></snapshots>
        </repository>
    </repositories>
</project>

3. 必补配置:application.yml

代码中通过构造器注入的 EmbeddingModelDeepSeekChatModel 都依赖自动装配,必须在配置文件中填写 API 信息,否则启动会报 Bean 找不到。

yaml 复制代码
spring:
  ai:
    deepseek:
      api-key: 你的 DeepSeek API Key
      base-url: https://api.deepseek.com
      chat:
        options:
          model: deepseek-chat
      embedding:
        options:
          model: deepseek-embedding

二、核心概念扫盲

1. Advisor 拦截机制

Spring AI 的 ChatClient 提供了类似 Spring MVC Interceptor 的 Advisor 机制,可以在大模型请求发起前、响应返回后做切面增强。

RAG 的本质就是一个前置拦截器

  1. 拦截用户提问
  2. 去向量库检索相关文档
  3. 将文档拼入提问/系统提示
  4. 把增强后的请求发给大模型

官方把这套流程封装成了专门的 RAG Advisor,不用我们手写拼接逻辑。

2. 两种 RAG Advisor 对比

很多人容易搞混这两个类,直接上核心差异:

对比项 QuestionAnswerAdvisor RetrievalAugmentationAdvisor
所属依赖 spring-ai-advisors-vector-store spring-ai-rag
定位 高度封装的开箱即用问答RAG 模块化可定制的RAG增强器
内部结构 内置检索逻辑 + 内置QA提示词模板 拆分为「文档检索器」+「查询增强器」,可独立替换
定制能力 弱,仅支持调整检索参数 强,可自定义检索逻辑、查询改写、上下文拼接规则
适用场景 快速Demo、简单知识库问答 生产级定制化RAG、需扩展重排/多轮改写的场景

三、完整代码实现

1. 向量库初始化:启动写入测试数据

使用 Spring AI 自带的 SimpleVectorStore 内存向量库,无需额外部署中间件,适合本地演示。项目启动时自动写入测试文档并完成向量化。

java 复制代码
package com.qcby.springairagwithadvisor.config;

import jakarta.annotation.PostConstruct;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class VectorStoreInitializer {

    private final VectorStore vectorStore;

    public VectorStoreInitializer(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    @PostConstruct
    public void init() {
        System.out.println("初始化向量数据,写入到 SimpleVectorStore 内存中...");
        List<Document> docs = List.of(
                new Document("Spring AI 是一个开源 AI 集成项目"),
                new Document("Milvus 是一款高性能向量数据库"),
                new Document("DeepSeek 是一个开源大语言模型")
        );
        vectorStore.add(docs);
        System.out.println("SimpleVectorStore 初始化完成,已写入 " + docs.size() + " 条文档。");
    }
}

知识点补充

  • Document 是 Spring AI 中文本的最小单元,包含内容、元数据、唯一ID
  • vectorStore.add() 会自动调用 EmbeddingModel 生成向量,再存入向量库
  • SimpleVectorStore 是纯内存实现,服务重启数据会丢失,生产请替换为 Milvus/PGVector

2. AI 核心配置类:两种 Advisor 装配

这是整个项目的核心,分别配置向量库、两种 RAG Advisor 以及 ChatClient。

java 复制代码
package com.qcby.springairagwithadvisor.config;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.ai.rag.generation.augmentation.ContextualQueryAugmenter;
import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever;
import org.springframework.ai.vectorstore.SearchRequest;
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
public class AIConfig {

    private final EmbeddingModel embeddingModel;

    public AIConfig(EmbeddingModel embeddingModel) {
        this.embeddingModel = embeddingModel;
    }

    /**
     * 内存向量库
     */
    @Bean
    public VectorStore vectorStore() {
        return SimpleVectorStore.builder(embeddingModel).build();
    }

    /**
     * 方式一:QuestionAnswerAdvisor - 开箱即用问答型RAG
     */
    @Bean
    public QuestionAnswerAdvisor questionAnswerAdvisor(VectorStore vectorStore) {
        return QuestionAnswerAdvisor.builder(vectorStore)
                .searchRequest(SearchRequest.builder()
                        .similarityThreshold(0.5d) // 相似度阈值,过滤低相关文档
                        .topK(6)                    // 返回最相关的前6条
                        .build())
                .build();
    }

    /**
     * 方式二:RetrievalAugmentationAdvisor - 模块化可定制RAG
     */
    @Bean
    public RetrievalAugmentationAdvisor retrievalAugmentationAdvisor(VectorStore vectorStore) {
        // 1. 文档检索器:负责向量相似度搜索
        VectorStoreDocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
                .vectorStore(vectorStore)
                .similarityThreshold(0.5)
                .topK(6)
                .build();

        // 2. 查询增强器:负责把检索到的上下文拼入用户提问
        ContextualQueryAugmenter augmenter = ContextualQueryAugmenter.builder()
                .allowEmptyContext(true) // 无匹配文档时不报错,正常放行
                .build();

        return RetrievalAugmentationAdvisor.builder()
                .documentRetriever(retriever)
                .queryAugmenter(augmenter)
                .build();
    }

    /**
     * 聊天客户端:配置默认系统提示
     */
    @Bean
    public ChatClient chatClient(DeepSeekChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("你是一个助手,回答用户问题时不要提及回复是从上下文信息中获取的," +
                        "不要回答你不知道的问题,如果你不知道答案,就回答:抱歉,我不清楚这个问题。")
                .build();
    }
}

关键知识点

  1. QuestionAnswerAdvisor:内部自带了一套标准的 QA Prompt 模板,会自动把检索到的文档作为上下文注入系统提示,开发者完全不用管 Prompt 拼接。
  2. RetrievalAugmentationAdvisor :Spring AI 新 RAG 模块的标准抽象,把「检索」和「增强」拆成了独立组件:
    • DocumentRetriever 可替换:支持混合检索、自定义重排策略
    • QueryAugmenter 可替换:支持查询改写、多轮上下文压缩等高级玩法
  3. similarityThreshold:余弦相似度阈值,取值 0~1,阈值越高召回越精准但容易漏内容,需要根据模型调优。

3. 接口层:两种 RAG 效果对比

写两个测试接口,分别挂载不同的 Advisor,直观对比两种实现的效果。

java 复制代码
package com.qcby.springairagwithadvisor.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
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;

import java.util.List;

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

    @Autowired
    private ChatClient chatClient;

    @Autowired
    private QuestionAnswerAdvisor qaAdvisor;

    @Autowired
    private RetrievalAugmentationAdvisor ragAdvisor;

    /**
     * 方式一:QuestionAnswerAdvisor
     */
    @GetMapping("/chat1")
    public String ask1(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .advisors(List.of(qaAdvisor))
                .call()
                .content();
    }

    /**
     * 方式二:RetrievalAugmentationAdvisor
     */
    @GetMapping("/chat2")
    public String ask2(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .advisors(List.of(ragAdvisor))
                .call()
                .content();
    }
}

四、效果测试

测试1:知识库内问题

请求:GET /ai/chat1?message=Spring AI是什么

两个接口均返回正确结果:

Spring AI 是一个开源 AI 集成项目。

说明 RAG 检索生效,模型正确读取了向量库中的私有知识。

测试2:知识库外问题

请求:GET /ai/chat2?message=今天北京天气怎么样

返回:

抱歉,我不清楚这个问题。

说明相似度过滤与系统提示生效,模型不会凭空编造答案。


五、踩坑与注意事项

  1. 依赖不要导错

    QuestionAnswerAdvisor 不在 spring-ai-rag 包中,必须单独引入 spring-ai-advisors-vector-store,否则会报类找不到。

  2. Embedding 模型必须一致

    写入向量库和查询时必须使用同一个 Embedding 模型,否则向量空间不匹配,检索完全失效。

  3. SimpleVectorStore 仅用于演示

    纯内存实现,重启数据丢失,并发能力弱,生产环境务必替换为持久化向量数据库。

  4. Advisor 是请求级生效

    .advisors() 只对当前本次请求生效,不会修改 ChatClient 全局配置;也可以在 ChatClient.builder() 中通过 defaultAdvisors() 设置全局默认 RAG。


六、总结

  • 快速上手、简单 Demo :选 QuestionAnswerAdvisor,零 Prompt 开发,开箱即用。
  • 生产定制、流程扩展 :选 RetrievalAugmentationAdvisor,模块化设计方便后续接入重排、查询改写、多轮记忆等能力。
  • Spring AI 的 Advisor 机制把 RAG 从「手写业务逻辑」变成了「配置组件」,大幅降低了落地门槛,也让代码结构更清晰易维护。
相关推荐
阿部多瑞 ABU1 小时前
新-潘多拉降临操场
人工智能
mifengxing1 小时前
LeetCode 238 除自身以外数组的乘积|无除法O(n)时间+O(1)空间双解法
java·算法·leetcode
小妖同学学AI1 小时前
拒绝手动SSH:用战斗机与AI混沌构建的硬核Homelab架构
人工智能
江畔柳前堤1 小时前
YOLO 目标检测全流程深度剖析
人工智能·yolo·目标检测·计算机视觉·unity·面试·vllm
jikemaoshiyanshi1 小时前
业务部门想了解开箱即用 AI Agent,AWS 中国峰会有哪些案例展示?
大数据·人工智能
2401_832298101 小时前
绿色AI发展新赛道:算力优化与低碳智能的双向赋能
人工智能
Z-D-K2 小时前
一个AI的真实日记(3)
人工智能·ai·aigc·人机交互·agent·agi
ivywriter2 小时前
【数字孪生】到底能解决什么实际问题?
大数据·人工智能·机器人
(轻舟已过万重山)2 小时前
第39章 评估迭代:上线只是开始,迭代才是关键
大数据·数据库·人工智能