RAG入门
什么是RAG
RAG(检索增强生成) 是一种提升大模型问答准确性与实用性的技术框架,核心是 **「先检索、再生成」**,解决大模型幻觉、知识过时、领域知识不足的痛点。
简单来说,RAG 不依赖重新训练大模型,而是先从外部专属知识库(如文档、数据库)中检索与用户问题最相关的信息片段,再将这些片段作为参考资料,让大模型基于这些真实数据生成答案,既保证回答的精准性,又能实现知识的灵活更新。
RAG与传统AI模型的区别:
| 对比维度 | 传统大模型 | RAG(检索增强生成) |
|---|---|---|
| 知识来源 | 来源于预训练数据,知识固化在模型参数中 | 来源于外部专属知识库+ 模型参数知识,可灵活扩展 |
| 知识更新 | 需重新训练 / 微调模型,成本高、周期长 | 无需改模型,直接更新知识库即可,实时性强 |
| 回答准确性 | 易产生幻觉(编造不存在的信息),无法溯源 | 基于检索到的真实数据生成,答案可溯源,大幅降低幻觉 |
| 领域适配成本 | 适配垂直领域需海量标注数据 + 高额算力微调 | 只需构建领域知识库,低成本快速适配(如旅行、法律、医疗) |
| 上下文依赖 | 受模型上下文窗口长度限制,无法处理超长文本 | 可通过检索突破窗口限制,调用超长领域文档的关键片段 |
RAG工作流程
RAG技术实现主要包含以下4个核心步骤:
- 文档收集和切割
- 向量转换和存储
- 文档过滤和清除
- 查询增强和关联
文档收集和切割
获取领域专属数据源,并拆分为语义完整、长度适中的文本块(Chunk),避免大模型上下文窗口超限,同时提升检索精准度。
- 文档收集
- 来源:公开领域数据(如文旅局官网攻略、景点官方介绍)、自有数据(如用户整理的旅行笔记);
- 类型:文本、PDF、Markdown、网页等均可。
- 文档切割(核心)
- 粒度选择 :中文场景推荐 500-1000 字 / 块(可根据模型窗口调整,如 GPT-3.5 窗口 4k,块长度不超过 1k);
- 切割原则 :按语义边界拆分,而非纯字符数(如一篇攻略拆分为「开放时间」「门票政策」「游玩路线」3 个块,而非生硬截断);
- 辅助策略 :给每个块添加元数据标签 (如
景点=故宫、类型=门票),方便后续精准检索。
向量转换和存储
- **向量转换:**是使用Embedding模型将文本数据转换为高维向量的过程,可用捕获到文本的语义特征,该过程会直接影响到后续检索的效果
- **向量存储:**将生成的向量和对应文本存入向量数据库,支持高效的相似性搜索
文档过滤和搜索
- 查询处理:将用户问题也转换为向量表示
- 过滤机制:基于元数据、关键词或自定义规则进行过滤
- 相似度搜索:在向量数据库中查找与问题向量最相似的文档块,常用的相似度搜索算法有余弦相似度、欧氏距离等
- 上下文组装:将检索到的多个文档块组装成连贯上下文
查询增强和关联
- 提示词组装:将检索到的相关文档与用户问题组合成增强提示
- 上下文融合:大模型基于增强提示生成回答
- 源引用:在回答中添加信息来源引用
- 后处理:格式化、摘要或其他处理以优化最终输出
RAG 相关技术
Embedding 和 Embedding 模型
Embedding 嵌入是将高维离散数据(如文字、图片)转换为低维连续向量的过程。这些向量能在数学空间中表示原始数据的语义特征,使计算机能够理解数据间的相似性。
Embedding 模型是执行这种转换算法的机器学习模型,如 Word2Vec(文本)、ResNet(图像)等。不同的 Embedding 模型产生的向量表示和维度数不同,一般维度越高表达能力更强,可以捕获更丰富的语义信息和更细微的差别,但同样占用更多存储空间。
向量数据库
向量数据库是专门存储和检索向量数据的数据库系统。通过高效索引算法实现快速相似性搜索,支持 K 近邻查询等操作。
注意,并不是只有向量数据库才能存储向量数据,只不过与传统数据库不同,向量数据库优化了高维向量的存储和检索。
AI 的流行带火了一波向量数据库和向量存储,比如 Milvus、Pinecone 等。此外,一些传统数据库也可以通过安装插件实现向量存储和检索,比如 PGVector、Redis Stack 的 RediSearch 等。
RAG实战:SpringAI + 本地知识库
Spring AI 框架为我们实现 RAG 提供了全流程的支持,参考 Spring AI 和 Spring AI Alibaba 的官方文档。
文档准备
可用通过AI去生成对应知识库文档,提供一段提示词作为参考:
帮我生成两篇Markdown文章(注意输出格式),主题是【旅游常见的问题和回答】,两篇文章的问题是针对自驾游,跟团的旅游方式,内容形式为1问1答的形式,每个问题标题使用4级标题,每篇内容要有至少10个问题,要求每个回答最后注明此回答的出处(包含来源标题和真实网址)。注意每个内容要贴近旅游方式回答内容尽量详细清晰,若多篇文章中出现冲突内容,选择最合理的建议,每个问题尽量不要有相同的答案并在500字左右
在resoures目录下新建doc文件夹,将文档粘贴过来即可
文档读取
首先,我们要对自己准备好的知识库文档进行处理,然后保存到向量数据库中。这个过程俗称 ETL(抽取、转换、加载),Spring AI 提供了对 ETL 的支持,参考 官方文档。
ETL 的 3 大核心组件,按照顺序执行:
- DocumentReader:读取文档,得到文档列表
- DocumentTransformer:转换文档,得到处理后的文档列表
- DocumentWriter:将文档列表保存到存储中(可以是向量数据库,也可以是其他存储)

刚开始学习 RAG,我们不需要关注太多 ETL 的细节、也不用对文档进行特殊处理,下面我们就先用 Spring AI 读取准备好的 Markdown 文档,为写入到向量数据库做准备。
引入依赖
SpringAI通过DocumentReader组件实现文档抽取,也就是把文档加载到内存中
Spring AI 提供了很多种 DocumentReaders,用于加载不同类型的文件。
我们可以使用 MarkdownDocumentReader 来读取 Markdown 文档。需要先引入依赖,可以在 Maven 中央仓库 找到。
首先在pom.xml引入依赖:
XML
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-markdown-document-reader</artifactId>
<version>1.0.0-M6</version>
</dependency>
编写文档读取器
新建rag包,存放RAG相关类型。我们先编自己读取旅游问答的DocumentReader实现
java
public class TravelMarkdownDocumentReader implements DocumentReader {
@Autowired
private ResourcePatternResolver resourcePatternResolver;
// 指定Markdown文档的路径模式,默认读取classpath*/doc/*.md
@Value("${travel.document.markdown.path.pattern:classpath*:/doc/*.md}")
private String pathPattern;
@Override
public List<Document> get() {
List<Document> documents = new ArrayList<>();
try{
// 获取指定路径下的所有Markdown文件
Resource[] markdownResources = resourcePatternResolver.getResources(pathPattern);
for (Resource markdownResource : markdownResources) {
// 创建MarkdownDocumentReaderConfig对象, 并设置参数
MarkdownDocumentReaderConfig readerConfig =
MarkdownDocumentReaderConfig.builder()
.withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock( false)
.withIncludeBlockquote( false)
.withAdditionalMetadata("filename",markdownResource.getFilename())
.build();
// 创建读取器
MarkdownDocumentReader reader =
new MarkdownDocumentReader(markdownResource, readerConfig);
documents.addAll(reader.get());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return documents;
}
}
上述代码中:
@Autowired 自动装配**ResourcePatternResolver** (Spring 资源解析器),用于定位和读取资源文件。@Value 注解读取配置项**travel.document.markdown.path.pattern** ,若未配置则使用默认值**classpath*/doc/*.md**,指定要读取的 Markdown 文件路径模式。
核心的**get()** 方法实现了接口逻辑:首先初始化空的**Document** 列表,通过**resourcePatternResolver** 获取匹配路径模式的所有 Markdown 资源文件;遍历每个资源时,构建**MarkdownDocumentReaderConfig** 配置对象 ------ 开启 "按水平线分割文档"、排除代码块和块引用、为文档添加 "文件名" 元数据;接着创建**MarkdownDocumentReader** 读取器,解析当前 Markdown 资源并将结果合并到列表中。若过程中出现 IO 异常,会捕获并转为运行时异常抛出,最终返回解析后的**Document**列表。
测试旅游文档读取器
创建测试类:
java
@SpringBootTest
class TravelMarkdownDocumentReaderTest {
@Resource
private DocumentReader travelMarkdownDocumentReader;
@Test
void readDocuments(){
List<Document> documents = travelMarkdownDocumentReader.get();
for (int i = 0; i < documents.size() ; i++) {
Document document = documents.get(i);
//读取我的文档有唯一的ID
String id = document.getId();
// 读取我的文档内容
String content = document.getText();
// 读取我的文档元数据
Map<String, Object> metadata = document.getMetadata();
System.out.println("------------------------------------");
System.out.println("文档ID:" + id);
System.out.println("文档内容:" + content);
System.out.println("文档元数据:" + metadata);
System.out.println("------------------------------------");
}
}
}
运行测试效果如下,可见markdown文档读取器是以标题进行Doument进行拆分,每个Dounment都有唯一ID,元数据集合,文本内容:
向量转换和存储
- 向量转换(Embedding):将非结构化的文本(如你的旅游问答 MD 内容)转为计算机可计算的数值向量(Embedding 向量),保留文本的语义信息。
- 向量存储(VectorStore) :专门存储向量并支持相似性检索 的数据库(区别于普通数据库),Spring AI 内置了对 Chroma、Milvus、Redis、PGVector 等的适配,这里选Chroma(轻量、无依赖、适合测试 / 小体量场景)。
VectorStore接口介绍
VectorStore是 Spring AI 框架中统一向量存储操作的顶级接口,它的设计目标是:
- 屏蔽不同向量数据库(Chroma、Milvus、Redis、PGVector 等)的底层差异;
- 提供标准化的 "向量写入、删除、相似性检索"API;
- 让业务代码无需关心具体用的是哪种向量库,只需面向接口编程。
简单来说,它就像 "向量数据库的操作门面"------ 无论你用 Chroma 还是 Redis,调用的都是VectorStore的同一套方法,切换向量库时只需替换实现类和配置,业务代码无需修改。
SimpleVectorStore介绍
SimpleVectorStore是 Spring AI 提供的极简内存型 VectorStore 实现类 ,它完全基于 JVM 内存存储向量数据,无需依赖任何外部向量数据库(如 Chroma、Redis),也无需配置文件、无需启动额外服务,是快速测试、原型开发的最佳选择。
配置向量数据库
在rag包下新建TravelVctorStoreConfig类,实现初始化向量数据库并保存文档的方法:
java
public class TravelVectorStoreConfig {
@Resource
private DocumentReader travelMarkdownDocumentReader;
/*
* 构建向量数据库
* @param dashscopeEmbeddingClient
* */
@Bean
public VectorStore travelVectorStore(EmbeddingModel dashscopeEmbeddingModel){
// 读取文档
List<Document> documents = travelMarkdownDocumentReader.get();
// 创建向量数据库
SimpleVectorStore vectorStore = SimpleVectorStore.builder(dashscopeEmbeddingModel).build();
// 加载文档到向量数据库
vectorStore.doAdd(documents);
return vectorStore;
}
}
代码核心逻辑简述
- 依赖注入 :通过
@Resource注入你之前实现的travelMarkdownDocumentReader(Markdown 文档读取器),用于读取指定路径的 MD 文档; - Bean 定义 :
@Bean注解标识该方法会向 Spring 容器注册一个VectorStore类型的 Bean(名称为travelVectorStore); - 参数注入 :方法参数
dashscopeEmbeddingModel(通义千问等 DashScope 系列的 Embedding 模型)会被 Spring 自动注入,作为向量转换的核心依赖; - 核心执行流程 :
- 调用
travelMarkdownDocumentReader.get()读取 Markdown 文档,得到Document列表; - 基于传入的 Embedding 模型构建
SimpleVectorStore(内存型向量库)实例; - 调用
doAdd()方法将文档列表转换为向量并加载到内存向量库中; - 返回构建好的
SimpleVectorStore实例,使其成为 Spring 容器中可注入使用的 Bean。
- 调用
查询增强和关联
查询增强(Query Enhancement) 和关联检索(Relevance Retrieval) 的核心概念、实现逻辑,以及如何结合你之前的SimpleVectorStore代码落地这两个关键环节 ------ 这两个环节是提升 RAG 检索精准度的核心,能解决 "用户查询模糊 / 简短导致检索结果不相关" 的问题。
SpringAI通过Advisor特性提供了开箱即用的RAG功能,主要是QuestionAnswerAdvisor问答拦截器和RetrievalAugmentationAdvisor检索增强拦截器,前者更简单易用,后者更灵活更强大
TravelApp实现RAG对话
了解QuestionAnswerAdvisor问答顾问
QuestionAnswerAdvisor是 RAG(检索增强生成)架构中 **"问答生成" 环节的核心组件 **,核心目标是:接收用户的自然语言问题 → 从向量库中检索相关的文档依据 → 结合大语言模型(LLM)生成有依据、不胡编、贴合你的旅游文档内容的精准回答。它区别于纯大模型问答的关键是:回答不是 "凭空生成",而是基于你提前存入向量库的旅游文档(如自驾篇 MD),确保回答的准确性和溯源性。
修改TravelApp构造函数代码:
其实要修改的就是上述框中的代码,将我们定义的VectorStore对象通过参数注入,用于创建QuestionAnswerAdvistor知识库问答顾问,并添加到ChatClient的默认顾问集合中这样每次用户提问就会先查询知识库,再进行精确回答
测试RAG对话
编写单元测试增加测试方法,testRAG:
java
@Test
void testRAG(){
String chatId = UUID.randomUUID().toString();
// 写一个与知识库相关的问题
String result = travelApp.chat(chatId,"自驾游前车辆必做哪些检查?");
System.out.println(result);
}
运行上述代码得到的结果
知识库md
从结果可看出大模型的输出中包含科泰养车店,这是本地知识库中的内容,只是为了测试虚构的,证明了大模型确实参考了提供的知识库数据进行回答
RAG实战:SpringAI+云知识库
在上一节,我们学习了文档读取,文档加载,向量数据库是在本地通过编程的方式实现的,其实还要另一种方式,直接使用别人的云知识库来简化RAG开发。但是缺点是额外的费用,以及数据隐私问题
很多AI大模型应用开发平台都提供了云知识库服务,这里我们还是选择阿里云百炼,因为Spring AI Alibaba 可以轻松集成,简化RAG开发
准备云知识库
首先我们可以利用云知识库完成文档读取,文档处理,文档加载,保存到向量数据库,知识库管理等操作。
上传文档数据
在应用数据模块中,上传原始文档数据到平台,由平台进行解析内容和结构:
首先推荐大家新建一个"旅游知识"类目,不要把所有的文档放入默认类目,方便管理。点击【类目管理】旁边的+号,新增类目:
新增后,点击对应的旅游知识类目,然后导入数据按钮

然后将旅游相关的md文档导入进去

点击确定,导入完成
创建云知识库
点击阿里云百炼平台的知识库菜单,创建一个知识库:
现在的知识库是需要收费的我们可以使用免费30天的知识库作为学习
创建知识库名称使用旅游大师,其他默认即可
进入到选择数据步骤这里选择推荐类目方式,并开启自动同步知识索引选项。然后选择旅游知识类中的文档
索引设置保持默认即可
基于云知识库的RAG开发
配置云知识库知识线索增强顾问
由于本地知识库使用的是QuestionAnswerAdvisor(知识问答顾问),云知识库使用的是RetrievalAugmentationAdvisor(检索增强顾问),都是顾问类的实例,我们可以都定义为Spring 中的Bean对象,注入TravelApp应用,这样就可以实现本地知识库和云知识库的切换了
我们将顾问对象定义到TravelVectorStoreConfig配置类中
java
@Configuration
public class TravelVectorStoreConfig {
@Resource
private DocumentReader travelMarkdownDocumentReader;
/*
* 构建向量数据库
* @param dashscopeEmbeddingClient
* */
@Bean
public VectorStore travelVectorStore(EmbeddingModel dashscopeEmbeddingModel){
// 读取文档
List<Document> documents = travelMarkdownDocumentReader.get();
// 创建向量数据库
SimpleVectorStore vectorStore = SimpleVectorStore.builder(dashscopeEmbeddingModel).build();
// 加载文档到向量数据库
vectorStore.doAdd(documents);
return vectorStore;
}
// 定义基于本地知识库的RAG Advisor
@Bean
public Advisor localRagAdvisor(VectorStore vectorStore){
return new QuestionAnswerAdvisor(vectorStore);
}
// 注入apikey
@Value("${spring.ai.dashscope.api-key}")
private String apikey;
@Bean
public Advisor cloudRagAdvisor(VectorStore vectorStore){
DashScopeApi dashScopeApi = new DashScopeApi(apikey);
// 定义知识库的名称
final String KNOWLEDGE_index = "旅游大师";
// 创建知识库检索器
DashScopeDocumentRetrieverOptions options = DashScopeDocumentRetrieverOptions.builder()
.withIndexName(KNOWLEDGE_index)
.build();
// 构建检索器
DocumentRetriever documentRetriever = new DashScopeDocumentRetriever(dashScopeApi, options);
// 构建增强顾问
return RetrievalAugmentationAdvisor.builder()
.documentRetriever(documentRetriever)
.build();
}
}
注意:上述代码中指定的是知识库名称(非ID),可以在平台查看知识库列表获得。

修改TravelApp添加指定的RAG顾问
改造TreavelApp类的构造函数:
构造函数中不再注入VctorStore而是直接注入Advisor对象,通过@Qualifier注解指定为实例7中的cloudRagAdvisor云知识库检索增强顾问对象
如果想要改回本地知识库只需要改回@Qualifier("localRagAdvisor")即可
我们继续运行之前的测试方法:
可以看到大模型从我们云知识库获取信息并给出对应的回答
至此我们就已经学习了RAG知识库的基本内容开发