Spring AI 代码分析(七)--文档的处理

news/2025/11/24 23:43:06/文章来源:https://www.cnblogs.com/wasp520/p/19266226

文档处理能力分析

请关注微信公众号:阿呆-bot

1. 工程结构概览

Spring AI 提供了完整的文档处理能力,包括文档读取、文本分块和预处理。这些能力是 RAG 应用的基础。

document-readers/                 # 文档读取器
├── pdf-reader/                   # PDF 读取器
│   ├── PagePdfDocumentReader.java      # 按页读取
│   └── ParagraphPdfDocumentReader.java  # 按段落读取
├── markdown-reader/              # Markdown 读取器
│   └── MarkdownDocumentReader.java
├── tika-reader/                  # 通用文档读取器(Tika)
│   └── TikaDocumentReader.java
└── jsoup-reader/                 # HTML 读取器└── JsoupDocumentReader.javaspring-ai-commons/                # 核心处理能力
├── document/
│   └── Document.java        # 文档对象
└── transformer/└── splitter/                # 文本分块├── TextSplitter.java├── TokenTextSplitter.java└── CharacterTextSplitter.java

2. 技术体系与模块关系

文档处理流程:读取 → 分块 → 嵌入 → 存储

image.png

3. 关键场景示例代码

3.1 PDF 文档读取

PDF 读取支持按页和按段落两种方式:

// 按页读取
Resource pdfResource = new ClassPathResource("document.pdf");
PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(pdfResource);
List<Document> documents = pdfReader.get();// 按段落读取(更智能)
ParagraphPdfDocumentReader paragraphReader = new ParagraphPdfDocumentReader(pdfResource, config);
List<Document> documents = paragraphReader.get();

3.2 Markdown 文档读取

Markdown 读取器可以按标题、段落或水平线分组:

MarkdownDocumentReader markdownReader = new MarkdownDocumentReader("classpath:docs/*.md", config);
List<Document> documents = markdownReader.get();

3.3 Tika 通用读取

Tika 可以读取多种格式(PDF、Word、PPT 等):

TikaDocumentReader tikaReader = new TikaDocumentReader("classpath:document.docx");
List<Document> documents = tikaReader.get();

3.4 文档分块

将长文档分割成适合嵌入的小块:

// Token 分块(推荐)
TokenTextSplitter splitter = TokenTextSplitter.builder().chunkSize(800)           // 目标 token 数.minChunkSizeChars(350)  // 最小字符数.build();List<Document> chunks = splitter.split(documents);// 字符分块
CharacterTextSplitter charSplitter = new CharacterTextSplitter(1000, 200);
List<Document> chunks = charSplitter.split(documents);

3.5 完整流程

文档处理的完整流程:

// 1. 读取文档
TikaDocumentReader reader = new TikaDocumentReader("document.pdf");
List<Document> documents = reader.get();// 2. 分块
TokenTextSplitter splitter = new TokenTextSplitter();
List<Document> chunks = splitter.split(documents);// 3. 嵌入并存储
vectorStore.add(chunks);

4. 核心实现图

4.1 文档处理流程

image.png

5. 入口类与关键类关系

image.png

6. 关键实现逻辑分析

6.1 PDF 读取实现

PDF 读取有两种方式:

方式一:按页读取

public class PagePdfDocumentReader implements DocumentReader {@Overridepublic List<Document> get() {List<Document> documents = new ArrayList<>();int pageCount = document.getNumberOfPages();for (int i = 0; i < pageCount; i++) {String pageText = extractTextFromPage(i);Document doc = new Document(pageText);doc.getMetadata().put("page", i);documents.add(doc);}return documents;}
}

方式二:按段落读取(更智能)

public class ParagraphPdfDocumentReader implements DocumentReader {@Overridepublic List<Document> get() {// 1. 提取段落List<Paragraph> paragraphs = paragraphManager.flatten();// 2. 将相邻段落合并为文档List<Document> documents = new ArrayList<>();for (int i = 0; i < paragraphs.size(); i++) {Paragraph from = paragraphs.get(i);Paragraph to = (i + 1 < paragraphs.size()) ? paragraphs.get(i + 1) : from;String text = getTextBetweenParagraphs(from, to);Document doc = new Document(text);addMetadata(from, to, doc);documents.add(doc);}return documents;}
}

按段落读取的优势:

  • 保持语义完整性:段落是自然的语义单元
  • 更好的检索效果:段落级别的文档更适合向量搜索
  • 保留布局信息:可以保留 PDF 的布局结构

6.2 Markdown 读取实现

Markdown 读取器使用 CommonMark 解析器:

public class MarkdownDocumentReader implements DocumentReader {@Overridepublic List<Document> get() {List<Document> documents = new ArrayList<>();for (Resource resource : markdownResources) {// 1. 解析 MarkdownNode document = parser.parse(loadContent(resource));// 2. 访问文档节点DocumentVisitor visitor = new DocumentVisitor(config);document.accept(visitor);// 3. 收集文档documents.addAll(visitor.getDocuments());}return documents;}
}

Markdown 读取器可以按以下方式分组:

  • 按标题分组:每个标题及其内容成为一个文档
  • 按段落分组:每个段落成为一个文档
  • 按水平线分组:水平线分隔的内容成为独立文档

6.3 Tika 通用读取实现

Tika 使用自动检测解析器:

public class TikaDocumentReader implements DocumentReader {@Overridepublic List<Document> get() {try (InputStream stream = resource.getInputStream()) {// 1. 自动检测文档类型并解析parser.parse(stream, handler, metadata, context);// 2. 提取文本String text = handler.toString();// 3. 格式化文本text = textFormatter.format(text);// 4. 创建文档Document doc = new Document(text);doc.getMetadata().put(METADATA_SOURCE, resourceName());return List.of(doc);}}
}

Tika 的优势:

  • 支持多种格式:PDF、Word、PPT、Excel、HTML 等
  • 自动检测:无需指定文档类型
  • 提取元数据:自动提取文档的元数据

6.4 文本分块实现

文本分块是 RAG 应用的关键步骤:

public abstract class TextSplitter implements DocumentTransformer {@Overridepublic List<Document> apply(List<Document> documents) {List<Document> chunks = new ArrayList<>();for (Document doc : documents) {// 1. 分割文本List<String> textChunks = splitText(doc.getText());// 2. 为每个分块创建文档for (int i = 0; i < textChunks.size(); i++) {Map<String, Object> metadata = new HashMap<>(doc.getMetadata());// 3. 添加分块元数据metadata.put("parent_document_id", doc.getId());metadata.put("chunk_index", i);metadata.put("total_chunks", textChunks.size());Document chunk = Document.builder().text(textChunks.get(i)).metadata(metadata).score(doc.getScore()).build();chunks.add(chunk);}}return chunks;}protected abstract List<String> splitText(String text);
}

6.5 Token 分块实现

Token 分块使用编码器计算 token 数:

public class TokenTextSplitter extends TextSplitter {@Overrideprotected List<String> splitText(String text) {// 1. 编码为 tokensList<Integer> tokens = encoding.encode(text).boxed();List<String> chunks = new ArrayList<>();while (!tokens.isEmpty() && chunks.size() < maxNumChunks) {// 2. 取目标大小的 tokensList<Integer> chunk = tokens.subList(0, Math.min(chunkSize, tokens.size()));String chunkText = decodeTokens(chunk);// 3. 在标点符号处截断(保持语义)int lastPunctuation = findLastPunctuation(chunkText);if (lastPunctuation > minChunkSizeChars) {chunkText = chunkText.substring(0, lastPunctuation + 1);}// 4. 过滤太短的分块if (chunkText.length() > minChunkLengthToEmbed) {chunks.add(chunkText.trim());}// 5. 移除已处理的 tokenstokens = tokens.subList(getEncodedTokens(chunkText).size(), tokens.size());}return chunks;}
}

Token 分块的优势:

  • 精确控制大小:按 token 数分割,而不是字符数
  • 保持语义:在标点符号处截断
  • 适合嵌入模型:token 数是嵌入模型的输入单位

7. 文档分块策略

7.1 Token 分块(推荐)

适合大多数场景,特别是使用 OpenAI 等基于 token 的模型:

TokenTextSplitter splitter = TokenTextSplitter.builder().chunkSize(800)              // 目标 token 数.minChunkSizeChars(350)     // 最小字符数(避免过小).minChunkLengthToEmbed(5)    // 最小嵌入长度.maxNumChunks(10000)        // 最大分块数.keepSeparator(true)        // 保留分隔符.build();

7.2 字符分块

适合固定大小的分块需求:

CharacterTextSplitter splitter = new CharacterTextSplitter(1000,  // chunkSize200    // chunkOverlap(重叠部分,保持上下文)
);

7.3 自定义分块

可以实现自己的分块策略:

public class CustomTextSplitter extends TextSplitter {@Overrideprotected List<String> splitText(String text) {// 自定义分块逻辑// 例如:按句子、按段落、按章节等return customSplit(text);}
}

8. 外部依赖

不同读取器的依赖:

8.1 PDF Reader

  • PDFBox:Apache PDFBox,PDF 解析库
  • 无其他依赖

8.2 Markdown Reader

  • CommonMark:Markdown 解析库
  • 无其他依赖

8.3 Tika Reader

  • Apache Tika:通用文档解析库
  • 支持 100+ 种格式

8.4 Text Splitter

  • tiktoken:Token 编码库(用于 TokenTextSplitter)
  • 无其他依赖(CharacterTextSplitter)

9. 工程总结

Spring AI 的文档处理能力设计有几个亮点:

统一的 Document 抽象。所有读取器都返回 Document 对象,这让后续处理(分块、嵌入、存储)变得统一。不管是从 PDF 还是 Word 读取,出来的都是 Document,处理起来很方便。

灵活的读取策略。不同格式有不同的读取策略(按页、按段落、按标题),可以根据需求选择最合适的方式。PDF 可以按页读,也可以按段落读,看你的需求。

智能的分块机制。Token 分块不仅考虑大小,还考虑语义完整性(在标点符号处截断),这提高了检索效果。不会在句子中间截断,保持语义完整。

元数据保留。分块时会保留原始文档的元数据,并添加分块相关的元数据(parent_document_id、chunk_index 等),这有助于追踪和调试。想知道某个分块来自哪个文档?看元数据就行。

可扩展性。所有组件都通过接口定义,可以轻松实现自定义的读取器和分块器。想支持新的文档格式?实现 DocumentReader 接口就行。

总的来说,Spring AI 的文档处理能力既全面又灵活。它支持多种文档格式,提供了智能的分块策略,同时保持了高度的可扩展性。这种设计让开发者可以轻松构建基于文档的 RAG 应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/975415.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java调用第三方接口的方法

.方式一:通过JDK网络类Java.net.HttpURLConnection (1)HttpClientUtil工具类 public class HttpClientUtil { //以post方式调用对方接口方法 public static String doPost(String pathUrl, String data){ …

2025留学代写危机应对指南:5家靠谱机构助你重返校园

2025留学代写危机应对指南:5家靠谱机构助你重返校园留学期间因代写被抓,不仅可能面临挂科、处分,更会影响签证合法性与学业连续性,严重时甚至被遣返。这类危机的处理需精准把握海外院校学术诚信政策、申诉流程及沟…

2025美国大学停学应对全攻略:5大靠谱机构助你重返学术轨道

2025美国大学停学应对全攻略:5大靠谱机构助你重返学术轨道美国高校以严格的学术标准和管理体系著称,不少中国留学生因学术表现不佳、出勤问题或文化适应障碍遭遇停学危机。停学不仅打乱留学计划,更可能影响签证状态…

2025美国紧急转学机构推荐深度解析:靠谱机构认准这些核心优势,危机中重启留学之路​

2025美国紧急转学机构推荐深度解析:靠谱机构认准这些核心优势,危机中重启留学之路​美国留学期间,因学业适应、专业匹配、院校环境等问题需要紧急转学的情况屡见不鲜。紧急转学时间紧、院校空缺信息多变,且需精准匹…

第35天(中等题 数据结构)

打卡第三十五天 2道中等题题目:思路:先统计每个数字的出现次数,遍历数组,对于每个数字 i,检查 target - i 是否存在且还有剩余次数,找到一对就减少两个数字的计数 代码: class Solution { public:vector<vec…

2025美国留学求职机构实力解析:你的职场Offer引路人在哪?

2025美国留学求职机构实力解析:你的职场Offer引路人在哪?美国作为全球教育与就业的核心高地,其留学申请的复杂性与职场竞争的激烈程度双重叠加,让专业的留学求职机构成为学子们的重要助力。优质机构不仅能攻克院校…

Universal Fit 3-Button Metal Flip Remote Key (5pcs/lot) – KEYDIY KD NB29-3 for Euro/American Cars

Universal Remote Keys for European & American Vehicles: The KD NB29-3 Solution Problem: The Key Replacement Headache For automotive repair shops and car owners in Europe and America, finding reliab…

2025美国科研中介TOP5解析:从课题对接至成果落地全程护航

2025美国科研中介TOP5解析:从课题对接至成果落地全程护航美国顶尖高校的申请中,优质科研经历已成为核心竞争力,而科研课题匹配、教授资源对接、成果转化等环节的专业性,让美国科研中介成为学子的重要助力。本文综合…

根据缺少的文件查找deb包

有时候运行程序会出现有的文件找不到但是你又不知道在哪个包里面的问题。 下载这个可能需要fq。 sudo apt install apt-file sudo apt-file update # 寻找libncurses.so.5的deb包 sudo apt-file find libncurses.so.5 …

第一个Vue2程序

在文件开头引入 Vue<script src = "../js/vue.js"></script>创建 Vue 托管的容器<div id="app"> </div>创建 Vue 并挂载到 id 为 app 的 html 元素上new Vue({template :…

2025美国留学生求职中介TOP5:厚仁教育领衔,精准匹配名企资源

2025美国留学生求职中介TOP5:厚仁教育领衔,精准匹配名企资源美国作为全球职业发展高地,其求职市场竞争激烈、招聘流程独特,签证政策与身份规划更是留学生求职的关键壁垒。专业的美国求职中介能凭借本土资源、行业经…

2025年11月美国付费实习中介实力榜:谁为你的职场竞争力加码?实战派机构都有这些特质

2025年11月美国付费实习中介实力榜:谁为你的职场竞争力加码?实战派机构都有这些特质美国作为全球职业发展高地,优质实习经历是留学生突破就业壁垒的核心竞争力。但美国企业实习申请流程严苛、人脉资源壁垒高,专业的…

CF1097F Alex and a TV Show

Sol 思路挺曲折的。 以下所有公式均表示模 \(2\) 意义下的答案。 假设 \(s_i\) 表示集合 \(s\) 的 \(i\) 的出现次数对 \(2\) 取模的余数。 如果没有 \(3\) 操作直接 bitset 就可以了。 令 \(V\) 表示值域上限。考虑 \…

Git 最速上手

配置git身份git config --globle user.name "用户名" git config --globle user.email "邮箱"初始化本地 Git 仓库git init将项目文件添加到暂存区git add ." . " 会将当前目录下所有文…

Ubuntu 24.04 安装 libncurses.so.5

用Ubuntu24在编译AOSP时遇到这个问题。 参考:https://askubuntu.com/questions/1531398/how-to-install-libncurses-so-5-for-ubuntu-24-04 解决: # 注意权限,可以先创建,再给写权限 echo "deb http://securi…

Universal 3-Button Flip Remote Key for VW - 5pcs/Lot (VW Compatible, Mechanic Owner Friendly)

KEYDIY KD NB34-3 Universal Flip Remote Key: The Solution to VW Key Woes In the world of automotive repair, few issues cause as much frustration as unreliable, overpriced key replacements—especially fo…

48

所学时间:10小时 博客量:1 代码量:几百 所学知识:人机交互,ai故事生成平台前端

生成对抗网络训练优化技术解析

本文深入探讨生成对抗网络的三大训练优化技术:Wasserstein距离解决模式崩溃问题、博弈论平衡控制提升训练稳定性、渐进式增长架构实现高分辨率图像生成。通过理论分析和代码实例展示如何改进GAN训练过程。GANs in com…

基于相控微波光子滤波器的旋转诱导相位差解调

级联MRR-ODSB-MPF 📚 使用须知本博客内容仅供学习参考 欢迎交流讨论文献阅读:Sagnac-ODSB-MPF核心原理首次将相位控制MPF用于Sagnac解调。将旋转引起的相位差 Δφ,通过色散元件,转化为微波光子滤波器的频率响应偏…