大家好,我是直奔標杆!专注Java开发者AI转型实战分享,和大家一起从零基础吃透Spring AI,少走弯路、直奔技术标杆~

欢迎来到《Spring AI 零基础到实战》专栏第十三课,也是我们RAG全链路实战的关键一环!

在上一节(Java开发者AI转型第十二课!吃透Embeddings向量化:让Java代码读懂文本语义)中,我们一起解锁了AI时代的“文本转码术”——Embedding向量化,成功将普通文本转换成了计算机能理解的高维浮点数矩阵。

但实操过的朋友都懂,新问题随之而来:传统关系型数据库根本“扛不住”这种高维向量!

如果硬把这些浮点数数组塞进MySQL的VARCHAR或JSON字段,后续用户提问时,需要全表扫描所有向量、逐个计算余弦相似度(向量夹角),这种操作不仅效率极低,在实际项目中完全不可落地。

为了解决向量的高效相似度检索难题,AI时代的专属新基建——向量数据库(Vector Database)应运而生,而这也是我们今天的核心主题。

本节课,我们就来打通RAG的最后一公里——L(Load,加载数据入库),借助Spring AI的优雅接口抽象,将前两节课提取、切分好的文档,完整写入向量数据库,彻底完成ETL全链路的知识准备工作,为后续的语义检索打下坚实基础!

本节学习目标(一起打卡进阶)

  • 认知升级:搞懂传统关系型数据库在AI检索场景的短板,明确向量数据库的不可替代性,避免踩选型坑;

  • 架构吃透:掌握Spring AI VectorStore接口的抽象设计精髓,理解其如何屏蔽底层存储差异;

  • 实战落地:上手本地开发利器SimpleVectorStore,实现内存级极速入库与JSON文件持久化,快速验证功能;

  • 生产适配:串联ETL全链路代码,演示如何零业务代码修改,热切换到企业级Redis向量数据库,适配生产环境。

VectorStore抽象艺术:Spring AI的“数据库无关”设计

作为Java开发者,我们早就习惯了Spring Data JPA、MyBatis的“屏蔽底层”特性——不用关心底层是MySQL、Oracle还是SQL Server,一套代码适配多种数据库。

Spring AI完美继承了这种设计哲学,推出了VectorStore统一接口,让我们彻底摆脱向量数据库的底层差异束缚:今天用本地内存做Demo测试,明天切换到Redis、Milvus等企业级向量库,一行业务代码都不用改!

这里和大家分享一个核心知识点(避坑重点):在Spring AI中,我们完全不需要在业务代码中显式调用EmbeddingModel!只需将切分好的List<Document>丢给VectorStore.add()方法,框架内部会自动拦截文档、调用Embedding模型生成向量,再将向量与原始文本、元数据一起落库,极大简化了开发流程。

简单来说,VectorStore就相当于AI知识库的“专属书架”,负责统一管理向量数据的存储与检索,而底层具体用哪种“书架”(向量数据库),我们完全可以灵活切换。

本地实战:SimpleVectorStore快速上手(新手友好)

对于我们开发者来说,本地调试时,专门部署Milvus这类分布式向量数据库太繁琐,Spring AI贴心提供了开箱即用的本地向量存储——SimpleVectorStore,完美适配本地开发、快速验证场景。

它的核心优势的是:数据存于内存,读写速度极快;同时支持将整个向量库序列化为本地JSON文件,下次服务器重启时直接反序列化加载,避免重复计算Token、重复向量化,大大提升调试效率。

话不多说,直接上实操步骤(代码可直接复制,注释已补全,新手也能看懂):

1. 引入依赖

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

2. 注册SimpleVectorStore Bean(核心配置)

/**
 * 账号:直奔標杆
 * 专注Java AI转型实战,分享可落地的技术方案
 */
// 当spring.ai.vectorstore.type未配置或值为simple时,启用该配置
@ConditionalOnProperty(
        name = {"spring.ai.vectorstore.type"},
        havingValue = "simple",
        matchIfMissing = true
)
@Configuration
public class VectorStoreConfig {

    // 重点:VectorStore必须依赖EmbeddingModel,框架会自动调用它完成文本向量化
    @Bean
    public SimpleVectorStore vectorStore(EmbeddingModel embeddingModel) {
        SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build();

        // 优化点:启动时自动加载本地已保存的向量库,避免重复工作
        File vectorStoreFile = new File("local_vector_store.json");
        if (vectorStoreFile.exists()) {
            vectorStore.load(vectorStoreFile);
            System.out.println(" [System] 从本地JSON文件成功加载向量数据库,无需重复向量化!");
        }
        return vectorStore;
    }
}

全链路实战:串联ETL,实现文档自动入库

结合前面第10课(文档解析)、第11课(文本切分)的内容,我们来串联完整的ETL管道,实现“读取PDF→文本切分→向量化→入库”全自动化,模拟企业私有知识库的真实入库流程。

以下代码可直接运行,我会标注关键步骤和注意事项,大家可以跟着实操,遇到问题欢迎在评论区交流~

/**
 * 账号:直奔標杆
 * ETL全链路实战:文档提取→切分→向量化→入库
 */
public class TestStore {
    // 读取classpath下的PDF文档(可替换为自己的文档路径)
    @Value("classpath:/docs/alibaba-java-guide.pdf")
    private Resource pdfResource;

    // 注入VectorStore(自动适配SimpleVectorStore,后续切换Redis无需修改此处)
    @Autowired
    private VectorStore vectorStore;

    @Test
    void simpleStoreTest() {
        // ---------------- 1. E (Extract) - 提取:读取PDF文档 ----------------
        System.out.println("--- 1. 执行ETL-E (Extract) 读取PDF文档 ---");
        PagePdfDocumentReader reader = new PagePdfDocumentReader(pdfResource);
        List<Document> rawDocuments = reader.get();
        System.out.println("成功读取PDF,总页数:" + rawDocuments.size());

        // ---------------- 2. T (Transform) - 转换:文本切分 ----------------
        System.out.println("--- 2. 执行ETL-T (Transform) 文本Token切分 ---");
        // 切分参数优化:chunkSize=800,overlap=350,避免语义割裂(新手建议参考这个参数)
        TokenTextSplitter splitter = new TokenTextSplitter(800, 350, 5, 10000, true);
        List<Document> chunkedDocuments = splitter.apply(rawDocuments);
        System.out.println("文本切分完成,共得到 " + chunkedDocuments.size() + " 个文本块");

        // ---------------- 3. L (Load) - 加载:向量化并入库 ----------------
        System.out.println("--- 3. 执行ETL-L (Load) 向量化并入库 ---");
        // 核心代码:Spring AI自动拦截文档,调用EmbeddingModel生成向量,再写入数据库
        vectorStore.add(chunkedDocuments);

        // 关键:SimpleVectorStore手动持久化,防止断电丢失数据
        if (vectorStore instanceof SimpleVectorStore simpleStore) {
            File vectorStoreFile = new File("local_vector_store.json");
            simpleStore.save(vectorStoreFile);
            System.out.println("向量数据已持久化到本地JSON文件,路径:" + vectorStoreFile.getAbsolutePath());
        }
    }
}

运行结果与说明

运行测试方法后,控制台会输出如下日志(大家可对照自己的运行结果排查问题):

--- 1. 执行ETL-E (Extract) 读取PDF文档 ---
成功读取PDF,总页数:3
--- 2. 执行ETL-T (Transform) 文本Token切分 ---
文本切分完成,共得到 6 个文本块
--- 3. 执行ETL-L (Load) 向量化并入库 ---
.....Calling EmbeddingModel for document id = 5231761f-b1c4-4812-8aff-3a9b74021690
向量数据已持久化到本地JSON文件,路径:xxx/local_vector_store.json

运行结束后,会在项目根目录生成local_vector_store.json文件,里面包含了每个文本块的原始文本、向量数据、元数据(文件名、页码、切分索引等),示例如下(简化版):

{
  "59acb5f7-f8d2-4bcf-b10d-68b22859fec7" : {
    "text" : "是尽可能少踩坑,杜绝踩重复的坑,切实提升质量意识。.....",
    "embedding" : [ 0.03476889, 0.013548773, 0.02090146, ....],
    "id" : "59acb5f7-f8d2-4bcf-b10d-68b22859fec7",
    "metadata" : {
      "file_name" : "alibaba-java-guide.pdf",
      "chunk_index" : 1,
      "page_number" : 1,
      "parent_document_id" : "5b2369b6-81b0-4d0f-a5fd-5f5c8e9853a3",
      "total_chunks" : 2
    }
  }
}

源码解析(关键理解点)

很多朋友会好奇,Spring AI是如何自动完成向量化并入库的?这里给大家拆解核心调用链路,不用深入源码,理解这个逻辑即可:

// 核心调用链路: VectorStore#add → AbstractObservationVectorStore.doAdd
// Spring一贯风格:doXxx方法定义抽象逻辑,子类(如SimpleVectorStore)实现具体业务
public void doAdd(List<Document> documents) {
        //... 省略无关逻辑
        // 1. 调用EmbeddingModel,将单个文档转为向量
        float[] embedding = this.embeddingModel.embed(document);
     // 2. 封装存储对象,包含文档ID、文本、元数据、向量
        SimpleVectorStoreContent storeContent = new SimpleVectorStoreContent(document.getId(), document.getText(), document.getMetadata(), embedding);
        // 3. 写入内存存储(SimpleVectorStore的核心逻辑)
        this.store.put(document.getId(), storeContent);
        //...
}

生产演进:零代码切换到Redis向量数据库

SimpleVectorStore适合本地调试,但生产环境中,面对超大文件、高并发语义检索(带Metadata条件过滤),就必须切换到企业级向量数据库。

这里给大家推荐Redis向量库(最易落地,很多项目已在用Redis,无需额外新增基础设施)——Redis Stack(带RediSearch模块),它不仅是K-V缓存,更是具备内存级极速检索能力的向量数据库,适配生产场景需求。

重点来了:借助Spring AI的VectorStore抽象,我们只需2步配置,就能零业务代码修改,实现从SimpleVectorStore到Redis的切换!

第一步:替换依赖(pom.xml)

移除或保留spring-ai-vector-store依赖(Redis依赖会自动关联),新增Spring官方Redis Vector Store Starter:

<!-- Redis向量库,依赖spring-ai-vector-store,无需重复引入 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-redis-store-spring-boot-starter</artifactId>
</dependency>

第二步:部署Redis Stack并配置(application.yml)

首先部署带向量搜索插件的Redis(Redis Stack),推荐用Docker快速部署(命令可直接复制):

# 拉取Redis Stack镜像(带RediSearch模块,支持向量存储)
docker pull redis/redis-stack:latest
# 启动容器,映射端口,设置密码和持久化
docker run -itd \
  --restart always \
  --name redis-stack \
  -p 6379:6379 \
  -p 8001:8001 \
  -e REDIS_ARGS="--requirepass 123456 --appendonly yes" \
  -v 本地目录/docker-data/redis/conf:/usr/local/etc/redis \
  -v 本地目录/docker-data/redis/data:/data \
  redis/redis-stack:latest

部署完成后,在application.yml中配置Redis连接和向量索引参数:

spring:
  data:
    redis:
      host: localhost  # 替换为你的Redis地址(生产环境填云端地址)
      port: 6379
      password: 123456 # 对应上面启动命令中的密码
  ai:
    vectorstore:
      type: redis  # 指定使用Redis向量库,替代默认的simple
      redis:
        index-name: spring-ai-document-index # 自动创建的索引名称
        prefix: "document:" # Redis Key前缀,便于区分向量数据
        initialize-schema: true # 开启自动建表和索引初始化,无需手动操作

配置完成后,再次调用vectorStore.add()方法,数据会自动写入Redis向量库,业务代码完全不用修改!这就是Spring AI抽象设计的魅力,极大降低了生产环境的适配成本。

总结:ETL全链路打通,知识库地基筑牢!

到这里,我们用四节课的时间,彻底打通了RAG架构中最底层、最繁琐的ETL全链路:从物理文件(PDF等)提取文本,到切分成分割合理、语义完整的文本块,再到通过Embedding向量化,最终存入向量数据库,每一步都落地实操,没有多余的理论堆砌。

回顾一下核心收获:我们不仅掌握了VectorStore的抽象设计和SimpleVectorStore的本地实战,还实现了零代码切换到Redis向量库,完成了从本地调试到生产适配的全流程。现在,我们的AI已经拥有了一个私有化的“超级知识库”,为后续的语义检索、智能问答做好了充分准备。

最后和大家说一句:AI转型不用慌,跟着直奔標杆一步一步实操,每节课吃透一个核心知识点,慢慢就能从零基础成长为能落地AI项目的Java开发者~

下节预告(重点关注!)

知识存进向量库了,下一步就是“取”——如何从海量向量中,精准检索出与用户问题最相关的内容?

比如用户提问:“公司最新的离职赔偿 N+1 是怎么算的?”,我们如何将这个问题向量化,在向量数据库中“大海捞针”,快速召回最相关的3个法律条文段落?

下一节课(第十四课):《Java开发者AI转型第十四课!大海捞针实战:向量数据库召回与相似度检索全解析》,我们将解锁VectorStore的核心方法similaritySearch(),完成RAG架构的最后一块核心拼图,不见不散!

往期回顾(串联学习,效果更佳)

  • Java开发者AI转型第十课!化繁为简!Spring AI 全能文档解析器 (Document Readers) 与元数据提取实操

  • Java开发者AI转型第十一课!文本切分避坑指南:Spring AI 智能分块与Overlap语义防割裂实战

  • Java开发者AI转型第十二课!吃透Embeddings向量化:让Java代码读懂文本语义

欢迎大家在评论区留言交流实操中遇到的问题,也可以点赞收藏,后续反复回顾!一起加油,直奔技术标杆~

 

Logo

一站式 AI 云服务平台

更多推荐