RAG相关技术介绍及Spring AI中使用--第三期

Spring AI 实现 RAG Spring AI框架实现RAG(检索增强生成)技术,整合了信息检索与生成模型,用于构建智能问答系统。该方案采用Spring Boot框架,支持多种大语言模型(如OpenAI、Qwen)和向量数据库(Elasticsearch)。核心流程包括:使用Tika读取多格式文档并向量化存储,进行相似性搜索匹配问题,通过提示词模板优化LLM生成质量。配置文件中可自定义嵌入模型、向量维度和相似性算法。Spring AI的模块化设计简化了RAG实现,但需注意版本兼容性和向量维度匹配问题。 阅读详情

前言

上一期介绍了RAG系统中的关键ETL(提取、转换、加载)流程中的提取,重点讲解SpringAI框架中的文档读取器实现。ETL过程包括从各类数据源提取内容、进行文本清洗和标准化处理,最后加载到向量数据库。文章详细演示了SpringAI提供的多种DocumentReader实现,包括处理JSON、纯文本、HTML、Markdown、PDF以及各类办公文档的读取器,并展示了MySQL数据库读取器的使用示例。这些工具为构建RAG系统提供了高效的数据预处理能力,确保原始数据能有效转化为适合AI模型检索结构化格式
本文主要说到ETL中的T(转换), L(加载) 和 重排序(Re-Ranking)

一.Transformers(文档转换器)

在大模型应用(如 RAG、知识库问答)中,原始文档通常太长、格式混乱或缺乏结构化元数据,无法直接输入给 LLM 使用。为此,Spring AI 提供了一组 文档转换器 (Document Transformers)。它们就像一个“智能流水线”,把原始文档一步步加工成适合向量化、检索和生成的高质量片段。

主要包含以下 4 个组件:

  1. TextSplitter (文本切分器):把长文切成小块。

  2. ContentFormatTransformer (内容格式转换器):统一清洗文本。

  3. KeywordMetadataEnricher (关键词提取器):借助 AI 模型自动提取关键词,类似给文档“贴标签”。

  4. SummaryMetadataEnricher (摘要生成器):借助 AI 模型生成文档摘要。

总结:这些工具共同作用,提升后续 AI 处理的准确性与效率。

1.TokenTextSplitter(文本切分器)

1.1关于 TokenTextSplitter

TokenTextSplitter 是 TextSplitter 的一个实现,它基于 OpenAI 推荐的 CL100K_BASE 的分词编码方案,用于将长文本按 token数量 切分为多个较小的 Document 实例。

💡 注意:一个 token 不等于一个汉字!中文通常每 1~2 个汉字占 1 个 token,具体取决于词汇和语境。

可以把 TokenTextSplitter 理解为一个“智能断句”的分块专家。

1.2代码应用

@SpringBootTest
public class TransformersTest {

    @Test
    void testSplitter(@Value("classpath:/file/rule.txt") Resource resource) {
        TextReader textReader = new TextReader(resource);
        List<Document> documents = textReader.get();
        System.out.println("加载文档的大小: " + documents.size());

        TokenTextSplitter tokenTextSplitter = new TokenTextSplitter();
        List<Document> documentsSplit = tokenTextSplitter.apply(documents);
        System.out.println("分段后的文档大小: " + documentsSplit.size());
        documentsSplit.forEach(document -> {
            System.out.println(document.getText());
            System.out.println(document.getMetadata());
                }
        );
    }
}

1.3结果
1.4TokenTextSplitter的全参构造函数

1.5参数说明

参数默认值含义
defaultChunkSize800每块目标 token 数 (尽量不超过)
minChunkSizeChars350每块最少字符数 (防过短)
minChunkLengthToEmbed5小于该长度的块会被丢弃
maxNumChunks10,000单文档最多切多少块 (防爆炸)
keepSeparatorTRUE是否保留分隔符 (如换行符\r\n)

1.6 加入全参后的代码

 @Test
    void testSplitter(@Value("classpath:/file/rule.txt") Resource resource) {
        TextReader textReader = new TextReader(resource);
        List<Document> documents = textReader.get();
        System.out.println("加载文档的大小: " + documents.size());

//        TokenTextSplitter tokenTextSplitter = new TokenTextSplitter();
        TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(500, 300, 5, 5000, true);//全参构造
        List<Document> documentsSplit = tokenTextSplitter.apply(documents);
        System.out.println("分段后的文档大小: " + documentsSplit.size());
        documentsSplit.forEach(document -> {
            System.out.println(document.getText());
            System.out.println(document.getMetadata());
                }
        );
    }

1.7加入参数后的运行结果

1.8处理流程

  1. 使用 CL100K_BASE 编码将文本编码,得到 token 列表。

  2. 根据 defaultChunkSize 的设置,获取前 N 个 token (N=defaultChunkSize),对这个块进行处理:

    • a. 将数据库解码回文本,并找到最后一个句号/问号/感叹号/换行符。(注:图中文案“数据库”疑似“数据块”的笔误)

    • b. 若位置合适,则在该点截断 (是否大于 minChunkSizeChars)。

    • c. 清理格式 (去空格、换行替换等),检查长度是否够用。

    • d. 加入结果列表,从原 token 列表中移除已处理部分。

  3. 继续循环处理剩余未处理的令牌,直到处理完所有令牌或达到 maxNumChunks。

💡 通俗比喻:

想象你要把一本小说发到朋友圈,但每条只能发 200 字。 我们不会从"他说"中间剪开,而是会找到句号、问号等地方断开。

TokenTextSplitter 就是像一个"懂语法的裁缝",它先按 AI 能理解的单位 (token) 数好长度,然后尽量在句号、问号这些自然停顿的地方下剪刀,剪出一段段既不过长又能独立成意的小段落,最后还把边角料检查一遍,确保没有漏掉重要内容。

2.ContentFormatTransformer(内容格式转化器)

ContentFormatTransformer 是 Spring AI 框架中用于统一管理和调整文档内容格式的处理器。

💡 通俗比喻: 它就像一个 "文档化妆师",不改变文档原本的内容,而是决定在什么时候,哪些信息要展示,哪些信息要隐藏,并且统一排版格式。

它常用于处理从文件中读取的文档,确保它们以整洁、安全的方式被 AI 看到和使用。

简单说,就是让杂乱的文档变得整齐划一,该露的露,该藏的藏。常用于 RAG 系统中对加载的文档进行标准化预处理,从而提升后续检索与生成的质量和可控性。

这里不做过多介绍

3.KeywordMetadataEnricher(关键词提取器)

3.1关于KeywordMetadataEnricher

eywordMetadataEnricher 是一个基于大模型的元数据增强器,利用 ChatModel 分析文档内容,生成关键词列表,并作为字符串添加至文档元数据中。

默认使用的元数据键是 "excerpt_keywords"。

可以把 KeywordMetadataEnricher 看做一个 "自动打标签的机器人"。

比如:你有一批文章要归档,人工标注主题太慢了。

这个组件就像请了个实习生,快速扫一眼就说:"这篇讲的是 人工智能、机器学习、深度神经网络。"

然后把这些词贴在文件夹上,方便以后搜索查找。

3.2代码演示

    @Test
    void testEnricher(@Autowired ChatModel chatModel, @Value("classpath:/file/rule.txt") Resource resource) {
        //加载文档
        TextReader textReader = new TextReader(resource);
        List<Document> documents = textReader.get();
        System.out.println("加载文档的大小: " + documents.size());

        //文档分块
        TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(500, 300, 5, 5000, true);//全参构造
        List<Document> documentsSplit = tokenTextSplitter.apply(documents);
        System.out.println("分段后的文档大小: " + documentsSplit.size());

        //关键词生成
        KeywordMetadataEnricher keywordMetadataEnricher = KeywordMetadataEnricher.builder(chatModel)
                .keywordCount(2).build(); //每个文档提取关键词的数量
        List<Document> enricher = keywordMetadataEnricher.apply(documentsSplit);
        System.out.println("关键词生成后的文档大小: " + enricher.size());

        enricher.forEach(document -> {
                    System.out.println(document.getText());
                    System.out.println(document.getMetadata());
                }
        );
    }

3.3结果

两个关键词

3.4构造函数参数解析

KeywordMetadataEnricher 提供了两种构造函数选项。

  1. KeywordMetadataEnricher(ChatModel chatModel, int keywordCount):指定关键词数量(使用默认提示词模版)
  2. KeywordMetadataEnricher(ChatModel chatModel, PromptTemplate keywordsTemplate):自定义提示词模版

3.5提示词模板代码

    @Test
    void testEnricher(@Autowired ChatModel chatModel, @Value("classpath:/file/rule.txt") Resource resource) {
        //加载文档
        TextReader textReader = new TextReader(resource);
        List<Document> documents = textReader.get();
        System.out.println("加载文档的大小: " + documents.size());

        //文档分块
        TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(500, 300, 5, 5000, true);//全参构造
        List<Document> documentsSplit = tokenTextSplitter.apply(documents);
        System.out.println("分段后的文档大小: " + documentsSplit.size());

        //关键词生成
//        KeywordMetadataEnricher keywordMetadataEnricher = KeywordMetadataEnricher.builder(chatModel)
//                .keywordCount(2).build();
        KeywordMetadataEnricher keywordMetadataEnricher = KeywordMetadataEnricher.builder(chatModel)
                .keywordsTemplate(new PromptTemplate("根据给定的文本:{context_str},生成关键字,只允许以下关键字" +
                        " [会员宗旨,会员类型,会员注册,积分制度,会员权益,会员行为,会员服务,公告,隐私保护] " +
                        "只返回关键字,其他信息不返回")).build();
        List<Document> enricher = keywordMetadataEnricher.apply(documentsSplit);
        System.out.println("关键词生成后的文档大小: " + enricher.size());

        enricher.forEach(document -> {
                    System.out.println(document.getText());
                    System.out.println(document.getMetadata());
                }
        );
    }

3.6结果

3.7注意事项

  • KeywordMetadataEnricher 需要一个功能良好的 ChatModel 来生成关键字(API 调用可能会产生费用)
  • 关键字个数必须大于等于 1
  • enricher 会向每个处理过的文档添加 "excerpt_keywords" 元数据字段
  • 生成的关键字以逗号分隔的字符串返回,如:"会员权益,会员行为,会员服务,公告"
  • 在 Builder 模式 中,如果设置了 keywordsTemplate 参数,keywordCount 参数将被忽略

4.SummaryMetadataEnricher(摘要生成器)

4.1关于SummaryMetadataEnricher

SummaryMetadataEnricher 利用 ChatModel 为每个文档生成摘要信息,并将其作为元数据的一部分添加到文档中。也可以根据需要,指定为当前文档或相邻文档(上一个和下一个)生成摘要,添加到元数据中。

可以把 SummaryMetadataEnricher 看做一个 "会写读书笔记的助手"。

4.2快速使用-代码

SummaryMetadataEnricher 支持三种摘要类型(通过 SummaryType 枚举指定)

  • CURRENT:当前文档摘要
  • PREVIOUS:前一个文档摘要
  • NEXT:下一个文档摘要

比如你在读一系列连载文章,每看完一篇,都有个小助手帮你写一句总结:"上一篇讲了 AI 发展史,这一篇重点介绍大模型训练方法,下一篇会讨论部署挑战。"

这让你更容易理解前后逻辑关系。

    @Test
    void testSummary(@Autowired ChatModel chatModel, @Value("classpath:/file/rule.txt") Resource resource) {
        //加载文档
        TextReader textReader = new TextReader(resource);
        List<Document> documents = textReader.get();

        //文档分块
        TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(500, 300, 5, 5000, true);//全参构造
        List<Document> documentsSplit = tokenTextSplitter.apply(documents);

        SummaryMetadataEnricher summaryMetadataEnricher = new  SummaryMetadataEnricher(chatModel,
                List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT));

        List<Document> enricher = summaryMetadataEnricher.apply(documentsSplit);
        enricher.forEach(document -> {
                    System.out.println(document.getText());
                    System.out.println(document.getMetadata());
                }
        );
    }

4.3代码结果

上面图中对应的前一个文档信息 当前文档信息 后一个文档信息 第一个文档没有前一个文档信息 最后一个文档没有下一个文档的信息 还有一个问题 : 文档生成的摘要太多了  可以传入自定义模板

4.4构造函数介绍

SummaryMetadataEnricher 提供了 2 个构造函数

  1. SummaryMetadataEnricher(ChatModel chatModel, List<SummaryType> summaryTypes)
  2. SummaryMetadataEnricher(ChatModel chatModel, List<SummaryType> summaryTypes, String summaryTemplate, MetadataMode metadataMode)
参数类型作用
chatModelChatModel生成摘要的 LLM 客户端实现 (如 OpenAI、Ollama 等)
summaryTypesList<SummaryType>指定需生成的摘要类型(PREVIOUS, CURRENT, NEXT)
summaryTemplateString(可选)用于生成摘要的自定义模板(可选)
metadataModeMetadataMode(可选)指定在生成摘要时如何处理文档元数据

 默认的提示词模板

4.5传入自定义的提示词模板代码

    @Test
    void testSummary(@Autowired ChatModel chatModel, @Value("classpath:/file/rule.txt") Resource resource) {
        //加载文档
        TextReader textReader = new TextReader(resource);
        List<Document> documents = textReader.get();

        //文档分块
        TokenTextSplitter tokenTextSplitter = new TokenTextSplitter(500, 300, 5, 5000, true);//全参构造
        List<Document> documentsSplit = tokenTextSplitter.apply(documents);

        String summaryTemplate = """
        根据给定的文本:
        {context_str}
        生成摘要信息,限制在50字以内。只返回摘要信息,其他信息不返回
        """;
        SummaryMetadataEnricher summaryMetadataEnricher = new SummaryMetadataEnricher(chatModel,
                List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT),
                summaryTemplate, MetadataMode.NONE);

        List<Document> enricher = summaryMetadataEnricher.apply(documentsSplit);
        enricher.forEach(document -> {
                    System.out.println(document.getText());
                    System.out.println(document.getMetadata());
                }
        );
    }

4.6结果

给文档生成摘要的同时也可以加上前面的关键词生成 也可以对文档不拆分 但是必须得加载文档

4.7工作流程

  1. 输入接收:接收一个有序的 List<Document>,顺序决定相邻关系。
  2. 遍历处理:对每个文档,根据文档内容和摘要模版生成摘要信息。
  3. 摘要生成:根据配置的 summaryTypes,分别构建针对当前、前一个、后一个文档的摘要,并添加到对应文档的元数据中。
  4. 边界处理:首文档无 "prev_section_summary",末文档无 "next_section_summary"。
  5. 返回值:返回经过元数据增强的新文档列表。

5.总结

四大组件总览表

组件主要功能是否需要 LLM典型用途
TokenTextSplitter切分长文本×向量化前预处理
ContentFormatTransformer统一文档格式×格式标准化
KeywordMetadataEnricher提取关键词并写入元数据√打标签、增强检索
SummaryMetadataEnricher生成摘要 (当前 / 前后)√上下文理解、RAG
  • TokenTextSplitter 是裁缝,精准剪出合身布料;
  • ContentFormatTransformer 是保洁,清除表面灰尘;
  • KeywordMetadataEnricher 是标签员,贴上关键词便签;
  • SummaryMetadataEnricher 是讲解员,写出前后关联的导览笔记。

四者协同工作,最终将杂乱无章的原始文档打造成 AI 可读、可检、可理解的高质量知识资产。

二.Writers

刚说完Transformers(ETL中的T)接下来介绍最后一个L(加载) 这个加载不是文档的加载而是数据的写入

1.FileDocumentWriter

1.1关于FileDocumentWriter

FileDocumentWriter 是一个实现了 DocumentWriter 接口的类,用于将一组 Document 对象的内容写入到指定的文本文件中。适用于调试、日志记录或生成人类可读的文档输出。

1.2代码演示

    @Test
    void testFileWritter(@Value("classpath:/file/rule.txt") Resource resource){
        //读取文档
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();
        //文档分割
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> splitterDoc = splitter.apply(documents);

        //文档写入
        FileDocumentWriter writer = new FileDocumentWriter("output.txt");
        writer.accept(splitterDoc);
        System.out.println("文档写入完成");
    }

1.3结果

图中红框标出的地方是每个文档分割的地方

1.4构造函数

FileDocumentWriter 提供了三个构造函数,便于灵活配置:

  1. FileDocumentWriter(String fileName):写入指定文件,不带标记、无元数据、覆盖写入
  2. FileDocumentWriter(String fileName, boolean withDocumentMarkers):指定是否在输出中包含文档标记
  3. FileDocumentWriter(String fileName, boolean withDocumentMarkers, MetadataMode metadataMode, boolean append):完整配置
  • withDocumentMarkers:是否写入文档分隔标记
  • metadataMode:控制写入哪些内容(正文、元数据等)
  • append:是否追加到文件末尾(true)还是覆盖(false)

1.5全参构造函数代码

    @Test
    void testFileWritter(@Value("classpath:/file/rule.txt") Resource resource){
        //读取文档
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();
        //文档分割
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> splitterDoc = splitter.apply(documents);

        //文档写入
        FileDocumentWriter writer = new FileDocumentWriter("output.txt",
                true, MetadataMode.ALL, true);
        writer.accept(splitterDoc);
        System.out.println("文档写入完成");
    }

1.6结果

2.SimpleVectorStore

2.1关于SimpleVectorStore

除了 FileDocumentWriter 之外,Spring AI 还提供了与各种向量存储(vector stores)集成,上面讲的 SimpleVectorStore 就是其中一种。

详细参考:https://docs.spring.io/spring-ai/reference/api/vectordbs.html

当使用向量数据库存储 document 时,可以省略向量化这一步,向量数据库会在底层自动完成向量化。

下面挑几种向量数据库进行讲解,先来回顾下 SimpleVectorStore。


SimpleVectorStore

SimpleVectorStore 是 Spring AI 内置的一个 "内存版" 向量数据库,无需外部依赖,开箱即用,适合本地测试和快速原型开发。

SimpleVectorStore 实现了 VectorStore,VectorStore 继承了 DocumentWriter(文档写入的接口,后续会讲),所以具备文档写入的能力。

2.2代码

    //存入向量数据库
    @Test
    void testSimpleVectorWritter(@Value("classpath:/file/rule.txt") Resource resource){
        //读取文档
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();
        //文档分割
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> splitterDoc = splitter.apply(documents);

        //文档写入向量数据库
        vectorStore.accept(splitterDoc);
        System.out.println("文档写入完成");
    }

2.3结果

因为SimpleVectorStore是内存存储的 所以要debug观察

3.Redis Vector Store

3.1关于Redis Vector Store

我们还可以使用 Redis 来存储向量。Redis 是一个开源(BSD 许可)内存数据结构存储,用作数据库、缓存、消息代理和流引擎。Redis 提供数据结构,例如字符串、哈希、列表、集合、带有范围查询的排序集、位图、超日志、地理空间索引和流。大多数开发人员都熟悉 Redis,并且多年来已被众多大型企业采用。

Redis Search 扩展了 Redis OSS 的核心特性,通过加载 RedisSearch,可以将 Redis 用作向量数据库,支持向量存储和相似性搜索。

3.2环境准备

使用 Redis 来存储向量,需要准备相关环境:

  • 一个 Redis 实例(必需)
  • Redis 客户端(非必需)
  • Embedding 模型接入(必需)
我选择使用 Docker 来启动 Redis 实例(根据自己情况进行选择即可)
docker run -d --name redis-stack -p 6579:6379 -p 8001:8001 redis/redis-stack:latest

第一个端口是redis服务的端口 第二个是redis可视化界面端口

远程服务器要注意开放端口供本地访问

Redis 客户端
  • Redis DeskTop Manager
  • Another Redis Desktop Manager
  • Navicat
Embedding 模型接入

需要一个实现了 EmbeddingModel 接口的服务来生成向量,比如:DashScopeEmbeddingModel、OpenAiEmbeddingModel 都可以。

3.3引入依赖

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>

3.4添加配置

spring:
  ai:
    dashscope:
      api-key: ${DASH_SCOPE_API_KEY} #阿里百炼平台申请的key
      embedding:
        options:
          model: text-embedding-v1   #配置向量模型

    vectorstore:
      redis:
        initialize-schema: true    # 是否自动创建索引结构
        index-name: spring-ai-index # 向量索引名称
        prefix: "embedding:"        # Redis Key 前缀
  data:
    redis:
      url: redis://你的服务地址:6579

3.5代码

    @Autowired
    private RedisVectorStore redisVectorStore;

    @Test
    void testRedisVectorStore(@Value("classpath:/file/rule.txt") Resource resource) {
        //读取文档
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();

        //文档分割
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> splitterDoc = splitter.apply(documents);

        //加载文档到redis向量存储
        redisVectorStore.add(splitterDoc);
        System.out.println("向量存储加载文档完成");

    }

3.6观察存储结果

3.7相似性检索

注意: 文档只需加载一次

    @Test
    void testRedisVectorStore(@Value("classpath:/file/rule.txt") Resource resource) {
//        //读取文档
//        TextReader reader = new TextReader(resource);
//        List<Document> documents = reader.get();
//
//        //文档分割
//        TokenTextSplitter splitter = new TokenTextSplitter();
//        List<Document> splitterDoc = splitter.apply(documents);
//
//        //加载文档到redis向量存储
//        redisVectorStore.add(splitterDoc);
//        System.out.println("向量存储加载文档完成");

        //相似性检索
        SearchRequest searchRequest = SearchRequest.builder()
                .query("大语言模型") //查询的文本
                .topK(5) //匹配文档的前N个
                .similarityThreshold(0.6) //相似度分数大于等于0.5
                .build();
        List<Document> searchResults = redisVectorStore.similaritySearch(searchRequest);
        searchResults.forEach(System.out::println);
    }

3.8相似性检索结果

4.Pinecone Vector Store

4.1关于Pinecone Vector Store

Pinecone 是为机器学习应用量身打造的生产级向量数据库服务,适用于高维向量数据的高效存储、索引与查询。它屏蔽了基础设施管理,提供无缝扩展、实时数据写入和强大安全保障,让开发者和数据科学家能够以极低运维成本,快速构建高效的相似度搜索、推荐系统和 AI 应用。

Pinecone 是一个全托管的向量数据库平台,负责所有后端维护、扩展、更新和监控,让用户专注于应用开发,无需担心数据库管理。

Pinecone 官方地址:https://www.pinecone.io/(需科学上网)

4.2环境准备

完成注册(选择个人免费)

注册成功会⽣成⼀个默认的APIKey注意保存好你的key (也可以新建)

设置 PINECONE_API_KEY ,将Key添加进环境变量

创建索引,索引名字和项⽬中配置保持⼀致

输入索引名字 其他默认 (索引可以理解为一个数据库)

4.3引入依赖

注释掉redis的依赖  否则bean会冲突

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-pinecone</artifactId>
</dependency>

4.4添加配置

注意模型配置生成的维度 要和PINECONE数据库的维度一样

spring:
  ai:
    dashscope:
      api-key: ${DASH_SCOPE_API_KEY}
      embedding:
        options:
          model: text-embedding-v4   #配置向量模型

    vectorstore:
      pinecone:
        api-key: ${PINECONE_API_KEY}
        index-name: pinecone-index #上面创建的索引名称

4.5代码

    @Autowired
    private PineconeVectorStore pineconeVectorStore;

    @Test
    void testPineconeVectorStore(@Value("classpath:/file/rule.txt") Resource resource) {
        //读取文档
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();

        //文档分割
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> splitterDoc = splitter.apply(documents);

        //加载文档到redis向量存储
        pineconeVectorStore.add(splitterDoc);
        System.out.println("向量存储加载文档完成");

    }

4.6观察存储内容

4.7相似性检索

    @Test
    void testPineconeVectorStore(@Value("classpath:/file/rule.txt") Resource resource) {
//        //读取文档
//        TextReader reader = new TextReader(resource);
//        List<Document> documents = reader.get();
//
//        //文档分割
//        TokenTextSplitter splitter = new TokenTextSplitter();
//        List<Document> splitterDoc = splitter.apply(documents);
//
//        //加载文档到redis向量存储
//        pineconeVectorStore.add(splitterDoc);
//        System.out.println("向量存储加载文档完成");

        //相似性检索
        SearchRequest searchRequest = SearchRequest.builder()
                .query("会员") //查询的文本
                .topK(2) //匹配文档的前N个
                .build();
        List<Document> searchResults = pineconeVectorStore.similaritySearch(searchRequest);
        searchResults.forEach(System.out::println);
        System.out.println("相似性检索完成");
    }

4.8检索结果

三.重排序(Re-Ranking)

1.问题切入

在当前 Retrieval-Augmented Generation (RAG) 架构广泛应用的背景下,向量数据库已成为连接大模型与外部知识的核心桥梁。通过将文本转化为高维向量,并基于余弦相似度等度量方式进行相似搜索,我们可以快速从海量文档中 "找到看起来相关的片段",但是,在实际应用中,我们发现其排序结果并不总是最优。

也就是:最相似的向量 ≠ 最相关的答案

所以,重排序 (Re-Ranking) 正在成为高质量 RAG 系统不可或缺的一环。

比如:查询 "如何申请产假"文档 A: "员工请病假需提交医院证明."文档 B: "女职工享有 188 天带薪产假及哺乳时间."

从词向量角度看,"请假"、"申请"、"病假"、"产假" 可能处于相近空间区域,系统或许会误判文档 A 更相关;但从真实需求出发,文档 B 显然更具回答价值。

真正相关的文档被排在后面,会导致送入给 LLM 的上下文质量大打折扣,导致检索的结果不准确。

为解决这个问题,业界普遍采用重排序的模式来提升检索质量。

2.什么是重排序?

Re-Ranking (重排序) 是指在初步检索出一批候选文档后,使用一个更加精细、专精于相关性判断的模型,重新评估每个文档与查询之间的匹配程度,并按新得分重新排序。

这一过程类似于搜索引擎的工作机制:

  1. 先用倒排索引 + 向量检索快速召回几百个候选网页。
  2. 再用精排排序模型打分筛选,最终呈现前 10 条结果。

工作流程如下:

  1. 粗排阶段 (Retrieve):使用向量数据库进行快速 ANN 搜索,召回一批候选文档(如 top-50)
  2. 精排阶段 (Rerank):调用更精细的排序模型(如 Cross-Encoder 类重排序模型),计算每个文档与原始查询之间的细粒度语义匹配分数;
  3. 根据重排序后的得分重新排列文档顺序,选取 top-k 高质量上下文;
  4. 将优化后的上下文注入 Prompt,交由 LLM 生成最终回答。

比如:查询:"怎么请产假"召回可能返回关于 "病假"、"调休" 的文档(因词向量接近);经过 re-ranker 分析后,"产假规定"、"女职工权益保护" 类文档会被提至前列,确保 LLM 接收到最贴切的信息。

3.Spring AI中的实现方式

Spring AI 提供了开箱即用的支持组件:RetrievalRerankAdvisor,极大简化了集成流程。

为方便测试,向量数据库使用了 SimpleVectorStore

@SpringBootTest
public class RerankTest {

    private SimpleVectorStore simpleVectorStore;

    @Autowired
    public RerankTest(EmbeddingModel embeddingModel) {
        this.simpleVectorStore = SimpleVectorStore.builder(embeddingModel).build();
    }

    @BeforeEach
    void testSimpleVectorStore(@Value("classpath:/file/rule.txt") Resource resource){
        // 读取文件
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();
        
        // 分割文档
        TokenTextSplitter splitter = new TokenTextSplitter(200, 50, 5, 1000, true);
        List<Document> apply = splitter.apply(documents);
        
        // 添加向量存储
        simpleVectorStore.add(apply);
        System.out.println("向量存储写入完成");
    }

    @Test
    void testRerank(@Autowired DashScopeRerankModel rerankModel,
                    @Autowired DashScopeChatModel chatModel){
        ChatClient client = ChatClient.builder(chatModel).build();
        //1. 定义一个advisor, 提供rerank模型
        RetrievalRerankAdvisor rerankAdvisor = new RetrievalRerankAdvisor(simpleVectorStore,
                rerankModel, SearchRequest.builder().topK(5).build());
        
        //2. 把这个advisor 绑定给chatclient
        String content = client.prompt()
                .user("金卡会员打几折")
                .advisors(rerankAdvisor)
                .call()
                .content();
        System.out.println(content);

    }

4.通过Debug观察精排前后Document的顺序变化

精排之前

精排之后(重排序)

四.RAG的应用场景

RAG 不是取代 LLM,而是给它装上 "知识雷达"。当答案需要基于特定文档(如企业内部文档、非公开文档)时,RAG 会让 AI 准确率明显提升。下面是一些 RAG 的落地场景:

  1. 智能客服:实时关联企业产品文档、用户手册、历史工单等,让客服回答动态匹配最新产品信息
  2. 企业知识库:将公司 Wiki、SOP 文档、会议纪要等文档导入 RAG 知识库,员工快速检索获得精准答案
  3. 法律咨询服务:构建法律条文 + 历史判例知识库,让 AI 当 "临时律师"

【Spring AI】第三弹:深入解析 RAG 核心特性、关键流程、基于 Spring AI + 知识库实现 RAG 检索增强器 深入解析 RAG 核心特性、关键流程、基于 Spring AI + 知识库实现 RAG 检索增强器 阅读详情

相关推荐

RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用

RAG(Retrieval Augmented Generation,检索增强生成)与模块化 RAG (Modular RAG)简介。Spring AI Alibaba 模块化 RAG 原理与实践。

Apache Dubbo 官方博客 1600

Spring AI Advisor RAG使用指南

摘要: 本文基于Spring AI 1.0.0版本,介绍了如何利用其Retrieval Augmented Generation(RAG)技术增强大语言模型(LLM)的能力。RAG通过结合外部知识检索与生成模型,解决LLM知识局限性问题。文章详细讲解了Spring AI的核心组件(如VectorStore、QuestionAnswerAdvisor和模块化的RetrievalAugmentationAdvisor),并提供了环境配置、依赖管理及代码示例。此外,还涵盖高级功能如自定义Prompt模板、动态过滤

2503_92145588的博客 1645

Spring AI--RAG知识库

RAG(检索增强生成)是一种结合信息检索和AI生成的技术,通过查询外部知识库提高大模型回答的准确性。其工作流程包括文档收集切割、向量转换存储、检索过滤和查询增强四个步骤,关键技术涉及Embedding模型、向量数据库和混合检索策略。文章还演示了基于SpringAI的RAG实现,包括本地知识库处理和云知识库服务对接。通过实例代码展示了文档加载、向量存储及检索增强的应用,为开发者提供了从理论到实践的完整指南。

qq_57465625的博客 914

Spring AI 中的 DocumentTransformer 与 RAG 深度解析

本文深入解析SpringAI中的DocumentTransformer和RAG模块,详细介绍了核心组件及其使用方法。DocumentTransformer提供文本分割、元数据格式化、摘要生成等功能,通过组合多个转换器可构建强大的数据处理流水线。RAG模块则通过文档检索和生成模型的结合实现精准回答,包含QuestionAnswerAdvisor、RetrievalAugmentationAdvisor等核心组件。文章还分享了多查询扩展、上下文感知、文档合并等高级技巧,以及性能优化策略,并提供了完整的实战案例代

weixin_38436441的博客 2751

SAP 配额修改 + 修改记录写入实现

采购配额修改并写入修改记录 cdhdr 和 cdpos

GAVIN 1095

三菱A800变频器矢量控制实战:从速度控制到位置控制的PLG参数设置全解析

本文深入解析了三菱A800变频器结合FR-A8AP选件板与PLG(脉冲编码器)实现高精度矢量控制的实战应用。详细阐述了从速度控制、转矩控制到位置控制三种高级模式的切换逻辑、核心参数设置及硬件接线要点,帮助工程师充分发挥A800变频器的闭环控制潜力,应对复杂工业自动化场景。

weixin_28308325的博客 326

RAG相关技术介绍及Spring AI中使用--第一期

RAG(检索增强生成)是一种结合信息检索和文本生成的技术架构,旨在解决传统大语言模型的知识局限性和幻觉问题。其核心流程包括:1)文档加载与分割;2)向量转换(使用Embedding模型);3)向量存储;4)相似性检索;5)生成增强回答。通过将外部知识库(如企业文档)转换为向量存储在向量数据库中,当用户提问时,系统会先检索相关文档片段,再将其作为上下文输入大模型生成更准确的回答。SpringAI框架提供了完整的RAG实现支持,包括文本向量化、向量存储(如SimpleVectorStore)和检索增强组件

C语言 1523

《Scratch图形化等级考试(1~4级)历届真题解析》专栏总目录

❤️ 专栏名称:《🌸 专栏介绍:中国电子学会《全国青少年软件编程等级考试》Scratch图形化等级考试(1~4级)历届真题解析。🚀 订阅专栏:原价 99.9,🔥 火爆订阅中 🔥,限时 19.9。订阅后可阅读专栏内所有文章,本专栏持续更新中,欢迎订阅!

人生就是一个不断学习的过程 1万+

Spring AI——入门介绍

本文介绍了 Spring AI 的基础使用。

代码星辰的博客 1万+

【赛题解析&命令脚本配置】2023年全国职业院校技能大赛GZ073网络系统管理赛项赛题第9套模块A:网络构建 & (三)无线网络配置+(四)出口网络配置-命令配置

【赛题解析&命令脚本配置】2023年全国职业院校技能大赛GZ073网络系统管理赛项赛题第9套模块A:网络构建 & (三)无线网络配置+(四)出口网络配置-命令配置

君逍遥o 1017

Open3D (C++) 使用点云创建数字高程模型DEM

Open3D (C++) 使用点云创建数字高程模型DEM

点云侠的博客 1391

RAG相关技术介绍及Spring AI中使用--第二期

本文介绍了RAG(检索增强生成)系统中的关键ETL(提取、转换、加载)流程,重点讲解SpringAI框架中的文档读取器实现。ETL过程包括从各类数据源提取内容、进行文本清洗和标准化处理,最后加载到向量数据库。文章详细演示了SpringAI提供的多种DocumentReader实现,包括处理JSON、纯文本、HTML、Markdown、PDF以及各类办公文档的读取器,并展示了MySQL数据库读取器的使用示例。这些工具为构建RAG系统提供了高效的数据预处理能力,确保原始数据能有效转化为适合AI模型检索结构化格式

C语言 1119

华为MetaERP Oracle EBS OM(订单管理)模块详解作为ERP财务解决方案架构师,理解OM模块的业务全景和数据模型至关重要。OM是EBS分销链路的核心引擎,贯穿“订单到收款“(Orde

Oracle EBS OM模块是管理销售订单全生命周期的核心模块,涵盖客户管理、订单执行、发运开票、定价及信用控制等功能。其标准流程包括订单录入、预订、发放、发运确认和自动开票等环节,核心数据表涉及订单、客户、发运和定价等业务对象。OM模块与库存、应收、发运等模块深度集成,架构师需重点设计订单类型、信用控制策略和自动开票接口等关键环节,确保业务流程高效流转。

金牌架构师 357

Spring AI之RAG入门

本文介绍了RAG(检索增强生成)技术的概念、应用场景及核心流程,并详细说明了如何利用SpringAI框架实现RAG功能。

遥远 3036

使用R语言整理GEO数据的简易教程

本教程介绍了使用R语言从GEO数据库下载数据并进行数据整理的基本步骤。根据实际研究问题,你可能需要进行更复杂的数据处理和分析。R语言作为一种强大的生物信息学工具,为GEO数据的整理、分析和可视化提供了丰富的功能和工具。希望这个教程能够帮助你入门GEO数据整理和R语言的应用。如果你在使用过程中遇到问题,或需要进一步的指导,请随时向我询问。祝你在GEO数据分析的旅程中取得成功!

Mrrunsen的博客 3740

OpenAI最强编程助手Codex:下载安装、使用指南(含使用方式、提示技巧、趋势)

OpenAI最近上线了最强编程Agent——Codex,一起来看看吧!

Code_流苏:在代码中寻诗意,在实践中觅真知 1万+

Spring AI 第三期

第三期来啦

🌟【Java萌新成长日记】—— 从零开始,和代码做朋友 1079

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Spring AI入门及浅实践,实战 Spring‎ AI RAG

本文将介绍如何在Spring AI项目中实现RAG(检索增强生成)功能,重点讲解必要的依赖配置和实现步骤。文章列出了项目所需的Maven依赖,包括Spring Boot Web、测试工具、Hutool工具包、Lombok、Knife4j文档工具以及阿里云DashScope SDK等核心组件。同时提供了application.yml和application-local.yml的配置文件模板,帮助开发者快速搭建运行环境。通过这套配置,开发者可以轻松集成阿里云大模型服务,实现基于Spring AI的RAG功能开发

WDJ54068923的博客 2563

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Spring AI Java程序员的AI之Spring AI(三)RAG实战

Spring AI目前也可以实现RAG我们也分步骤来看如何实现。

qq1071643762的博客 4894

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Spring AI 框架介绍

Spring AI 现在(2024/12)已经支持语言,图像,语音类的生成式AI模型。GPT类的模型特点是预训练,这种预训练将AI转变为一个通用的开发工具,而不需要广泛的机器学习或模型训练背景。

xiaoyi52的专栏 9251

RAG 系统中的相关技术和工具

Apache Tika 是一个开源的内容分析工具包,由 Apache 基金会维护,主要用于从各种文件格式中提取元数据和文本内容。它支持超过 1000 种文件类型,包括 PDF、Word、Excel、图像和多媒体文件。Tika 的核心是使用统一的接口解析文件,将内容转换为 XHTML 或纯文本,便于后续处理。处理大文件和内存优化:Tika 支持流式解析和分块读取,避免一次性加载整个文件导致的 Out of Memory 错误。

weixin_51474829的博客 1584

Spring AI 介绍

Spring AI 是 Spring 生态系统推出的面向人工智能工程的应用框架,旨在将 Spring 的设计原则(如可移植性、模块化)引入 AI 领域,为 Java 开发者提供便捷的 AI 集成方案。其核心特点包括生态融合、跨模型支持、提示工程、嵌入技术、Advisor 拦截器机制和检索增强生成(RAG)。Spring AI 通过标准化接口、模块化设计和快速集成,降低了 Java 开发者使用 AI 技术的门槛。然而,它强制依赖 JDK 17+ 和 Spring Boot 3.3+,对老旧系统不友好,且部分功

王小工小工历程 3650

Spring AI:RAG 增强检索介绍

Spring AI:RAG 增强检索介绍

weixin_46619605的博客 1943

Spring AI RAG

检索增强生成(RAG)技术解析与应用 本文系统介绍了检索增强生成(RAG)技术的核心原理与实现方法。该技术通过将外部知识库实时检索结果融入生成过程,有效解决了传统大模型的"知识冻结"问题。RAG采用ETL流程处理非结构化数据,核心步骤包括文档智能分块、向量编码、相似检索和生成增强。 Spring AI提供了模块化实现方案,包含检索模块(VectorStoreDocumentRetriever)和生成模块(QuestionAnswerAdvisor)。关键技术点涉及动态知识增强机制、语义连

王小工小工历程 1270

999-Spring AI Alibaba 框架使用示例总体介绍

Spring AI Alibaba 框架使用示例总体介绍,共几十个案例,每个案例包括案例的实现目录,技术栈和依赖,案例源码实现,案例实现思路,案例的扩展和总结。

rengang66的博客 2479

使用 Spring AI + Elasticsearch 让 RAG 变得简单

使用私人数据定制你的人工智能聊天机器人体验。Spring AI 最近将 Elasticsearch 添加为向量存储,Elastic 团队为其提供了优化。我们很高兴展示使用 Spring AI 和 Elasticsearch 向量数据库()在 Java 中构建完整的 RAG 应用程序是多么简单和直观。开发人员现在可以将 Spring 的模块化功能与 Elasticsearch 的高级检索和 AI 工具结合使用,并快速构建用于企业用例的 Spring Boot 应用程序。

Elastic 中国社区官方博客 4999
上一篇: RAG相关技术介绍及Spring AI中使用--第二期
下一篇: Agent 的规划与执行范式:Plan-Execute-Replan 与 ReAct
刘 大 望
博客等级 码龄2年 896粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值