Spring AI VectorStore与RAG Pipeline源码深度解析:从PDF到检索增强生成的完整实现
摘要:本文深入分析Spring AI中VectorStore与RAG(检索增强生成)的完整实现原理,涵盖VectorStore接口设计与五种实现选型、EmbeddingModel向量化机制、TokenTextSplitter文本分割策略、余弦相似度搜索算法、QuestionAnswerAdvisor源码解析、ETL文档处理管道,以及实战踩坑经验。适合希望从源码层面彻底理解Spring AI RAG机制的Java开发者。
文章目录
- 一、什么是RAG?为什么需要它?
- 二、一张图看懂RAG全流程架构
- 三、VectorStore:AI的"图书馆"
- 四、EmbeddingModel:把文字变成数字
- 五、TokenTextSplitter:大文档怎么切成小块
- 六、相似度搜索:如何在向量空间中找到"最相近"的内容
- 七、QuestionAnswerAdvisor:RAG的核心拦截器
- 八、ETL管道:从PDF到VectorStore的自动化流水线
- 九、在ChatClient中使用RAG
- 十、实战踩坑指南
- 十一、本篇小结与系列索引
一、什么是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 → Milvus 或 PgVector
- 本地开发验证 → 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之前自动完成以下操作:
- 提取用户问题
- 在VectorStore中执行相似度搜索
- 将检索结果格式化为参考文档
- 注入到系统提示词中
这一切对上层调用者完全透明。
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 |
技术手册、论文、合同 | |
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 检索不到结果的排查清单
按以下顺序逐步排查:
- 确认文档是否成功入库 → 调用
vectorStore.similaritySearch("")看看有没有数据 - 检查 chunkSize 是否合理 → 文档被切得太碎时,单个chunk可能失去语义
- 检查 EmbeddingModel 是否一致 → 入库和查询必须使用同一个模型
- 降低阈值测试 → 将
similarityThreshold设为0,确认是否有结果返回 - 检查字符编码 → 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 系列」。
🔗 系列文章导航:
- Spring AI 入门:环境搭建与第一个 AI 对话
- Spring AI 一次对话的完整生命线
- Spring AI Message 体系详解
- Spring AI RAG 检索增强生成入门
- Tool Calling 工具调用
- ★ VectorStore与RAG Pipeline (本文)
👋 如果本文对你有帮助,欢迎点赞、收藏、评论!有问题欢迎评论区交流~