SpringAI(GA):RAG下的ETL快速上手

article/2025/7/24 6:33:24

原文链接:SpringAI(GA):RAG下的ETL快速上手

教程说明

说明:本教程将采用2025年5月20日正式的GA版,给出如下内容

  1. 核心功能模块的快速上手教程
  2. 核心功能模块的源码级解读
  3. Spring ai alibaba增强的快速上手教程 + 源码级解读

版本:JDK21 + SpringBoot3.4.5 + SpringAI 1.0.0 + SpringAI Alibaba 1.0.0.2

将陆续完成如下章节教程。本章是第六章(Rag增强问答质量)下的ETL Pipeline快速上手篇

代码开源如下:https://github.com/GTyingzi/spring-ai-tutorial

往届解读可参考:

第一章内容

SpringAI(GA)的chat:快速上手+自动注入源码解读

SpringAI(GA):ChatClient调用链路解读

第二章内容

SpringAI的Advisor:快速上手+源码解读

SpringAI(GA):Sqlite、Mysql、Redis消息存储快速上手

第三章内容

SpringAI(GA):Tool工具整合—快速上手

第五章内容

SpringAI(GA):内存、Redis、ES的向量数据库存储—快速上手

SpringAI(GA):向量数据库理论源码解读+Redis、Es接入源码

第六章内容

SpringAI(GA):RAG快速上手+模块化解读

获取更好的观赏体验,可付费获取飞书云文档Spring AI最新教程权限,目前49.9,随着内容不断完善,会逐步涨价。

注:M6版快速上手教程+源码解读飞书云文档已免费提供

RAG 的 ETL Pipeline 快速上手

[!TIP]
提取(Extract)、转换(Transform)和加载(Load)框架是《第六章:Rag 增强问答质量》中数据处理的链路,将原始数据源导入到向量化存储的流程,确保数据处于最佳格式,以便 AI 模型进行检索

实战代码可见:https://github.com/GTyingzi/spring-ai-tutorial 下的 rag/rag-etl-pipeline

源码解读可见:《ETL Pipeline 源码解析》

pom 文件

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-autoconfigure-model-openai</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-commons</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-rag</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-jsoup-document-reader</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-markdown-document-reader</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-pdf-document-reader</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-tika-document-reader</artifactId></dependency></dependencies>

application.yml

server:port: 8080spring:application:name: rag-etl-pipelineai:openai:api-key: ${DASHSCOPEAPIKEY}base-url: https://dashscope.aliyuncs.com/compatible-modechat:options:model: qwen-maxembedding:options:model: text-embedding-v1

提取文档

Constant
package com.spring.ai.tutorial.rag.model;public class Constant {public static final String PREFIX = "classpath:data/";public static final String TEXTFILEPATH = PREFIX + "/text.txt";public static final String JSONFILEPATH = PREFIX + "/text.json";public static final String MARKDOWNFILEPATH = PREFIX + "/text.md";public static final String PDFFILEPATH = PREFIX + "/google-ai-agents-whitepaper.pdf";;public static final String HTMLFILEPATH = PREFIX + "/spring-ai.html";
}

ReaderController
package com.spring.ai.tutorial.rag.controller;import com.spring.ai.tutorial.rag.model.Constant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.JsonReader;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.reader.jsoup.JsoupDocumentReader;
import org.springframework.ai.reader.markdown.MarkdownDocumentReader;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.reader.pdf.ParagraphPdfDocumentReader;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/reader")
public class ReaderController {private static final Logger logger = LoggerFactory.getLogger(ReaderController.class);@GetMapping("/text")public List<Document> readText() {logger.info("start read text file");Resource resource = new DefaultResourceLoader().getResource(Constant.TEXTFILEPATH);TextReader textReader = new TextReader(resource); // 适用于文本数据return textReader.read();}@GetMapping("/json")public List<Document> readJson() {logger.info("start read json file");Resource resource = new DefaultResourceLoader().getResource(Constant.JSONFILEPATH);JsonReader jsonReader = new JsonReader(resource); // 只可以传json格式文件return jsonReader.read();}@GetMapping("/pdf-page")public List<Document> readPdfPage() {logger.info("start read pdf file by page");Resource resource = new DefaultResourceLoader().getResource(Constant.PDFFILEPATH);PagePdfDocumentReader pagePdfDocumentReader = new PagePdfDocumentReader(resource); // 只可以传pdf格式文件return pagePdfDocumentReader.read();}@GetMapping("/pdf-paragraph")public List<Document> readPdfParagraph() {logger.info("start read pdf file by paragraph");Resource resource = new DefaultResourceLoader().getResource(Constant.PDFFILEPATH);ParagraphPdfDocumentReader paragraphPdfDocumentReader = new ParagraphPdfDocumentReader(resource); // 有目录的pdf文件return paragraphPdfDocumentReader.read();}@GetMapping("/markdown")public List<Document> readMarkdown() {logger.info("start read markdown file");MarkdownDocumentReader markdownDocumentReader = new MarkdownDocumentReader(Constant.MARKDOWNFILEPATH); // 只可以传markdown格式文件return markdownDocumentReader.read();}@GetMapping("/html")public List<Document> readHtml() {logger.info("start read html file");Resource resource = new DefaultResourceLoader().getResource(Constant.HTMLFILEPATH);JsoupDocumentReader jsoupDocumentReader = new JsoupDocumentReader(resource); // 只可以传html格式文件return jsoupDocumentReader.read();}@GetMapping("/tika")public List<Document> readTika() {logger.info("start read file with Tika");Resource resource = new DefaultResourceLoader().getResource(Constant.HTMLFILEPATH);TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(resource); // 可以传多种文档格式return tikaDocumentReader.read();}
}
效果

读取文本文件

读取 json 文件

读取 pdf 文件

读取带目录的 pdf 文件

读取 markdown 文件

读取 html 文件

利用 tika 读取任意文档格式

转换文档

TransformerController
package com.spring.ai.tutorial.rag.controller;import com.spring.ai.tutorial.rag.model.Constant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.document.DefaultContentFormatter;
import org.springframework.ai.document.Document;
import org.springframework.ai.model.transformer.KeywordMetadataEnricher;
import org.springframework.ai.model.transformer.SummaryMetadataEnricher;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.transformer.ContentFormatTransformer;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/transformer")
public class TransformerController {private static final Logger logger = LoggerFactory.getLogger(TransformerController.class);private final List<Document> documents;private final ChatModel chatModel;public TransformerController(ChatModel chatModel) {logger.info("start read pdf file by page");Resource resource = new DefaultResourceLoader().getResource(Constant.PDFFILEPATH);PagePdfDocumentReader pagePdfDocumentReader = new PagePdfDocumentReader(resource); // 只可以传pdf格式文件this.documents = pagePdfDocumentReader.read();this.chatModel = chatModel;}@GetMapping("/token-text-splitter")public List<Document> tokenTextSplitter() {logger.info("start token text splitter");TokenTextSplitter tokenTextSplitter = TokenTextSplitter.builder()// 每个文本块的目标token数量.withChunkSize(800)// 每个文本块的最小字符数.withMinChunkSizeChars(350)// 丢弃小于此长度的文本块.withMinChunkLengthToEmbed(5)// 文本中生成的最大块数.withMaxNumChunks(10000)// 是否保留分隔符.withKeepSeparator(true).build();return tokenTextSplitter.split(this.documents);}@GetMapping("/content-format-transformer")public List<Document> contentFormatTransformer() {logger.info("start content format transformer");DefaultContentFormatter defaultContentFormatter = DefaultContentFormatter.defaultConfig();ContentFormatTransformer contentFormatTransformer = new ContentFormatTransformer(defaultContentFormatter);return contentFormatTransformer.apply(this.documents);}@GetMapping("/keyword-metadata-enricher")public List<Document> keywordMetadataEnricher() {logger.info("start keyword metadata enricher");KeywordMetadataEnricher keywordMetadataEnricher = new KeywordMetadataEnricher(this.chatModel, 3);return keywordMetadataEnricher.apply(this.documents);}@GetMapping("/summary-metadata-enricher")public List<Document> summaryMetadataEnricher() {logger.info("start summary metadata enricher");List<SummaryMetadataEnricher.SummaryType> summaryTypes = List.of(SummaryMetadataEnricher.SummaryType.NEXT,SummaryMetadataEnricher.SummaryType.CURRENT,SummaryMetadataEnricher.SummaryType.PREVIOUS);SummaryMetadataEnricher summaryMetadataEnricher = new SummaryMetadataEnricher(this.chatModel, summaryTypes);return summaryMetadataEnricher.apply(this.documents);}
}
效果

TokenTextSplitter 切分

DefaultContentFormatter 格式化

KeywordMetadataEnricher 提取关键字

SummaryMetadataEnricher 提取摘要

写出文档

WriterController
package com.spring.ai.tutorial.rag.controller;import com.spring.ai.tutorial.rag.model.Constant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.writer.FileDocumentWriter;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/writer")
public class WriterController {private static final Logger logger = LoggerFactory.getLogger(WriterController.class);private final List<Document> documents;private final SimpleVectorStore simpleVectorStore;public WriterController(EmbeddingModel embeddingModel) {logger.info("start read pdf file by page");Resource resource = new DefaultResourceLoader().getResource(Constant.PDFFILEPATH);PagePdfDocumentReader pagePdfDocumentReader = new PagePdfDocumentReader(resource); // 只可以传pdf格式文件this.documents = pagePdfDocumentReader.read();this.simpleVectorStore = SimpleVectorStore.builder(embeddingModel).build();}@GetMapping("/file")public void writeFile() {logger.info("Writing file...");String fileName = "output.txt";FileDocumentWriter fileDocumentWriter = new FileDocumentWriter(fileName, true);fileDocumentWriter.accept(this.documents);}@GetMapping("/vector")public void writeVector() {logger.info("Writing vector...");simpleVectorStore.add(documents);}@GetMapping("/search")public List<Document> search() {logger.info("start search data");return simpleVectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(2).build());}
}
效果

Document 写出文本文件

写入 vector

从 vector 中查找


http://www.hkcw.cn/article/APKjIAMeYK.shtml

相关文章

AI大模型赋能,aPaaS+iPaaS构建新一代数智化应用|爱分析报告

01 aPaaS和iPaaS成为企业用户关注重点 PaaS市场定义 根据Gartner的定义&#xff0c;PaaS&#xff08;Platform as a Service&#xff09;平台是应用基础架构&#xff08;中间件&#xff09;服务的广泛集合&#xff0c; 包含应用平台、集成、业务流程管理、数据服务和AI应用等…

性能优化 - 工具篇:基准测试 JMH

文章目录 Pre引言1. JMH 简介2. JMH 执行流程详解3. 关键注解详解3.1 Warmup3.2 Measurement3.3 BenchmarkMode3.4 OutputTimeUnit3.5 Fork3.6 Threads3.7 Group 与 GroupThreads3.8 State3.9 Setup 与 TearDown3.10 Param3.11 CompilerControl 4. 示例代码与分析4.1 关键点解读…

郑钦文淋雨一直走 从黑洞到彩虹的心情旅程

6月1日,郑钦文在法网女单1/8决赛中获胜。赛后她用张韶涵的《淋雨一直走》来形容自己的心情:“有时掉进黑洞,有时爬上彩虹。”谈及决胜盘前的调整和获胜关键,郑钦文表示第二盘第一局曾有40-0的领先优势,但未能把握住机会,反而让对手进入了状态。在丢掉第二盘后,她去卫生间…

印度拉拢蒙古能抄中国稀土的作业吗 绕不开的运输难题

印度在与巴基斯坦的冲突中失利后不久,便与蒙古国展开了联合军演。蒙古国空军成立100周年阅兵式上,仅有的两架米格-29战机飞过乌兰巴托上空。五天后,印度陆军特遣队跨越5000公里抵达这片草原,启动了一场被网友戏称为“蒙古出海军,印度出空军”的联合军演。5月31日,“游牧大…

斯瓦泰克2比1莱巴金娜 逆转晋级法网八强

6月1日,在法国网球公开赛女单第四轮比赛中,四届赛会冠军、5号种子斯瓦泰克以2-1(1-6、6-3、7-5)逆转战胜12号种子莱巴金娜,本赛季三次击败对手,并取得法网25连胜。斯瓦泰克在这场比赛中获得了430分和44万欧元的奖金。她连续六年闯入法网八强,这是她第11次跻身大满贯八强…

南京五台山体育场再现人浪 雨夜观赛创纪录

6月1日晚,江苏省城市足球联赛的一场焦点战在南京五台山体育场举行,对阵双方是南京队与无锡队。最终,南京队以1:0小胜无锡队,取得赛事两连胜,并将城市联赛排名提升至第三。比赛日下午5点不到,五台山体育场已经开始有序检票,球迷方阵先行进场布置助威横幅。由于比赛日一直…

Linux服务器 TensorFlow找不到GPU

记录一下这次长达两天的心累Debug&#xff1a;Could not find cuda drivers on your machine, GPU will not be used.&#xff0c;先说一下我的项目情况 使用VSCode ssh连接实验室服务器&#xff0c;无root权限&#xff0c;不能使用sudo指令&#xff0c;Linux系统&#xff0c;…

xdma 驱动测试与分析

目录 1. 简介 2. 基本测试 2.1 H2C 测试 2.1.1 MRRS 2.1.2 抓取 H2C 数据 2.1.3 数据位宽 2.1.4 数据对比 2.1.5 写入地址测试 2.1.6 带宽测试 2.1.6.1 x86_Gen2x4 2.1.6.2 x86_Gen3x4 2.1.6.3 x86_Gen3x8 2.1.6.4 ZCU102_Gen2x1 2.1.6.5 AGX_Gen3x4 2.1.7 带宽…

基因编辑首次临床救人,罕见病婴儿绝处逢生 医学奇迹搬进现实

得了绝症竟然能靠“改写基因”治好?2025年5月,患有罕见病CPS1的婴儿KJ通过基因编辑重获新生,这场医学奇迹直接把科幻片情节搬进现实。当科学家用“分子剪刀”剪断遗传病的枷锁,我们正站在改写生命规则的转折点——未来癌症、衰老甚至农作物都能被重新编码,但这把双刃剑也藏…

郎永淳儿子哥大双硕士家里蹲:360万换月薪8000,学历还值钱吗? 精英教育的困境

北京的五月,蝉鸣未起,但社交媒体上关于“前央视名嘴郎永淳儿子找不到工作”的话题已经沸沸扬扬。25岁的郎俣,顶着哥伦比亚大学经济统计学和哲学双硕士的光环回国一年,却以“家里蹲”的状态成了网友口中的“高配版躺平青年”。郎俣的故事像一部现实版《变形记》。14岁赴美留…

26岁女孩骨质疏松上热搜!竟然是因为这个习惯……很多人都有,快自查 过度防晒导致维生素D缺乏

随着天气逐渐变热,日晒也变得越来越强烈,很多人开始重视防晒。然而,最近有一则新闻引起了广泛关注:一名26岁的女孩因为长期全面防晒,竟然被确诊为骨质疏松。这让人不禁疑惑,防晒和骨质疏松之间到底有什么联系?过度防晒会导致人体缺乏维生素D。维生素D是一种脂溶性维生素…

“芒种不过午,三伏棉衣捂”,今年芒种在何时? 芒种三大特点解析

2025年6月5日17点57分将迎来芒种节气。这个节气在农事上具有重要意义,有“芒种芒种,连收带种”的说法,意味着北方忙着收麦子,南方忙着种稻子,全国进入“夏收、夏种、夏管”的忙碌模式。今年的芒种被认为不一般,有三个特点。首先是芒种在端午后。2025年的端午节是5月31日,…

SSRF 接收器

接收请求 IP.php <?php // 定义日志文件路径 $logFile hackip.txt;// 处理删除请求 if (isset($_POST[delete])) {$ipToDelete $_POST[ip];$lines file($logFile, FILE_IGNORE_NEW_LINES);$newLines array();foreach ($lines as $line) {$parts explode( | , $line);…

UCS(Universal Control System)能成为下一代通用控制系统吗?

UCS&#xff08;Universal Control System&#xff09;是下一代革命性通用控制系统。它以 “云 - 网 - 端” 极简架构&#xff0c;以及软件定义、全数字化、云原生等特性&#xff0c;号称颠覆了应用近 50 年的传统 DCS 技术架构。具体介绍如下&#xff1a; 架构组成&#xff1a…

卢伟冰称竞争从来不是小米未来的关键 挑战在于自身

近日,小米集团ceo卢伟冰发博回答投资者提问:小米未来困难或挑战是什么。他表示:“竞争从来不是,未来也不会是关键。我一直深信: 不可胜在己,可胜在敌”责任编辑:zx0176

南通成苏超榜一大哥 自称“南哥” 三连胜领跑积分榜

江苏省城市足球联赛第三轮比赛中,南通队以4:0客场战胜泰州队,赢得了“苏中德比”,并取得三连胜。目前,南通队在积分榜上排名第一,继续领跑“苏超”。比赛上半场双方互有攻守,但比分一直保持在0:0。下半场开始后,南通队在短短25分钟内连进3球,最终在比赛结束前再入一球,…

券商6月金股出炉 亚钾国际青岛啤酒获最多推荐

根据不完全统计,在各家券商的“金股”名单中,亚钾国际和青岛啤酒获得最多推荐,均被3家券商看好。东鹏饮料、万马科技、巨化股份、沪电股份等7只股票也获得了2家券商的推荐。5月A股市场整体先扬后抑,上证指数累计上涨2.9%,深证成指上涨1.42%,创业板指上涨2.32%。进入6月,…

差分隐私的应用领域

差分隐私作为隐私保护的“黄金标准”&#xff0c;已从理论研究渗透至多个行业的数据全生命周期管理。其核心价值在于通过数学严格性平衡数据开放与隐私保护。 一、政府与公共数据发布 1.场景扩展 宏观统计&#xff1a;人口普查、经济普查、自然灾害损失评估&#xff1b; 公共…

【PowerShell专栏】PowerShell对象的Method方法

PowerShell中的方法Method与属性中的Property一样&#xff0c;默认的Method方法也是从基类继承下来的方法。我们也不能为PowerShell对象创建Method方法&#xff0c;图为Process对象Method方法。 在对象添加任何的Method方法都会出错&#xff0c;提示不能为PowerShell对象添加Me…

未来数日将成加沙停火谈判关键窗口期 谈判僵局待破

加沙地带人道主义危机不断加剧。由于在停火方案上存在分歧,巴勒斯坦伊斯兰抵抗运动(哈马斯)和以色列之间的停火谈判再度陷入僵局。哈马斯官员指责美方提案偏袒以色列。5月31日晚间,哈马斯高级成员巴塞姆纳伊姆再次表达了对美方提出的加沙停火提案的强烈不满,指出美方立场“…