一. RAG是什么?

在这里插入图片描述

  • 大模型训练后仍会有一些接触不到的隐私数据, 例如公司的机密文件等等, 而如果对这些相关知识进行提问时, 大模型就会产生幻觉, 以为自己知道, 从而给出错误答案
  • RAG (检索增强生成) 用于构建外部的知识库, 客户端调用大模型进行回答之前, 会先去RAG知识库中检索相关内容, 然后添加到模型上下文, 大模型结合训练数据与上下文内容, 来给出更准确的答案

二. RAG知识库构建流程

参考文档 Spring AI Alibaba

1. ETL流程介绍

在这里插入图片描述
① ETL(Extract、Transform、Load)框架是 Retrieval Augmented Generation (RAG) 用例中数据处理的支柱, 即RAG知识库的构建遵循ETL流程

  • Extract: 从原始数据中读取内容, 将源数据解析为基础结构Document, 原始数据可以是各种类型的文件
  • Transform: 将数据读取后得到的Documents集合, 进行数据进行切割, 分块, 生成更精细更可用Documents集合, 如给每个Document块统一格式、去除乱码、添加标签等
  • Load: 将转化后的数据向量化并存入向量数据库

② ETL的三大接口

  • DocumentReader: 将不同的文件来源, 转化为相同的实体类文档
public interface DocumentTransformer extends Function<List<Document>, List<Document>> {

	default List<Document> transform(List<Document> transform) {
		return apply(transform);
	}
}
  • DocumentTransformer: 转换一批文档作为处理工作流的一部分, 对数据进行精细打磨
public interface DocumentTransformer extends Function<List<Document>, List<Document>> {

    default List<Document> transform(List<Document> transform) {
		return apply(transform);
	}
}
  • DocumentWriter: 管理 ETL 过程的最后阶段, 准备文档, 将文档写入到向量数据库
public interface DocumentWriter extends Consumer<List<Document>> {

    default void write(List<Document> documents) {
		accept(documents);
	}
}

2. ETL实例类介绍

下面的实例类, 都是Spring AI 官方提供的👇
在这里插入图片描述

(1) Document

  • Document: 文档的基本单位Document 是ETL API的核心数据模型, 它构成了整个数据处理流程的基本单元, 其中包含文本、元数据,以及可选的额外媒体类型,如图像、音频和视频
    属性介绍:
  • String id: 文档Document的唯一标识, 在一条ETL流程中, 传入的是一个Document集合, 每个文档之间用唯一id进行区分
  • String content: 从原始文档提取的主要文本内容
  • Map<String, Object> metadata: 键值对形式的元数据, 可以用来存储标签, 编码类型等, 后期检索时可以根据元数据中的内容来快速过滤和筛选
  • media: 用于支持多模态的RAG, 如语音,图片等, 检索时可以同时利用文本和媒体特征, getMimeType() -> 媒体类型, getData() -> 媒体的数据
    在这里插入图片描述

(2) Source

  • Source: 数据源, 要为哪些文件/内容构建RAG知识库, 可以是PDF、Excel、Text、HTML、PPT等等多种格式的文件

(3) Reader

  • Reader: 数据源读取器, 将它们转换为 Document 对象列表

  • SpringAI 提供了很多开箱即用的阅读器, 都实现了DocumentReader接口, 如JsonReader(JSON阅读器)、TextReader(文本阅读器)、JsoupDocumentReader (HEML阅读器)、MarkdownDocumentReader(Markdown阅读器)、PagePdfDocumentReader(PDF按照页面阅读器)、ParagraphPdfDocumentReader (PDF按照目录的阅读器)、还有万能阅读器TikaDocumentReader, 参考Tika阅读器文档

  • 阅读器类中主方法是List < Document >get(), 用来读取数据

  • 当然 Spring-AI-Alibaba社区 也提供了更多的拓展阅读器, 还有相对应的 阅读器文档

  • 对于Reader的各种类的使用示例在 Spring AI AlibabaSpring AI


(4) Transformer

Transformer: 对文档进行打标签, 数据清洗, 分块等精细处理, 主要方法是List apply(List< Document > documents)

  • TokenTextSplitter 是 TextSplitter(抽象类) 的实现,它使用 CL100K_BASE 编码基于 token 计数将文本拆分为块, TextSplitter 实现了 DocumentTransformer 接口, SpringAI还提供了一个按照句子分块的分割器 -> SentenceSplitter
    在这里插入图片描述
  • KeywordMetadataEnricher 使用生成式 AI 模型从文档内容中提取关键字并将它们添加为元数据, 同样实现了 DocumentTransformer 接口
    在这里插入图片描述
  • SummaryMetadataEnricher 使用生成式 AI 模型为文档创建摘要并将它们添加为元数据。它可以为当前文档以及相邻文档(上一个和下一个)生成摘要, 同样实现了 DocumentTransformer 接口
    在这里插入图片描述

(5) Writers

Writers: 将文档列表向量化并存储起来, 实现了 DocumentWriter 接口

  • FileDocumentWriter 是一个 DocumentWriter 实现,它将 Document 对象列表的内容写入文件, 主要方法是accept(List< Document > docs), 这个方法应用的很少, 一般都是存储到向量数据库中
    在这里插入图片描述

(6) VectorStore

VectorStore: 向量数据库接口, 将文档列表向量化并存储到该数据库, 同时 VectorStore接口 继承了DocumentWriter 接口

  • 向量数据库使用时可以自动将文档转化为向量, 需要传入 EmbeddingModel 模型
  • SpringAI支持很多向量数据库, 如Redis, pinecone等, 同时SpringAI内置了一个内存数据库, 可以直接简单测试
  • 主要方法是 void add(List< Document > documents)
 // 在test运行前, 需要运行的方法
    @TestConfiguration
    static class TestConfig {
        @Bean
        public SimpleVectorStore vectorStore(DashScopeEmbeddingModel embeddingModel) {
            return SimpleVectorStore.builder(embeddingModel).build();// SpringAI 自带的内存向量数据库
        }
    }

    // 将上面静态类中创建的 SimpleVectorStore 注入
    @Autowired
    private SimpleVectorStore vectorStore;

    // 在test方法运行前的启动方法
    @BeforeEach
    void setUp() {
        // 1. 定义文本文件
        Document doc1 = Document.builder()
                .text("2025年夏季奥运会将于巴黎举行, 预计吸引全球数百万观众")
                .build();
        Document doc2 = Document.builder()
                .text("对比学习框架下多语言BERT模型的语义表示分析")
                .build();
        Document doc3 = Document.builder()
                .text("暮色中的老槐树在风中摇曳, 枯枝划破绯红的晚霞")
                .build();
        Document doc4 = Document.builder()
                .text("基于Transformer的预训练模型在机器翻译中的迁移学习研究")
                .build();
        // 2. 将文本进行向量化, 并且存入向量数据库 (无需再手动向量化)
        vectorStore.add(Arrays.asList(doc1, doc2, doc3, doc4));
        System.out.println("向量数据库初始化完成");
    }

在这里插入图片描述

三. RAG的使用

1. 向量数据库的相似度检索

VectorStore 可以进行相似度检索, 这也是大模型获取上下文的入口, 方法是List< Document > similaritySearch(SearchRequest request) 或者 List< Document > similaritySearch(String query)

SearchRequest 的主要参数介绍

  • query(String query): 要检索的内容
  • topK(int topK): 返回前 K 条相关的文档
  • similarityThreshold(double threshold): 只返回等于或者等于 threshold 的文档
  • similarityThresholdAll(): 返回所有的文档
    // 在test运行前, 需要运行的方法
    @TestConfiguration
    static class TestConfig {
        @Bean
        public SimpleVectorStore vectorStore(DashScopeEmbeddingModel embeddingModel) {
            return SimpleVectorStore.builder(embeddingModel).build();// SpringAI 自带的内存向量数据库
        }
    }

    // 将上面静态类中创建的 SimpleVectorStore 注入
    @Autowired
    private SimpleVectorStore vectorStore;

    // 在test方法运行前的启动方法
    @BeforeEach
    void setUp() {
        // 1. 定义文本文件
        Document doc1 = Document.builder()
                .text("我是景画")
                .build();
        Document doc2 = Document.builder()
                .text("对比学习框架下多语言BERT模型的语义表示分析")
                .build();
        Document doc3 = Document.builder()
                .text("暮色中的老槐树在风中摇曳, 枯枝划破绯红的晚霞")
                .build();
        Document doc4 = Document.builder()
                .text("基于Transformer的预训练模型在机器翻译中的迁移学习研究")
                .build();
        // 2. 将文本进行向量化, 并且存入向量数据库 (无需再手动向量化)
        vectorStore.add(Arrays.asList(doc1, doc2, doc3, doc4));
        System.out.println("向量数据库初始化完成");
    }

    @Test // 通过SearchRequest去向量数据库中查询, 可以设置更多参数
    void searchRequest() {
        SearchRequest request = SearchRequest.builder()
                .query("机器学习")
                .similarityThreshold(0.3) // 相似度阈值 -> 分数,大于该分数的才会加入返回列表
                .topK(5) // 取前几个
                .build();
        List<Document> documents = vectorStore.similaritySearch(request);
        documents.forEach(System.out :: println);
    }

在这里插入图片描述

2. Advisor

  • Spring AI 使用 Advisor API 为常见的 RAG 流程提供开箱即用的支持, 用于实现检索增强生成, 向量数据库存储 AI 模型不知道的数据。当用户问题发送到 AI 模型时, QuestionAnswerAdvisor 会查询向量数据库以查找与用户问题相关的文档。
  • 另一个实例类 RetrievalAugmentationAdvisor 更灵活可控, 可以设置的参数更多, 可以通过实现自定义的 Retriever 接口来控制检索逻辑, 比如结合关键词匹配与向量相似度的混合检索策略, 或者引入重排序 (re-ranker) 机制提升召回质量. 同时, 还可以通过 PromptTemplate 灵活定义上下文如何融入最终提示
  • 通过构建 Advisor 时, 将向量数据库作为参数传入, 来使用外部知识
    @Test // 设置返回条数和模版
    void testRag2() {
        ChatClient client = ChatClient.builder(chatModel).build();
        String message="你的名字是什么?";
        // 提示词模版
        PromptTemplate template = new PromptTemplate(
                """
			{query}

			先面试上下文信息

			---------------------
			{question_answer_context}
			---------------------

            根据给定的上下文信息, 回答问题, 并遵循以下规则:
            1. 相关知识在上下文时, 直接回答上下文信息
            2. 相关知识不存在上下文中时, 直接说 "不知道"
            3. 回复中避免使用"根据您提供的信息..."或者"根据上下文..."之类的语句

			"""
        );
        QuestionAnswerAdvisor advisor = QuestionAnswerAdvisor
                .builder(vectorStore)
                .promptTemplate(template)
                .searchRequest(SearchRequest
                        .builder()
                        .query(message)
                        .topK(5)
                        .similarityThreshold(0.4)
                        .build())
                .build();

        String content = client
                .prompt()
                .user(message)
                .advisors(advisor)
                .call()
                .content();
        System.out.println(content);
    }

在这里插入图片描述

3. 重排序

Rerank(重排序) 指的是初步检索出一批候选文档后, 使用更专业的相关性判断模型, 重新计算候选文档总与查询条件的相似度, 来重新排序

重排序工作流程👇

  • 粗排(Retrieve): 通过向量数据库快速搜索, 挑选候选文档
  • 精排(Rerank): 调用专业RerankModel模型, 更细力度计算候选文档与查询条件的相似分数
  • 根据精排后的得分重新排列文档, 再从中选取前 k 个高相似度文档
  • 将优化后的文档内容, 加入提示词上下文, 交给大模型
package com.ran.rag;

import com.alibaba.cloud.ai.advisor.RetrievalRerankAdvisor;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel;
import com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankModel;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;

import java.util.Arrays;
import java.util.List;

// 文本转向量
@SpringBootTest
public class RerankTest {

    // 在test运行前, 需要运行的方法
    @TestConfiguration
    static class TestConfig {
        @Bean
        public SimpleVectorStore vectorStore(DashScopeEmbeddingModel embeddingModel) {
            return SimpleVectorStore.builder(embeddingModel).build();// SpringAI 自带的内存向量数据库
        }
    }

    // 将上面静态类中创建的 SimpleVectorStore 注入
    @Autowired
    private SimpleVectorStore vectorStore;

    // 在test方法运行前的启动方法
    @BeforeEach
    void setUp(@Value("classpath:file/rule.txt") Resource resource) {
        // 1. 读取文本
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();
        // 2. 分割
        TokenTextSplitter splitter = new TokenTextSplitter(200, 10, 5, 1000,true);
        List<Document> apply = splitter.apply(documents);
        // 3. 将文本进行向量化, 并且存入向量数据库 (无需再手动向量化)
        vectorStore.add(apply);
        System.out.println("向量数据库初始化完成");
    }

    @Test // 通过字符串去向量数据库中查询
    void testRerank(@Autowired DashScopeChatModel chatModel, @Autowired DashScopeRerankModel rerankModel) {
        ChatClient client = ChatClient.builder(chatModel).build();
        RetrievalRerankAdvisor rerankAdvisor = new RetrievalRerankAdvisor(vectorStore,rerankModel,
                SearchRequest
                        .builder()
                        .topK(3)
                        .build());

        String content = client.prompt().user("金卡会员的费用?")
                .advisors(rerankAdvisor)
                .call()
                .content();
        System.out.println(content);
    }

}

在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐