Spring AI Alibaba 1.x 系列【73】两步 RAG

文章目录

  • [1. RAG 概述](#1. RAG 概述)
    • [1.1 什么是 RAG](#1.1 什么是 RAG)
    • [1.2 核心流程](#1.2 核心流程)
    • [1.3 三种 RAG 架构](#1.3 三种 RAG 架构)
    • [1.4 构建模块](#1.4 构建模块)
  • [2. 两步 RAG](#2. 两步 RAG)
    • [2.1 原理](#2.1 原理)
    • [2.2 三种实现方式](#2.2 三种实现方式)
  • [3. 方式一:MessagesModelHook 实现](#3. 方式一:MessagesModelHook 实现)
    • [3.1 原理](#3.1 原理)
    • [3.2 实现代码](#3.2 实现代码)
    • [3.3 Agent 配置](#3.3 Agent 配置)
    • [3.4 调用示例](#3.4 调用示例)
  • [4. 方式二:ModelInterceptor 实现](#4. 方式二:ModelInterceptor 实现)
    • [4.1 原理](#4.1 原理)
    • [4.2 实现代码](#4.2 实现代码)
    • [4.3 Agent 配置](#4.3 Agent 配置)
    • [4.4 调用示例](#4.4 调用示例)
  • [5. 方式三:AgentHook 实现(性能优化)](#5. 方式三:AgentHook 实现(性能优化))
    • [5.1 原理](#5.1 原理)
    • [5.2 AgentHook 实现](#5.2 AgentHook 实现)
    • [5.3 配套的 RagContextInterceptor](#5.3 配套的 RagContextInterceptor)
    • [5.4 Agent 配置](#5.4 Agent 配置)
    • [5.5 调用示例](#5.5 调用示例)
  • [6. 三种方式对比总结](#6. 三种方式对比总结)

1. RAG 概述

1.1 什么是 RAG

大型语言模型(LLM)虽然强大,但有两个关键限制:

  • 有限上下文 ------ 无法一次性摄取整个语料库
  • 静态知识 ------ 训练数据在某个时间点被冻结

检索增强生成Retrieval-Augmented Generation,RAG) 通过在查询时获取相关的外部知识来解决这些问题:使用特定上下文信息来增强 LLM 的回答。

1.2 核心流程

复制代码
用户查询 → 检索相关文档 → 构建上下文 → 注入 LLM → 生成回答

每个组件都是模块化的:可以独立替换加载器、分割器、嵌入模型或向量存储。

1.3 三种 RAG 架构

架构 描述 控制性 灵活性 延迟 场景
两步 RAG 检索总是在生成之前执行 FAQ、文档问答
Agentic RAG Agent 自主决定何时检索 可变 多工具研究助手
混合 RAG 结合两者,含验证步骤 可变 高精度领域问答

1.4 构建模块

Spring AI Alibaba 基于 Spring AI 提供了完整的 RAG 组件栈:

复制代码
┌─────────────────────────────────────────┐
│                ETL Pipeline               │
│  DocumentReader → TextSplitter → VectorStore │
├─────────────────────────────────────────┤
│              检索层                       │
│  VectorStore.similaritySearch()           │
├─────────────────────────────────────────┤
│              Agent 集成层                 │
│  MessagesModelHook / ModelInterceptor    │
│  AgentHook / ToolCallback                │
└─────────────────────────────────────────┘
  • DocumentReader :从 PDFWordMarkdownTXT 等格式加载文档
  • TextSplitter:将大文档分割为适合上下文窗口的块
  • EmbeddingModel :将文本转为向量(如 DashScope text-embedding-v4
  • VectorStore :存储和检索向量(SimpleVectorStore / Milvus / Elasticsearch 等)
  • Hook / Interceptor :在 Agent 生命周期中注入检索逻辑

2. 两步 RAG

2.1 原理

两步 RAG 是最简单、最可预测的 RAG 架构:

复制代码
用户查询 → [Step 1: 检索文档] → [Step 2: 增强上下文 → 生成回答] → 返回结果

检索步骤总是在生成步骤之前执行。这种模式适合大多数场景,如 FAQ 机器人、文档问答等。

2.2 三种实现方式

Spring AI Alibaba 提供了三种在 Agent 中实现两步 RAG 的方式:

方式 执行时机 检索次数 适用场景
MessagesModelHook 每次模型调用前 每次 reasoning 循环 需要根据每次推理动态检索
ModelInterceptor 每次模型调用前 每次 reasoning 循环 需要访问完整请求信息
AgentHook Agent 开始时 只检索一次 查询不变时优化性能

3. 方式一:MessagesModelHook 实现

3.1 原理

MessagesModelHook 在每次模型调用前触发,可以修改发送给 LLM 的消息列表。我们将检索到的文档内容注入为 SystemMessage

复制代码
ReAct 循环:
  [Think] → [MessagesModelHook: 检索文档 → 注入 SystemMessage] → [LLM 调用] → [Act] → ...

3.2 实现代码

java 复制代码
public class RagMessagesHook extends MessagesModelHook {

    private final VectorStore vectorStore;
    private final int topK;

    @Override
    public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {
        // 1. 提取用户问题
        String userQuestion = extractUserQuestion(previousMessages);

        // 2. 检索相关文档
        List<Document> relevantDocs = vectorStore.similaritySearch(
                SearchRequest.builder().query(userQuestion).topK(topK).build());

        // 3. 构建上下文
        String context = relevantDocs.stream()
                .map(Document::getText)
                .collect(Collectors.joining("\n\n"));

        // 4. 构建增强的消息列表(SystemMessage + 原有消息)
        List<Message> enhancedMessages = new ArrayList<>();
        enhancedMessages.add(new SystemMessage("基于以下上下文回答问题:\n" + context));
        enhancedMessages.addAll(previousMessages);

        return new AgentCommand(enhancedMessages, UpdatePolicy.REPLACE);
    }
}

3.3 Agent 配置

java 复制代码
ReactAgent agent = ReactAgent.builder()
        .name("two-step-rag-hook")
        .model(chatModel)
        .hooks(new RagMessagesHook(vectorStore, topK))
        .build();

3.4 调用示例

bash 复制代码
curl "http://localhost:8088/rag/two-step/messages-hook?query=Spring AI Alibaba支持哪些向量数据库?"

4. 方式二:ModelInterceptor 实现

4.1 原理

ModelInterceptorMessagesModelHook 类似,都在每次模型调用前触发。区别在于 Interceptor 可以访问更完整的请求信息(包括 systemMessagetools 等),通过 ModelRequest.Builder 修改请求。

复制代码
ReAct 循环:
  [Think] → [ModelInterceptor: 检索文档 → 增强 systemPrompt] → [LLM 调用] → [Act] → ...

4.2 实现代码

java 复制代码
public class RagModelInterceptor extends ModelInterceptor {

    private final VectorStore vectorStore;
    private final int topK;

    @Override
    public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
        // 1. 提取用户查询
        String userQuery = extractUserQuery(request);

        // 2. 检索相关文档
        List<Document> relevantDocs = vectorStore.similaritySearch(
                SearchRequest.builder().query(userQuery).topK(topK).build());

        // 3. 构建 RAG 上下文
        String context = relevantDocs.stream()
                .map(Document::getText)
                .collect(Collectors.joining("\n\n"));

        // 4. 增强 systemPrompt(合并原有 prompt + RAG 上下文)
        String ragPrompt = "上下文:\n" + context;
        SystemMessage enhancedMessage = request.getSystemMessage() == null
                ? new SystemMessage(ragPrompt)
                : new SystemMessage(request.getSystemMessage().getText() + "\n\n" + ragPrompt);

        // 5. 构建增强请求
        ModelRequest enhancedRequest = ModelRequest.builder(request)
                .systemMessage(enhancedMessage)
                .build();

        return handler.call(enhancedRequest);
    }
}

4.3 Agent 配置

java 复制代码
ReactAgent agent = ReactAgent.builder()
        .name("two-step-rag-interceptor")
        .model(chatModel)
        .interceptors(new RagModelInterceptor(vectorStore, topK))
        .build();

4.4 调用示例

bash 复制代码
curl "http://localhost:8088/rag/two-step/model-interceptor?query=什么是两步RAG?"

5. 方式三:AgentHook 实现(性能优化)

5.1 原理

前两种方式在 每次 ReAct reasoning 循环 中都执行检索。如果 Agent 多次调用模型,检索也会执行多次,造成不必要的开销。

AgentHookAgent 开始时只执行一次 ,将检索结果缓存到 RunnableConfig.metadata 中。后续的 ModelInterceptor 直接从缓存读取,避免重复检索。

复制代码
AgentHook (只执行一次):
  检索文档 → 缓存到 config.metadata["rag_context"]
      │
      ▼
ReAct 循环:
  [Think] → [RagContextInterceptor: 读取缓存] → [LLM 调用] → [Act] → ...
             ↑ 不再检索,直接读取

5.2 AgentHook 实现

java 复制代码
@HookPositions({HookPosition.BEFORE_AGENT})
public class QueryEnhancementHook extends AgentHook {

    public static final String RAG_CONTEXT_KEY = "rag_context";

    @Override
    public CompletableFuture<Map<String, Object>> beforeAgent(
            OverAllState state, RunnableConfig config) {
        // 1. 提取用户问题
        String userQuery = extractUserQuery(state);

        // 2. 检索相关文档(只执行一次)
        List<Document> docs = vectorStore.similaritySearch(
                SearchRequest.builder().query(userQuery).topK(topK).build());
        String context = docs.stream()
                .map(Document::getText)
                .collect(Collectors.joining("\n\n"));

        // 3. 缓存到 config metadata
        config.metadata().ifPresent(meta -> meta.put(RAG_CONTEXT_KEY, context));

        return CompletableFuture.completedFuture(Map.of());
    }
}

5.3 配套的 RagContextInterceptor

java 复制代码
public class RagContextInterceptor extends ModelInterceptor {

    @Override
    public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {
        // 从 metadata 读取缓存的上下文(不再检索)
        String ragContext = (String) request.getContext()
                .get(QueryEnhancementHook.RAG_CONTEXT_KEY);

        if (ragContext == null || ragContext.isEmpty()) {
            return handler.call(request);
        }

        // 注入上下文到 systemPrompt
        String ragPrompt = "上下文:\n" + ragContext;
        SystemMessage enhancedMessage = new SystemMessage(ragPrompt);

        ModelRequest enhancedRequest = ModelRequest.builder(request)
                .systemMessage(enhancedMessage)
                .build();
        return handler.call(enhancedRequest);
    }
}

5.4 Agent 配置

java 复制代码
ReactAgent agent = ReactAgent.builder()
        .name("two-step-rag-agent-hook")
        .model(chatModel)
        .hooks(new QueryEnhancementHook(vectorStore, topK))
        .interceptors(new RagContextInterceptor())
        .build();

5.5 调用示例

bash 复制代码
curl "http://localhost:8088/rag/two-step/agent-hook?query=Spring AI Alibaba有哪些核心特性?"

6. 三种方式对比总结

维度 MessagesModelHook ModelInterceptor AgentHook
检索频率 每次 LLM 调用 每次 LLM 调用 Agent 开始时 1 次
性能 一般 一般 最优
灵活性 可动态调整检索 可访问完整请求 上下文固定
适合场景 推理中检索需求变化 需要修改 tools 等 查询不变,追求性能
实现复杂度 中(需配合 Interceptor)

相关推荐
_Evan_Yao1 小时前
面向对象实战:用 Java/Python 设计一个简单的“怪物战斗”小游戏
java·开发语言
asdfg12589631 小时前
一文通俗理解JDBC中的核心概念+案例
java·数据库·oracle·jdbc
ai产品老杨1 小时前
解耦视频高并发与边缘计算AI布控:基于Docker的高性能安防平台,破局GB28181/RTSP协议兼容与源码交付痛点
人工智能·音视频·边缘计算
CHrisFC1 小时前
LIMS 系统 AI 建设路径:从自动化到智能化的演进之路
运维·人工智能·自动化
布朗克1681 小时前
26 多线程基础——Thread、Runnable与线程安全
java·安全·多线程
饼干哥哥1 小时前
一口气搭了300个AI Agents并发处理跨境运营的dirty work
人工智能
AI行业学习1 小时前
CC‑Switch v3.16.1-下载、配置、安装(2026‑06‑01 最新官方版)
开发语言·人工智能·windows·python
小糖学代码1 小时前
机器学习:5.深度学习
人工智能·深度学习·机器学习
轮子飞了1 小时前
Spring Ai 集成 DashScope 多模态模型实现身份证信息识别
java·人工智能·spring