当前位置: 首页 > news >正文

RAG 知识库实战指南:基于 Spring AI 构建 AI 知识问答应用

前言:本文围绕 RAG 知识库,分析了 AI 知识问答需求RAG 技术催生原因,介绍 RAG 核心流程、技术,及 Spring AI + 本地知识库的实战步骤,实现 AI 基于私有知识库精准问答。希望可以帮到大家!

思维导图

一、AI 知识问答需求分析

AI 知识问答应用场景

随着 AI 技术的快速发展,越来越多的公司开始利用 AI 重构传统业务,打造全新的用户体验和商业价值。其中,AI 知识问答是一个典型应用场景,广泛运用到教育、电商等行业,比如:

  • 教育场景:AI 针对学生的薄弱环节提供个性化辅导
  • 电商场景:AI 根据用户肤质推荐适合的护肤方案

其中,知识的来源可能来源于网络,也可能是自己公司私有的数据,从而让AI提供更精准的服务。

如何让 AI 获取知识?

在实现这个需求前,我们需要思考一个关键问题:知识从哪里获取呢?

首先 AI 原本就拥有一些通用的知识,对于不会的知识,还可以利用互联网搜索。但是这些都是从网络获取的、公开的知识。对于企业来说,数据是命脉,也是自己独特的价值,随着业务的发展,企业肯定会积累一波自己的知识库。如果让 AI 能够利用这些知识库进行问答,效果可能会更好,而且更加个性化。

如果不给 AI 提供特定领域的知识库,AI 可能会面临这些问题

  1. 知识有限:AI 不知道你的最新的算法内容
  2. 编故事:当 AI 不知道答案时,它可能会 “自圆其说” 编造内容
  3. 无法个性化:不了解你的特色服务和回答风格

那么如何让 AI 利用自己的知识库进行问答呢?这就需要用到 AI 主流的技术 —— RAG

二、RAG 概念

什么是 RAG?

RAG(检索增强生成)是一种结合信息检索技术和 AI 内容生成的混合架构,可以解决大模型的知识时效性限制和幻觉问题。作用就是让 AI 回答问题前先查一查特定的知识库来获取知识,确保回答是基于真实资料不是凭空想象

可以简单了解下 RAG 传统 AI 模型的区别:

特性传统大语言模型RAG 增强模型
知识时效性受训练数据截止日期限制可接入最新知识库
领域专业性泛化知识,专业深度有限可接入专业领域知识
响应准确性可能产生 “幻觉”基于检索的事实依据
可控性依赖原始训练可通过知识库定制输出
资源消耗较高(需要大模型参数)模型可更小,结合外部知识

RAG 工作流程

RAG 技术实现主要包含以下 4 个核心步骤,让我们逐步学习:

  • 文档收集和切割
  • 向量转换和存储
  • 文档过滤和检索
  • 查询增强和关联
1、文档收集和切割

文档收集:从各种来源(网页、PDF、数据库等)收集原始文档

文档预处理:清洗、标准化文本格式

文档切割:将长文档分割成适当大小的片段

  • 基于固定大小(如 512 个 token)
  • 基于语义边界(如段落、章节)
  • 基于递归分割策略(如递归字符 n-gram 切割)

2、向量转换和存储

向量转换:使用 Embedding 模型将文本块转换为高维向量表示,可以捕获到文本的语义特征

向量存储:将生成的向量和对应文本存入向量数据库,支持高效的相似性搜索

3、文档过滤和检索

查询处理:将用户问题也转换为向量表示

过滤机制:基于元数据、关键词或自定义规则进行过滤

相似度搜索:在向量数据库中查找与问题向量最相似的文档块

上下文组装:将检索到的多个文档块组装成连贯上下文

4、查询增强和关联

提示词组装:将检索到的相关文档与用户问题组合成增强提示

上下文融合:大模型基于增强提示生成回答

源引用:在回答中添加信息来源引用

后处理:格式化、摘要或其他处理以优化最终输出

完整工作流程

分别理解上述 4 个步骤后,我们可以将它们组合起来,形成完整的 RAG 检索增强生成工作流程:

上述工作流程中涉及了很多技术名词,让我们分别进行解释。

RAG 相关技术

Embedding 和 Embedding 模型

Embedding 嵌入是将高维离散数据(如文字、图片)转换为低维连续向量的过程。这些向量能在数学空间中表示原始数据的语义特征,使计算机能够理解数据间的相似性。

Embedding 模型是执行这种转换算法的机器学习模型,如 Word2Vec(文本)、ResNet(图像)等。不同的 Embedding 模型产生的向量表示和维度数不同,一般维度越高表达能力更强,可以捕获更丰富的语义信息和更细微的差别,但同样占用更多存储空间。

向量数据库

向量数据库是专门存储和检索向量数据的数据库系统。通过高效索引算法实现快速相似性搜索,支持 K 近邻查询等操作。

注意,并不是只有向量数据库才能存储向量数据,只不过与传统数据库不同,向量数据库优化了高维向量的存储和检索。

AI 的流行带火了一波向量数据库和向量存储,比如 Milvus、Pinecone 等。此外,一些传统数据库也可以通过安装插件实现向量存储和检索,比如 PGVector、Redis Stack 的 RediSearch 等。

召回

召回是信息检索中的第一阶段,目标是从大规模数据集中快速筛选出可能相关的候选项子集。强调速度和广度,而非精确度。

精排和 Rank 模型

精排(精确排序)是搜索 / 推荐系统的最后阶段,使用计算复杂度更高的算法,考虑更多特征和业务规则,对少量候选项进行更复杂、精细的排序。

比如,短视频推荐先通过召回获取数万个可能相关视频,再通过粗排缩减至数百条,最后精排阶段会考虑用户最近的互动、视频热度、内容多样性等复杂因素,确定最终展示的 10 个视频及顺序。

Rank 模型负责对召回阶段筛选出的候选集进行精确排序,考虑多种特征评估相关性。

现代 Rank 模型通常基于深度学习,如 BERT、LambdaMART 等,综合考虑查询与候选项的相关性、用户历史行为等因素。举个例子,电商推荐系统会根据商品特征、用户偏好、点击率等给每个候选商品打分并排序。

混合检索策略

混合检索策略结合多种检索方法的优势,提高搜索效果。常见组合包括关键词检索、语义检索、知识图谱等。

比如在 AI 大模型开发平台 Dify 中,就为用户提供了 “基于全文检索的关键词搜索 + 基于向量检索的语义检索” 的混合检索策略,用户还可以自己设置不同检索方式的权重。

了解了 RAG 概念后,我们来学习如何利用编程开发实现 RAG。想要在程序中让 AI 使用知识库,首先建议利用一个 AI 开发框架,比如 Spring AI;然后可以通过 2 种模式进行开发 —— 基于本地知识库或云知识库服务实现 RAG。下面分别讲解这 2 种模式。

三、RAG 实战:Spring AI + 本地知识库

对比维度标准 RAG 开发步骤简化版 RAG 开发步骤
核心环节1. 文档收集和切割
2. 向量转换和存储
3. 切片过滤和检索
4. 查询增强和关联
1. 文档准备
2. 文档读取
3. 向量转换和存储
4. 查询增强

1、文档准备

首先准备用于给 AI 知识库提供知识的文档,推荐 Markdown 格式,尽量结构化。

2、文档读取

首先,我们要对自己准备好的知识库文档进行处理,然后保存到向量数据库中。(ETL)

ETL 的 3 大核心组件,按照顺序执行:

  • DocumentReader:读取文档,得到文档列表
  • DocumentTransformer:转换文档,得到处理后的文档列表
  • DocumentWriter:将文档列表保存到存储中(可以是向量数据库,也可以是其他存储)

1)引入依赖

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-markdown-document-reader</artifactId><version>1.0.0-M6</version>
</dependency>

2)在根目录下新建 rag 包,编写文档加载器类 AiCodeAppDocumentLoader,负责读取所有 Markdown 文档并转换为 Document 列表。代码如下:

@Component
@Slf4j
public class AiCodeDocumentLoader {public final ResourcePatternResolver resourcePatternResolver;public AiCodeDocumentLoader(ResourcePatternResolver resourcePatternResolver) {this.resourcePatternResolver = resourcePatternResolver;
}public List<Document> loadDocuments() {List<Document> allDocuments = new ArrayList<>();try {// 这里可以修改为你要加载的多个 Markdown 文件的路径模式Resource[] resources = resourcePatternResolver.getResources("classpath:document/*.md");for (Resource resource : resources) {String fileName = resource.getFilename();MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder().withHorizontalRuleCreateDocument(true).withIncludeCodeBlock(false).withIncludeBlockquote(false).withAdditionalMetadata("filename", fileName).build();MarkdownDocumentReader reader = new MarkdownDocumentReader(resource, config);allDocuments.addAll(reader.get());}} catch (IOException e) {log.error("Markdown 文档加载失败", e);}return allDocuments;
}

上述代码中,我们通过 MarkdownDocumentReaderConfig 文档加载配置来指定读取文档的细节,比如是否读取代码块、引用块等。特别需要注意的是,我们还指定了额外的元信息配置,提取文档的文件名作为文档的元信息,可以便于后续知识库实现更精确的检索。

3、向量转换和存储

我们先使用 Spring AI 内置的、基于内存读写的向量数据库 SimpleVectorStore 来保存文档。

简单了解下源码,在将文档写入到数据库前,会先调用 Embedding 大模型将文档转换为向量,实际保存到数据库中的是向量类型的数据。

在 rag 包下新建 AiCodeVectorStoreConfig 类,实现初始化向量数据库并且保存文档的方法。

@Configuration
public class AiCodeVectorStoreConfig {@Resourceprivate AiCodeDocumentLoader aiCodeDocumentLoader;@BeanVectorStore aiCodeVectorStore(EmbeddingModel dashscopeEmbeddingModel){SimpleVectorStore simpleVectorStore = SimpleVectorStore.builder(dashscopeEmbeddingModel).build();// 加载文档List<Document> documents = aiCodeDocumentLoader.loadDocuments();simpleVectorStore.add(documents);return simpleVectorStore;}
}

4、查询增强

Spring AI 的 Advisor 特性(如 QuestionAnswerAdvisor)提供开箱即用的 RAG 功能,其查询增强原理为:向量数据库存储 AI 模型未知数据,用户提问时,Advisor 会查询该数据库获取相关文档,附加到用户问题中作为上下文,辅助 AI 生成回答。

1).引入依赖:

▼xml复制代码<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>

2).选用 QuestionAnswerAdvisor 问答拦截器新增和 RAG 知识库进行对话的方法。代码如下:

@Resource
private VectorStore vectorStore;public String doChatWithRag(String message, String chatId) {ChatResponse chatResponse = chatClient.prompt().user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))// 开启日志,便于观察效果.advisors(new MyLoggerAdvisor())// 应用知识库问答.advisors(new QuestionAnswerAdvisor(vectorStore)).call().chatResponse();String content = chatResponse.getResult().getOutput().getText();log.info("content: {}", content);return content;
}

测试

编写单元测试代码,故意提问一个文档内有回答的问题:

▼java复制代码@Test
void doChatWithRag() {String chatId = UUID.randomUUID().toString();String message = "算法刚入门者该如何学习算法?";String answer =  aiCodeApp.doChatWithRag(message, chatId);Assertions.assertNotNull(answer);
}

运行程序后,通过 Debug 可发现:

  • 加载的文档被自动按照小标题拆分,并补充了 metadata 元信息;
  • 根据用户问题检索到相关文档切片,每个切片有对应的分数和元信息;

AI 会基于检索到的知识库内容生成回答,且回复成功包含知识库里的内容。大功告成!

http://www.dtcms.com/a/313320.html

相关文章:

  • Git用法记录
  • UE5的渲染Debug技巧
  • C语言字符串拷贝的三重境界:从下标到指针的华丽变身
  • 设备健康管理标准规范:技术架构与合规性实现指南
  • 《人形机器人的觉醒:技术革命与碳基未来》——类人关节设计:人工肌肉研发进展及一款超生物肌肉Hypermusclet的设计与制造
  • K8S服务发现原理及开发框架的配合
  • k8s黑马教程笔记
  • LeetCode 刷题【29. 两数相除】
  • 波士顿房价预测工具 - XGBoost实现
  • 2.4.1-2.4.3控制范围-控制进度-控制成本
  • C++ 生成动态库.dll 及 C++调用DLL,C++ 生成静态库.lib及 C++调用lib
  • 其它IO函数
  • 在 ArkUI 中实现丝滑嵌套滚动:让你的页面像抖音一样顺滑
  • Redis——运维篇
  • 避不开的数据拷贝
  • 北斗变形监测技术应用与优势
  • 【AI云原生】1、Function Calling:大模型幻觉破解与Agent底层架构全指南(附Go+Python实战代码)》
  • 子区间问题
  • 差分 前缀和
  • 无人机集群协同三维路径规划,采用冠豪猪优化器(Crested Porcupine Optimizer, CPO)实现,Matlab代码
  • 【Django】-8- 视图和模型的关联
  • Linux下Redis常用命令
  • Java线程安全类设计思路总结
  • 深入理解Python的`__missing__`方法:动态处理字典中不存在的键: Effective Python 第18条
  • 网络规划与设计5个阶段内容
  • 大模型学习--第一天
  • Linux命令基础(上)
  • day 44 文件的规范书写与拆分
  • LCL滤波器及其电容电流前馈有源阻尼设计软件【LCLAD_designer】
  • 机器学习——决策树(DecisionTree)