在上一节教程中,我们初步体验了如何基于 Spring AI 快速搭建一个 RAG(检索增强生成)应用。然而,一个生产级别的 RAG 系统远不止简单的"文档 -> 向量 -> 检索"流程。在本节中,我们将深入 Spring AI 的底层,学习 RAG 知识库应用开发的核心特性和高级知识点,带你掌握构建高效、精准 RAG 应用的最佳实践和调优技巧。
我们将重点拆解 RAG 流程中的关键环节,并以 Spring AI 框架为例,详细讲解其核心组件。
一、重温 RAG 工作流程
首先,让我们回顾一下 RAG 应用的标准工作流程,它主要分为两个阶段:
- 建立索引:将原始知识库文档进行预处理、切割、向量化,并存入向量数据库。
- 检索与生成:根据用户问题,在向量数据库中进行相似性搜索,找到最相关的文档片段,最后将它们与问题一起提交给大模型(LLM)生成答案。
这个流程在 Spring AI 中得到了完整的支持和抽象。
二、RAG 核心流程的工程化实现
在 Spring AI 中,对文档的处理遵循经典的 ETL(抽取、转换、加载) 模式。这是整个RAG应用的基石。
1. 文档 (Document)
首先,我们需要理解 Spring AI 中的核心数据模型------Document。它不仅仅是一个文本块,还可以包含丰富的元数据(Metadata,如来源、标题、作者等)和多媒体附件。这使得文档在检索时可以携带更多上下文信息,从而提高检索的精准度。
2. 抽取 (Extract) - DocumentReader
这是 ETL 的第一步,目标是从各种数据源加载原始文档,并将其转换为 Spring AI 的 Document 对象。
Spring AI 提供了丰富的 DocumentReader 实现类,方便处理不同类型的数据源:
-
JsonReader:读取 JSON 文档。 -
TextReader:读取纯文本文件。 -
MarkdownReader:读取 Markdown 文件。 -
PDFReader:读取 PDF 文档。Spring AI 基于 Apache PdfBox 库提供了更细粒度的解析:PagePdfDocumentReader:按页读取 PDF。ParagraphPdfDocumentReader:按段落读取 PDF。
-
HtmlReader:读取 HTML 文档,基于 jsoup 库实现。 -
TikaDocumentReader:基于 Apache Tika 库,可以处理多种格式的文档,功能强大且灵活。
核心源码 :
DocumentReader 接口非常简单,它实现了 Supplier<List<Document>> 接口,其核心方法 read() 负责返回一个 Document 列表。
java
复制下载
csharp
public interface DocumentReader extends Supplier<List<Document>> {
default List<Document> read() {
return get();
}
}
此外,Spring AI Alibaba 社区还提供了更多实用的 DocumentReader,例如加载飞书文档、提取 B 站视频信息、加载邮件等,极大地扩展了数据源的多样性。
3. 转换 (Transform) - DocumentTransformer
抽取后的文档往往需要进一步处理,才能用于向量化。这个阶段是保证 RAG 效果的核心步骤 ,特别是如何将大文档合理拆分为便于检索的知识碎片(即文本切分)。
Spring AI 通过 DocumentTransformer 组件实现这一功能,它负责将一组 Document 转换为另一组 Document。常见的转换操作包括:
-
文本分割器 (TextSplitter) :将长文档切分成更小的、语义完整的块。
TokenTextSplitter是其核心实现,它基于 Token 数量进行分割,并会尝试在句子边界(语义边界)处进行切分,以创建有意义的文本段落。这是一种成本较低且效果不错的文本切分方式。 -
元数据增强器 (MetadataEnricher) :为文档补充更多的元信息,以便在后续检索中提供更丰富的过滤条件。例如:
KeywordMetadataEnricher:使用 AI 模型提取文档关键词并添加到元数据中。SummaryMetadataEnricher:使用 AI 模型为文档生成摘要并添加到元数据。高级用法:它不仅可以为当前文档生成摘要,还能关联前一个和后一个相邻的文档,从而联系上下文,生成更完整的摘要。
-
内容格式化工具 (ContentFormatter) :用于统一文档的格式,例如进行格式化、元数据过滤或使用自定义模板。
4. 加载 (Load) - DocumentWriter
ETL 的最后一步是将处理好的文档写入到最终的存储中,以便后续检索。DocumentWriter 组件负责这一过程。
Spring AI 提供了两种内置的 DocumentWriter 实现:
FileDocumentWriter:将文档写入文件系统。VectorStoreWriter:将文档写入向量数据库(这也是 RAG 应用中最常用的方式)。
5. ETL 流程实例
通过组合上述三大组件,我们可以轻松实现一个完整的 ETL 流程:
ini
// 抽取:从PDF文件中抽取文档
PDFReader pdfReader = new PagePdfDocumentReader("knowledge_base.pdf");
List<Document> documents = pdfReader.read();
// 转换:分割文本并添加摘要
TokenTextSplitter splitter = new TokenTextSplitter(500, 50);
List<Document> splitDocuments = splitter.apply(documents);
SummaryMetadataEnricher enricher = new SummaryMetadataEnricher(chatModel,
List.of(SummaryType.CURRENT));
List<Document> enrichedDocuments = enricher.apply(splitDocuments);
// 加载:将文档写入到向量数据库中
vectorStore.write(enrichedDocuments);
// 或者使用更优雅的链式调用
vectorStore.write(
enricher.apply(
splitter.apply(
pdfReader.read()
)
)
);
三、向量转换与存储 - VectorStore
经过 ETL 处理的文档,最终需要被转换为向量并存储起来。VectorStore 是 Spring AI 中用于与向量数据库交互的核心接口,它继承自 DocumentWriter,定义了向量存储的基本 CRUD 操作,包括添加、删除和相似性搜索。
工作原理:
- 嵌入转换 :当通过
VectorStore.add()添加文档时,Spring AI 会自动调用配置好的嵌入模型(如 OpenAI 的text-embedding-ada-002)将文档内容转换为向量表示。 - 相似度计算 :当执行搜索时,用户的查询文本同样会被转换为向量,然后通过
SearchRequest对象构建搜索请求,并在向量数据库中执行相似性搜索(而非精确匹配)。 - 相似度度量:常用的相似度计算方法包括余弦相似度、欧氏距离和点积。
- 过滤与排序:系统根据相似度阈值过滤结果,并按相似度排序,返回最相关的文档。
Spring AI 支持多种向量数据库,如 PGVector、Chroma、Weaviate 等。以 PGVector 为例,整合步骤非常简单:
- 准备一个安装了
vector插件的 PostgreSQL 数据库。 - 引入
spring-ai-starter-vector-store-pgvector依赖。 - 在配置文件中设置数据库连接和向量存储参数(如索引类型、向量维度、距离类型等)。
- 在代码中自动注入
VectorStore对象,即可开始使用。
四、文档过滤与检索 - SearchRequest
当向量数据库中存储了大量文档后,如何高效地检索到最相关的信息?这不仅仅是简单的相似度搜索,还涉及到过滤 和排序。
Spring AI 通过 SearchRequest 类来构建复杂的相似度搜索请求:
less
List<Document> results = vectorStore.similaritySearch(
SearchRequest.builder()
.query("Spring AI 如何实现 RAG?")
.topK(5) // 返回前5个最相关的结果
.similarityThreshold(0.7) // 相似度阈值,低于0.7的结果将被过滤掉
.filterExpression("category == 'tutorial'") // 元数据过滤表达式
.build()
);
核心特性:
topK:控制返回结果的数量,K 值越大,召回的可能越多,但噪音也可能增加。similarityThreshold:相似度阈值,只返回相似度高于此值的结果,有效过滤低质量匹配。filterExpression:基于元数据的过滤表达式,例如只检索特定分类、时间范围或来源的文档。这是提高检索精准度的重要手段。
五、查询增强与关联 - 上下文查询增强器
这是 RAG 进阶的关键环节。用户的原始查询往往过于简单或缺乏上下文,直接用于检索可能效果不佳。查询增强旨在通过多种策略优化查询,提高检索质量。
Spring AI 提供了多种 QueryTransformer 实现来增强查询:
1. 查询扩展 (Query Expansion)
将用户的原始查询扩展为多个相关查询,提高召回率。
java
复制下载
ini
// 使用 AI 模型将 "如何实现RAG?" 扩展为多个相关查询
QueryExpander expander = new QueryExpander(chatModel);
List<String> expandedQueries = expander.expand("如何实现RAG?");
// 可能得到: ["Spring AI RAG 实现步骤", "RAG 架构设计", "Spring AI 向量存储配置"]
2. 上下文关联 (Contextual Augmentation)
将对话历史或用户上下文信息融入当前查询,适用于多轮对话场景。
java
复制下载
ini
// 将上一轮的对话内容作为上下文增强当前查询
ContextualQueryAugmenter augmenter = new ContextualQueryAugmenter(chatHistory);
String enhancedQuery = augmenter.augment("那具体怎么配置?");
// 增强后: "基于刚才提到的 Spring AI,如何具体配置 RAG?"
3. HyDE (Hypothetical Document Embeddings)
一种高级检索策略:先生成一个假设性的答案文档,再将该假设答案用于检索。这种方法能显著提升检索的语义匹配度。
ini
// HyDE 策略:先用 LLM 生成一个假设答案,再用这个答案去检索
HydeQueryTransformer hyde = new HydeQueryTransformer(chatModel);
String hypotheticalAnswer = hyde.generateHypotheticalAnswer(query);
List<Document> results = vectorStore.similaritySearch(hypotheticalAnswer);
结语
通过深入理解 Spring AI 的 ETL 流程和 VectorStore 的机制,我们可以清晰地看到,一个健壮的 RAG 应用不仅仅是简单地调用几个 API。从文档的抽取、精细化的文本转换,到高效的向量存储和检索,每一个环节都至关重要。掌握这些核心组件,将为你后续优化 RAG 应用的性能、提升答案质量打下坚实的基础。