Spring AI VectorStore与RAG Pipeline源码深度解析:从PDF到检索增强生成的完整实现

Spring AI VectorStore与RAG Pipeline源码深度解析:从PDF到检索增强生成的完整实现

摘要:本文深入分析Spring AI中VectorStore与RAG(检索增强生成)的完整实现原理,涵盖VectorStore接口设计与五种实现选型、EmbeddingModel向量化机制、TokenTextSplitter文本分割策略、余弦相似度搜索算法、QuestionAnswerAdvisor源码解析、ETL文档处理管道,以及实战踩坑经验。适合希望从源码层面彻底理解Spring AI RAG机制的Java开发者。


文章目录


一、什么是RAG?为什么需要它?

1.1 AI"幻觉"问题

大语言模型(LLM)最令人头疼的问题之一就是幻觉(Hallucination):它会以极其自信的口吻一本正经地生成并不真实的信息。

典型场景:

场景 问题 AI的"幻觉"表现
查询内部文档 "我们的产品A定价是多少?" AI编造了一个看起来合理的价格
知识库问答 "2024年最新的接口协议是什么?" 回答了训练截止前的旧版本协议
私有知识问答 "公司HR政策中的年假规定是?" 给出了通用行业做法而非公司规定

根本原因:LLM的知识被锁定在训练时间点,既无法获取最新信息,也无从了解企业私有知识。

1.2 RAG的解决思路

RAG(Retrieval-Augmented Generation,检索增强生成) 是目前解决上述问题的最有效方案。其核心思路非常直观:

在生成回答之前,先从知识库中检索 出与用户问题最相关的内容,然后将这些内容作为上下文 提供给LLM,让LLM基于证据生成回答。

一句话总结:RAG = 给AI开卷考试,允许它翻书再答题。

1.3 RAG的核心价值

  • 消除幻觉:有据可依,而非凭空捏造
  • 知识实时更新:更新知识库即可,无需重新训练模型
  • 私有知识注入:将企业内部文档变成AI的"外挂大脑"
  • 来源可追溯:可以在回答中标注参考的文档来源

二、一张图看懂RAG全流程架构

复制代码
┌──────────────────────────────────────────────────────────────────────┐
│                    Spring AI RAG 全流程架构                           │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌──────────┐   ┌─────────────┐   ┌──────────────┐   ┌───────────┐ │
│  │ 原始文档  │ → │ 文档读取    │ → │ 文本分割     │ → │ 向量化    │ │
│  │ PDF/MD   │   │DocumentReader│  │TextSplitter  │  │EmbedModel │ │
│  └──────────┘   └─────────────┘   └──────────────┘   └─────┬─────┘ │
│                                                           │         │
│                                                    float[]向量       │
│                                                           ↓         │
│                                                   ┌─────────────┐  │
│                                                   │ VectorStore  │  │
│                                                   │ Redis/Pg/... │  │
│                                                   └──────┬──────┘  │
│  ═════════════════════ 用户提问时 ════════════════════════           │
│                                                          ↓          │
│  ┌──────────┐   ┌──────────────────┐   ┌──────────────┐            │
│  │ 用户提问  │ → │ 向量化+相似度搜索  │ → │ Top-K相关文档 │           │
│  └──────────┘   │ similaritySearch  │   └──────┬───────┘            │
│                 └──────────────────┘          │                     │
│                                               ↓                     │
│                                         ┌──────────┐               │
│                                         │ 注入提示词 │               │
│                                         │ (Advisor) │               │
│                                         └─────┬─────┘               │
│                                               ↓                     │
│                                         ┌──────────┐               │
│                                         │ LLM 生成  │ ← 有据可依!  │
│                                         │ 最终回答  │               │
│                                         └──────────┘               │
└──────────────────────────────────────────────────────────────────────┘

整个流程分为两个阶段:

  • 离线阶段(文档入库):读取文档 → 分割为小块 → 向量化 → 存入VectorStore
  • 在线阶段(用户提问):向量化问题 → 相似度搜索 → 注入上下文 → LLM生成回答

三、VectorStore:AI的"图书馆"

3.1 核心接口设计

VectorStore 是整个RAG系统的基石,负责存储文档向量并提供相似度搜索能力:

java 复制代码
// org.springframework.ai.vectorstore.VectorStore
public interface VectorStore {
    
    // 📥 入库:将文档列表写入向量数据库
    void add(List<Document> documents);
    
    // 🗑️ 出库:按ID批量删除文档
    void delete(List<String> documentIds);
    
    // 🔍 简单搜索:返回最相关的文档
    List<Document> similaritySearch(String query);
    
    // 🔍 高级搜索:支持TopK、阈值过滤、元数据过滤
    List<Document> similaritySearch(SearchRequest request);
}

接口设计的核心原则是统一抽象:无论底层使用Redis、PostgreSQL还是Milvus,上层代码调用的接口完全一致。

3.2 SearchRequest:搜索参数详解

java 复制代码
public class SearchRequest {
    private String query;                    // 查询文本
    private int topK = 4;                    // 返回前K个结果(默认4条)
    private double similarityThreshold = 0.0; // 相似度阈值(0~1,默认不过滤)
    private Map<String, Object> filterExpression; // 元数据过滤条件
}

这四个参数的组合覆盖了绝大多数检索场景:

参数组合 适用场景
topK=3 + threshold=0.7 只要最相关的3条高质量结果
topK=10 + threshold=0.0 + 过滤条件 按分类精确筛选
topK=1 + threshold=0.9 只要高度匹配的唯一答案

3.3 Document:文档数据模型

java 复制代码
// org.springframework.ai.document.Document
public class Document {
    
    private final String id;                    // 唯一标识(UUID)
    private final String content;               // 文档正文内容
    private final Map<String, Object> metadata; // 元数据(来源、分类等)
    private final float[] embedding;            // 向量表示(768/1536/4096维等)
    
    // 快捷构造器
    public Document(String content) {
        this(content, Map.of());  // 自动生成ID和空元数据
    }
}

注意metadata 是隐藏的神器------存入文件名、章节号、作者、创建日期等任意元数据,后续做过滤搜索全靠它。

3.4 五种VectorStore实现选型指南

Spring AI内置支持多种主流向量数据库:

维度 Redis PgVector Milvus Qdrant Chroma
部署难度 ⭐ 最简单 ⭐⭐ 中等 ⭐⭐⭐ 较复杂 ⭐⭐⭐ 较复杂 ⭐⭐ 简单
性能(百万级) ⭐⭐⭐ 够用 ⭐⭐⭐⭐ 强 ⭐⭐⭐⭐⭐ 最强 ⭐⭐⭐⭐ 很强 ⭐⭐ 一般
运维成本 低(PG扩展) 低(嵌入式)
适合场景 已有Redis的项目 已有PG的项目 大规模生产环境 高性能过滤场景 开发测试/小规模

选型建议

  • 项目已有Redis或PostgreSQL → 优先选对应实现,零额外基础设施成本
  • 从零开始做生产级RAG → MilvusPgVector
  • 本地开发验证 → Chroma(纯内存,无需独立服务端)

四、EmbeddingModel:把文字变成数字

4.1 接口定义

java 复制代码
public interface EmbeddingModel {
    
    // 对单个请求进行向量化
    EmbeddingResponse embed(EmbeddingRequest request);
    
    // 批量向量化(默认实现逐个调用)
    default List<float[]> embed(List<String> texts) {
        return texts.stream()
            .map(text -> embed(new EmbeddingRequest(text)))
            .map(response -> response.getResult().getOutput())
            .toList();
    }
}

4.2 一个文本的向量化之旅

复制代码
原始文本: "Spring AI 是一个 AI 应用开发框架"
    ↓
EmbeddingModel.embed() 被调用
    ↓
HTTP POST 到 Ollama / OpenAI / 智谱AI 等
    ↓
模型内部处理(分词 → 编码 → 映射到高维空间)
    ↓
返回: float[] embedding
= [0.1234, -0.5678, 0.9012, ..., 0.3456]
     ↑                          ↑
     共 768/1536/4096 个浮点数(取决于模型)

关键认知 :这个向量不是随机数字------语义相近的文本在向量空间中距离更近。这是相似度搜索的理论基础。

4.3 从文档到入库的完整代码

java 复制代码
// 准备文档
List<Document> documents = List.of(
    new Document("Spring AI 是一个 AI 框架", 
        Map.of("source", "docs", "category", "framework")),
    new Document("Spring Boot 是一个 Web 框架",
        Map.of("source", "docs", "category", "web"))
);

// Step 1: 提取所有文本内容
List<String> texts = documents.stream()
    .map(Document::getContent)
    .toList();

// Step 2: 批量向量化
List<float[]> embeddings = embeddingModel.embed(texts);

// Step 3: 将向量写回每个Document对象
for (int i = 0; i < documents.size(); i++) {
    documents.get(i).setEmbedding(embeddings.get(i));
}

// Step 4: 存入VectorStore
vectorStore.add(documents);  // ✅ 入库完成!

注意 :入库和查询时使用的 EmbeddingModel 必须是同一个模型,否则向量维度或语义空间不一致,相似度搜索结果将毫无意义。


五、TokenTextSplitter:大文档怎么切成小块

5.1 为什么必须分割?

LLM有上下文长度限制(4K/8K/128K tokens)。一份100页的PDF不可能一次性塞进去。解决方案:先切小块,每块单独向量化存储,查询时只检索最相关的几块。

5.2 TokenTextSplitter 核心源码

java 复制代码
// org.springframework.ai.document.TokenTextSplitter
public class TokenTextSplitter implements TextSplitter {
    
    private final int chunkSize;      // 每块最大Token数(如256/512/1024)
    private final int chunkOverlap;   // 块间重叠Token数(如20~50)
    private final Tokenizer tokenizer;// Token计数器
    
    public List<String> split(String text) {
        List<String> chunks = new ArrayList<>();
        
        // 第一层:按段落分割(保留语义完整性)
        String[] paragraphs = text.split("\n\n");
        
        for (String paragraph : paragraphs) {
            int tokens = tokenizer.countTokens(paragraph);
            
            if (tokens <= this.chunkSize) {
                // 小段落直接作为一个chunk
                chunks.add(paragraph);
            } else {
                // 大段落继续细分
                chunks.addAll(splitLargeChunk(paragraph));
            }
        }
        return chunks;
    }
    
    // 第二层:按句子粒度细切
    private List<String> splitLargeChunk(String text) {
        List<String> chunks = new ArrayList<>();
        String[] sentences = text.split("。|!|?");
        
        StringBuilder currentChunk = new StringBuilder();
        for (String sentence : sentences) {
            int currentTokens = tokenizer.countTokens(currentChunk.toString());
            int sentenceTokens = tokenizer.countTokens(sentence);
            
            if (currentTokens + sentenceTokens <= this.chunkSize) {
                currentChunk.append(sentence).append("。");
            } else {
                if (currentChunk.length() > 0) {
                    chunks.add(currentChunk.toString());
                }
                currentChunk = new StringBuilder(sentence).append("。");
            }
        }
        
        if (currentChunk.length() > 0) {
            chunks.add(currentChunk.toString());
        }
        return chunks;
    }
}

5.3 切割效果示意

复制代码
原文档(约1000 tokens):
"Spring AI 是一个 AI 应用开发框架...[中间500 tokens]...
它提供了 ChatModel、VectorStore...[结尾部分]..."

    ↓ TokenTextSplitter(chunkSize=256, chunkOverlap=20)

┌─────────────────────────────────────────────────┐
│ Chunk 1 [tokens 0 ~ 256]                        │
│ "Spring AI 是一个 AI 应用开发框架..."            │
├──────────────────┬──────────────────────────────┤
│ Chunk 2 [236~492]│ ← 与Chunk1重叠20 tokens       │
│ "...框架的核心特性包括 ChatModel..."              │
├──────────────────┬──────────────────────────────┤
│ Chunk 3 [472~728]│ ← 与Chunk2重叠20 tokens       │
│ "...ChatModel 支持 GPT 和 Ollama..."             │
├──────────────────┬──────────────────────────────┤
│ Chunk 4 [708~1000]│ ← 与Chunk3重叠20 tokens      │
│ "...Ollama 还支持本地部署..."                      │
└─────────────────────────────────────────────────┘

💡 为什么要有 overlap?
→ 保证被切断的句子/概念不会丢失上下文信息

5.4 chunkSize 参数选型建议

场景 推荐chunkSize 原因
FAQ类短问答 128 ~ 256 问题本身很短,不需要大块
技术文档 512 ~ 1024 需要完整的代码示例/说明
法律合同 256 ~ 512 条款之间相对独立
小说/长文 1024 ~ 2048 需要叙事连贯性

六、相似度搜索:如何在向量空间中找到"最相近"的内容

6.1 余弦相似度算法

java 复制代码
/**
 * 余弦相似度计算 --- RAG检索的核心算法
 * 返回值范围:-1.0(完全相反)~ 1.0(完全相同),通常关注 0~1 区间
 */
public float cosineSimilarity(float[] a, float[] b) {
    float dotProduct = 0.0f;   // 点积
    float normA = 0.0f;        // A向量的模
    float normB = 0.0f;        // B向量的模
    
    for (int i = 0; i < a.length; i++) {
        dotProduct += a[i] * b[i];
        normA += a[i] * a[i];
        normB += b[i] * b[i];
    }
    
    return dotProduct / (float)(Math.sqrt(normA) * Math.sqrt(normB));
}

相似度值的直观含义:

相似度值 含义
0.90 ~ 1.00 语义几乎相同
0.75 ~ 0.90 相关,主题一致
0.50 ~ 0.75 有一定关联
0.00 ~ 0.50 基本无关

6.2 以Redis为例看完整搜索流程

java 复制代码
// RedisVectorStore 的 similaritySearch 实现
public class RedisVectorStore implements VectorStore {
    
    @Override
    public List<Document> similaritySearch(SearchRequest request) {
        
        // Step 1: 把用户的查询文本也变成向量
        float[] queryEmbedding = embeddingModel.embed(request.getQuery());
        
        // Step 2: 从Redis取出所有已存储的文档向量
        List<Document> allDocuments = getAllDocuments();
        
        // Step 3: 逐一计算余弦相似度,过滤 + 排序 + 取TopK
        return allDocuments.stream()
            .map(doc -> new DocumentWithScore(
                doc,
                cosineSimilarity(queryEmbedding, doc.getEmbedding())
            ))
            .filter(dws -> dws.score >= request.getSimilarityThreshold())
            .sorted((a, b) -> Float.compare(b.score, a.score))
            .limit(request.getTopK())
            .map(dws -> dws.document)
            .toList();
    }
}

6.3 一次真实搜索的全过程

复制代码
用户提问: "Spring 框架有什么特点?"
    ↓
Step 1: 向量化问题
  "Spring 框架有什么特点?" → [0.234, 0.567, 0.891, ...]

Step 2: 计算与所有文档的相似度
  ├─ "Spring AI 是一个 AI 框架"     → 相似度 0.85 ✅
  ├─ "Spring Boot 是一个 Web 框架"  → 相似度 0.78 ✅
  ├─ "Java 是一门编程语言"           → 相似度 0.32 ❌
  └─ "Python 的 Django 框架很流行"  → 相似度 0.18 ❌

Step 3: 设 threshold=0.5 → 过滤后剩 2 条
Step 4: 取 topK=2 → 返回这两条给 AI 作为参考素材

七、QuestionAnswerAdvisor:RAG的核心拦截器

7.1 工作原理概述

QuestionAnswerAdvisor 继承了 CallAroundAdvisor 接口,在请求发送给LLM之前自动完成以下操作:

  1. 提取用户问题
  2. 在VectorStore中执行相似度搜索
  3. 将检索结果格式化为参考文档
  4. 注入到系统提示词中

这一切对上层调用者完全透明

7.2 源码深度解析

java 复制代码
/**
 * QuestionAnswerAdvisor --- RAG的核心拦截器
 */
public class QuestionAnswerAdvisor implements CallAroundAdvisor {
    
    private final VectorStore vectorStore;
    private final EmbeddingModel embeddingModel;
    private final int topK;  // 默认检索前4条
    
    @Override
    public String getName() {
        return "QuestionAnswerAdvisor";
    }
    
    @Override
    public int getOrder() {
        return 2;  // 执行顺序:MemoryAdvisor之后,其他Advisor之前
    }
    
    /**
     * ★★★ 核心方法:在请求发出前注入检索结果 ★★★
     */
    @Override
    public AdvisedRequest before(AdvisedRequest request) {
        
        // Step 1: 提取用户的问题文本
        String userQuestion = extractUserQuestion(request);
        
        // Step 2: 在VectorStore中搜索相关文档
        List<Document> relevantDocs = vectorStore.similaritySearch(
            SearchRequest.builder()
                .query(userQuestion)
                .topK(this.topK)
                .similarityThreshold(0.5)
                .build()
        );
        
        // Step 3: 将文档列表格式化为可读文本
        String ragContext = buildRagContext(relevantDocs);
        
        // Step 4: 将检索结果追加到系统提示词
        String enhancedSystemPrompt = request.getSystemText() + "\n\n" +
            "【参考文档】\n" + ragContext + "\n" +
            "请基于以上参考文档回答用户问题。如果文档中没有相关信息,请如实告知。";
        
        // Step 5: 返回增强后的请求(对上层透明)
        return AdvisedRequest.from(request)
            .systemText(enhancedSystemPrompt)
            .build();
    }
    
    private String buildRagContext(List<Document> documents) {
        return documents.stream()
            .map(doc -> "- " + doc.getContent())
            .collect(Collectors.joining("\n"));
    }
    
    @Override
    public ChatResponse after(AdvisedRequest request,
                               AdvisedResponse<ChatResponse> response) {
        return response.getChatResponse();  // 后置处理无特殊逻辑
    }
}

7.3 RAG完整数据流可视化

复制代码
用户提问: "Spring AI 有哪些核心模块?"
    │
    ▼
╔══════════════════════════════════════════════════╗
║  QuestionAnswerAdvisor.before() 工作中            ║
╠══════════════════════════════════════════════════╣
║                                                  ║
║  ① 向量化问题                                    ║
║     → [0.12, 0.78, ...]                          ║
║                                                  ║
║  ② VectorStore.similaritySearch(topK=3)         ║
║     • "Spring AI包含ChatModel、EmbeddingModel..." ║
║     • "Spring AI支持OpenAI、Ollama等多种模型..."  ║
║     • "VectorStore提供统一的向量存储抽象..."      ║
║                                                  ║
║  ③ 构建增强系统提示词                             ║
║     原始: "你是一个AI助手"                         ║
║     增强: "你是一个AI助手" +                       ║
║           "【参考文档】\n• ...ChatModel...\n..."  ║
║           "请基于以上参考文档回答..."              ║
║                                                  ║
╚══════════════════════════════════════════════════╝
    │
    ▼ 发送给 LLM(LLM 已拿到"参考资料")
    │
    ▼ LLM 生成有据可依的回答

八、ETL管道:从PDF到VectorStore的自动化流水线

8.1 DocumentReader:统一的文档读取接口

java 复制代码
// org.springframework.ai.document.DocumentReader
public interface DocumentReader {
    List<Document> read(Resource resource);  // 统一入口
}

Spring AI 内置了多种实现:

Reader 支持格式 典型场景
PdfDocumentReader PDF 技术手册、论文、合同
MarkdownDocumentReader Markdown (.md) 技术博客、README、Wiki
WebDocumentReader HTML网页 在线文档、新闻文章
JsonDocumentReader JSON 结构化数据、API响应

8.2 DocumentTransformer:文档加工流水线

java 复制代码
// org.springframework.ai.document.DocumentTransformer
public interface DocumentTransformer {
    List<Document> transform(List<Document> documents);
}

// 最常用的转换器:文本分割
public class TextSplitterDocumentTransformer implements DocumentTransformer {
    
    private final TextSplitter textSplitter;
    
    @Override
    public List<Document> transform(List<Document> documents) {
        return documents.stream()
            .flatMap(doc -> {
                List<String> chunks = textSplitter.split(doc.getContent());
                return chunks.stream()
                    .map(chunk -> new Document(
                        chunk,
                        doc.getMetadata()  // 元数据透传!继承原文档的来源信息
                    ));
            })
            .toList();
    }
}

8.3 完整的ETL Pipeline代码

从一份PDF到可检索的向量存储,全流程代码如下:

java 复制代码
// ========== ETL Pipeline 完整流程 ==========

// Step 1: 读取原始文档
DocumentReader reader = new PdfDocumentReader();
List<Document> documents = reader.read(
    new FileSystemResource("company-docs/product-manual.pdf")
);

// Step 2: 文本分割(大文档 → 小chunks)
DocumentTransformer splitter = new TextSplitterDocumentTransformer(
    new TokenTextSplitter(
        512,    // 每个chunk最大512 tokens
        50      // chunk之间重叠50 tokens
    )
);
List<Document> chunks = splitter.transform(documents);
// 假设原来2个文档 → 切割后变成了15个chunk

// Step 3: 向量化所有chunk
List<float[]> embeddings = embeddingModel.embed(
    chunks.stream().map(Document::getContent).toList()
);
for (int i = 0; i < chunks.size(); i++) {
    chunks.get(i).setEmbedding(embeddings.get(i));
}

// Step 4: 写入VectorStore
vectorStore.add(chunks);  // ✅ 入库完成!

// ===== 之后即可检索 =====
List<Document> results = vectorStore.similaritySearch(
    SearchRequest.builder()
        .query("产品的定价策略是什么?")
        .topK(3)
        .similarityThreshold(0.7)
        .build()
);

九、在ChatClient中使用RAG

9.1 一行注册,开箱即用

java 复制代码
// 构建带有RAG能力的ChatClient
ChatClient chatClient = ChatClient.builder(chatModel)
    // 注册RAG Advisor ------ 一行代码让AI具备知识检索能力
    .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, embeddingModel, 3))
    // 可叠加记忆能力
    .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
    .build();

9.2 调用方式与普通对话完全一致

java 复制代码
// 用户无需知道 RAG 的存在,调用方式完全相同
String answer = chatClient.prompt()
    .user("Spring AI 有哪些核心模块?")
    .call()
    .content();

/*
 * 幕后自动完成的操作(对上层透明):
 *
 * ① QuestionAnswerAdvisor 拦截请求
 * ② 向量化用户问题
 * ③ 在VectorStore中搜索最相关的3篇文档
 * ④ 将文档内容注入系统提示词
 * ⑤ 发送给LLM(LLM看到了参考资料)
 * ⑥ LLM基于文档生成有据可依的回答
 * ⑦ 返回给用户
 */

这正是Spring AI的设计理念:底层复杂度完全封装,上层API保持极致简洁


十、实战踩坑指南

10.1 chunkSize 调不好会怎样?

问题 现象 解决方法
太大(如2048) 检索到的内容太泛泛,噪声多,回答质量下降 缩小到256~512
太小(如64) 丢失上下文,AI看到的信息支离破碎 放大到256以上
没有overlap 关键信息恰好在切割边界处丢失 设置20~50的重叠

10.2 相似度阈值设多少合适?

复制代码
推荐阈值范围(基于实践经验):

0.7 ~ 0.9  → 严格匹配(适合FAQ、事实查询)
0.5 ~ 0.7  → 适中(适合大多数知识库场景)✅ 推荐
0.3 ~ 0.5  → 宽松(适合探索性搜索)

注意:首次上线建议先设为0,观察实际相似度分布后再调整。

10.3 检索不到结果的排查清单

按以下顺序逐步排查:

  1. 确认文档是否成功入库 → 调用 vectorStore.similaritySearch("") 看看有没有数据
  2. 检查 chunkSize 是否合理 → 文档被切得太碎时,单个chunk可能失去语义
  3. 检查 EmbeddingModel 是否一致 → 入库和查询必须使用同一个模型
  4. 降低阈值测试 → 将 similarityThreshold 设为0,确认是否有结果返回
  5. 检查字符编码 → PDF中的特殊字符、中文乱码会破坏向量语义

10.4 避免重复入库

多次调用 vectorStore.add() 可能产生重复文档,导致冗余结果。建议:

java 复制代码
// 方案1:入库前先按来源删除旧数据
List<String> oldIds = vectorStore.similaritySearch(
    SearchRequest.builder()
        .filterExpression(Map.of("source", "product-manual.pdf"))
        .topK(1000)
        .build()
).stream().map(Document::getId).toList();

if (!oldIds.isEmpty()) {
    vectorStore.delete(oldIds);
}

// 方案2:在 metadata 中记录版本号,按版本管理

十一、本篇小结与系列索引

11.1 核心知识点汇总

主题 一句话总结 重要度
RAG核心思想 给AI开卷考试------检索相关知识后再回答 ⭐⭐⭐⭐⭐
VectorStore接口 统一的向量存储抽象,屏蔽底层差异 ⭐⭐⭐⭐⭐
五种实现选型 Redis/PgVector/Milvus/Qdrant/Chroma各有适用场景 ⭐⭐⭐⭐
Document数据模型 内容+元数据+向量三合一 ⭐⭐⭐⭐
EmbeddingModel 文本→向量的转换器,RAG的基础设施 ⭐⭐⭐⭐⭐
TokenTextSplitter 按Token粒度切分大文档,chunkSize+overlap是关键 ⭐⭐⭐⭐
余弦相似度 向量空间中的"距离度量",决定检索相关性 ⭐⭐⭐⭐
QuestionAnswerAdvisor RAG的核心------自动检索+注入提示词 ⭐⭐⭐⭐⭐
ETL Pipeline Read→Split→Embed→Store 四步标准流程 ⭐⭐⭐⭐

11.2 关键类速查表

类 / 接口 所在模块 核心职责
VectorStore spring-ai-vectorstore 向量存储核心接口
Document spring-ai-common 文档数据模型(内容+元数据+向量)
EmbeddingModel spring-ai-model 向量化模型接口
TextSplitter spring-ai-document 文本分割策略接口
TokenTextSplitter spring-ai-document 按Token切分的具体实现
SearchRequest spring-ai-vectorstore 搜索参数封装
QuestionAnswerAdvisor spring-ai-advisor ★ RAG核心拦截器
DocumentReader spring-ai-document 多格式文档读取接口
DocumentTransformer spring-ai-document 文档转换处理链接口

📖 本文属于「亦暖筑序 · Spring AI 系列」

🔗 系列文章导航

相关推荐
黎阳之光2 小时前
【从虚拟到实体:黎阳之光实时三维重构,开启AI空间智能新纪元
大数据·人工智能·算法·安全·数字孪生
小小AK2 小时前
金蝶云星空与旺店通·企业奇门系统对接方案
人工智能
VBsemi-专注于MOSFET研发定制2 小时前
面向AI低空应急指挥平台的无人机动力与负载管理MOSFET选型策略与器件适配手册
人工智能·无人机
卤煮最下饭2 小时前
AI Glasses配合灵珠智能体实现“星幕识影”
人工智能
拓端研究室2 小时前
2026年人工智能AI原生型公司:面向规模化AI应用的企业架构设计研究报告
人工智能·ai-native
郝学胜-神的一滴2 小时前
深度学习激活函数核心精讲:Sigmoid 原理、推导与工程实践
人工智能·pytorch·python·深度学习·神经网络·机器学习
勇哥是也2 小时前
前端也能玩 AI:阿里云百炼流式对话开发
前端·人工智能·阿里云
熊猫钓鱼>_>2 小时前
私有化AI视频助手搭建实录:当Ollama遇上OpenClaw
人工智能·音视频·agent·qwen·ollama·openclaw·happyhorse-1.0