买机票便宜的网站建设,网站 繁体 js,手机旅游网站建设,网站建设 北京昌平1. 工程结构概览Spring AI 提供了完整的文档处理能力#xff0c;包括文档读取、文本分块和预处理。这些能力是 RAG 应用的基础。document-readers/ # 文档读取器├── pdf-reader/ # PDF 读取器│ ├── PagePdfDocumentReader.java # 按页读取│ └── ParagraphPdfDocume…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.java2. 技术体系与模块关系文档处理流程读取 → 分块 → 嵌入 → 存储image.png3. 关键场景示例代码3.1 PDF 文档读取PDF 读取支持按页和按段落两种方式// 按页读取Resource pdfResource new ClassPathResource(document.pdf);PagePdfDocumentReader pdfReader new PagePdfDocumentReader(pdfResource);ListDocument documents pdfReader.get();// 按段落读取更智能ParagraphPdfDocumentReader paragraphReader new ParagraphPdfDocumentReader(pdfResource, config);ListDocument documents paragraphReader.get();3.2 Markdown 文档读取Markdown 读取器可以按标题、段落或水平线分组MarkdownDocumentReader markdownReader new MarkdownDocumentReader(classpath:docs/*.md, config);ListDocument documents markdownReader.get();3.3 Tika 通用读取Tika 可以读取多种格式PDF、Word、PPT 等TikaDocumentReader tikaReader new TikaDocumentReader(classpath:document.docx);ListDocument documents tikaReader.get();3.4 文档分块将长文档分割成适合嵌入的小块// Token 分块推荐TokenTextSplitter splitter TokenTextSplitter.builder().chunkSize(800) // 目标 token 数.minChunkSizeChars(350) // 最小字符数.build();ListDocument chunks splitter.split(documents);// 字符分块CharacterTextSplitter charSplitter new CharacterTextSplitter(1000, 200);ListDocument chunks charSplitter.split(documents);3.5 完整流程文档处理的完整流程// 1. 读取文档TikaDocumentReader reader new TikaDocumentReader(document.pdf);ListDocument documents reader.get();// 2. 分块TokenTextSplitter splitter new TokenTextSplitter();ListDocument chunks splitter.split(documents);// 3. 嵌入并存储vectorStore.add(chunks);4. 核心实现图4.1 文档处理流程image.png5. 入口类与关键类关系image.png6. 关键实现逻辑分析6.1 PDF 读取实现PDF 读取有两种方式方式一按页读取public class PagePdfDocumentReader implements DocumentReader {Overridepublic ListDocument get() {ListDocument 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 ListDocument get() {// 1. 提取段落ListParagraph paragraphs paragraphManager.flatten();// 2. 将相邻段落合并为文档ListDocument 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 ListDocument get() {ListDocument 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 ListDocument 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 ListDocument apply(ListDocument documents) {ListDocument chunks new ArrayList();for (Document doc : documents) {// 1. 分割文本ListString textChunks splitText(doc.getText());// 2. 为每个分块创建文档for (int i 0; i textChunks.size(); i) {MapString, 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 ListString splitText(String text);}6.5 Token 分块实现Token 分块使用编码器计算 token 数public class TokenTextSplitter extends TextSplitter {Overrideprotected ListString splitText(String text) {// 1. 编码为 tokensListInteger tokens encoding.encode(text).boxed();ListString chunks new ArrayList();while (!tokens.isEmpty() chunks.size() maxNumChunks) {// 2. 取目标大小的 tokensListInteger 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 ListString splitText(String text) {// 自定义分块逻辑// 例如按句子、按段落、按章节等return customSplit(text);}}8. 外部依赖不同读取器的依赖8.1 PDF ReaderPDFBoxApache PDFBoxPDF 解析库无其他依赖8.2 Markdown ReaderCommonMarkMarkdown 解析库无其他依赖8.3 Tika ReaderApache Tika通用文档解析库支持 100 种格式8.4 Text SplittertiktokenToken 编码库用于 TokenTextSplitter无其他依赖CharacterTextSplitter9. 工程总结Spring AI 的文档处理能力设计有几个亮点统一的 Document 抽象。所有读取器都返回 Document 对象这让后续处理分块、嵌入、存储变得统一。不管是从 PDF 还是 Word 读取出来的都是 Document处理起来很方便。灵活的读取策略。不同格式有不同的读取策略按页、按段落、按标题可以根据需求选择最合适的方式。PDF 可以按页读也可以按段落读看你的需求。智能的分块机制。Token 分块不仅考虑大小还考虑语义完整性在标点符号处截断这提高了检索效果。不会在句子中间截断保持语义完整。元数据保留。分块时会保留原始文档的元数据并添加分块相关的元数据parent_document_id、chunk_index 等这有助于追踪和调试。想知道某个分块来自哪个文档看元数据就行。可扩展性。所有组件都通过接口定义可以轻松实现自定义的读取器和分块器。想支持新的文档格式实现 DocumentReader 接口就行。